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

📄 i810_audio.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* *	Intel i810 and friends ICH driver for Linux *	Alan Cox <alan@redhat.com> * *  Built from: *	Low level code:  Zach Brown (original nonworking i810 OSS driver) *			 Jaroslav Kysela <perex@suse.cz> (working ALSA driver) * *	Framework: Thomas Sailer <sailer@ife.ee.ethz.ch> *	Extended by: Zach Brown <zab@redhat.com>   *			and others.. * *  Hardware Provided By: *	Analog Devices (A major AC97 codec maker) *	Intel Corp  (you've probably heard of them already) * * AC97 clues and assistance provided by *	Analog Devices *	Zach 'Fufu' Brown *	Jeff Garzik * *	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 program is distributed in the hope that it will be useful, *	but WITHOUT ANY WARRANTY; without even the implied warranty of *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the *	GNU General Public License for more details. * *	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. * * *	Intel 810 theory of operation * *	The chipset provides three DMA channels that talk to an AC97 *	CODEC (AC97 is a digital/analog mixer standard). At its simplest *	you get 48Khz audio with basic volume and mixer controls. At the *	best you get rate adaption in the codec. We set the card up so *	that we never take completion interrupts but instead keep the card *	chasing its tail around a ring buffer. This is needed for mmap *	mode audio and happens to work rather well for non-mmap modes too. * *	The board has one output channel for PCM audio (supported) and *	a stereo line in and mono microphone input. Again these are normally *	locked to 48Khz only. Right now recording is not finished. * *	There is no midi support, no synth support. Use timidity. To get *	esd working you need to use esd -r 48000 as it won't probe 48KHz *	by default. mpg123 can't handle 48Khz only audio so use xmms. * *	Fix The Sound On Dell * *	Not everyone uses 48KHz. We know of no way to detect this reliably *	and certainly not to get the right data. If your i810 audio sounds *	stupid you may need to investigate other speeds. According to Analog *	they tend to use a 14.318MHz clock which gives you a base rate of *	41194Hz. * *	This is available via the 'ftsodell=1' option.  * *	If you need to force a specific rate set the clocking= option */ #include <linux/module.h>#include <linux/version.h>#include <linux/string.h>#include <linux/ctype.h>#include <linux/ioport.h>#include <linux/sched.h>#include <linux/delay.h>#include <linux/sound.h>#include <linux/malloc.h>#include <linux/soundcard.h>#include <linux/pci.h>#include <asm/io.h>#include <asm/dma.h>#include <linux/init.h>#include <linux/poll.h>#include <linux/spinlock.h>#include <linux/smp_lock.h>#include <linux/ac97_codec.h>#include <linux/wrapper.h>#include <asm/uaccess.h>#include <asm/hardirq.h>#ifndef PCI_DEVICE_ID_INTEL_82801#define PCI_DEVICE_ID_INTEL_82801	0x2415#endif#ifndef PCI_DEVICE_ID_INTEL_82901#define PCI_DEVICE_ID_INTEL_82901	0x2425#endif#ifndef PCI_DEVICE_ID_INTEL_ICH2#define PCI_DEVICE_ID_INTEL_ICH2	0x2445#endif#ifndef PCI_DEVICE_ID_INTEL_440MX#define PCI_DEVICE_ID_INTEL_440MX	0x7195#endifstatic int ftsodell=0;static unsigned int clocking=48000;#define ADC_RUNNING	1#define DAC_RUNNING	2#define I810_FMT_16BIT	1#define I810_FMT_STEREO	2#define I810_FMT_MASK	3/* the 810's array of pointers to data buffers */struct sg_item {#define BUSADDR_MASK	0xFFFFFFFE	u32 busaddr;	#define CON_IOC 	0x80000000 /* interrupt on completion */#define CON_BUFPAD	0x40000000 /* pad underrun with last sample, else 0 */#define CON_BUFLEN_MASK	0x0000ffff /* buffer length in samples */	u32 control;};/* an instance of the i810 channel */#define SG_LEN 32struct i810_channel {	/* these sg guys should probably be allocated	   seperately as nocache. Must be 8 byte aligned */	struct sg_item sg[SG_LEN];	/* 32*8 */	u32 offset;			/* 4 */	u32 port;			/* 4 */	u32 used;	u32 num;};/* * we have 3 seperate dma engines.  pcm in, pcm out, and mic. * each dma engine has controlling registers.  These goofy * names are from the datasheet, but make it easy to write * code while leafing through it. */#define ENUM_ENGINE(PRE,DIG) 									\enum {												\	PRE##_BDBAR =	0x##DIG##0,		/* Buffer Descriptor list Base Address */	\	PRE##_CIV =	0x##DIG##4,		/* Current Index Value */			\	PRE##_LVI =	0x##DIG##5,		/* Last Valid Index */				\	PRE##_SR =	0x##DIG##6,		/* Status Register */				\	PRE##_PICB =	0x##DIG##8,		/* Position In Current Buffer */		\	PRE##_PIV =	0x##DIG##a,		/* Prefetched Index Value */			\	PRE##_CR =	0x##DIG##b		/* Control Register */				\}ENUM_ENGINE(OFF,0);	/* Offsets */ENUM_ENGINE(PI,0);	/* PCM In */ENUM_ENGINE(PO,1);	/* PCM Out */ENUM_ENGINE(MC,2);	/* Mic In */enum {	GLOB_CNT =	0x2c,			/* Global Control */	GLOB_STA = 	0x30,			/* Global Status */	CAS	 = 	0x34			/* Codec Write Semaphore Register */};/* interrupts for a dma engine */#define DMA_INT_FIFO		(1<<4)  /* fifo under/over flow */#define DMA_INT_COMPLETE	(1<<3)  /* buffer read/write complete and ioc set */#define DMA_INT_LVI		(1<<2)  /* last valid done */#define DMA_INT_CELV		(1<<1)  /* last valid is current */#define DMA_INT_MASK (DMA_INT_FIFO|DMA_INT_COMPLETE|DMA_INT_LVI)/* interrupts for the whole chip */#define INT_SEC		(1<<11)#define INT_PRI		(1<<10)#define INT_MC		(1<<7)#define INT_PO		(1<<6)#define INT_PI		(1<<5)#define INT_MO		(1<<2)#define INT_NI		(1<<1)#define INT_GPI		(1<<0)#define INT_MASK (INT_SEC|INT_PRI|INT_MC|INT_PO|INT_PI|INT_MO|INT_NI|INT_GPI)#define DRIVER_VERSION "0.01"/* magic numbers to protect our data structures */#define I810_CARD_MAGIC		0x5072696E /* "Prin" */#define I810_STATE_MAGIC	0x63657373 /* "cess" */#define I810_DMA_MASK		0xffffffff /* DMA buffer mask for pci_alloc_consist */#define NR_HW_CH		3/* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */#define NR_AC97		2static const unsigned sample_size[] = { 1, 2, 2, 4 };static const unsigned sample_shift[] = { 0, 1, 1, 2 };enum {	ICH82801AA = 0,	ICH82901AB,	INTEL440MX,	INTELICH2,};static char * card_names[] = {	"Intel ICH 82801AA",	"Intel ICH 82901AB",	"Intel 440MX",	"Intel ICH2"};static struct pci_device_id i810_pci_tbl [] __initdata = {	{PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801,	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, ICH82801AA},	{PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82901,	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, ICH82901AB},	{PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_440MX,	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, INTEL440MX},	{PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH2,	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, INTELICH2},	{0,}};MODULE_DEVICE_TABLE (pci, i810_pci_tbl);/* "software" or virtual channel, an instance of opened /dev/dsp */struct i810_state {	unsigned int magic;	struct i810_card *card;	/* Card info */	/* single open lock mechanism, only used for recording */	struct semaphore open_sem;	wait_queue_head_t open_wait;	/* file mode */	mode_t open_mode;	/* virtual channel number */	int virt;	struct dmabuf {		/* wave sample stuff */		unsigned int rate;		unsigned char fmt, enable;		/* hardware channel */		struct i810_channel *channel;		/* OSS buffer management stuff */		void *rawbuf;		dma_addr_t dma_handle;		unsigned buforder;		unsigned numfrag;		unsigned fragshift;		/* our buffer acts like a circular ring */		unsigned hwptr;		/* where dma last started, updated by update_ptr */		unsigned swptr;		/* where driver last clear/filled, updated by read/write */		int count;		/* bytes to be comsumed or been generated by dma machine */		unsigned total_bytes;	/* total bytes dmaed by hardware */		unsigned error;		/* number of over/underruns */		wait_queue_head_t wait;	/* put process on wait queue when no more space in buffer */		/* redundant, but makes calculations easier */		unsigned fragsize;		unsigned dmasize;		unsigned fragsamples;		/* OSS stuff */		unsigned mapped:1;		unsigned ready:1;		unsigned endcleared:1;		unsigned update_flag;		unsigned ossfragshift;		int ossmaxfrags;		unsigned subdivision;	} dmabuf;};struct i810_card {	struct i810_channel channel[3];	unsigned int magic;	/* We keep i810 cards in a linked list */	struct i810_card *next;	/* The i810 has a certain amount of cross channel interaction	   so we use a single per card lock */	spinlock_t lock;	/* PCI device stuff */	struct pci_dev * pci_dev;	u16 pci_id;	/* soundcore stuff */	int dev_audio;	/* structures for abstraction of hardware facilities, codecs, banks and channels*/	struct ac97_codec *ac97_codec[NR_AC97];	struct i810_state *states[NR_HW_CH];	u16 ac97_features;		/* hardware resources */	unsigned long iobase;	unsigned long ac97base;	u32 irq;		/* Function support */	struct i810_channel *(*alloc_pcm_channel)(struct i810_card *);	struct i810_channel *(*alloc_rec_pcm_channel)(struct i810_card *);	void (*free_pcm_channel)(struct i810_card *, int chan);};static struct i810_card *devs = NULL;static int i810_open_mixdev(struct inode *inode, struct file *file);static int i810_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd,				unsigned long arg);static loff_t i810_llseek(struct file *file, loff_t offset, int origin);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 i810_ac97_get(struct ac97_codec *dev, u8 reg);static void i810_ac97_set(struct ac97_codec *dev, u8 reg, u16 data);static struct i810_channel *i810_alloc_pcm_channel(struct i810_card *card){	if(card->channel[1].used==1)		return NULL;	card->channel[1].used=1;	card->channel[1].offset = 0;	card->channel[1].port = 0x10;	card->channel[1].num=1;	return &card->channel[1];}static struct i810_channel *i810_alloc_rec_pcm_channel(struct i810_card *card){	if(card->channel[0].used==1)		return NULL;	card->channel[0].used=1;	card->channel[0].offset = 0;	card->channel[0].port = 0x00;	card->channel[1].num=0;	return &card->channel[0];}static void i810_free_pcm_channel(struct i810_card *card, int channel){	card->channel[channel].used=0;}/* set playback sample rate */static unsigned int i810_set_dac_rate(struct i810_state * state, unsigned int rate){		struct dmabuf *dmabuf = &state->dmabuf;	u32 dacp;	struct ac97_codec *codec=state->card->ac97_codec[0];		if(!(state->card->ac97_features&0x0001))	{		dmabuf->rate = clocking;		return clocking;	}				if (rate > 48000)		rate = 48000;	if (rate < 8000)		rate = 8000;			/*	 *	Adjust for misclocked crap	 */	 	rate = ( rate * clocking)/48000;		/* Analog codecs can go lower via magic registers but others	   might not */	   	if(rate < 8000)		rate = 8000;	if(rate != i810_ac97_get(codec, AC97_PCM_FRONT_DAC_RATE))	{		/* Power down the DAC */		dacp=i810_ac97_get(codec, AC97_POWER_CONTROL);		i810_ac97_set(codec, AC97_POWER_CONTROL, dacp|0x0200);		/* Load the rate and read the effective rate */		i810_ac97_set(codec, AC97_PCM_FRONT_DAC_RATE, rate);		rate=i810_ac97_get(codec, AC97_PCM_FRONT_DAC_RATE);		/* Power it back up */		i810_ac97_set(codec, AC97_POWER_CONTROL, dacp);	}	rate=(rate * 48000) / clocking;	dmabuf->rate = rate;#ifdef DEBUG	printk("i810_audio: called i810_set_dac_rate : rate = %d\n", rate);#endif	return rate;}/* set recording sample rate */static unsigned int i810_set_adc_rate(struct i810_state * state, unsigned int rate){	struct dmabuf *dmabuf = &state->dmabuf;	u32 dacp;	struct ac97_codec *codec=state->card->ac97_codec[0];		if(!(state->card->ac97_features&0x0001))	{		dmabuf->rate = clocking;		return clocking;	}				if (rate > 48000)		rate = 48000;	if (rate < 8000)		rate = 8000;	/*	 *	Adjust for misclocked crap	 */	 	rate = ( rate * clocking)/48000;		/* Analog codecs can go lower via magic registers but others	   might not */	   	if(rate < 8000)		rate = 8000;	if(rate != i810_ac97_get(codec, AC97_PCM_LR_DAC_RATE))	{		/* Power down the ADC */		dacp=i810_ac97_get(codec, AC97_POWER_CONTROL);		i810_ac97_set(codec, AC97_POWER_CONTROL, dacp|0x0100);		/* Load the rate and read the effective rate */		i810_ac97_set(codec, AC97_PCM_LR_DAC_RATE, rate);		rate=i810_ac97_get(codec, AC97_PCM_LR_DAC_RATE);		/* Power it back up */		i810_ac97_set(codec, AC97_POWER_CONTROL, dacp);	}	rate = (rate * 48000) / clocking;	dmabuf->rate = rate;#ifdef DEBUG	printk("i810_audio: called i810_set_adc_rate : rate = %d\n", rate);#endif	return rate;}/* prepare channel attributes for playback */ static void i810_play_setup(struct i810_state *state){//	struct dmabuf *dmabuf = &state->dmabuf;//	struct i810_channel *channel = dmabuf->channel;	/* Fixed format. .. */	//if (dmabuf->fmt & I810_FMT_16BIT)	//if (dmabuf->fmt & I810_FMT_STEREO)}/* prepare channel attributes for recording */static void i810_rec_setup(struct i810_state *state)

⌨️ 快捷键说明

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