au1550_ac97.c
来自「linux 内核源代码」· C语言 代码 · 共 2,130 行 · 第 1/4 页
C
2,130 行
/* * au1550_ac97.c -- Sound driver for Alchemy Au1550 MIPS Internet Edge * Processor. * * Copyright 2004 Embedded Edge, LLC * dan@embeddededge.com * * Mostly copied from the au1000.c driver and some from the * PowerMac dbdma driver. * We assume the processor can do memory coherent DMA. * * Ported to 2.6 by Matt Porter <mporter@kernel.crashing.org> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 675 Mass Ave, Cambridge, MA 02139, USA. * */#undef DEBUG#include <linux/module.h>#include <linux/string.h>#include <linux/ioport.h>#include <linux/sched.h>#include <linux/delay.h>#include <linux/sound.h>#include <linux/slab.h>#include <linux/soundcard.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/kernel.h>#include <linux/poll.h>#include <linux/bitops.h>#include <linux/spinlock.h>#include <linux/smp_lock.h>#include <linux/ac97_codec.h>#include <linux/mutex.h>#include <asm/io.h>#include <asm/uaccess.h>#include <asm/hardirq.h>#include <asm/mach-au1x00/au1xxx_psc.h>#include <asm/mach-au1x00/au1xxx_dbdma.h>#include <asm/mach-au1x00/au1xxx.h>#undef OSS_DOCUMENTED_MIXER_SEMANTICS/* misc stuff */#define POLL_COUNT 0x50000#define AC97_EXT_DACS (AC97_EXTID_SDAC | AC97_EXTID_CDAC | AC97_EXTID_LDAC)/* The number of DBDMA ring descriptors to allocate. No sense making * this too large....if you can't keep up with a few you aren't likely * to be able to with lots of them, either. */#define NUM_DBDMA_DESCRIPTORS 4#define err(format, arg...) printk(KERN_ERR format "\n" , ## arg)/* Boot options * 0 = no VRA, 1 = use VRA if codec supports it */static int vra = 1;module_param(vra, bool, 0);MODULE_PARM_DESC(vra, "if 1 use VRA if codec supports it");static struct au1550_state { /* soundcore stuff */ int dev_audio; struct ac97_codec *codec; unsigned codec_base_caps; /* AC'97 reg 00h, "Reset Register" */ unsigned codec_ext_caps; /* AC'97 reg 28h, "Extended Audio ID" */ int no_vra; /* do not use VRA */ spinlock_t lock; struct mutex open_mutex; struct mutex sem; mode_t open_mode; wait_queue_head_t open_wait; struct dmabuf { u32 dmanr; unsigned sample_rate; unsigned src_factor; unsigned sample_size; int num_channels; int dma_bytes_per_sample; int user_bytes_per_sample; int cnt_factor; void *rawbuf; unsigned buforder; unsigned numfrag; unsigned fragshift; void *nextIn; void *nextOut; int count; unsigned total_bytes; unsigned error; wait_queue_head_t wait; /* redundant, but makes calculations easier */ unsigned fragsize; unsigned dma_fragsize; unsigned dmasize; unsigned dma_qcount; /* OSS stuff */ unsigned mapped:1; unsigned ready:1; unsigned stopped:1; unsigned ossfragshift; int ossmaxfrags; unsigned subdivision; } dma_dac, dma_adc;} au1550_state;static unsignedld2(unsigned int x){ unsigned r = 0; if (x >= 0x10000) { x >>= 16; r += 16; } if (x >= 0x100) { x >>= 8; r += 8; } if (x >= 0x10) { x >>= 4; r += 4; } if (x >= 4) { x >>= 2; r += 2; } if (x >= 2) r++; return r;}static voidau1550_delay(int msec){ unsigned long tmo; signed long tmo2; if (in_interrupt()) return; tmo = jiffies + (msec * HZ) / 1000; for (;;) { tmo2 = tmo - jiffies; if (tmo2 <= 0) break; schedule_timeout(tmo2); }}static u16rdcodec(struct ac97_codec *codec, u8 addr){ struct au1550_state *s = (struct au1550_state *)codec->private_data; unsigned long flags; u32 cmd, val; u16 data; int i; spin_lock_irqsave(&s->lock, flags); for (i = 0; i < POLL_COUNT; i++) { val = au_readl(PSC_AC97STAT); au_sync(); if (!(val & PSC_AC97STAT_CP)) break; } if (i == POLL_COUNT) err("rdcodec: codec cmd pending expired!"); cmd = (u32)PSC_AC97CDC_INDX(addr); cmd |= PSC_AC97CDC_RD; /* read command */ au_writel(cmd, PSC_AC97CDC); au_sync(); /* now wait for the data */ for (i = 0; i < POLL_COUNT; i++) { val = au_readl(PSC_AC97STAT); au_sync(); if (!(val & PSC_AC97STAT_CP)) break; } if (i == POLL_COUNT) { err("rdcodec: read poll expired!"); data = 0; goto out; } /* wait for command done? */ for (i = 0; i < POLL_COUNT; i++) { val = au_readl(PSC_AC97EVNT); au_sync(); if (val & PSC_AC97EVNT_CD) break; } if (i == POLL_COUNT) { err("rdcodec: read cmdwait expired!"); data = 0; goto out; } data = au_readl(PSC_AC97CDC) & 0xffff; au_sync(); /* Clear command done event. */ au_writel(PSC_AC97EVNT_CD, PSC_AC97EVNT); au_sync(); out: spin_unlock_irqrestore(&s->lock, flags); return data;}static voidwrcodec(struct ac97_codec *codec, u8 addr, u16 data){ struct au1550_state *s = (struct au1550_state *)codec->private_data; unsigned long flags; u32 cmd, val; int i; spin_lock_irqsave(&s->lock, flags); for (i = 0; i < POLL_COUNT; i++) { val = au_readl(PSC_AC97STAT); au_sync(); if (!(val & PSC_AC97STAT_CP)) break; } if (i == POLL_COUNT) err("wrcodec: codec cmd pending expired!"); cmd = (u32)PSC_AC97CDC_INDX(addr); cmd |= (u32)data; au_writel(cmd, PSC_AC97CDC); au_sync(); for (i = 0; i < POLL_COUNT; i++) { val = au_readl(PSC_AC97STAT); au_sync(); if (!(val & PSC_AC97STAT_CP)) break; } if (i == POLL_COUNT) err("wrcodec: codec cmd pending expired!"); for (i = 0; i < POLL_COUNT; i++) { val = au_readl(PSC_AC97EVNT); au_sync(); if (val & PSC_AC97EVNT_CD) break; } if (i == POLL_COUNT) err("wrcodec: read cmdwait expired!"); /* Clear command done event. */ au_writel(PSC_AC97EVNT_CD, PSC_AC97EVNT); au_sync(); spin_unlock_irqrestore(&s->lock, flags);}static voidwaitcodec(struct ac97_codec *codec){ u16 temp; u32 val; int i; /* codec_wait is used to wait for a ready state after * an AC97C_RESET. */ au1550_delay(10); /* first poll the CODEC_READY tag bit */ for (i = 0; i < POLL_COUNT; i++) { val = au_readl(PSC_AC97STAT); au_sync(); if (val & PSC_AC97STAT_CR) break; } if (i == POLL_COUNT) { err("waitcodec: CODEC_READY poll expired!"); return; } /* get AC'97 powerdown control/status register */ temp = rdcodec(codec, AC97_POWER_CONTROL); /* If anything is powered down, power'em up */ if (temp & 0x7f00) { /* Power on */ wrcodec(codec, AC97_POWER_CONTROL, 0); au1550_delay(100); /* Reread */ temp = rdcodec(codec, AC97_POWER_CONTROL); } /* Check if Codec REF,ANL,DAC,ADC ready */ if ((temp & 0x7f0f) != 0x000f) err("codec reg 26 status (0x%x) not ready!!", temp);}/* stop the ADC before calling */static voidset_adc_rate(struct au1550_state *s, unsigned rate){ struct dmabuf *adc = &s->dma_adc; struct dmabuf *dac = &s->dma_dac; unsigned adc_rate, dac_rate; u16 ac97_extstat; if (s->no_vra) { /* calc SRC factor */ adc->src_factor = ((96000 / rate) + 1) >> 1; adc->sample_rate = 48000 / adc->src_factor; return; } adc->src_factor = 1; ac97_extstat = rdcodec(s->codec, AC97_EXTENDED_STATUS); rate = rate > 48000 ? 48000 : rate; /* enable VRA */ wrcodec(s->codec, AC97_EXTENDED_STATUS, ac97_extstat | AC97_EXTSTAT_VRA); /* now write the sample rate */ wrcodec(s->codec, AC97_PCM_LR_ADC_RATE, (u16) rate); /* read it back for actual supported rate */ adc_rate = rdcodec(s->codec, AC97_PCM_LR_ADC_RATE); pr_debug("set_adc_rate: set to %d Hz\n", adc_rate); /* some codec's don't allow unequal DAC and ADC rates, in which case * writing one rate reg actually changes both. */ dac_rate = rdcodec(s->codec, AC97_PCM_FRONT_DAC_RATE); if (dac->num_channels > 2) wrcodec(s->codec, AC97_PCM_SURR_DAC_RATE, dac_rate); if (dac->num_channels > 4) wrcodec(s->codec, AC97_PCM_LFE_DAC_RATE, dac_rate); adc->sample_rate = adc_rate; dac->sample_rate = dac_rate;}/* stop the DAC before calling */static voidset_dac_rate(struct au1550_state *s, unsigned rate){ struct dmabuf *dac = &s->dma_dac; struct dmabuf *adc = &s->dma_adc; unsigned adc_rate, dac_rate; u16 ac97_extstat; if (s->no_vra) { /* calc SRC factor */ dac->src_factor = ((96000 / rate) + 1) >> 1; dac->sample_rate = 48000 / dac->src_factor; return; } dac->src_factor = 1; ac97_extstat = rdcodec(s->codec, AC97_EXTENDED_STATUS); rate = rate > 48000 ? 48000 : rate; /* enable VRA */ wrcodec(s->codec, AC97_EXTENDED_STATUS, ac97_extstat | AC97_EXTSTAT_VRA); /* now write the sample rate */ wrcodec(s->codec, AC97_PCM_FRONT_DAC_RATE, (u16) rate); /* I don't support different sample rates for multichannel, * so make these channels the same. */ if (dac->num_channels > 2) wrcodec(s->codec, AC97_PCM_SURR_DAC_RATE, (u16) rate); if (dac->num_channels > 4) wrcodec(s->codec, AC97_PCM_LFE_DAC_RATE, (u16) rate); /* read it back for actual supported rate */ dac_rate = rdcodec(s->codec, AC97_PCM_FRONT_DAC_RATE); pr_debug("set_dac_rate: set to %d Hz\n", dac_rate); /* some codec's don't allow unequal DAC and ADC rates, in which case * writing one rate reg actually changes both. */ adc_rate = rdcodec(s->codec, AC97_PCM_LR_ADC_RATE); dac->sample_rate = dac_rate; adc->sample_rate = adc_rate;}static voidstop_dac(struct au1550_state *s){ struct dmabuf *db = &s->dma_dac; u32 stat; unsigned long flags; if (db->stopped) return; spin_lock_irqsave(&s->lock, flags); au_writel(PSC_AC97PCR_TP, PSC_AC97PCR); au_sync(); /* Wait for Transmit Busy to show disabled. */ do { stat = au_readl(PSC_AC97STAT); au_sync(); } while ((stat & PSC_AC97STAT_TB) != 0); au1xxx_dbdma_reset(db->dmanr); db->stopped = 1; spin_unlock_irqrestore(&s->lock, flags);}static voidstop_adc(struct au1550_state *s){ struct dmabuf *db = &s->dma_adc; unsigned long flags; u32 stat; if (db->stopped) return; spin_lock_irqsave(&s->lock, flags); au_writel(PSC_AC97PCR_RP, PSC_AC97PCR); au_sync(); /* Wait for Receive Busy to show disabled. */ do { stat = au_readl(PSC_AC97STAT); au_sync(); } while ((stat & PSC_AC97STAT_RB) != 0); au1xxx_dbdma_reset(db->dmanr); db->stopped = 1; spin_unlock_irqrestore(&s->lock, flags);}static voidset_xmit_slots(int num_channels){ u32 ac97_config, stat; ac97_config = au_readl(PSC_AC97CFG); au_sync(); ac97_config &= ~(PSC_AC97CFG_TXSLOT_MASK | PSC_AC97CFG_DE_ENABLE); au_writel(ac97_config, PSC_AC97CFG); au_sync(); switch (num_channels) { case 6: /* stereo with surround and center/LFE, * slots 3,4,6,7,8,9 */ ac97_config |= PSC_AC97CFG_TXSLOT_ENA(6); ac97_config |= PSC_AC97CFG_TXSLOT_ENA(9); case 4: /* stereo with surround, slots 3,4,7,8 */ ac97_config |= PSC_AC97CFG_TXSLOT_ENA(7); ac97_config |= PSC_AC97CFG_TXSLOT_ENA(8); case 2: /* stereo, slots 3,4 */ case 1: /* mono */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?