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 + -
显示快捷键?