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

📄 ac97_codec.c

📁 是关于linux2.5.1的完全源码
💻 C
📖 第 1 页 / 共 5 页
字号:
/* *  Copyright (c) by Jaroslav Kysela <perex@suse.cz> *  Universal interface for Audio Codec '97 * *  For more details look to AC '97 component specification revision 2.2 *  by Intel Corporation (http://developer.intel.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 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA * */#include <sound/driver.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/slab.h>#include <sound/core.h>#include <sound/pcm.h>#include <sound/ac97_codec.h>#include <sound/asoundef.h>#include <sound/initval.h>MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");MODULE_DESCRIPTION("Universal interface for Audio Codec '97");MODULE_LICENSE("GPL");static int enable_loopback;MODULE_PARM(enable_loopback, "i");MODULE_PARM_DESC(enable_loopback, "Enable AC97 ADC/DAC Loopback Control");MODULE_PARM_SYNTAX(enable_loopback, SNDRV_BOOLEAN_FALSE_DESC);#define chip_t ac97_t/* */static void snd_ac97_proc_init(snd_card_t * card, ac97_t * ac97);static void snd_ac97_proc_done(ac97_t * ac97);static int patch_wolfson(ac97_t * ac97);static int patch_tritech_tr28028(ac97_t * ac97);static int patch_sigmatel_stac9708(ac97_t * ac97);static int patch_sigmatel_stac9721(ac97_t * ac97);static int patch_sigmatel_stac9744(ac97_t * ac97);static int patch_sigmatel_stac9756(ac97_t * ac97);static int patch_cirrus_cs4299(ac97_t * ac97);static int patch_ad1819(ac97_t * ac97);static int patch_ad1881(ac97_t * ac97);typedef struct {	unsigned int id;	unsigned int mask;	char *name;	int (*patch)(ac97_t *ac97);} ac97_codec_id_t;static const ac97_codec_id_t snd_ac97_codec_id_vendors[] = {{ 0x414b4d00, 0xffffff00, "Asahi Kasei",	NULL },{ 0x41445300, 0xffffff00, "Analog Devices",	NULL },{ 0x414c4300, 0xffffff00, "Realtek",		NULL },{ 0x414c4700, 0xffffff00, "Avance Logic",	NULL },{ 0x43525900, 0xffffff00, "Cirrus Logic",	NULL },{ 0x48525300, 0xffffff00, "Intersil",		NULL },{ 0x49434500, 0xffffff00, "ICEnsemble",		NULL },{ 0x4e534300, 0xffffff00, "National Semiconductor", NULL },{ 0x53494c00, 0xffffff00, "Silicon Laboratory",	NULL },{ 0x54524100, 0xffffff00, "TriTech",		NULL },{ 0x54584e00, 0xffffff00, "Texas Instruments",	NULL },{ 0x57454300, 0xffffff00, "Winbond",		NULL },{ 0x574d4c00, 0xffffff00, "Wolfson",		patch_wolfson },{ 0x594d4800, 0xffffff00, "Yamaha",		NULL },{ 0x83847600, 0xffffff00, "SigmaTel",		NULL },{ 0x45838300, 0xffffff00, "ESS Technology",	NULL },{ 0,	      0, 	  NULL,			NULL }};static const ac97_codec_id_t snd_ac97_codec_ids[] = {{ 0x414b4d00, 0xffffffff, "AK4540",		NULL },{ 0x414b4d01, 0xffffffff, "AK4542",		NULL },{ 0x414b4d02, 0xffffffff, "AK4543",		NULL },{ 0x414b4d06, 0xffffffff, "AK4544A",		NULL },{ 0x414b4d07, 0xffffffff, "AK4545",		NULL },{ 0x41445303, 0xffffffff, "AD1819",		patch_ad1819 },{ 0x41445340, 0xffffffff, "AD1881",		patch_ad1881 },{ 0x41445348, 0xffffffff, "AD1881A",		patch_ad1881 },{ 0x41445360, 0xffffffff, "AD1885",		patch_ad1881 },{ 0x41445361, 0xffffffff, "AD1886",		patch_ad1881 },{ 0x41445362, 0xffffffff, "AD1887",		patch_ad1881 },{ 0x414c4300, 0xfffffff0, "RL5306",	 	NULL },{ 0x414c4310, 0xfffffff0, "RL5382", 		NULL },{ 0x414c4320, 0xfffffff0, "RL5383", 		NULL },{ 0x414c4710, 0xffffffff, "ALC200/200P",	NULL },{ 0x43525900, 0xfffffff8, "CS4297",		NULL },{ 0x43525910, 0xfffffff8, "CS4297A",		NULL },{ 0x42525920, 0xfffffff8, "CS4294/4298",	NULL },{ 0x42525928, 0xfffffff8, "CS4294",		NULL },{ 0x43525930, 0xfffffff8, "CS4299",		patch_cirrus_cs4299 },{ 0x43525948, 0xfffffff8, "CS4201",		NULL },{ 0x43525958, 0xfffffff8, "CS4205",		NULL },{ 0x43525960, 0xfffffff8, "CS4291",		NULL },{ 0x48525300, 0xffffff00, "HMP9701",		NULL },{ 0x49434501, 0xffffffff, "ICE1230",		NULL },{ 0x49434511, 0xffffffff, "ICE1232",		NULL }, // alias VIA VT1611A?{ 0x4e534300, 0xffffffff, "LM4540/43/45/46/48",	NULL }, // only guess --jk{ 0x4e534331, 0xffffffff, "LM4549",		NULL },{ 0x53494c22, 0xffffffff, "Si3036",		NULL },{ 0x53494c23, 0xffffffff, "Si3038",		NULL },{ 0x54524108, 0xffffffff, "TR28028",		patch_tritech_tr28028 }, // added by xin jin [07/09/99]{ 0x54524123, 0xffffffff, "TR28602",		NULL }, // only guess --jk [TR28023 = eMicro EM28023 (new CT1297)]{ 0x54584e20, 0xffffffff, "TLC320AD9xC",	NULL },{ 0x57454301, 0xffffffff, "W83971D",		NULL },{ 0x574d4c00, 0xffffffff, "WM9701A",		NULL },{ 0x574d4c03, 0xffffffff, "WM9703/9704",	NULL },{ 0x574d4c04, 0xffffffff, "WM9704 (quad)",	NULL },{ 0x594d4800, 0xffffffff, "YMF743",		NULL },{ 0x83847600, 0xffffffff, "STAC9700/83/84",	NULL },{ 0x83847604, 0xffffffff, "STAC9701/3/4/5",	NULL },{ 0x83847605, 0xffffffff, "STAC9704",		NULL },{ 0x83847608, 0xffffffff, "STAC9708/11",	patch_sigmatel_stac9708 },{ 0x83847609, 0xffffffff, "STAC9721/23",	patch_sigmatel_stac9721 },{ 0x83847644, 0xffffffff, "STAC9744",		patch_sigmatel_stac9744 },{ 0x83847656, 0xffffffff, "STAC9756/57",	patch_sigmatel_stac9756 },{ 0x45838308, 0xffffffff, "ESS1988",		NULL },{ 0, 	      0,	  NULL,			NULL }};#define AC97_ID_AK4540		0x414b4d00#define AC97_ID_AK4542		0x414b4d01#define AC97_ID_AD1819		0x41445303#define AC97_ID_AD1881		0x41445340#define AC97_ID_AD1881A		0x41445348#define AC97_ID_AD1885		0x41445360#define AC97_ID_AD1886		0x41445361#define AC97_ID_AD1887		0x41445362#define AC97_ID_TR28028		0x54524108#define AC97_ID_STAC9700	0x83847600#define AC97_ID_STAC9704	0x83847604#define AC97_ID_STAC9705	0x83847605#define AC97_ID_STAC9708	0x83847608#define AC97_ID_STAC9721	0x83847609#define AC97_ID_STAC9744	0x83847644#define AC97_ID_STAC9756	0x83847656static const char *snd_ac97_stereo_enhancements[] ={  /*   0 */ "No 3D Stereo Enhancement",  /*   1 */ "Analog Devices Phat Stereo",  /*   2 */ "Creative Stereo Enhancement",  /*   3 */ "National Semi 3D Stereo Enhancement",  /*   4 */ "YAMAHA Ymersion",  /*   5 */ "BBE 3D Stereo Enhancement",  /*   6 */ "Crystal Semi 3D Stereo Enhancement",  /*   7 */ "Qsound QXpander",  /*   8 */ "Spatializer 3D Stereo Enhancement",  /*   9 */ "SRS 3D Stereo Enhancement",  /*  10 */ "Platform Tech 3D Stereo Enhancement",  /*  11 */ "AKM 3D Audio",  /*  12 */ "Aureal Stereo Enhancement",  /*  13 */ "Aztech 3D Enhancement",  /*  14 */ "Binaura 3D Audio Enhancement",  /*  15 */ "ESS Technology Stereo Enhancement",  /*  16 */ "Harman International VMAx",  /*  17 */ "Nvidea 3D Stereo Enhancement",  /*  18 */ "Philips Incredible Sound",  /*  19 */ "Texas Instruments 3D Stereo Enhancement",  /*  20 */ "VLSI Technology 3D Stereo Enhancement",  /*  21 */ "TriTech 3D Stereo Enhancement",  /*  22 */ "Realtek 3D Stereo Enhancement",  /*  23 */ "Samsung 3D Stereo Enhancement",  /*  24 */ "Wolfson Microelectronics 3D Enhancement",  /*  25 */ "Delta Integration 3D Enhancement",  /*  26 */ "SigmaTel 3D Enhancement",  /*  27 */ "Reserved 27",  /*  28 */ "Rockwell 3D Stereo Enhancement",  /*  29 */ "Reserved 29",  /*  30 */ "Reserved 30",  /*  31 */ "Reserved 31"};/* *  I/O routines */static int snd_ac97_valid_reg(ac97_t *ac97, unsigned short reg){	/* filter some registers for buggy codecs */	switch (ac97->id) {	case AC97_ID_AK4540:	case AC97_ID_AK4542:		if (reg <= 0x1c || reg == 0x20 || reg == 0x26 || reg >= 0x7c)			return 1;		return 0;	case AC97_ID_AD1819:	/* AD1819 */	case AC97_ID_AD1881:	/* AD1881 */	case AC97_ID_AD1881A:	/* AD1881A */		if (reg >= 0x3a && reg <= 0x6e)	/* 0x59 */			return 0;		return 1;	case AC97_ID_AD1885:	/* AD1885 */	case AC97_ID_AD1886:	/* AD1886 */	case AC97_ID_AD1887:	/* AD1887 - !!verify!! --jk */		if (reg == 0x5a)			return 1;		if (reg >= 0x3c && reg <= 0x6e)	/* 0x59 */			return 0;		return 1;	case AC97_ID_STAC9700:	case AC97_ID_STAC9704:	case AC97_ID_STAC9705:	case AC97_ID_STAC9708:	case AC97_ID_STAC9721:	case AC97_ID_STAC9744:	case AC97_ID_STAC9756:		if (reg <= 0x3a || reg >= 0x5a)			return 1;		return 0;	}	return 1;}void snd_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short value){	if (!snd_ac97_valid_reg(ac97, reg))		return;	ac97->write(ac97, reg, value);}unsigned short snd_ac97_read(ac97_t *ac97, unsigned short reg){	if (!snd_ac97_valid_reg(ac97, reg))		return 0;	return ac97->read(ac97, reg);}void snd_ac97_write_cache(ac97_t *ac97, unsigned short reg, unsigned short value){	if (!snd_ac97_valid_reg(ac97, reg))		return;	spin_lock(&ac97->reg_lock);	ac97->write(ac97, reg, ac97->regs[reg] = value);	spin_unlock(&ac97->reg_lock);	set_bit(reg, ac97->reg_accessed);}#ifndef CONFIG_SND_DEBUG#define snd_ac97_write_cache_test snd_ac97_write_cache#elsestatic void snd_ac97_write_cache_test(ac97_t *ac97, unsigned short reg, unsigned short value){	return snd_ac97_write_cache(ac97, reg, value);	if (!snd_ac97_valid_reg(ac97, reg))		return;	spin_lock(&ac97->reg_lock);	ac97->write(ac97, reg, value);	ac97->regs[reg] = ac97->read(ac97, reg);	if (value != ac97->regs[reg])		snd_printk("AC97 reg=%02x val=%04x real=%04x\n", reg, value, ac97->regs[reg]);	spin_unlock(&ac97->reg_lock);}#endifint snd_ac97_update(ac97_t *ac97, unsigned short reg, unsigned short value){	int change;	if (!snd_ac97_valid_reg(ac97, reg))		return -EINVAL;	spin_lock(&ac97->reg_lock);	change = ac97->regs[reg] != value;	if (change) {		ac97->write(ac97, reg, value);		ac97->regs[reg] = value;	}	spin_unlock(&ac97->reg_lock);	return change;}int snd_ac97_update_bits(ac97_t *ac97, unsigned short reg, unsigned short mask, unsigned short value){	int change;	unsigned short old, new;	if (!snd_ac97_valid_reg(ac97, reg))		return -EINVAL;	spin_lock(&ac97->reg_lock);	old = ac97->regs[reg];	new = (old & ~mask) | value;	change = old != new;	if (change) {		ac97->write(ac97, reg, new);		ac97->regs[reg] = new;	}	spin_unlock(&ac97->reg_lock);	return change;}int snd_ac97_ad18xx_update_pcm_bits(ac97_t *ac97, int codec, unsigned short mask, unsigned short value){	int change;	unsigned short old, new;	down(&ac97->spec.ad18xx.mutex);	spin_lock(&ac97->reg_lock);	old = ac97->spec.ad18xx.pcmreg[codec];	new = (old & ~mask) | value;	change = old != new;	if (change) {		/* select single codec */		ac97->write(ac97, AC97_AD_SERIAL_CFG, ac97->spec.ad18xx.unchained[codec] | ac97->spec.ad18xx.chained[codec]);		/* update PCM bits */		ac97->write(ac97, AC97_PCM, new);		/* select all codecs */		ac97->write(ac97, AC97_AD_SERIAL_CFG, 0x7000);		ac97->spec.ad18xx.pcmreg[codec] = new;	}	spin_unlock(&ac97->reg_lock);	up(&ac97->spec.ad18xx.mutex);	return change;}/* * */static int snd_ac97_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo){	static char *texts[8] = {		"Mic", "CD", "Video", "Aux", "Line",		"Mix", "Mix Mono", "Phone"	};	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;	uinfo->count = 2;	uinfo->value.enumerated.items = 8;	if (uinfo->value.enumerated.item > 7)		uinfo->value.enumerated.item = 7;	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);	return 0;}static int snd_ac97_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){	ac97_t *ac97 = snd_kcontrol_chip(kcontrol);	unsigned short val;		val = ac97->regs[AC97_REC_SEL];	ucontrol->value.enumerated.item[0] = (val >> 8) & 7;	ucontrol->value.enumerated.item[1] = (val >> 0) & 7;	return 0;}static int snd_ac97_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){	ac97_t *ac97 = snd_kcontrol_chip(kcontrol);	unsigned short val;		if (ucontrol->value.enumerated.item[0] > 7 ||	    ucontrol->value.enumerated.item[1] > 7)		return -EINVAL;	val = (ucontrol->value.enumerated.item[0] << 8) |	      (ucontrol->value.enumerated.item[1] << 0);	return snd_ac97_update(ac97, AC97_REC_SEL, val);}#define AC97_ENUM_DOUBLE(xname, reg, shift, invert) \{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_ac97_info_enum_double, \  get: snd_ac97_get_enum_double, put: snd_ac97_put_enum_double, \  private_value: reg | (shift << 8) | (invert << 24) }static int snd_ac97_info_enum_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo){	static char *texts1[2] = { "pre 3D", "post 3D" };	static char *texts2[2] = { "Mix", "Mic" };

⌨️ 快捷键说明

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