📄 via82cxxx_audio.c
字号:
/* 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); 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; if (chan->is_record) atomic_set (&chan->n_bufs, 0); else atomic_set (&chan->n_bufs, VIA_DMA_BUFFERS); atomic_set (&chan->hw_ptr, 0);}/** * via_chan_init - Initialize PCM channel * @card: Private audio chip info * @chan: Channel to be initialized * * Performs all the preparations necessary to begin * using a PCM channel. * * Currently the preparations include allocating the * scatter-gather DMA table and buffers, setting the * PCM channel to a known state, 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_init (struct via_info *card, struct via_channel *chan){ int i; DPRINTK ("ENTER\n"); /* bzero channel structure, and init members to defaults */ via_chan_init_defaults (card, chan); /* alloc DMA-able memory for scatter-gather table */ chan->sgtable = pci_alloc_consistent (card->pdev, (sizeof (struct via_sgd_table) * VIA_DMA_BUFFERS), &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) * VIA_DMA_BUFFERS)); /* alloc DMA-able memory for scatter-gather buffers */ for (i = 0; i < VIA_DMA_BUFFERS; i++) { chan->sgbuf[i].cpuaddr = pci_alloc_consistent (card->pdev, VIA_DMA_BUF_SIZE, &chan->sgbuf[i].handle); if (!chan->sgbuf[i].cpuaddr) goto err_out_nomem; if (i < (VIA_DMA_BUFFERS - 1)) chan->sgtable[i].count = cpu_to_le32 (VIA_DMA_BUF_SIZE | VIA_FLAG); else chan->sgtable[i].count = cpu_to_le32 (VIA_DMA_BUF_SIZE | VIA_EOL); chan->sgtable[i].addr = cpu_to_le32 (chan->sgbuf[i].handle);#ifndef VIA_NDEBUG memset (chan->sgbuf[i].cpuaddr, 0xBC, VIA_DMA_BUF_SIZE);#endif#if 1 DPRINTK ("dmabuf #%d (h=%lx, 32(h)=%lx, v2p=%lx, a=%p)\n", i, (long)chan->sgbuf[i].handle, (long)chan->sgtable[i].addr, virt_to_phys(chan->sgbuf[i].cpuaddr), chan->sgbuf[i].cpuaddr);#endif assert ((VIA_DMA_BUF_SIZE % PAGE_SIZE) == 0); } /* stop any existing channel output */ via_chan_clear (chan); via_chan_status_clear (chan->iobase); via_chan_pcm_fmt (chan, 1); /* set location of DMA-able scatter-gather info table */ DPRINTK("outl (0x%X, 0x%04lX)\n", cpu_to_le32 (chan->sgt_handle), chan->iobase + VIA_PCM_TABLE_ADDR); via_ac97_wait_idle (card); outl (cpu_to_le32 (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_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){ int i; DPRINTK ("ENTER\n"); synchronize_irq(); spin_lock_irq (&card->lock); /* stop any existing channel output */ via_chan_stop (chan->iobase); via_chan_status_clear (chan->iobase); via_chan_pcm_fmt (chan, 1); spin_unlock_irq (&card->lock); /* 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 < VIA_DMA_BUFFERS; i++) if (chan->sgbuf[i].cpuaddr) { assert ((VIA_DMA_BUF_SIZE % PAGE_SIZE) == 0); pci_free_consistent (card->pdev, VIA_DMA_BUF_SIZE, chan->sgbuf[i].cpuaddr, chan->sgbuf[i].handle); chan->sgbuf[i].cpuaddr = NULL; chan->sgbuf[i].handle = 0; } if (chan->sgtable) { pci_free_consistent (card->pdev, (sizeof (struct via_sgd_table) * VIA_DMA_BUFFERS), (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 & FM out channels) */ if (!chan->is_record) chan->pcm_fmt |= VIA_CHAN_TYPE_INT_SELECT; outb (chan->pcm_fmt, chan->iobase + 2); DPRINTK ("EXIT, pcm_fmt = 0x%02X, reg = 0x%02X\n", chan->pcm_fmt, inb (chan->iobase + 2));}/** * via_chan_clear - Stop DMA channel operation, and reset pointers * @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_channel *chan){ DPRINTK ("ENTER\n"); via_chan_stop (chan->iobase); 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); if (chan->is_record) atomic_set (&chan->n_bufs, 0); else atomic_set (&chan->n_bufs, VIA_DMA_BUFFERS); 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 (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 (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 * halted) when %SNDCTL_DSP_CHANNELS or SNDCTL_DSP_STEREO 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 enable or disable stereo. */static int via_chan_set_stereo (struct via_info *card, struct via_channel *chan, int val){ DPRINTK ("ENTER, channels = %d\n", val); via_chan_clear (chan); switch (val) { /* mono */ case 1: chan->pcm_fmt &= ~VIA_PCM_FMT_STEREO; via_chan_pcm_fmt (chan, 0); break; /* stereo */ case 2: chan->pcm_fmt |= VIA_PCM_FMT_STEREO; via_chan_pcm_fmt (chan, 0); break; /* unknown */ default: printk (KERN_WARNING PFX "unknown number of channels\n"); val = -EINVAL; break; } DPRINTK ("EXIT, returning %d\n", val); return val;}#ifdef VIA_CHAN_DUMP_BUFS/** * via_chan_dump_bufs - Display DMA table contents * @chan: Channel whose DMA table will be displayed * * Debugging function which displays the contents of the * scatter-gather DMA table for the given channel @chan. */static void via_chan_dump_bufs (struct via_channel *chan){ int i; for (i = 0; i < VIA_DMA_BUFFERS; i++) { DPRINTK ("#%02d: addr=%x, count=%u, flag=%d, eol=%d\n", i, chan->sgtable[i].addr, chan->sgtable[i].count & 0x00FFFFFF, chan->sgtable[i].count & VIA_FLAG ? 1 : 0, chan->sgtable[i].count & VIA_EOL ? 1 : 0); } DPRINTK ("buf_in_use = %d, nextbuf = %d\n", atomic_read (&chan->buf_in_use), atomic_read (&chan->sw_ptr));}#endif /* VIA_CHAN_DUMP_BUFS *//** * via_chan_flush_frag - Flush partially-full playback buffer to hardware * @chan: Channel whose DMA table will be displayed * * Flushes partially-full playback buffer to hardware. */static void via_chan_flush_frag (struct via_channel *chan){ DPRINTK ("ENTER\n"); assert (chan->slop_len > 0); if (chan->sw_ptr == (VIA_DMA_BUFFERS - 1)) chan->sw_ptr = 0; else chan->sw_ptr++; chan->slop_len = 0; assert (atomic_read (&chan->n_bufs) > 0); atomic_dec (&chan->n_bufs); DPRINTK ("EXIT\n");}/** * via_chan_maybe_start - Initiate audio hardware DMA operation * @chan: Channel whose DMA is to be started * * Initiate DMA operation, if the DMA engine for the given * channel @chan is not already active. */static inline void via_chan_maybe_start (struct via_channel *chan){ if (!chan->is_active && chan->is_enabled) { chan->is_active = 1; sg_begin (chan); DPRINTK("starting channel %s\n", chan->name); }}/**************************************************************** * * Interface to ac97-codec module * * *//** * via_ac97_wait_idle - Wait until AC97 codec is not busy * @card: Private info for specified board * * Sleep until the AC97 codec is no longer busy. * Returns the final value read from the SGD * register being polled. */static u8 via_ac97_wait_idle (struct via_info *card){ u8 tmp8; int counter = VIA_COUNTER_LIMIT; DPRINTK ("ENTER/EXIT\n"); assert (card != NULL); assert (card->pdev != NULL); do { udelay (15); tmp8 = inb (card->baseaddr + 0x83);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -