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