📄 ac97_codec.c
字号:
/* * 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 + -