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

📄 i810_audio.c

📁 宋宝华的《Linux设备驱动开发详解》第一版的源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
	type val;							\	if (card->use_mmio)						\		val=read##size(card->iobase_mmio+off);			\	else								\		val=in##size(card->iobase+off);				\	val;								\})#define I810_IOREADL(card, off)		I810_IOREAD(l, u32, card, off)#define I810_IOREADW(card, off)		I810_IOREAD(w, u16, card, off)#define I810_IOREADB(card, off)		I810_IOREAD(b, u8,  card, off)#define I810_IOWRITE(size, val, card, off)				\({									\	if (card->use_mmio)						\		write##size(val, card->iobase_mmio+off);		\	else								\		out##size(val, card->iobase+off);			\})#define I810_IOWRITEL(val, card, off)	I810_IOWRITE(l, val, card, off)#define I810_IOWRITEW(val, card, off)	I810_IOWRITE(w, val, card, off)#define I810_IOWRITEB(val, card, off)	I810_IOWRITE(b, val, card, off)#define GET_CIV(card, port) MODULOP2(I810_IOREADB((card), (port) + OFF_CIV), SG_LEN)#define GET_LVI(card, port) MODULOP2(I810_IOREADB((card), (port) + OFF_LVI), SG_LEN)/* set LVI from CIV */#define CIV_TO_LVI(card, port, off) \	I810_IOWRITEB(MODULOP2(GET_CIV((card), (port)) + (off), SG_LEN), (card), (port) + OFF_LVI)static struct ac97_quirk ac97_quirks[] __devinitdata = {	{		.vendor = 0x0e11,		.device = 0x00b8,		.name = "Compaq Evo D510C",		.type = AC97_TUNE_HP_ONLY	},	{		.vendor = 0x1028,		.device = 0x00d8,		.name = "Dell Precision 530",   /* AD1885 */		.type = AC97_TUNE_HP_ONLY	},	{		.vendor = 0x1028,		.device = 0x0126,		.name = "Dell Optiplex GX260",  /* AD1981A */		.type = AC97_TUNE_HP_ONLY	},	{		.vendor = 0x1028,		.device = 0x012d,		.name = "Dell Precision 450",   /* AD1981B*/		.type = AC97_TUNE_HP_ONLY	},	{       /* FIXME: which codec? */		.vendor = 0x103c,		.device = 0x00c3,		.name = "Hewlett-Packard onboard",		.type = AC97_TUNE_HP_ONLY	},	{		.vendor = 0x103c,		.device = 0x12f1,		.name = "HP xw8200",    /* AD1981B*/		.type = AC97_TUNE_HP_ONLY	},	{		.vendor = 0x103c,		.device = 0x3008,		.name = "HP xw4200",    /* AD1981B*/		.type = AC97_TUNE_HP_ONLY	},	{		.vendor = 0x10f1,		.device = 0x2665,		.name = "Fujitsu-Siemens Celsius",      /* AD1981? */		.type = AC97_TUNE_HP_ONLY	},	{		.vendor = 0x10f1,		.device = 0x2885,		.name = "AMD64 Mobo",   /* ALC650 */		.type = AC97_TUNE_HP_ONLY	},	{		.vendor = 0x110a,		.device = 0x0056,		.name = "Fujitsu-Siemens Scenic",       /* AD1981? */		.type = AC97_TUNE_HP_ONLY	},	{		.vendor = 0x11d4,		.device = 0x5375,		.name = "ADI AD1985 (discrete)",		.type = AC97_TUNE_HP_ONLY	},	{		.vendor = 0x1462,		.device = 0x5470,		.name = "MSI P4 ATX 645 Ultra",		.type = AC97_TUNE_HP_ONLY	},	{		.vendor = 0x1734,		.device = 0x0088,		.name = "Fujitsu-Siemens D1522",	/* AD1981 */		.type = AC97_TUNE_HP_ONLY	},	{		.vendor = 0x8086,		.device = 0x4856,		.name = "Intel D845WN (82801BA)",		.type = AC97_TUNE_SWAP_HP	},	{		.vendor = 0x8086,		.device = 0x4d44,		.name = "Intel D850EMV2",       /* AD1885 */		.type = AC97_TUNE_HP_ONLY	},	{		.vendor = 0x8086,		.device = 0x4d56,		.name = "Intel ICH/AD1885",		.type = AC97_TUNE_HP_ONLY	},	{		.vendor = 0x1028,		.device = 0x012d,		.name = "Dell Precision 450",   /* AD1981B*/		.type = AC97_TUNE_HP_ONLY	},	{		.vendor = 0x103c,		.device = 0x3008,		.name = "HP xw4200",    /* AD1981B*/		.type = AC97_TUNE_HP_ONLY	},	{		.vendor = 0x103c,		.device = 0x12f1,		.name = "HP xw8200",    /* AD1981B*/		.type = AC97_TUNE_HP_ONLY	},	{ } /* terminator */};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 u16 i810_ac97_get(struct ac97_codec *dev, u8 reg);static void i810_ac97_set(struct ac97_codec *dev, u8 reg, u16 data);static u16 i810_ac97_get_mmio(struct ac97_codec *dev, u8 reg);static void i810_ac97_set_mmio(struct ac97_codec *dev, u8 reg, u16 data);static u16 i810_ac97_get_io(struct ac97_codec *dev, u8 reg);static void i810_ac97_set_io(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;	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;	return &card->channel[0];}static struct i810_channel *i810_alloc_rec_mic_channel(struct i810_card *card){	if(card->channel[2].used==1)		return NULL;	card->channel[2].used=1;	return &card->channel[2];}static void i810_free_pcm_channel(struct i810_card *card, int channel){	card->channel[channel].used=0;}static int i810_valid_spdif_rate ( struct ac97_codec *codec, int rate ){	unsigned long id = 0L;	id = (i810_ac97_get(codec, AC97_VENDOR_ID1) << 16);	id |= i810_ac97_get(codec, AC97_VENDOR_ID2) & 0xffff;#ifdef DEBUG	printk ( "i810_audio: codec = %s, codec_id = 0x%08lx\n", codec->name, id);#endif	switch ( id ) {		case 0x41445361: /* AD1886 */			if (rate == 48000) {				return 1;			}			break;		default: /* all other codecs, until we know otherwiae */			if (rate == 48000 || rate == 44100 || rate == 32000) {				return 1;			}			break;	}	return (0);}/* i810_set_spdif_output *  *  Configure the S/PDIF output transmitter. When we turn on *  S/PDIF, we turn off the analog output. This may not be *  the right thing to do. * *  Assumptions: *     The DSP sample rate must already be set to a supported *     S/PDIF rate (32kHz, 44.1kHz, or 48kHz) or we abort. */static int i810_set_spdif_output(struct i810_state *state, int slots, int rate){	int	vol;	int	aud_reg;	int	r = 0;	struct ac97_codec *codec = state->card->ac97_codec[0];	if(!codec->codec_ops->digital) {		state->card->ac97_status &= ~SPDIF_ON;	} else {		if ( slots == -1 ) { /* Turn off S/PDIF */			codec->codec_ops->digital(codec, 0, 0, 0);			/* If the volume wasn't muted before we turned on S/PDIF, unmute it */			if ( !(state->card->ac97_status & VOL_MUTED) ) {				aud_reg = i810_ac97_get(codec, AC97_MASTER_VOL_STEREO);				i810_ac97_set(codec, AC97_MASTER_VOL_STEREO, (aud_reg & ~VOL_MUTED));			}			state->card->ac97_status &= ~(VOL_MUTED | SPDIF_ON);			return 0;		}		vol = i810_ac97_get(codec, AC97_MASTER_VOL_STEREO);		state->card->ac97_status = vol & VOL_MUTED;				r = codec->codec_ops->digital(codec, slots, rate, 0);		if(r)			state->card->ac97_status |= SPDIF_ON;		else			state->card->ac97_status &= ~SPDIF_ON;		/* Mute the analog output */		/* Should this only mute the PCM volume??? */		i810_ac97_set(codec, AC97_MASTER_VOL_STEREO, (vol | VOL_MUTED));	}	return r;}/* i810_set_dac_channels * *  Configure the codec's multi-channel DACs * *  The logic is backwards. Setting the bit to 1 turns off the DAC.  * *  What about the ICH? We currently configure it using the *  SNDCTL_DSP_CHANNELS ioctl.  If we're turnning on the DAC,  *  does that imply that we want the ICH set to support *  these channels? *   *  TODO: *    vailidate that the codec really supports these DACs *    before turning them on.  */static void i810_set_dac_channels(struct i810_state *state, int channel){	int	aud_reg;	struct ac97_codec *codec = state->card->ac97_codec[0];		/* No codec, no setup */		if(codec == NULL)		return;	aud_reg = i810_ac97_get(codec, AC97_EXTENDED_STATUS);	aud_reg |= AC97_EA_PRI | AC97_EA_PRJ | AC97_EA_PRK;	state->card->ac97_status &= ~(SURR_ON | CENTER_LFE_ON);	switch ( channel ) {		case 2: /* always enabled */			break;		case 4:			aud_reg &= ~AC97_EA_PRJ;			state->card->ac97_status |= SURR_ON;			break;		case 6:			aud_reg &= ~(AC97_EA_PRJ | AC97_EA_PRI | AC97_EA_PRK);			state->card->ac97_status |= SURR_ON | CENTER_LFE_ON;			break;		default:			break;	}	i810_ac97_set(codec, AC97_EXTENDED_STATUS, aud_reg);}/* set playback sample rate */static unsigned int i810_set_dac_rate(struct i810_state * state, unsigned int rate){		struct dmabuf *dmabuf = &state->dmabuf;	u32 new_rate;	struct ac97_codec *codec=state->card->ac97_codec[0];		if(!(state->card->ac97_features&0x0001))	{		dmabuf->rate = clocking;#ifdef DEBUG		printk("Asked for %d Hz, but ac97_features says we only do %dHz.  Sorry!\n",		       rate,clocking);#endif		       		return clocking;	}				if (rate > 48000)		rate = 48000;	if (rate < 8000)		rate = 8000;	dmabuf->rate = rate;			/*	 *	Adjust for misclocked crap	 */	rate = ( rate * clocking)/48000;	if(strict_clocking && rate < 8000) {		rate = 8000;		dmabuf->rate = (rate * 48000)/clocking;	}        new_rate=ac97_set_dac_rate(codec, rate);	if(new_rate != rate) {		dmabuf->rate = (new_rate * 48000)/clocking;	}#ifdef DEBUG	printk("i810_audio: called i810_set_dac_rate : asked for %d, got %d\n", rate, dmabuf->rate);#endif	rate = new_rate;	return dmabuf->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 new_rate;	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;	dmabuf->rate = rate;	/*	 *	Adjust for misclocked crap	 */	 	rate = ( rate * clocking)/48000;	if(strict_clocking && rate < 8000) {		rate = 8000;		dmabuf->rate = (rate * 48000)/clocking;	}	new_rate = ac97_set_adc_rate(codec, rate);		if(new_rate != rate) {		dmabuf->rate = (new_rate * 48000)/clocking;		rate = new_rate;	}#ifdef DEBUG	printk("i810_audio: called i810_set_adc_rate : rate = %d/%d\n", dmabuf->rate, rate);#endif	return dmabuf->rate;}/* get current playback/recording dma buffer pointer (byte offset from LBA),   called with spinlock held! */   static inline unsigned i810_get_dma_addr(struct i810_state *state, int rec){	struct dmabuf *dmabuf = &state->dmabuf;	unsigned int civ, offset, port, port_picb, bytes = 2;		if (!dmabuf->enable)		return 0;	if (rec)		port = dmabuf->read_channel->port;	else		port = dmabuf->write_channel->port;	if(state->card->pci_id == PCI_DEVICE_ID_SI_7012) {		port_picb = port + OFF_SR;		bytes = 1;	} else		port_picb = port + OFF_PICB;	do {		civ = GET_CIV(state->card, port);		offset = I810_IOREADW(state->card, port_picb);		/* Must have a delay here! */ 		if(offset == 0)			udelay(1);		/* Reread both registers and make sure that that total		 * offset from the first reading to the second is 0.		 * There is an issue with SiS hardware where it will count		 * picb down to 0, then update civ to the next value,		 * then set the new picb to fragsize bytes.  We can catch		 * it between the civ update and the picb update, making		 * it look as though we are 1 fragsize ahead of where we		 * are.  The next to we get the address though, it will		 * be back in the right place, and we will suddenly think		 * we just went forward dmasize - fragsize bytes, causing		 * totally stupid *huge* dma overrun messages.  We are		 * assuming that the 1us delay is more than long enough		 * that we won't have to worry about the chip still being		 * out of sync with reality ;-)		 */	} while (civ != GET_CIV(state->card, port) || offset != I810_IOREADW(state->card, port_picb));		 	return (((civ + 1) * dmabuf->fragsize - (bytes * offset))		% dmabuf->dmasize);}/* Stop recording (lock held) */static inline void __stop_adc(struct i810_state *state){	struct dmabuf *dmabuf = &state->dmabuf;	struct i810_card *card = state->card;	dmabuf->enable &= ~ADC_RUNNING;	I810_IOWRITEB(0, card, PI_CR);	// wait for the card to acknowledge shutdown	while( I810_IOREADB(card, PI_CR) != 0 ) ;	// now clear any latent interrupt bits (like the halt bit)

⌨️ 快捷键说明

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