nec_vrc5477.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,017 行 · 第 1/4 页

C
2,017
字号
/*********************************************************************** * Copyright 2001 MontaVista Software Inc. * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net * * drivers/sound/nec_vrc5477.c *     AC97 sound dirver for NEC Vrc5477 chip (an integrated,  *     multi-function controller chip for MIPS CPUs) * * VRA support Copyright 2001 Bradley D. LaRonde <brad@ltc.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 code is derived from ite8172.c, which is written by Steve Longerbeam. * * Features: *   Currently we only support the following capabilities: *	. mono output to PCM L/R (line out). *	. stereo output to PCM L/R (line out). *	. mono input from PCM L (line in). *	. stereo output from PCM (line in). *	. sampling rate at 48k or variable sampling rate  *	. support /dev/dsp, /dev/mixer devices, standard OSS devices. *	. only support 16-bit PCM format (hardware limit, no software *	  translation)  *	. support duplex, but no trigger or realtime. *	 *   Specifically the following are not supported: *	. app-set frag size. *	. mmap'ed buffer access *//*  * Original comments from ite8172.c file. *//* * * 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. *  2. The following support is untested: *      * Memory mapping the audio buffers, and the ioctl controls that go *        with it. *      * S/PDIF output. *  3. The following is not supported: *      * I2S input. *      * legacy audio mode. *  4. Support for volume button interrupts is implemented but doesn't *     work yet. * *  Revision history *    02.08.2001  0.1   Initial release */#include <linux/module.h>#include <linux/string.h>#include <linux/kernel.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/pci.h>#include <linux/init.h>#include <linux/poll.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/interrupt.h>#include <asm/io.h>#include <asm/dma.h>#include <asm/uaccess.h>/* -------------------debug macros -------------------------------------- *//* #undef VRC5477_AC97_DEBUG */#define VRC5477_AC97_DEBUG#undef VRC5477_AC97_VERBOSE_DEBUG/* #define VRC5477_AC97_VERBOSE_DEBUG */#if defined(VRC5477_AC97_VERBOSE_DEBUG)#define VRC5477_AC97_DEBUG#endif#if defined(VRC5477_AC97_DEBUG)#define ASSERT(x)  if (!(x)) { \	panic("assertion failed at %s:%d: %s\n", __FILE__, __LINE__, #x); }#else#define	ASSERT(x)#endif /* VRC5477_AC97_DEBUG */#if defined(VRC5477_AC97_VERBOSE_DEBUG)static u16 inTicket; 		/* check sync between intr & write */static u16 outTicket;#endif/* --------------------------------------------------------------------- */#undef OSS_DOCUMENTED_MIXER_SEMANTICSstatic const unsigned sample_shift[] = { 0, 1, 1, 2 };#define         VRC5477_INT_CLR         0x0#define         VRC5477_INT_STATUS	0x0#define         VRC5477_CODEC_WR        0x4#define         VRC5477_CODEC_RD        0x8#define         VRC5477_CTRL            0x18#define         VRC5477_ACLINK_CTRL     0x1c#define         VRC5477_INT_MASK        0x24#define		VRC5477_DAC1_CTRL	0x30#define		VRC5477_DAC1L		0x34#define		VRC5477_DAC1_BADDR	0x38#define		VRC5477_DAC2_CTRL	0x3c#define		VRC5477_DAC2L		0x40#define		VRC5477_DAC2_BADDR	0x44#define		VRC5477_DAC3_CTRL	0x48#define		VRC5477_DAC3L		0x4c#define		VRC5477_DAC3_BADDR	0x50#define		VRC5477_ADC1_CTRL	0x54#define		VRC5477_ADC1L		0x58#define		VRC5477_ADC1_BADDR	0x5c#define		VRC5477_ADC2_CTRL	0x60#define		VRC5477_ADC2L		0x64#define		VRC5477_ADC2_BADDR	0x68#define		VRC5477_ADC3_CTRL	0x6c#define		VRC5477_ADC3L		0x70#define		VRC5477_ADC3_BADDR	0x74#define		VRC5477_CODEC_WR_RWC	(1 << 23)#define		VRC5477_CODEC_RD_RRDYA	(1 << 31)#define		VRC5477_CODEC_RD_RRDYD	(1 << 30)#define		VRC5477_ACLINK_CTRL_RST_ON	(1 << 15)#define		VRC5477_ACLINK_CTRL_RST_TIME	0x7f#define		VRC5477_ACLINK_CTRL_SYNC_ON	(1 << 30)#define		VRC5477_ACLINK_CTRL_CK_STOP_ON	(1 << 31)#define		VRC5477_CTRL_DAC2ENB		(1 << 15) #define		VRC5477_CTRL_ADC2ENB		(1 << 14) #define		VRC5477_CTRL_DAC1ENB		(1 << 13) #define		VRC5477_CTRL_ADC1ENB		(1 << 12) #define		VRC5477_INT_MASK_NMASK		(1 << 31) #define		VRC5477_INT_MASK_DAC1END	(1 << 5) #define		VRC5477_INT_MASK_DAC2END	(1 << 4) #define		VRC5477_INT_MASK_DAC3END	(1 << 3) #define		VRC5477_INT_MASK_ADC1END	(1 << 2) #define		VRC5477_INT_MASK_ADC2END	(1 << 1) #define		VRC5477_INT_MASK_ADC3END	(1 << 0) #define		VRC5477_DMA_ACTIVATION		(1 << 31)#define		VRC5477_DMA_WIP			(1 << 30)#define VRC5477_AC97_MODULE_NAME "NEC_Vrc5477_audio"#define PFX VRC5477_AC97_MODULE_NAME ": "/* --------------------------------------------------------------------- */struct vrc5477_ac97_state {	/* list of vrc5477_ac97 devices */	struct list_head devs;	/* the corresponding pci_dev structure */	struct pci_dev *dev;	/* soundcore stuff */	int dev_audio;	/* hardware resources */	unsigned long io;	unsigned int irq;#ifdef VRC5477_AC97_DEBUG	/* debug /proc entry */	struct proc_dir_entry *ps;	struct proc_dir_entry *ac97_ps;#endif /* VRC5477_AC97_DEBUG */	struct ac97_codec *codec;	unsigned dacChannels, adcChannels;	unsigned short dacRate, adcRate;	unsigned short extended_status;	spinlock_t lock;	struct semaphore open_sem;	mode_t open_mode;	wait_queue_head_t open_wait;	struct dmabuf {		void *lbuf, *rbuf;		dma_addr_t lbufDma, rbufDma;		unsigned bufOrder;		unsigned numFrag;		unsigned fragShift;		unsigned fragSize;	/* redundant */		unsigned fragTotalSize;	/* = numFrag * fragSize(real)  */		unsigned nextIn;		unsigned nextOut;		int count;		unsigned error; /* over/underrun */		wait_queue_head_t wait;		/* OSS stuff */		unsigned stopped:1;		unsigned ready:1;	} dma_dac, dma_adc;	#define	WORK_BUF_SIZE	2048	struct {		u16 lchannel;		u16 rchannel;	} workBuf[WORK_BUF_SIZE/4];};/* --------------------------------------------------------------------- */static LIST_HEAD(devs);/* --------------------------------------------------------------------- */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;}/* --------------------------------------------------------------------- */static u16 rdcodec(struct ac97_codec *codec, u8 addr){	struct vrc5477_ac97_state *s = 		(struct vrc5477_ac97_state *)codec->private_data;	unsigned long flags;	u32 result;	spin_lock_irqsave(&s->lock, flags);	/* wait until we can access codec registers */	while (inl(s->io + VRC5477_CODEC_WR) & 0x80000000);	/* write the address and "read" command to codec */	addr = addr & 0x7f;	outl((addr << 16) | VRC5477_CODEC_WR_RWC, s->io + VRC5477_CODEC_WR);	/* get the return result */	udelay(100); /* workaround hardware bug */	while ( (result = inl(s->io + VRC5477_CODEC_RD)) &                 (VRC5477_CODEC_RD_RRDYA | VRC5477_CODEC_RD_RRDYD) ) {		/* we get either addr or data, or both */		if (result & VRC5477_CODEC_RD_RRDYA) {			ASSERT(addr == ((result >> 16) & 0x7f) );		}		if (result & VRC5477_CODEC_RD_RRDYD) {			break;		}	}	spin_unlock_irqrestore(&s->lock, flags);	return result & 0xffff;}static void wrcodec(struct ac97_codec *codec, u8 addr, u16 data){	struct vrc5477_ac97_state *s = 		(struct vrc5477_ac97_state *)codec->private_data;	unsigned long flags;	spin_lock_irqsave(&s->lock, flags);	/* wait until we can access codec registers */	while (inl(s->io + VRC5477_CODEC_WR) & 0x80000000);	/* write the address and value to codec */	outl((addr << 16) | data, s->io + VRC5477_CODEC_WR);	spin_unlock_irqrestore(&s->lock, flags);}static void waitcodec(struct ac97_codec *codec){	struct vrc5477_ac97_state *s = 		(struct vrc5477_ac97_state *)codec->private_data;	/* wait until we can access codec registers */	while (inl(s->io + VRC5477_CODEC_WR) & 0x80000000);}static int ac97_codec_not_present(struct ac97_codec *codec){	struct vrc5477_ac97_state *s = 		(struct vrc5477_ac97_state *)codec->private_data;	unsigned long flags;	unsigned short count  = 0xffff; 	spin_lock_irqsave(&s->lock, flags);	/* wait until we can access codec registers */	do {	       if (!(inl(s->io + VRC5477_CODEC_WR) & 0x80000000))		       break;	} while (--count);	if (count == 0) {		spin_unlock_irqrestore(&s->lock, flags);		return -1;	}	/* write 0 to reset */	outl((AC97_RESET << 16) | 0, s->io + VRC5477_CODEC_WR);	/* test whether we get a response from ac97 chip */	count  = 0xffff; 	do { 	       if (!(inl(s->io + VRC5477_CODEC_WR) & 0x80000000))		       break;	} while (--count);	if (count == 0) {		spin_unlock_irqrestore(&s->lock, flags);		return -1;	}	spin_unlock_irqrestore(&s->lock, flags);	return 0;}/* --------------------------------------------------------------------- */static void vrc5477_ac97_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 void set_adc_rate(struct vrc5477_ac97_state *s, unsigned rate){	wrcodec(s->codec, AC97_PCM_LR_ADC_RATE, rate);	s->adcRate = rate;}static void set_dac_rate(struct vrc5477_ac97_state *s, unsigned rate){	if(s->extended_status & AC97_EXTSTAT_VRA) {	wrcodec(s->codec, AC97_PCM_FRONT_DAC_RATE, rate);		s->dacRate = rdcodec(s->codec, AC97_PCM_FRONT_DAC_RATE);	}}/* --------------------------------------------------------------------- */static inline void stop_dac(struct vrc5477_ac97_state *s){	struct dmabuf* db = &s->dma_dac;	unsigned long flags;	u32 temp;    	spin_lock_irqsave(&s->lock, flags);	if (db->stopped) {		spin_unlock_irqrestore(&s->lock, flags);		return;	}	/* deactivate the dma */	outl(0, s->io + VRC5477_DAC1_CTRL);	outl(0, s->io + VRC5477_DAC2_CTRL);	/* wait for DAM completely stop */	while (inl(s->io + VRC5477_DAC1_CTRL) & VRC5477_DMA_WIP);	while (inl(s->io + VRC5477_DAC2_CTRL) & VRC5477_DMA_WIP);	/* disable dac slots in aclink */	temp = inl(s->io + VRC5477_CTRL);	temp &= ~ (VRC5477_CTRL_DAC1ENB | VRC5477_CTRL_DAC2ENB);	outl (temp, s->io + VRC5477_CTRL);	/* disable interrupts */	temp = inl(s->io + VRC5477_INT_MASK);	temp &= ~ (VRC5477_INT_MASK_DAC1END | VRC5477_INT_MASK_DAC2END); 	outl (temp, s->io + VRC5477_INT_MASK);	/* clear pending ones */	outl(VRC5477_INT_MASK_DAC1END | VRC5477_INT_MASK_DAC2END, 	     s->io +  VRC5477_INT_CLR);    	db->stopped = 1;    	spin_unlock_irqrestore(&s->lock, flags);}	static void start_dac(struct vrc5477_ac97_state *s){	struct dmabuf* db = &s->dma_dac;	unsigned long flags;	u32 dmaLength;	u32 temp;	spin_lock_irqsave(&s->lock, flags);	if (!db->stopped) {		spin_unlock_irqrestore(&s->lock, flags);		return;	}	/* we should have some data to do the DMA trasnfer */	ASSERT(db->count >= db->fragSize);	/* clear pending fales interrupts */	outl(VRC5477_INT_MASK_DAC1END | VRC5477_INT_MASK_DAC2END, 	     s->io +  VRC5477_INT_CLR);	/* enable interrupts */	temp = inl(s->io + VRC5477_INT_MASK);	temp |= VRC5477_INT_MASK_DAC1END | VRC5477_INT_MASK_DAC2END;	outl(temp, s->io +  VRC5477_INT_MASK);	/* setup dma base addr */	outl(db->lbufDma + db->nextOut, s->io + VRC5477_DAC1_BADDR);	if (s->dacChannels == 1) {		outl(db->lbufDma + db->nextOut, s->io + VRC5477_DAC2_BADDR);	} else {		outl(db->rbufDma + db->nextOut, s->io + VRC5477_DAC2_BADDR);	}	/* set dma length, in the unit of 0x10 bytes */	dmaLength = db->fragSize >> 4;	outl(dmaLength, s->io + VRC5477_DAC1L);	outl(dmaLength, s->io + VRC5477_DAC2L);	/* activate dma */	outl(VRC5477_DMA_ACTIVATION, s->io + VRC5477_DAC1_CTRL);	outl(VRC5477_DMA_ACTIVATION, s->io + VRC5477_DAC2_CTRL);	/* enable dac slots - we should hear the music now! */	temp = inl(s->io + VRC5477_CTRL);	temp |= (VRC5477_CTRL_DAC1ENB | VRC5477_CTRL_DAC2ENB);	outl (temp, s->io + VRC5477_CTRL);	/* it is time to setup next dma transfer */	ASSERT(inl(s->io + VRC5477_DAC1_CTRL) & VRC5477_DMA_WIP);	ASSERT(inl(s->io + VRC5477_DAC2_CTRL) & VRC5477_DMA_WIP);	temp = db->nextOut + db->fragSize;	if (temp >= db->fragTotalSize) {		ASSERT(temp == db->fragTotalSize);		temp = 0;	}	outl(db->lbufDma + temp, s->io + VRC5477_DAC1_BADDR);	if (s->dacChannels == 1) {		outl(db->lbufDma + temp, s->io + VRC5477_DAC2_BADDR);	} else {		outl(db->rbufDma + temp, s->io + VRC5477_DAC2_BADDR);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?