⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 nec_vrc5477.c

📁 iis s3c2410-uda1341语音系统的 开发
💻 C
📖 第 1 页 / 共 4 页
字号:
/*********************************************************************** * 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) * * 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/version.h>#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/wrapper.h>#include <asm/io.h>#include <asm/dma.h>#include <asm/uaccess.h>#include <asm/hardirq.h>#include <asm/ddb5xxx/debug.h>#undef VRC5477_AC97_VERBOSE_DEBUG/* one must turn on CONFIG_LL_DEBUG before VERBOSE_DEBUG is turned */#if defined(VRC5477_AC97_VERBOSE_DEBUG)#if !defined(CONFIG_LL_DEBUG)#error "You must turn CONFIG_LL_DEBUG"#endif#endif#if defined(VRC5477_AC97_VERBOSE_DEBUG)static u16 inTicket=0; 		/* check sync between intr & write */static u16 outTicket=0;#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 CONFIG_LL_DEBUG	/* debug /proc entry */	struct proc_dir_entry *ps;	struct proc_dir_entry *ac97_ps;#endif /* CONFIG_LL_DEBUG */	struct ac97_codec codec;	unsigned dacChannels, adcChannels;	unsigned short dacRate, adcRate;	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);/* --------------------------------------------------------------------- */extern 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) {			MIPS_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 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){	wrcodec(&s->codec, AC97_PCM_FRONT_DAC_RATE, rate);	s->dacRate = rate;}/* --------------------------------------------------------------------- */extern 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 */	MIPS_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 */	MIPS_ASSERT(inl(s->io + VRC5477_DAC1_CTRL) & VRC5477_DMA_WIP);	MIPS_ASSERT(inl(s->io + VRC5477_DAC2_CTRL) & VRC5477_DMA_WIP);	temp = db->nextOut + db->fragSize;	if (temp >= db->fragTotalSize) {		MIPS_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);	}	db->stopped = 0;#if defined(VRC5477_AC97_VERBOSE_DEBUG)	outTicket = *(u16*)(db->lbuf+db->nextOut);	if (db->count > db->fragSize) {		MIPS_ASSERT((u16)(outTicket+1) == *(u16*)(db->lbuf+temp));	}#endif	spin_unlock_irqrestore(&s->lock, flags);}	extern inline void stop_adc(struct vrc5477_ac97_state *s){	struct dmabuf* db = &s->dma_adc;	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_ADC1_CTRL);	outl(0, s->io + VRC5477_ADC2_CTRL);	/* disable adc slots in aclink */	temp = inl(s->io + VRC5477_CTRL);	temp &= ~ (VRC5477_CTRL_ADC1ENB | VRC5477_CTRL_ADC2ENB);	outl (temp, s->io + VRC5477_CTRL);	/* disable interrupts */        temp = inl(s->io + VRC5477_INT_MASK);        temp &= ~ (VRC5477_INT_MASK_ADC1END | VRC5477_INT_MASK_ADC2END);         outl (temp, s->io + VRC5477_INT_MASK);	/* clear pending ones */	outl(VRC5477_INT_MASK_ADC1END | VRC5477_INT_MASK_ADC2END, 	     s->io +  VRC5477_INT_CLR);    	db->stopped = 1;	spin_unlock_irqrestore(&s->lock, flags);}	static void start_adc(struct vrc5477_ac97_state *s){	struct dmabuf* db = &s->dma_adc;	unsigned long flags;

⌨️ 快捷键说明

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