au1000.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,235 行 · 第 1/4 页
C
2,235 行
/* * au1000.c -- Sound driver for Alchemy Au1000 MIPS Internet Edge * Processor. * * Copyright 2001 MontaVista Software Inc. * Author: MontaVista Software, Inc. * stevel@mvista.com or source@mvista.com * * 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. * * * Module command line parameters: * * Supported devices: * /dev/dsp standard OSS /dev/dsp device * /dev/mixer standard OSS /dev/mixer device * * Notes: * * 1. Much of the OSS buffer allocation, ioctl's, and mmap'ing are * taken, slightly modified or not at all, from the ES1371 driver, * so refer to the credits in es1371.c for those. The rest of the * code (probe, open, read, write, the ISR, etc.) is new. * * Revision history * 06.27.2001 Initial version * 03.20.2002 Added mutex locks around read/write methods, to prevent * simultaneous access on SMP or preemptible kernels. Also * removed the counter/pointer fragment aligning at the end * of read/write methods [stevel]. * 03.21.2002 Add support for coherent DMA on the audio read/write DMA * channels [stevel]. * */#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/poll.h>#include <linux/pci.h>#include <linux/bitops.h>#include <linux/proc_fs.h>#include <linux/spinlock.h>#include <linux/smp_lock.h>#include <linux/ac97_codec.h>#include <linux/wrapper.h>#include <linux/interrupt.h>#include <asm/io.h>#include <asm/uaccess.h>#include <asm/au1000.h>#include <asm/au1000_dma.h>/* --------------------------------------------------------------------- */#undef OSS_DOCUMENTED_MIXER_SEMANTICS#define AU1000_DEBUG#undef AU1000_VERBOSE_DEBUG#define USE_COHERENT_DMA#define AU1000_MODULE_NAME "Au1000 audio"#define PFX AU1000_MODULE_NAME#ifdef AU1000_DEBUG#define dbg(format, arg...) printk(KERN_DEBUG PFX ": " format "\n" , ## arg)#else#define dbg(format, arg...) do {} while (0)#endif#define err(format, arg...) printk(KERN_ERR PFX ": " format "\n" , ## arg)#define info(format, arg...) printk(KERN_INFO PFX ": " format "\n" , ## arg)#define warn(format, arg...) printk(KERN_WARNING PFX ": " format "\n" , ## arg)/* misc stuff */#define POLL_COUNT 0x5000#define AC97_EXT_DACS (AC97_EXTID_SDAC | AC97_EXTID_CDAC | AC97_EXTID_LDAC)/* Boot options */static int vra = 0; // 0 = no VRA, 1 = use VRA if codec supports itMODULE_PARM(vra, "i");MODULE_PARM_DESC(vra, "if 1 use VRA if codec supports it");/* --------------------------------------------------------------------- */struct au1000_state { /* soundcore stuff */ int dev_audio;#ifdef AU1000_DEBUG /* debug /proc entry */ struct proc_dir_entry *ps; struct proc_dir_entry *ac97_ps;#endif /* AU1000_DEBUG */ 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 semaphore open_sem; struct semaphore sem; mode_t open_mode; wait_queue_head_t open_wait; struct dmabuf { unsigned int dmanr; // DMA Channel number unsigned sample_rate; // Hz unsigned src_factor; // SRC interp/decimation (no vra) unsigned sample_size; // 8 or 16 int num_channels; // 1 = mono, 2 = stereo, 4, 6 int dma_bytes_per_sample;// DMA bytes per audio sample frame int user_bytes_per_sample;// User bytes per audio sample frame int cnt_factor; // user-to-DMA bytes per audio // sample frame void *rawbuf; dma_addr_t dmaaddr; unsigned buforder; unsigned numfrag; // # of DMA fragments in DMA buffer unsigned fragshift; void *nextIn; // ptr to next-in to DMA buffer void *nextOut;// ptr to next-out from DMA buffer int count; // current byte count in DMA buffer unsigned total_bytes; // total bytes written or read unsigned error; // over/underrun wait_queue_head_t wait; /* redundant, but makes calculations easier */ unsigned fragsize; // user perception of fragment size unsigned dma_fragsize; // DMA (real) fragment size unsigned dmasize; // Total DMA buffer size // (mult. of DMA fragsize) /* OSS stuff */ unsigned mapped:1; unsigned ready:1; unsigned stopped:1; unsigned ossfragshift; int ossmaxfrags; unsigned subdivision; } dma_dac , dma_adc;} au1000_state;/* --------------------------------------------------------------------- */static inline unsigned ld2(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;}#ifdef USE_COHERENT_DMAstatic inline void * dma_alloc(size_t size, dma_addr_t * dma_handle){ void* ret = (void *)__get_free_pages(GFP_ATOMIC | GFP_DMA, get_order(size)); if (ret != NULL) { memset(ret, 0, size); *dma_handle = virt_to_phys(ret); } return ret;}static inline void dma_free(size_t size, void* va, dma_addr_t dma_handle){ free_pages((unsigned long)va, get_order(size));}#elsestatic inline void * dma_alloc(size_t size, dma_addr_t * dma_handle){ return pci_alloc_consistent(NULL, size, dma_handle);}static inline void dma_free(size_t size, void* va, dma_addr_t dma_handle){ pci_free_consistent(NULL, size, va, dma_handle);}#endif/* --------------------------------------------------------------------- */static void au1000_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 u16 rdcodec(struct ac97_codec *codec, u8 addr){ struct au1000_state *s = (struct au1000_state *)codec->private_data; unsigned long flags; u32 cmd; u16 data; int i; spin_lock_irqsave(&s->lock, flags); for (i = 0; i < POLL_COUNT; i++) if (!(au_readl(AC97C_STATUS) & AC97C_CP)) break; if (i == POLL_COUNT) err("rdcodec: codec cmd pending expired!"); cmd = (u32) addr & AC97C_INDEX_MASK; cmd |= AC97C_READ; // read command au_writel(cmd, AC97C_CMD); /* now wait for the data */ for (i = 0; i < POLL_COUNT; i++) if (!(au_readl(AC97C_STATUS) & AC97C_CP)) break; if (i == POLL_COUNT) { err("rdcodec: read poll expired!"); return 0; } data = au_readl(AC97C_CMD) & 0xffff; spin_unlock_irqrestore(&s->lock, flags); return data;}static void wrcodec(struct ac97_codec *codec, u8 addr, u16 data){ struct au1000_state *s = (struct au1000_state *)codec->private_data; unsigned long flags; u32 cmd; int i; spin_lock_irqsave(&s->lock, flags); for (i = 0; i < POLL_COUNT; i++) if (!(au_readl(AC97C_STATUS) & AC97C_CP)) break; if (i == POLL_COUNT) err("wrcodec: codec cmd pending expired!"); cmd = (u32) addr & AC97C_INDEX_MASK; cmd &= ~AC97C_READ; // write command cmd |= ((u32) data << AC97C_WD_BIT); // OR in the data word au_writel(cmd, AC97C_CMD); spin_unlock_irqrestore(&s->lock, flags);}static void waitcodec(struct ac97_codec *codec){ u16 temp; int i; /* codec_wait is used to wait for a ready state after an AC97C_RESET. */ au1000_delay(10); // first poll the CODEC_READY tag bit for (i = 0; i < POLL_COUNT; i++) if (au_readl(AC97C_STATUS) & AC97C_READY) 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); au1000_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 void set_adc_rate(struct au1000_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);#ifdef AU1000_VERBOSE_DEBUG dbg("%s: set to %d Hz", __FUNCTION__, adc_rate);#endif // 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 void set_dac_rate(struct au1000_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);#ifdef AU1000_VERBOSE_DEBUG dbg("%s: set to %d Hz", __FUNCTION__, dac_rate);#endif // 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 void stop_dac(struct au1000_state *s){ struct dmabuf *db = &s->dma_dac; unsigned long flags; if (db->stopped) return; spin_lock_irqsave(&s->lock, flags); disable_dma(db->dmanr); db->stopped = 1; spin_unlock_irqrestore(&s->lock, flags);}static void stop_adc(struct au1000_state *s){ struct dmabuf *db = &s->dma_adc; unsigned long flags; if (db->stopped) return; spin_lock_irqsave(&s->lock, flags); disable_dma(db->dmanr); db->stopped = 1; spin_unlock_irqrestore(&s->lock, flags);}static void set_xmit_slots(int num_channels){ u32 ac97_config = au_readl(AC97C_CONFIG) & ~AC97C_XMIT_SLOTS_MASK; switch (num_channels) { case 1: // mono case 2: // stereo, slots 3,4 ac97_config |= (0x3 << AC97C_XMIT_SLOTS_BIT); break; case 4: // stereo with surround, slots 3,4,7,8 ac97_config |= (0x33 << AC97C_XMIT_SLOTS_BIT); break; case 6: // stereo with surround and center/LFE, slots 3,4,6,7,8,9 ac97_config |= (0x7b << AC97C_XMIT_SLOTS_BIT); break; } au_writel(ac97_config, AC97C_CONFIG);}static void set_recv_slots(int num_channels){ u32 ac97_config = au_readl(AC97C_CONFIG) & ~AC97C_RECV_SLOTS_MASK; /* * Always enable slots 3 and 4 (stereo). Slot 6 is * optional Mic ADC, which I don't support yet. */ ac97_config |= (0x3 << AC97C_RECV_SLOTS_BIT); au_writel(ac97_config, AC97C_CONFIG);}static void start_dac(struct au1000_state *s){ struct dmabuf *db = &s->dma_dac; unsigned long flags; unsigned long buf1, buf2; if (!db->stopped) return; spin_lock_irqsave(&s->lock, flags); au_readl(AC97C_STATUS); // read status to clear sticky bits // reset Buffer 1 and 2 pointers to nextOut and nextOut+dma_fragsize buf1 = virt_to_phys(db->nextOut); buf2 = buf1 + db->dma_fragsize; if (buf2 >= db->dmaaddr + db->dmasize) buf2 -= db->dmasize; set_xmit_slots(db->num_channels); init_dma(db->dmanr); if (get_dma_active_buffer(db->dmanr) == 0) { clear_dma_done0(db->dmanr); // clear DMA done bit set_dma_addr0(db->dmanr, buf1); set_dma_addr1(db->dmanr, buf2); } else { clear_dma_done1(db->dmanr); // clear DMA done bit set_dma_addr1(db->dmanr, buf1); set_dma_addr0(db->dmanr, buf2); } set_dma_count(db->dmanr, db->dma_fragsize>>1); enable_dma_buffers(db->dmanr); start_dma(db->dmanr);#ifdef AU1000_VERBOSE_DEBUG dump_au1000_dma_channel(db->dmanr);#endif db->stopped = 0; spin_unlock_irqrestore(&s->lock, flags);}static void start_adc(struct au1000_state *s){ struct dmabuf *db = &s->dma_adc; unsigned long flags; unsigned long buf1, buf2; if (!db->stopped) return; spin_lock_irqsave(&s->lock, flags); au_readl(AC97C_STATUS); // read status to clear sticky bits
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?