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

📄 ac97_codec.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * 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. * * 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 sigmatel_init(struct ac97_codec *codec);static int enable_eapd(struct ac97_codec *codec);#define arraysize(x)   (sizeof(x)/sizeof((x)[0]))static struct {	unsigned int id;	char *name;	int  (*init)  (struct ac97_codec *codec);} ac97_codec_ids[] = {	{0x414B4D00, "Asahi Kasei AK4540 rev 0", NULL},	{0x414B4D01, "Asahi Kasei AK4540 rev 1", NULL},	{0x41445340, "Analog Devices AD1881"  , NULL},	{0x41445360, "Analog Devices AD1885"  , enable_eapd},	{0x43525900, "Cirrus Logic CS4297"    , NULL},	{0x43525903, "Cirrus Logic CS4297"  ,	NULL},	{0x43525913, "Cirrus Logic CS4297A"   , NULL},	{0x43525923, "Cirrus Logic CS4298"    , NULL},	{0x4352592B, "Cirrus Logic CS4294"    , NULL},	{0x43525931, "Cirrus Logic CS4299"    , NULL},	{0x43525934, "Cirrus Logic CS4299"    , NULL},	{0x4e534331, "National Semiconductor LM4549" ,	NULL},	{0x53494c22, "Silicon Laboratory Si3036"     ,	NULL},	{0x53494c23, "Silicon Laboratory Si3038"     ,  NULL},	{0x83847600, "SigmaTel STAC????"      , NULL},	{0x83847604, "SigmaTel STAC9701/3/4/5", NULL},	{0x83847605, "SigmaTel STAC9704"      , NULL},	{0x83847608, "SigmaTel STAC9708"      , NULL},	{0x83847609, "SigmaTel STAC9721/23"   , sigmatel_init},	{0x54524103, "TriTech TR?????"	      , NULL},	{0x54524106, "TriTech TR28026"        , NULL},	{0x54524108, "TriTech TR28028"        , NULL},	{0x54524123, "TriTech TR?????"	      , NULL},		{0x574D4C00, "Wolfson WM9704"         , NULL},	{0x00000000, NULL, NULL}};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 */ "Reserved 27",	/*  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 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 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;			} 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) * scale) / 100;				left = ((100 - left) * scale) / 100;				if (right >= scale)					right = scale-1;				if (left >= scale)					left = scale-1;			}			val = (left << 8) | right;		}	} else if (oss_channel == SOUND_MIXER_BASS) {		val = codec->codec_read(codec , mh->offset) & ~0x0f00;		left = ((100 - left) * mh->scale) / 100;		if (left >= mh->scale)			left = mh->scale-1;		val |= (left << 8) & 0x0e00;	} else if (oss_channel == SOUND_MIXER_TREBLE) {		val = codec->codec_read(codec , mh->offset) & ~0x000f;		left = ((100 - left) * mh->scale) / 100;		if (left >= mh->scale)			left = mh->scale-1;		val |= left & 0x000e;	} else if(left == 0) {		val = AC97_MUTE;	} else if (oss_channel == SOUND_MIXER_SPEAKER) {		left = ((100 - left) * mh->scale) / 100;		if (left >= mh->scale)			left = mh->scale-1;		val = left << 1;	} else if (oss_channel == SOUND_MIXER_PHONEIN) {		left = ((100 - left) * mh->scale) / 100;		if (left >= mh->scale)			left = mh->scale-1;		val = left;	} else if (oss_channel == SOUND_MIXER_PHONEOUT) {		scale = (1 << codec->bit_resolution);		left = ((100 - left) * scale) / 100;		if (left >= mh->scale)			left = mh->scale-1;		val = left;	} else if (oss_channel == SOUND_MIXER_MIC) {		val = codec->codec_read(codec , mh->offset) & ~0x801f;		left = ((100 - left) * mh->scale) / 100;		if (left >= mh->scale)			left = mh->scale-1;		val |= left;		/*  the low bit is optional in the tone sliders and masking		    it lets us avoid the 0xf 'bypass'.. */	}#ifdef DEBUG	printk(" 0x%04x", val);#endif	codec->codec_write(codec, mh->offset, val);#ifdef DEBUG	val = codec->codec_read(codec, mh->offset);	printk(" -> 0x%04x\n", val);#endif}/* a thin wrapper for write_mixer */static void ac97_set_mixer(struct ac97_codec *codec, unsigned int oss_mixer, unsigned int val ) {	unsigned int left,right;	/* cleanse input a little */	right = ((val >> 8)  & 0xff) ;	left = (val  & 0xff) ;	if (right > 100) right = 100;	if (left > 100) left = 100;	codec->mixer_state[oss_mixer] = (right << 8) | left;	codec->write_mixer(codec, oss_mixer, left, right);}

⌨️ 快捷键说明

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