📄 via82xx_modem.c
字号:
{ struct via82xx_modem *chip = ac97->private_data; unsigned int xval; if(reg == AC97_GPIO_STATUS) { outl(val, VIAREG(chip, GPI_STATUS)); return; } xval = !ac97->num ? VIA_REG_AC97_CODEC_ID_PRIMARY : VIA_REG_AC97_CODEC_ID_SECONDARY; xval <<= VIA_REG_AC97_CODEC_ID_SHIFT; xval |= reg << VIA_REG_AC97_CMD_SHIFT; xval |= val << VIA_REG_AC97_DATA_SHIFT; snd_via82xx_codec_xwrite(chip, xval); snd_via82xx_codec_ready(chip, ac97->num);}static unsigned short snd_via82xx_codec_read(struct snd_ac97 *ac97, unsigned short reg){ struct via82xx_modem *chip = ac97->private_data; unsigned int xval, val = 0xffff; int again = 0; xval = ac97->num << VIA_REG_AC97_CODEC_ID_SHIFT; xval |= ac97->num ? VIA_REG_AC97_SECONDARY_VALID : VIA_REG_AC97_PRIMARY_VALID; xval |= VIA_REG_AC97_READ; xval |= (reg & 0x7f) << VIA_REG_AC97_CMD_SHIFT; while (1) { if (again++ > 3) { snd_printk(KERN_ERR "codec_read: codec %i is not valid [0x%x]\n", ac97->num, snd_via82xx_codec_xread(chip)); return 0xffff; } snd_via82xx_codec_xwrite(chip, xval); udelay (20); if (snd_via82xx_codec_valid(chip, ac97->num) >= 0) { udelay(25); val = snd_via82xx_codec_xread(chip); break; } } return val & 0xffff;}static void snd_via82xx_channel_reset(struct via82xx_modem *chip, struct viadev *viadev){ outb(VIA_REG_CTRL_PAUSE | VIA_REG_CTRL_TERMINATE | VIA_REG_CTRL_RESET, VIADEV_REG(viadev, OFFSET_CONTROL)); inb(VIADEV_REG(viadev, OFFSET_CONTROL)); udelay(50); /* disable interrupts */ outb(0x00, VIADEV_REG(viadev, OFFSET_CONTROL)); /* clear interrupts */ outb(0x03, VIADEV_REG(viadev, OFFSET_STATUS)); outb(0x00, VIADEV_REG(viadev, OFFSET_TYPE)); /* for via686 */ // outl(0, VIADEV_REG(viadev, OFFSET_CURR_PTR)); viadev->lastpos = 0;}/* * Interrupt handler */static irqreturn_t snd_via82xx_interrupt(int irq, void *dev_id){ struct via82xx_modem *chip = dev_id; unsigned int status; unsigned int i; status = inl(VIAREG(chip, SGD_SHADOW)); if (! (status & chip->intr_mask)) { return IRQ_NONE; }// _skip_sgd: /* check status for each stream */ spin_lock(&chip->reg_lock); for (i = 0; i < chip->num_devs; i++) { struct viadev *viadev = &chip->devs[i]; unsigned char c_status = inb(VIADEV_REG(viadev, OFFSET_STATUS)); c_status &= (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG|VIA_REG_STAT_STOPPED); if (! c_status) continue; if (viadev->substream && viadev->running) { spin_unlock(&chip->reg_lock); snd_pcm_period_elapsed(viadev->substream); spin_lock(&chip->reg_lock); } outb(c_status, VIADEV_REG(viadev, OFFSET_STATUS)); /* ack */ } spin_unlock(&chip->reg_lock); return IRQ_HANDLED;}/* * PCM callbacks *//* * trigger callback */static int snd_via82xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd){ struct via82xx_modem *chip = snd_pcm_substream_chip(substream); struct viadev *viadev = substream->runtime->private_data; unsigned char val = 0; switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_SUSPEND: val |= VIA_REG_CTRL_START; viadev->running = 1; break; case SNDRV_PCM_TRIGGER_STOP: val = VIA_REG_CTRL_TERMINATE; viadev->running = 0; break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: val |= VIA_REG_CTRL_PAUSE; viadev->running = 0; break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: viadev->running = 1; break; default: return -EINVAL; } outb(val, VIADEV_REG(viadev, OFFSET_CONTROL)); if (cmd == SNDRV_PCM_TRIGGER_STOP) snd_via82xx_channel_reset(chip, viadev); return 0;}/* * pointer callbacks *//* * calculate the linear position at the given sg-buffer index and the rest count */#define check_invalid_pos(viadev,pos) \ ((pos) < viadev->lastpos && ((pos) >= viadev->bufsize2 ||\ viadev->lastpos < viadev->bufsize2))static inline unsigned int calc_linear_pos(struct viadev *viadev, unsigned int idx, unsigned int count){ unsigned int size, res; size = viadev->idx_table[idx].size; res = viadev->idx_table[idx].offset + size - count; /* check the validity of the calculated position */ if (size < count) { snd_printd(KERN_ERR "invalid via82xx_cur_ptr (size = %d, count = %d)\n", (int)size, (int)count); res = viadev->lastpos; } else if (check_invalid_pos(viadev, res)) {#ifdef POINTER_DEBUG printk(KERN_DEBUG "fail: idx = %i/%i, lastpos = 0x%x, " "bufsize2 = 0x%x, offsize = 0x%x, size = 0x%x, " "count = 0x%x\n", idx, viadev->tbl_entries, viadev->lastpos, viadev->bufsize2, viadev->idx_table[idx].offset, viadev->idx_table[idx].size, count);#endif if (count && size < count) { snd_printd(KERN_ERR "invalid via82xx_cur_ptr, " "using last valid pointer\n"); res = viadev->lastpos; } else { if (! count) /* bogus count 0 on the DMA boundary? */ res = viadev->idx_table[idx].offset; else /* count register returns full size * when end of buffer is reached */ res = viadev->idx_table[idx].offset + size; if (check_invalid_pos(viadev, res)) { snd_printd(KERN_ERR "invalid via82xx_cur_ptr (2), " "using last valid pointer\n"); res = viadev->lastpos; } } } viadev->lastpos = res; /* remember the last position */ if (res >= viadev->bufsize) res -= viadev->bufsize; return res;}/* * get the current pointer on via686 */static snd_pcm_uframes_t snd_via686_pcm_pointer(struct snd_pcm_substream *substream){ struct via82xx_modem *chip = snd_pcm_substream_chip(substream); struct viadev *viadev = substream->runtime->private_data; unsigned int idx, ptr, count, res; snd_assert(viadev->tbl_entries, return 0); if (!(inb(VIADEV_REG(viadev, OFFSET_STATUS)) & VIA_REG_STAT_ACTIVE)) return 0; spin_lock(&chip->reg_lock); count = inl(VIADEV_REG(viadev, OFFSET_CURR_COUNT)) & 0xffffff; /* The via686a does not have the current index register, * so we need to calculate the index from CURR_PTR. */ ptr = inl(VIADEV_REG(viadev, OFFSET_CURR_PTR)); if (ptr <= (unsigned int)viadev->table.addr) idx = 0; else /* CURR_PTR holds the address + 8 */ idx = ((ptr - (unsigned int)viadev->table.addr) / 8 - 1) % viadev->tbl_entries; res = calc_linear_pos(viadev, idx, count); spin_unlock(&chip->reg_lock); return bytes_to_frames(substream->runtime, res);}/* * hw_params callback: * allocate the buffer and build up the buffer description table */static int snd_via82xx_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params){ struct via82xx_modem *chip = snd_pcm_substream_chip(substream); struct viadev *viadev = substream->runtime->private_data; int err; err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); if (err < 0) return err; err = build_via_table(viadev, substream, chip->pci, params_periods(hw_params), params_period_bytes(hw_params)); if (err < 0) return err; snd_ac97_write(chip->ac97, AC97_LINE1_RATE, params_rate(hw_params)); snd_ac97_write(chip->ac97, AC97_LINE1_LEVEL, 0); return 0;}/* * hw_free callback: * clean up the buffer description table and release the buffer */static int snd_via82xx_hw_free(struct snd_pcm_substream *substream){ struct via82xx_modem *chip = snd_pcm_substream_chip(substream); struct viadev *viadev = substream->runtime->private_data; clean_via_table(viadev, substream, chip->pci); snd_pcm_lib_free_pages(substream); return 0;}/* * set up the table pointer */static void snd_via82xx_set_table_ptr(struct via82xx_modem *chip, struct viadev *viadev){ snd_via82xx_codec_ready(chip, chip->ac97_secondary); outl((u32)viadev->table.addr, VIADEV_REG(viadev, OFFSET_TABLE_PTR)); udelay(20); snd_via82xx_codec_ready(chip, chip->ac97_secondary);}/* * prepare callback for playback and capture */static int snd_via82xx_pcm_prepare(struct snd_pcm_substream *substream){ struct via82xx_modem *chip = snd_pcm_substream_chip(substream); struct viadev *viadev = substream->runtime->private_data; snd_via82xx_channel_reset(chip, viadev); /* this must be set after channel_reset */ snd_via82xx_set_table_ptr(chip, viadev); outb(VIA_REG_TYPE_AUTOSTART|VIA_REG_TYPE_INT_EOL|VIA_REG_TYPE_INT_FLAG, VIADEV_REG(viadev, OFFSET_TYPE)); return 0;}/* * pcm hardware definition, identical for both playback and capture */static struct snd_pcm_hardware snd_via82xx_hw ={ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID | /* SNDRV_PCM_INFO_RESUME | */ SNDRV_PCM_INFO_PAUSE), .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_KNOT, .rate_min = 8000, .rate_max = 16000, .channels_min = 1, .channels_max = 1, .buffer_bytes_max = 128 * 1024, .period_bytes_min = 32, .period_bytes_max = 128 * 1024, .periods_min = 2, .periods_max = VIA_TABLE_SIZE / 2, .fifo_size = 0,};/* * open callback skeleton */static int snd_via82xx_modem_pcm_open(struct via82xx_modem *chip, struct viadev *viadev, struct snd_pcm_substream *substream){ struct snd_pcm_runtime *runtime = substream->runtime; int err; static unsigned int rates[] = { 8000, 9600, 12000, 16000 }; static struct snd_pcm_hw_constraint_list hw_constraints_rates = { .count = ARRAY_SIZE(rates), .list = rates, .mask = 0, }; runtime->hw = snd_via82xx_hw; if ((err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates)) < 0) return err; /* we may remove following constaint when we modify table entries in interrupt */ if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) return err; runtime->private_data = viadev; viadev->substream = substream; return 0;}/* * open callback for playback */static int snd_via82xx_playback_open(struct snd_pcm_substream *substream){ struct via82xx_modem *chip = snd_pcm_substream_chip(substream); struct viadev *viadev = &chip->devs[chip->playback_devno + substream->number]; return snd_via82xx_modem_pcm_open(chip, viadev, substream);}/* * open callback for capture */static int snd_via82xx_capture_open(struct snd_pcm_substream *substream){ struct via82xx_modem *chip = snd_pcm_substream_chip(substream); struct viadev *viadev = &chip->devs[chip->capture_devno + substream->pcm->device]; return snd_via82xx_modem_pcm_open(chip, viadev, substream);}/* * close callback */static int snd_via82xx_pcm_close(struct snd_pcm_substream *substream){ struct viadev *viadev = substream->runtime->private_data; viadev->substream = NULL; return 0;}/* via686 playback callbacks */static struct snd_pcm_ops snd_via686_playback_ops = { .open = snd_via82xx_playback_open, .close = snd_via82xx_pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_via82xx_hw_params, .hw_free = snd_via82xx_hw_free, .prepare = snd_via82xx_pcm_prepare, .trigger = snd_via82xx_pcm_trigger, .pointer = snd_via686_pcm_pointer, .page = snd_pcm_sgbuf_ops_page,};/* via686 capture callbacks */static struct snd_pcm_ops snd_via686_capture_ops = { .open = snd_via82xx_capture_open, .close = snd_via82xx_pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_via82xx_hw_params, .hw_free = snd_via82xx_hw_free, .prepare = snd_via82xx_pcm_prepare, .trigger = snd_via82xx_pcm_trigger, .pointer = snd_via686_pcm_pointer, .page = snd_pcm_sgbuf_ops_page,};static void init_viadev(struct via82xx_modem *chip, int idx, unsigned int reg_offset, int direction){ chip->devs[idx].reg_offset = reg_offset; chip->devs[idx].direction = direction; chip->devs[idx].port = chip->port + reg_offset;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -