ac97_patch.c
来自「优龙2410linux2.6.8内核源代码」· C语言 代码 · 共 1,701 行 · 第 1/4 页
C
1,701 行
{ // patch for SigmaTel ac97->build_ops = &patch_sigmatel_stac9700_ops; snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC1, 0xabba); snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC2, 0x0000); /* is this correct? --jk */ snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS1, 0xabba); snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS2, 0x0002); snd_ac97_write_cache(ac97, AC97_SIGMATEL_MULTICHN, 0x0000); return 0;}int patch_sigmatel_stac9756(ac97_t * ac97){ // patch for SigmaTel ac97->build_ops = &patch_sigmatel_stac9700_ops; snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC1, 0xabba); snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC2, 0x0000); /* is this correct? --jk */ snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS1, 0xabba); snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS2, 0x0002); snd_ac97_write_cache(ac97, AC97_SIGMATEL_MULTICHN, 0x0000); return 0;}static int snd_ac97_stac9758_output_jack_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo){ static char *texts[5] = { "Input/Disabled", "Front Output", "Rear Output", "Center/LFE Output", "Mixer Output" }; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; uinfo->value.enumerated.items = 5; if (uinfo->value.enumerated.item > 4) uinfo->value.enumerated.item = 4; strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); return 0;}static int snd_ac97_stac9758_output_jack_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t* ucontrol){ ac97_t *ac97 = snd_kcontrol_chip(kcontrol); int shift = kcontrol->private_value; unsigned short val; val = ac97->regs[AC97_SIGMATEL_OUTSEL]; if (!((val >> shift) & 4)) ucontrol->value.enumerated.item[0] = 0; else ucontrol->value.enumerated.item[0] = 1 + ((val >> shift) & 3); return 0;}static int snd_ac97_stac9758_output_jack_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol){ ac97_t *ac97 = snd_kcontrol_chip(kcontrol); int shift = kcontrol->private_value; unsigned short val; if (ucontrol->value.enumerated.item[0] > 4) return -EINVAL; if (ucontrol->value.enumerated.item[0] == 0) val = 0; else val = 4 | (ucontrol->value.enumerated.item[0] - 1); return snd_ac97_update_bits(ac97, AC97_SIGMATEL_OUTSEL, 7 << shift, val << shift);}static int snd_ac97_stac9758_input_jack_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo){ static char *texts[7] = { "Mic2 Jack", "Mic1 Jack", "Line In Jack", "Front Jack", "Rear Jack", "Center/LFE Jack", "Mute" }; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; uinfo->value.enumerated.items = 7; if (uinfo->value.enumerated.item > 6) uinfo->value.enumerated.item = 6; strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); return 0;}static int snd_ac97_stac9758_input_jack_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t* ucontrol){ ac97_t *ac97 = snd_kcontrol_chip(kcontrol); int shift = kcontrol->private_value; unsigned short val; val = ac97->regs[AC97_SIGMATEL_INSEL]; ucontrol->value.enumerated.item[0] = (val >> shift) & 7; return 0;}static int snd_ac97_stac9758_input_jack_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol){ ac97_t *ac97 = snd_kcontrol_chip(kcontrol); int shift = kcontrol->private_value; return snd_ac97_update_bits(ac97, AC97_SIGMATEL_INSEL, 7 << shift, ucontrol->value.enumerated.item[0] << shift);}static int snd_ac97_stac9758_phonesel_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo){ static char *texts[3] = { "None", "Front Jack", "Rear Jack" }; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; uinfo->value.enumerated.items = 3; if (uinfo->value.enumerated.item > 2) uinfo->value.enumerated.item = 2; strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); return 0;}static int snd_ac97_stac9758_phonesel_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t* ucontrol){ ac97_t *ac97 = snd_kcontrol_chip(kcontrol); ucontrol->value.enumerated.item[0] = ac97->regs[AC97_SIGMATEL_IOMISC] & 3; return 0;}static int snd_ac97_stac9758_phonesel_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol){ ac97_t *ac97 = snd_kcontrol_chip(kcontrol); return snd_ac97_update_bits(ac97, AC97_SIGMATEL_IOMISC, 3, ucontrol->value.enumerated.item[0]);}#define STAC9758_OUTPUT_JACK(xname, shift) \{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_ac97_stac9758_output_jack_info, \ .get = snd_ac97_stac9758_output_jack_get, \ .put = snd_ac97_stac9758_output_jack_put, \ .private_value = shift }#define STAC9758_INPUT_JACK(xname, shift) \{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_ac97_stac9758_input_jack_info, \ .get = snd_ac97_stac9758_input_jack_get, \ .put = snd_ac97_stac9758_input_jack_put, \ .private_value = shift }static const snd_kcontrol_new_t snd_ac97_sigmatel_stac9758_controls[] = { STAC9758_OUTPUT_JACK("Mic1 Jack", 1), STAC9758_OUTPUT_JACK("LineIn Jack", 4), STAC9758_OUTPUT_JACK("Front Jack", 7), STAC9758_OUTPUT_JACK("Rear Jack", 10), STAC9758_OUTPUT_JACK("Center/LFE Jack", 13), STAC9758_INPUT_JACK("Mic Input Source", 0), STAC9758_INPUT_JACK("Line Input Source", 8), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Headphone Amp", .info = snd_ac97_stac9758_phonesel_info, .get = snd_ac97_stac9758_phonesel_get, .put = snd_ac97_stac9758_phonesel_put }, AC97_SINGLE("Exchange Center/LFE", AC97_SIGMATEL_IOMISC, 4, 1, 0), AC97_SINGLE("Headphone +3dB Boost", AC97_SIGMATEL_IOMISC, 8, 1, 0)};static int patch_sigmatel_stac9758_specific(ac97_t *ac97){ int err; err = patch_sigmatel_stac97xx_specific(ac97); if (err < 0) return err; err = patch_build_controls(ac97, snd_ac97_sigmatel_stac9758_controls, ARRAY_SIZE(snd_ac97_sigmatel_stac9758_controls)); if (err < 0) return err; return 0;}static struct snd_ac97_build_ops patch_sigmatel_stac9758_ops = { .build_3d = patch_sigmatel_stac9700_3d, .build_specific = patch_sigmatel_stac9758_specific};int patch_sigmatel_stac9758(ac97_t * ac97){ static unsigned short regs[4] = { AC97_SIGMATEL_OUTSEL, AC97_SIGMATEL_IOMISC, AC97_SIGMATEL_INSEL, AC97_SIGMATEL_VARIOUS }; static unsigned short def_regs[4] = { /* OUTSEL */ 0xd794, /* IOMISC */ 0x2001, /* INSEL */ 0x0201, /* VARIOUS */ 0x0040 }; static unsigned short m675_regs[4] = { /* OUTSEL */ 0x9040, /* IOMISC */ 0x2102, /* INSEL */ 0x0203, /* VARIOUS */ 0x0041 }; unsigned short *pregs = def_regs; int i; /* Gateway M675 notebook */ if (ac97->pci && ac97->subsystem_vendor == 0x107b && ac97->subsystem_device == 0x0601) pregs = m675_regs; // patch for SigmaTel ac97->build_ops = &patch_sigmatel_stac9758_ops; for (i = 0; i < 4; i++) snd_ac97_write_cache(ac97, regs[i], pregs[i]); ac97->flags |= AC97_STEREO_MUTES; return 0;}/* * Cirrus Logic CS42xx codecs */static const snd_kcontrol_new_t snd_ac97_cirrus_controls_spdif[2] = { AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), AC97_CSR_SPDIF, 15, 1, 0), AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "AC97-SPSA", AC97_CSR_ACMODE, 0, 3, 0)};static int patch_cirrus_build_spdif(ac97_t * ac97){ int err; if ((err = patch_build_controls(ac97, &snd_ac97_controls_spdif[0], 3)) < 0) return err; if ((err = patch_build_controls(ac97, &snd_ac97_cirrus_controls_spdif[0], 1)) < 0) return err; switch (ac97->id & AC97_ID_CS_MASK) { case AC97_ID_CS4205: if ((err = patch_build_controls(ac97, &snd_ac97_cirrus_controls_spdif[1], 1)) < 0) return err; break; } /* set default PCM S/PDIF params */ /* consumer,PCM audio,no copyright,no preemphasis,PCM coder,original,48000Hz */ snd_ac97_write_cache(ac97, AC97_CSR_SPDIF, 0x0a20); return 0;}static struct snd_ac97_build_ops patch_cirrus_ops = { .build_spdif = patch_cirrus_build_spdif};int patch_cirrus_spdif(ac97_t * ac97){ /* Basically, the cs4201/cs4205/cs4297a has non-standard sp/dif registers. WHY CAN'T ANYONE FOLLOW THE BLOODY SPEC? *sigh* - sp/dif EA ID is not set, but sp/dif is always present. - enable/disable is spdif register bit 15. - sp/dif control register is 0x68. differs from AC97: - valid is bit 14 (vs 15) - no DRS - only 44.1/48k [00 = 48, 01=44,1] (AC97 is 00=44.1, 10=48) - sp/dif ssource select is in 0x5e bits 0,1. */ ac97->build_ops = &patch_cirrus_ops; ac97->flags |= AC97_CS_SPDIF; ac97->rates[AC97_RATES_SPDIF] &= ~SNDRV_PCM_RATE_32000; ac97->ext_id |= AC97_EI_SPDIF; /* force the detection of spdif */ snd_ac97_write_cache(ac97, AC97_CSR_ACMODE, 0x0080); return 0;}int patch_cirrus_cs4299(ac97_t * ac97){ /* force the detection of PC Beep */ ac97->flags |= AC97_HAS_PC_BEEP; return patch_cirrus_spdif(ac97);}/* * Conexant codecs */static const snd_kcontrol_new_t snd_ac97_conexant_controls_spdif[1] = { AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), AC97_CXR_AUDIO_MISC, 3, 1, 0),};static int patch_conexant_build_spdif(ac97_t * ac97){ int err; if ((err = patch_build_controls(ac97, &snd_ac97_controls_spdif[0], 3)) < 0) return err; if ((err = patch_build_controls(ac97, &snd_ac97_conexant_controls_spdif[0], 1)) < 0) return err; /* set default PCM S/PDIF params */ /* consumer,PCM audio,no copyright,no preemphasis,PCM coder,original,48000Hz */ snd_ac97_write_cache(ac97, AC97_CXR_AUDIO_MISC, snd_ac97_read(ac97, AC97_CXR_AUDIO_MISC) & ~(AC97_CXR_SPDIFEN|AC97_CXR_COPYRGT|AC97_CXR_SPDIF_MASK)); return 0;}static struct snd_ac97_build_ops patch_conexant_ops = { .build_spdif = patch_conexant_build_spdif};int patch_conexant(ac97_t * ac97){ ac97->build_ops = &patch_conexant_ops; ac97->flags |= AC97_CX_SPDIF; ac97->ext_id |= AC97_EI_SPDIF; /* force the detection of spdif */ return 0;}/* * Analog Device AD18xx, AD19xx codecs */int patch_ad1819(ac97_t * ac97){ unsigned short scfg; // patch for Analog Devices scfg = snd_ac97_read(ac97, AC97_AD_SERIAL_CFG); snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, scfg | 0x7000); /* select all codecs */ return 0;}static unsigned short patch_ad1881_unchained(ac97_t * ac97, int idx, unsigned short mask){ unsigned short val; // test for unchained codec snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000, mask); snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, 0x0000); /* ID0C, ID1C, SDIE = off */ val = snd_ac97_read(ac97, AC97_VENDOR_ID2); if ((val & 0xff40) != 0x5340) return 0; ac97->spec.ad18xx.unchained[idx] = mask; ac97->spec.ad18xx.id[idx] = val; ac97->spec.ad18xx.codec_cfg[idx] = 0x0000; return mask;}static int patch_ad1881_chained1(ac97_t * ac97, int idx, unsigned short codec_bits){ static int cfg_bits[3] = { 1<<12, 1<<14, 1<<13 }; unsigned short val; snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000, cfg_bits[idx]); snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, 0x0004); // SDIE val = snd_ac97_read(ac97, AC97_VENDOR_ID2); if ((val & 0xff40) != 0x5340) return 0; if (codec_bits) snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, codec_bits); ac97->spec.ad18xx.chained[idx] = cfg_bits[idx]; ac97->spec.ad18xx.id[idx] = val; ac97->spec.ad18xx.codec_cfg[idx] = codec_bits ? codec_bits : 0x0004; return 1;}static void patch_ad1881_chained(ac97_t * ac97, int unchained_idx, int cidx1, int cidx2){ // already detected? if (ac97->spec.ad18xx.unchained[cidx1] || ac97->spec.ad18xx.chained[cidx1]) cidx1 = -1; if (ac97->spec.ad18xx.unchained[cidx2] || ac97->spec.ad18xx.chained[cidx2]) cidx2 = -1; if (cidx1 < 0 && cidx2 < 0) return; // test for chained codecs snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000, ac97->spec.ad18xx.unchained[unchained_idx]); snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, 0x0002); // ID1C ac97->spec.ad18xx.codec_cfg[unchained_idx] = 0x0002; if (cidx1 >= 0) { if (patch_ad1881_chained1(ac97, cidx1, 0x0006)) // SDIE | ID1C patch_ad1881_chained1(ac97, cidx2, 0); else if (patch_ad1881_chained1(ac97, cidx2, 0x0006)) // SDIE | ID1C patch_ad1881_chained1(ac97, cidx1, 0); } else if (cidx2 >= 0) { patch_ad1881_chained1(ac97, cidx2, 0); }}int patch_ad1881(ac97_t * ac97){ static const char cfg_idxs[3][2] = { {2, 1}, {0, 2}, {0, 1} }; // patch for Analog Devices unsigned short codecs[3]; unsigned short val; int idx, num; init_MUTEX(&ac97->spec.ad18xx.mutex); val = snd_ac97_read(ac97, AC97_AD_SERIAL_CFG); snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, val); codecs[0] = patch_ad1881_unchained(ac97, 0, (1<<12)); codecs[1] = patch_ad1881_unchained(ac97, 1, (1<<14)); codecs[2] = patch_ad1881_unchained(ac97, 2, (1<<13)); snd_runtime_check(codecs[0] | codecs[1] | codecs[2], goto __end); for (idx = 0; idx < 3; idx++) if (ac97->spec.ad18xx.unchained[idx]) patch_ad1881_chained(ac97, idx, cfg_idxs[idx][0], cfg_idxs[idx][1]); if (ac97->spec.ad18xx.id[1]) { ac97->flags |= AC97_AD_MULTI; ac97->scaps |= AC97_SCAP_SURROUND_DAC; } if (ac97->spec.ad18xx.id[2]) { ac97->flags |= AC97_AD_MULTI; ac97->scaps |= AC97_SCAP_CENTER_LFE_DAC; } __end: /* select all codecs */ snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000, 0x7000); /* check if only one codec is present */ for (idx = num = 0; idx < 3; idx++) if (ac97->spec.ad18xx.id[idx])
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?