📄 via82cxxx_audio.c
字号:
/* * clear any existing stops / flags (sanity check mainly) */ via_chan_status_clear (card->baseaddr + VIA_BASE0_PCM_OUT_CHAN); via_chan_status_clear (card->baseaddr + VIA_BASE0_PCM_IN_CHAN); via_chan_status_clear (card->baseaddr + VIA_BASE0_FM_OUT_CHAN); /* * clear any enabled interrupt bits */ tmp = inb (card->baseaddr + VIA_BASE0_PCM_OUT_CHAN_TYPE); new_tmp = tmp & ~(VIA_IRQ_ON_FLAG|VIA_IRQ_ON_EOL|VIA_RESTART_SGD_ON_EOL); if (tmp != new_tmp) outb (0, card->baseaddr + VIA_BASE0_PCM_OUT_CHAN_TYPE); tmp = inb (card->baseaddr + VIA_BASE0_PCM_IN_CHAN_TYPE); new_tmp = tmp & ~(VIA_IRQ_ON_FLAG|VIA_IRQ_ON_EOL|VIA_RESTART_SGD_ON_EOL); if (tmp != new_tmp) outb (0, card->baseaddr + VIA_BASE0_PCM_IN_CHAN_TYPE); tmp = inb (card->baseaddr + VIA_BASE0_FM_OUT_CHAN_TYPE); new_tmp = tmp & ~(VIA_IRQ_ON_FLAG|VIA_IRQ_ON_EOL|VIA_RESTART_SGD_ON_EOL); if (tmp != new_tmp) outb (0, card->baseaddr + VIA_BASE0_FM_OUT_CHAN_TYPE); udelay(10); /* * clear any existing flags */ via_chan_status_clear (card->baseaddr + VIA_BASE0_PCM_OUT_CHAN); via_chan_status_clear (card->baseaddr + VIA_BASE0_PCM_IN_CHAN); via_chan_status_clear (card->baseaddr + VIA_BASE0_FM_OUT_CHAN); DPRINTK ("EXIT\n");}/** * via_set_rate - Set PCM rate for given channel * @ac97: Pointer to generic codec info struct * @chan: Private info for specified channel * @rate: Desired PCM sample rate, in Khz * * Sets the PCM sample rate for a channel. * * Values for @rate are clamped to a range of 4000 Khz through 48000 Khz, * due to hardware constraints. */static int via_set_rate (struct ac97_codec *ac97, struct via_channel *chan, unsigned rate){ struct via_info *card = ac97->private_data; int rate_reg; DPRINTK ("ENTER, rate = %d\n", rate); if (chan->rate == rate) goto out; if (card->locked_rate) { chan->rate = 48000; goto out; } if (rate > 48000) rate = 48000; if (rate < 4000) rate = 4000; rate_reg = chan->is_record ? AC97_PCM_LR_ADC_RATE : AC97_PCM_FRONT_DAC_RATE; via_ac97_write_reg (ac97, AC97_POWER_CONTROL, (via_ac97_read_reg (ac97, AC97_POWER_CONTROL) & ~0x0200) | 0x0200); via_ac97_write_reg (ac97, rate_reg, rate); via_ac97_write_reg (ac97, AC97_POWER_CONTROL, via_ac97_read_reg (ac97, AC97_POWER_CONTROL) & ~0x0200); udelay (10); /* the hardware might return a value different than what we * passed to it, so read the rate value back from hardware * to see what we came up with */ chan->rate = via_ac97_read_reg (ac97, rate_reg); if (chan->rate == 0) { card->locked_rate = 1; chan->rate = 48000; printk (KERN_WARNING PFX "Codec rate locked at 48Khz\n"); }out: DPRINTK ("EXIT, returning rate %d Hz\n", chan->rate); return chan->rate;}/**************************************************************** * * Channel-specific operations * * *//** * via_chan_init_defaults - Initialize a struct via_channel * @card: Private audio chip info * @chan: Channel to be initialized * * Zero @chan, and then set all static defaults for the structure. */static void via_chan_init_defaults (struct via_info *card, struct via_channel *chan){ memset (chan, 0, sizeof (*chan)); if (chan == &card->ch_out) { chan->name = "PCM-OUT"; chan->iobase = card->baseaddr + VIA_BASE0_PCM_OUT_CHAN; } else if (chan == &card->ch_in) { chan->name = "PCM-IN"; chan->iobase = card->baseaddr + VIA_BASE0_PCM_IN_CHAN; chan->is_record = 1; } else if (chan == &card->ch_fm) { chan->name = "PCM-OUT-FM"; chan->iobase = card->baseaddr + VIA_BASE0_FM_OUT_CHAN; } else { BUG(); } init_waitqueue_head (&chan->wait); chan->pcm_fmt = VIA_PCM_FMT_MASK; chan->is_enabled = 1; chan->frag_number = 0; chan->frag_size = 0; atomic_set(&chan->n_frags, 0); atomic_set (&chan->hw_ptr, 0);}/** * via_chan_init - Initialize PCM channel * @card: Private audio chip info * @chan: Channel to be initialized * * Performs some of the preparations necessary to begin * using a PCM channel. * * Currently the preparations consist in * setting the * PCM channel to a known state. */static void via_chan_init (struct via_info *card, struct via_channel *chan){ DPRINTK ("ENTER\n"); /* bzero channel structure, and init members to defaults */ via_chan_init_defaults (card, chan); /* stop any existing channel output */ via_chan_clear (card, chan); via_chan_status_clear (chan->iobase); via_chan_pcm_fmt (chan, 1); DPRINTK ("EXIT\n");}/** * via_chan_buffer_init - Initialize PCM channel buffer * @card: Private audio chip info * @chan: Channel to be initialized * * Performs some of the preparations necessary to begin * using a PCM channel. * * Currently the preparations include allocating the * scatter-gather DMA table and buffers, * and passing the * address of the DMA table to the hardware. * * Note that special care is taken when passing the * DMA table address to hardware, because it was found * during driver development that the hardware did not * always "take" the address. */static int via_chan_buffer_init (struct via_info *card, struct via_channel *chan){ int page, offset; int i; DPRINTK ("ENTER\n"); if (chan->sgtable != NULL) { DPRINTK ("EXIT\n"); return 0; } /* alloc DMA-able memory for scatter-gather table */ chan->sgtable = pci_alloc_consistent (card->pdev, (sizeof (struct via_sgd_table) * chan->frag_number), &chan->sgt_handle); if (!chan->sgtable) { printk (KERN_ERR PFX "DMA table alloc fail, aborting\n"); DPRINTK ("EXIT\n"); return -ENOMEM; } memset ((void*)chan->sgtable, 0, (sizeof (struct via_sgd_table) * chan->frag_number)); /* alloc DMA-able memory for scatter-gather buffers */ chan->page_number = (chan->frag_number * chan->frag_size) / PAGE_SIZE + (((chan->frag_number * chan->frag_size) % PAGE_SIZE) ? 1 : 0); for (i = 0; i < chan->page_number; i++) { chan->pgtbl[i].cpuaddr = pci_alloc_consistent (card->pdev, PAGE_SIZE, &chan->pgtbl[i].handle); if (!chan->pgtbl[i].cpuaddr) { chan->page_number = i; goto err_out_nomem; }#ifndef VIA_NDEBUG memset (chan->pgtbl[i].cpuaddr, 0xBC, chan->frag_size);#endif#if 1 DPRINTK ("dmabuf_pg #%d (h=%lx, v2p=%lx, a=%p)\n", i, (long)chan->pgtbl[i].handle, virt_to_phys(chan->pgtbl[i].cpuaddr), chan->pgtbl[i].cpuaddr);#endif } for (i = 0; i < chan->frag_number; i++) { page = i / (PAGE_SIZE / chan->frag_size); offset = (i % (PAGE_SIZE / chan->frag_size)) * chan->frag_size; chan->sgtable[i].count = cpu_to_le32 (chan->frag_size | VIA_FLAG); chan->sgtable[i].addr = cpu_to_le32 (chan->pgtbl[page].handle + offset);#if 1 DPRINTK ("dmabuf #%d (32(h)=%lx)\n", i, (long)chan->sgtable[i].addr);#endif } /* overwrite the last buffer information */ chan->sgtable[chan->frag_number - 1].count = cpu_to_le32 (chan->frag_size | VIA_EOL); /* set location of DMA-able scatter-gather info table */ DPRINTK ("outl (0x%X, 0x%04lX)\n", chan->sgt_handle, chan->iobase + VIA_PCM_TABLE_ADDR); via_ac97_wait_idle (card); outl (chan->sgt_handle, chan->iobase + VIA_PCM_TABLE_ADDR); udelay (20); via_ac97_wait_idle (card); DPRINTK ("inl (0x%lX) = %x\n", chan->iobase + VIA_PCM_TABLE_ADDR, inl(chan->iobase + VIA_PCM_TABLE_ADDR)); DPRINTK ("EXIT\n"); return 0;err_out_nomem: printk (KERN_ERR PFX "DMA buffer alloc fail, aborting\n"); via_chan_buffer_free (card, chan); DPRINTK ("EXIT\n"); return -ENOMEM;}/** * via_chan_free - Release a PCM channel * @card: Private audio chip info * @chan: Channel to be released * * Performs all the functions necessary to clean up * an initialized channel. * * Currently these functions include disabled any * active DMA operations, setting the PCM channel * back to a known state, and releasing any allocated * sound buffers. */static void via_chan_free (struct via_info *card, struct via_channel *chan){ DPRINTK ("ENTER\n"); spin_lock_irq (&card->lock); /* stop any existing channel output */ via_chan_status_clear (chan->iobase); via_chan_stop (chan->iobase); via_chan_status_clear (chan->iobase); spin_unlock_irq (&card->lock); synchronize_irq(); DPRINTK ("EXIT\n");}static void via_chan_buffer_free (struct via_info *card, struct via_channel *chan){ int i; DPRINTK ("ENTER\n"); /* zero location of DMA-able scatter-gather info table */ via_ac97_wait_idle(card); outl (0, chan->iobase + VIA_PCM_TABLE_ADDR); for (i = 0; i < chan->page_number; i++) if (chan->pgtbl[i].cpuaddr) { pci_free_consistent (card->pdev, PAGE_SIZE, chan->pgtbl[i].cpuaddr, chan->pgtbl[i].handle); chan->pgtbl[i].cpuaddr = NULL; chan->pgtbl[i].handle = 0; } chan->page_number = 0; if (chan->sgtable) { pci_free_consistent (card->pdev, (sizeof (struct via_sgd_table) * chan->frag_number), (void*)chan->sgtable, chan->sgt_handle); chan->sgtable = NULL; } DPRINTK ("EXIT\n");}/** * via_chan_pcm_fmt - Update PCM channel settings * @chan: Channel to be updated * @reset: Boolean. If non-zero, channel will be reset * to 8-bit mono mode. * * Stores the settings of the current PCM format, * 8-bit or 16-bit, and mono/stereo, into the * hardware settings for the specified channel. * If @reset is non-zero, the channel is reset * to 8-bit mono mode. Otherwise, the channel * is set to the values stored in the channel * information struct @chan. */static void via_chan_pcm_fmt (struct via_channel *chan, int reset){ DPRINTK ("ENTER, pcm_fmt=0x%02X, reset=%s\n", chan->pcm_fmt, reset ? "yes" : "no"); assert (chan != NULL); if (reset) /* reset to 8-bit mono mode */ chan->pcm_fmt = 0; /* enable interrupts on FLAG and EOL */ chan->pcm_fmt |= VIA_CHAN_TYPE_MASK; /* if we are recording, enable recording fifo bit */ if (chan->is_record) chan->pcm_fmt |= VIA_PCM_REC_FIFO; /* set interrupt select bits where applicable (PCM in & out channels) */ if (!chan->is_record) chan->pcm_fmt |= VIA_CHAN_TYPE_INT_SELECT; outb (chan->pcm_fmt, chan->iobase + VIA_PCM_TYPE); DPRINTK ("EXIT, pcm_fmt = 0x%02X, reg = 0x%02X\n", chan->pcm_fmt, inb (chan->iobase + VIA_PCM_TYPE));}/** * via_chan_clear - Stop DMA channel operation, and reset pointers * @card: the chip to accessed * @chan: Channel to be cleared * * Call via_chan_stop to halt DMA operations, and then resets * all software pointers which track DMA operation. */static void via_chan_clear (struct via_info *card, struct via_channel *chan){ DPRINTK ("ENTER\n"); via_chan_stop (chan->iobase); via_chan_buffer_free(card, chan); chan->is_active = 0; chan->is_mapped = 0; chan->is_enabled = 1; chan->slop_len = 0; chan->sw_ptr = 0; chan->n_irqs = 0; atomic_set (&chan->hw_ptr, 0); DPRINTK ("EXIT\n");}/** * via_chan_set_speed - Set PCM sample rate for given channel * @card: Private info for specified board * @chan: Channel whose sample rate will be adjusted * @val: New sample rate, in Khz * * Helper function for the %SNDCTL_DSP_SPEED ioctl. OSS semantics * demand that all audio operations halt (if they are not already * halted) when the %SNDCTL_DSP_SPEED is given. * * This function halts all audio operations for the given channel * @chan, and then calls via_set_rate to set the audio hardware * to the new rate. */static int via_chan_set_speed (struct via_info *card, struct via_channel *chan, int val){ DPRINTK ("ENTER, requested rate = %d\n", val); via_chan_clear (card, chan); val = via_set_rate (&card->ac97, chan, val); DPRINTK ("EXIT, returning %d\n", val); return val;}/** * via_chan_set_fmt - Set PCM sample size for given channel * @card: Private info for specified board * @chan: Channel whose sample size will be adjusted * @val: New sample size, use the %AFMT_xxx constants * * Helper function for the %SNDCTL_DSP_SETFMT ioctl. OSS semantics * demand that all audio operations halt (if they are not already * halted) when the %SNDCTL_DSP_SETFMT is given. * * This function halts all audio operations for the given channel * @chan, and then calls via_chan_pcm_fmt to set the audio hardware * to the new sample size, either 8-bit or 16-bit. */static int via_chan_set_fmt (struct via_info *card, struct via_channel *chan, int val){ DPRINTK ("ENTER, val=%s\n", val == AFMT_U8 ? "AFMT_U8" : val == AFMT_S16_LE ? "AFMT_S16_LE" : "unknown"); via_chan_clear (card, chan); assert (val != AFMT_QUERY); /* this case is handled elsewhere */ switch (val) { case AFMT_S16_LE: if ((chan->pcm_fmt & VIA_PCM_FMT_16BIT) == 0) { chan->pcm_fmt |= VIA_PCM_FMT_16BIT; via_chan_pcm_fmt (chan, 0); } break; case AFMT_U8: if (chan->pcm_fmt & VIA_PCM_FMT_16BIT) { chan->pcm_fmt &= ~VIA_PCM_FMT_16BIT; via_chan_pcm_fmt (chan, 0); } break; default: DPRINTK ("unknown AFMT: 0x%X\n", val); val = AFMT_S16_LE; } DPRINTK ("EXIT\n"); return val;}/** * via_chan_set_stereo - Enable or disable stereo for a DMA channel * @card: Private info for specified board * @chan: Channel whose stereo setting will be adjusted * @val: New sample size, use the %AFMT_xxx constants * * Helper function for the %SNDCTL_DSP_CHANNELS and %SNDCTL_DSP_STEREO ioctls. OSS semantics * demand that all audio operations halt (if they are not already
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -