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

📄 ac97_codec.c

📁 Source files for pxa ac97 sound driver The packet contains the files that is listed below driv
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * ac97_codec.c: Generic AC97 mixer/modem module * * Derived from ac97 mixer in maestro and trident driver. * * Copyright 2000 Silicon Integrated System Corporation * *	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. * ************************************************************************** * * The Intel Audio Codec '97 specification is available at the Intel * audio homepage: http://developer.intel.com/ial/scalableplatforms/audio/ * * The specification itself is currently available at: * ftp://download.intel.com/ial/scalableplatforms/ac97r22.pdf * ************************************************************************** * * History * v0.4 Mar 15 2000 Ollie Lho *	dual codecs support verified with 4 channels output * v0.3 Feb 22 2000 Ollie Lho *	bug fix for record mask setting * v0.2 Feb 10 2000 Ollie Lho *	add ac97_read_proc for /proc/driver/{vendor}/ac97 * v0.1 Jan 14 2000 Ollie Lho <ollie@sis.com.tw>  *	Isolated from trident.c to support multiple ac97 codec */#include <linux/module.h>#include <linux/version.h>#include <linux/kernel.h>#include <linux/string.h>#include <linux/errno.h>#include <linux/bitops.h>#include <linux/delay.h>#include <linux/ac97_codec.h>#include <asm/uaccess.h>static int ac97_read_mixer(struct ac97_codec *codec, int oss_channel);static void ac97_write_mixer(struct ac97_codec *codec, int oss_channel, 			     unsigned int left, unsigned int right);static void ac97_set_mixer(struct ac97_codec *codec, unsigned int oss_mixer, unsigned int val );static int ac97_recmask_io(struct ac97_codec *codec, int rw, int mask);static int ac97_mixer_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg);static int ac97_init_mixer(struct ac97_codec *codec);static int wolfson_init(struct ac97_codec * codec);static int tritech_init(struct ac97_codec * codec);static int tritech_maestro_init(struct ac97_codec * codec);static int sigmatel_9708_init(struct ac97_codec *codec);static int sigmatel_9721_init(struct ac97_codec *codec);static int sigmatel_9744_init(struct ac97_codec *codec);static int eapd_control(struct ac97_codec *codec, int);static int crystal_digital_control(struct ac97_codec *codec, int mode);/* *	AC97 operations. * *	If you are adding a codec then you should be able to use *		eapd_ops - any codec that supports EAPD amp control (most) *		null_ops - any ancient codec that supports nothing * *	The three functions are *		init - used for non AC97 standard initialisation *		amplifier - used to do amplifier control (1=on 0=off) *		digital - switch to digital modes (0 = analog) * *	Not all codecs support all features, not all drivers use all the *	operations yet */ static struct ac97_ops null_ops = { NULL, NULL, NULL };static struct ac97_ops default_ops = { NULL, eapd_control, NULL };static struct ac97_ops wolfson_ops = { wolfson_init, NULL, NULL };static struct ac97_ops tritech_ops = { tritech_init, NULL, NULL };static struct ac97_ops tritech_m_ops = { tritech_maestro_init, NULL, NULL };static struct ac97_ops sigmatel_9708_ops = { sigmatel_9708_init, NULL, NULL };static struct ac97_ops sigmatel_9721_ops = { sigmatel_9721_init, NULL, NULL };static struct ac97_ops sigmatel_9744_ops = { sigmatel_9744_init, NULL, NULL };static struct ac97_ops crystal_digital_ops = { NULL, eapd_control, crystal_digital_control };/* sorted by vendor/device id */static const struct {	u32 id;	char *name;	struct ac97_ops *ops;} ac97_codec_ids[] = {	{0x41445303, "Analog Devices AD1819",	&null_ops},	{0x41445340, "Analog Devices AD1881",	&null_ops},	{0x41445348, "Analog Devices AD1881A",	&null_ops},	{0x41445360, "Analog Devices AD1885",	&default_ops},	{0x41445460, "Analog Devices AD1885",	&default_ops},	{0x414B4D00, "Asahi Kasei AK4540",	&null_ops},	{0x414B4D01, "Asahi Kasei AK4542",	&null_ops},	{0x414B4D02, "Asahi Kasei AK4543",	&null_ops},	{0x414C4710, "ALC200/200P",		&null_ops},	{0x43525900, "Cirrus Logic CS4297",	&default_ops},	{0x43525903, "Cirrus Logic CS4297",	&default_ops},	{0x43525913, "Cirrus Logic CS4297A rev A", &default_ops},	{0x43525914, "Cirrus Logic CS4297A rev B", &default_ops},	{0x43525923, "Cirrus Logic CS4298",	&null_ops},	{0x4352592B, "Cirrus Logic CS4294",	&null_ops},	{0x4352592D, "Cirrus Logic CS4294",	&null_ops},	{0x43525931, "Cirrus Logic CS4299 rev A", &crystal_digital_ops},	{0x43525933, "Cirrus Logic CS4299 rev C", &crystal_digital_ops},	{0x43525934, "Cirrus Logic CS4299 rev D", &crystal_digital_ops},	{0x4352594d, "Cirrus Logic CS4201"	, &null_ops},	{0x45838308, "ESS Allegro ES1988",	&null_ops},	{0x49434511, "ICE1232",			&null_ops}, /* I hope --jk */	{0x4e534331, "National Semiconductor LM4549", &null_ops},	{0x50534304, "Philips UCB1400",		&default_ops},	{0x53494c22, "Silicon Laboratory Si3036", &null_ops},	{0x53494c23, "Silicon Laboratory Si3038", &null_ops},	{0x545200FF, "TriTech TR?????",		&tritech_m_ops},	{0x54524102, "TriTech TR28022",		&null_ops},	{0x54524103, "TriTech TR28023",		&null_ops},	{0x54524106, "TriTech TR28026",		&null_ops},	{0x54524108, "TriTech TR28028",		&tritech_ops},	{0x54524123, "TriTech TR A5",		&null_ops},	{0x574D4C00, "Wolfson WM9704",		&wolfson_ops},	{0x574D4C03, "Wolfson WM9703/9704",	&wolfson_ops},	{0x574D4C04, "Wolfson WM9704 (quad)",	&wolfson_ops},	{0x83847600, "SigmaTel STAC????",	&null_ops},	{0x83847604, "SigmaTel STAC9701/3/4/5", &null_ops},	{0x83847605, "SigmaTel STAC9704",	&null_ops},	{0x83847608, "SigmaTel STAC9708",	&sigmatel_9708_ops},	{0x83847609, "SigmaTel STAC9721/23",	&sigmatel_9721_ops},	{0x83847644, "SigmaTel STAC9744/45",	&sigmatel_9744_ops},	{0x83847656, "SigmaTel STAC9756/57",	&sigmatel_9744_ops},	{0x83847684, "SigmaTel STAC9783/84?",	&null_ops},	{0x57454301, "Winbond 83971D",		&null_ops},};static const char *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 */ "Winbond 3D Stereo Enhancement",	/*  28 */ "Rockwell 3D Stereo Enhancement",	/*  29 */ "Reserved 29",	/*  30 */ "Reserved 30",	/*  31 */ "Reserved 31"};/* this table has default mixer values for all OSS mixers. */static struct mixer_defaults {	int mixer;	unsigned int value;} mixer_defaults[SOUND_MIXER_NRDEVICES] = {	/* all values 0 -> 100 in bytes */	{SOUND_MIXER_VOLUME,	0x4343},	{SOUND_MIXER_BASS,	0x4343},	{SOUND_MIXER_TREBLE,	0x4343},	{SOUND_MIXER_PCM,	0x4343},	{SOUND_MIXER_SPEAKER,	0x4343},	{SOUND_MIXER_LINE,	0x4343},	{SOUND_MIXER_MIC,	0x0000},	{SOUND_MIXER_CD,	0x4343},	{SOUND_MIXER_ALTPCM,	0x4343},	{SOUND_MIXER_IGAIN,	0x4343},	{SOUND_MIXER_LINE1,	0x4343},	{SOUND_MIXER_PHONEIN,	0x4343},	{SOUND_MIXER_PHONEOUT,	0x4343},	{SOUND_MIXER_VIDEO,	0x4343},	{-1,0}};/* table to scale scale from OSS mixer value to AC97 mixer register value */	static struct ac97_mixer_hw {	unsigned char offset;	int scale;} ac97_hw[SOUND_MIXER_NRDEVICES]= {	[SOUND_MIXER_VOLUME]	=	{AC97_MASTER_VOL_STEREO,64},	[SOUND_MIXER_BASS]	=	{AC97_MASTER_TONE,	16},	[SOUND_MIXER_TREBLE]	=	{AC97_MASTER_TONE,	16},	[SOUND_MIXER_PCM]	=	{AC97_PCMOUT_VOL,	32},	[SOUND_MIXER_SPEAKER]	=	{AC97_PCBEEP_VOL,	16},	[SOUND_MIXER_LINE]	=	{AC97_LINEIN_VOL,	32},	[SOUND_MIXER_MIC]	=	{AC97_MIC_VOL,		32},	[SOUND_MIXER_CD]	=	{AC97_CD_VOL,		32},	[SOUND_MIXER_ALTPCM]	=	{AC97_HEADPHONE_VOL,	64},	[SOUND_MIXER_IGAIN]	=	{AC97_RECORD_GAIN,	16},	[SOUND_MIXER_LINE1]	=	{AC97_AUX_VOL,		32},	[SOUND_MIXER_PHONEIN]	= 	{AC97_PHONE_VOL,	32},	[SOUND_MIXER_PHONEOUT]	= 	{AC97_MASTER_VOL_MONO,	64},	[SOUND_MIXER_VIDEO]	=	{AC97_VIDEO_VOL,	32},};/* the following tables allow us to go from OSS <-> ac97 quickly. */enum ac97_recsettings {	AC97_REC_MIC=0,	AC97_REC_CD,	AC97_REC_VIDEO,	AC97_REC_AUX,	AC97_REC_LINE,	AC97_REC_STEREO, /* combination of all enabled outputs..  */	AC97_REC_MONO,	      /*.. or the mono equivalent */	AC97_REC_PHONE};static const unsigned int ac97_rm2oss[] = {	[AC97_REC_MIC] 	 = SOUND_MIXER_MIC,	[AC97_REC_CD] 	 = SOUND_MIXER_CD,	[AC97_REC_VIDEO] = SOUND_MIXER_VIDEO,	[AC97_REC_AUX] 	 = SOUND_MIXER_LINE1,	[AC97_REC_LINE]  = SOUND_MIXER_LINE,	[AC97_REC_STEREO]= SOUND_MIXER_IGAIN,	[AC97_REC_PHONE] = SOUND_MIXER_PHONEIN};/* indexed by bit position */static const unsigned int ac97_oss_rm[] = {	[SOUND_MIXER_MIC] 	= AC97_REC_MIC,	[SOUND_MIXER_CD] 	= AC97_REC_CD,	[SOUND_MIXER_VIDEO] 	= AC97_REC_VIDEO,	[SOUND_MIXER_LINE1] 	= AC97_REC_AUX,	[SOUND_MIXER_LINE] 	= AC97_REC_LINE,	[SOUND_MIXER_IGAIN]	= AC97_REC_STEREO,	[SOUND_MIXER_PHONEIN] 	= AC97_REC_PHONE};/* reads the given OSS mixer from the ac97 the caller must have insured that the ac97 knows   about that given mixer, and should be holding a spinlock for the card */static int ac97_read_mixer(struct ac97_codec *codec, int oss_channel) {	u16 val;	int ret = 0;	int scale;	struct ac97_mixer_hw *mh = &ac97_hw[oss_channel];	val = codec->codec_read(codec , mh->offset);	if (val & AC97_MUTE) {		ret = 0;	} else if (AC97_STEREO_MASK & (1 << oss_channel)) {		/* nice stereo mixers .. */		int left,right;		left = (val >> 8)  & 0x7f;		right = val  & 0x7f;		if (oss_channel == SOUND_MIXER_IGAIN) {			right = (right * 100) / mh->scale;			left = (left * 100) / mh->scale;		} else {			/* these may have 5 or 6 bit resolution */			if(oss_channel == SOUND_MIXER_VOLUME || oss_channel == SOUND_MIXER_ALTPCM)				scale = (1 << codec->bit_resolution);			else				scale = mh->scale;			right = 100 - ((right * 100) / scale);			left = 100 - ((left * 100) / scale);		}		ret = left | (right << 8);	} else if (oss_channel == SOUND_MIXER_SPEAKER) {		ret = 100 - ((((val & 0x1e)>>1) * 100) / mh->scale);	} else if (oss_channel == SOUND_MIXER_PHONEIN) {		ret = 100 - (((val & 0x1f) * 100) / mh->scale);	} else if (oss_channel == SOUND_MIXER_PHONEOUT) {		scale = (1 << codec->bit_resolution);		ret = 100 - (((val & 0x1f) * 100) / scale);	} else if (oss_channel == SOUND_MIXER_MIC) {		ret = 100 - (((val & 0x1f) * 100) / mh->scale);		/*  the low bit is optional in the tone sliders and masking		    it lets us avoid the 0xf 'bypass'.. */	} else if (oss_channel == SOUND_MIXER_BASS) {		ret = 100 - ((((val >> 8) & 0xe) * 100) / mh->scale);	} else if (oss_channel == SOUND_MIXER_TREBLE) {		ret = 100 - (((val & 0xe) * 100) / mh->scale);	}#ifdef DEBUG	printk("ac97_codec: read OSS mixer %2d (%s ac97 register 0x%02x), "	       "0x%04x -> 0x%04x\n",	       oss_channel, codec->id ? "Secondary" : "Primary",	       mh->offset, val, ret);#endif	return ret;}/* write the OSS encoded volume to the given OSS encoded mixer, again caller's job to   make sure all is well in arg land, call with spinlock held */static void ac97_write_mixer(struct ac97_codec *codec, int oss_channel,		      unsigned int left, unsigned int right){	u16 val = 0;	int scale;	struct ac97_mixer_hw *mh = &ac97_hw[oss_channel];#ifdef DEBUG	printk("ac97_codec: wrote OSS mixer %2d (%s ac97 register 0x%02x), "	       "left vol:%2d, right vol:%2d:",	       oss_channel, codec->id ? "Secondary" : "Primary",	       mh->offset, left, right);#endif	if (AC97_STEREO_MASK & (1 << oss_channel)) {		/* stereo mixers */		if (left == 0 && right == 0) {			val = AC97_MUTE;		} else {			if (oss_channel == SOUND_MIXER_IGAIN) {				right = (right * mh->scale) / 100;				left = (left * mh->scale) / 100;				if (right >= mh->scale)					right = mh->scale-1;				if (left >= mh->scale)					left = mh->scale-1;

⌨️ 快捷键说明

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