📄 ac97_patch.c
字号:
scfg = snd_ac97_read(ac97, AC97_AD_SERIAL_CFG); snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, scfg | 0x7000); /* select all codecs */ ac97->res_table = ad1819_restbl; return 0;}static unsigned short patch_ad1881_unchained(struct snd_ac97 * 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(struct snd_ac97 * 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(struct snd_ac97 * 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 (cidx2 < 0) patch_ad1881_chained1(ac97, cidx1, 0); else 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); }}static struct snd_ac97_build_ops patch_ad1881_build_ops = {#ifdef CONFIG_PM .resume = ad18xx_resume#endif};static int patch_ad1881(struct snd_ac97 * 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; 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)); if (! (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]) num++; if (num == 1) { /* ok, deselect all ID bits */ snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, 0x0000); ac97->spec.ad18xx.codec_cfg[0] = ac97->spec.ad18xx.codec_cfg[1] = ac97->spec.ad18xx.codec_cfg[2] = 0x0000; } /* required for AD1886/AD1885 combination */ ac97->ext_id = snd_ac97_read(ac97, AC97_EXTENDED_ID); if (ac97->spec.ad18xx.id[0]) { ac97->id &= 0xffff0000; ac97->id |= ac97->spec.ad18xx.id[0]; } ac97->build_ops = &patch_ad1881_build_ops; return 0;}static const struct snd_kcontrol_new snd_ac97_controls_ad1885[] = { AC97_SINGLE("Digital Mono Direct", AC97_AD_MISC, 11, 1, 0), /* AC97_SINGLE("Digital Audio Mode", AC97_AD_MISC, 12, 1, 0), */ /* seems problematic */ AC97_SINGLE("Low Power Mixer", AC97_AD_MISC, 14, 1, 0), AC97_SINGLE("Zero Fill DAC", AC97_AD_MISC, 15, 1, 0), AC97_SINGLE("Headphone Jack Sense", AC97_AD_JACK_SPDIF, 9, 1, 1), /* inverted */ AC97_SINGLE("Line Jack Sense", AC97_AD_JACK_SPDIF, 8, 1, 1), /* inverted */};static const DECLARE_TLV_DB_SCALE(db_scale_6bit_6db_max, -8850, 150, 0);static int patch_ad1885_specific(struct snd_ac97 * ac97){ int err; if ((err = patch_build_controls(ac97, snd_ac97_controls_ad1885, ARRAY_SIZE(snd_ac97_controls_ad1885))) < 0) return err; reset_tlv(ac97, "Headphone Playback Volume", db_scale_6bit_6db_max); return 0;}static struct snd_ac97_build_ops patch_ad1885_build_ops = { .build_specific = &patch_ad1885_specific,#ifdef CONFIG_PM .resume = ad18xx_resume#endif};static int patch_ad1885(struct snd_ac97 * ac97){ patch_ad1881(ac97); /* This is required to deal with the Intel D815EEAL2 */ /* i.e. Line out is actually headphone out from codec */ /* set default */ snd_ac97_write_cache(ac97, AC97_AD_MISC, 0x0404); ac97->build_ops = &patch_ad1885_build_ops; return 0;}static int patch_ad1886_specific(struct snd_ac97 * ac97){ reset_tlv(ac97, "Headphone Playback Volume", db_scale_6bit_6db_max); return 0;}static struct snd_ac97_build_ops patch_ad1886_build_ops = { .build_specific = &patch_ad1886_specific,#ifdef CONFIG_PM .resume = ad18xx_resume#endif};static int patch_ad1886(struct snd_ac97 * ac97){ patch_ad1881(ac97); /* Presario700 workaround */ /* for Jack Sense/SPDIF Register misetting causing */ snd_ac97_write_cache(ac97, AC97_AD_JACK_SPDIF, 0x0010); ac97->build_ops = &patch_ad1886_build_ops; return 0;}/* MISC bits (AD1888/AD1980/AD1985 register 0x76) */#define AC97_AD198X_MBC 0x0003 /* mic boost */#define AC97_AD198X_MBC_20 0x0000 /* +20dB */#define AC97_AD198X_MBC_10 0x0001 /* +10dB */#define AC97_AD198X_MBC_30 0x0002 /* +30dB */#define AC97_AD198X_VREFD 0x0004 /* VREF high-Z */#define AC97_AD198X_VREFH 0x0008 /* 0=2.25V, 1=3.7V */#define AC97_AD198X_VREF_0 0x000c /* 0V (AD1985 only) */#define AC97_AD198X_VREF_MASK (AC97_AD198X_VREFH | AC97_AD198X_VREFD)#define AC97_AD198X_VREF_SHIFT 2#define AC97_AD198X_SRU 0x0010 /* sample rate unlock */#define AC97_AD198X_LOSEL 0x0020 /* LINE_OUT amplifiers input select */#define AC97_AD198X_2MIC 0x0040 /* 2-channel mic select */#define AC97_AD198X_SPRD 0x0080 /* SPREAD enable */#define AC97_AD198X_DMIX0 0x0100 /* downmix mode: */ /* 0 = 6-to-4, 1 = 6-to-2 downmix */#define AC97_AD198X_DMIX1 0x0200 /* downmix mode: 1 = enabled */#define AC97_AD198X_HPSEL 0x0400 /* headphone amplifier input select */#define AC97_AD198X_CLDIS 0x0800 /* center/lfe disable */#define AC97_AD198X_LODIS 0x1000 /* LINE_OUT disable */#define AC97_AD198X_MSPLT 0x2000 /* mute split */#define AC97_AD198X_AC97NC 0x4000 /* AC97 no compatible mode */#define AC97_AD198X_DACZ 0x8000 /* DAC zero-fill mode *//* MISC 1 bits (AD1986 register 0x76) */#define AC97_AD1986_MBC 0x0003 /* mic boost */#define AC97_AD1986_MBC_20 0x0000 /* +20dB */#define AC97_AD1986_MBC_10 0x0001 /* +10dB */#define AC97_AD1986_MBC_30 0x0002 /* +30dB */#define AC97_AD1986_LISEL0 0x0004 /* LINE_IN select bit 0 */#define AC97_AD1986_LISEL1 0x0008 /* LINE_IN select bit 1 */#define AC97_AD1986_LISEL_MASK (AC97_AD1986_LISEL1 | AC97_AD1986_LISEL0)#define AC97_AD1986_LISEL_LI 0x0000 /* LINE_IN pins as LINE_IN source */#define AC97_AD1986_LISEL_SURR 0x0004 /* SURROUND pins as LINE_IN source */#define AC97_AD1986_LISEL_MIC 0x0008 /* MIC_1/2 pins as LINE_IN source */#define AC97_AD1986_SRU 0x0010 /* sample rate unlock */#define AC97_AD1986_SOSEL 0x0020 /* SURROUND_OUT amplifiers input sel */#define AC97_AD1986_2MIC 0x0040 /* 2-channel mic select */#define AC97_AD1986_SPRD 0x0080 /* SPREAD enable */#define AC97_AD1986_DMIX0 0x0100 /* downmix mode: */ /* 0 = 6-to-4, 1 = 6-to-2 downmix */#define AC97_AD1986_DMIX1 0x0200 /* downmix mode: 1 = enabled */#define AC97_AD1986_CLDIS 0x0800 /* center/lfe disable */#define AC97_AD1986_SODIS 0x1000 /* SURROUND_OUT disable */#define AC97_AD1986_MSPLT 0x2000 /* mute split (read only 1) */#define AC97_AD1986_AC97NC 0x4000 /* AC97 no compatible mode (r/o 1) */#define AC97_AD1986_DACZ 0x8000 /* DAC zero-fill mode *//* MISC 2 bits (AD1986 register 0x70) */#define AC97_AD_MISC2 0x70 /* Misc Control Bits 2 (AD1986) */#define AC97_AD1986_CVREF0 0x0004 /* C/LFE VREF_OUT 2.25V */#define AC97_AD1986_CVREF1 0x0008 /* C/LFE VREF_OUT 0V */#define AC97_AD1986_CVREF2 0x0010 /* C/LFE VREF_OUT 3.7V */#define AC97_AD1986_CVREF_MASK \ (AC97_AD1986_CVREF2 | AC97_AD1986_CVREF1 | AC97_AD1986_CVREF0)#define AC97_AD1986_JSMAP 0x0020 /* Jack Sense Mapping 1 = alternate */#define AC97_AD1986_MMDIS 0x0080 /* Mono Mute Disable */#define AC97_AD1986_MVREF0 0x0400 /* MIC VREF_OUT 2.25V */#define AC97_AD1986_MVREF1 0x0800 /* MIC VREF_OUT 0V */#define AC97_AD1986_MVREF2 0x1000 /* MIC VREF_OUT 3.7V */#define AC97_AD1986_MVREF_MASK \ (AC97_AD1986_MVREF2 | AC97_AD1986_MVREF1 | AC97_AD1986_MVREF0)/* MISC 3 bits (AD1986 register 0x7a) */#define AC97_AD_MISC3 0x7a /* Misc Control Bits 3 (AD1986) */#define AC97_AD1986_MMIX 0x0004 /* Mic Mix, left/right */#define AC97_AD1986_GPO 0x0008 /* General Purpose Out */#define AC97_AD1986_LOHPEN 0x0010 /* LINE_OUT headphone drive */#define AC97_AD1986_LVREF0 0x0100 /* LINE_OUT VREF_OUT 2.25V */#define AC97_AD1986_LVREF1 0x0200 /* LINE_OUT VREF_OUT 0V */#define AC97_AD1986_LVREF2 0x0400 /* LINE_OUT VREF_OUT 3.7V */#define AC97_AD1986_LVREF_MASK \ (AC97_AD1986_LVREF2 | AC97_AD1986_LVREF1 | AC97_AD1986_LVREF0)#define AC97_AD1986_JSINVA 0x0800 /* Jack Sense Invert SENSE_A */#define AC97_AD1986_LOSEL 0x1000 /* LINE_OUT amplifiers input select */#define AC97_AD1986_HPSEL0 0x2000 /* Headphone amplifiers */ /* input select Surround DACs */#define AC97_AD1986_HPSEL1 0x4000 /* Headphone amplifiers input */ /* select C/LFE DACs */#define AC97_AD1986_JSINVB 0x8000 /* Jack Sense Invert SENSE_B *//* Serial Config bits (AD1986 register 0x74) (incomplete) */#define AC97_AD1986_OMS0 0x0100 /* Optional Mic Selector bit 0 */#define AC97_AD1986_OMS1 0x0200 /* Optional Mic Selector bit 1 */#define AC97_AD1986_OMS2 0x0400 /* Optional Mic Selector bit 2 */#define AC97_AD1986_OMS_MASK \ (AC97_AD1986_OMS2 | AC97_AD1986_OMS1 | AC97_AD1986_OMS0)#define AC97_AD1986_OMS_M 0x0000 /* MIC_1/2 pins are MIC sources */#define AC97_AD1986_OMS_L 0x0100 /* LINE_IN pins are MIC sources */#define AC97_AD1986_OMS_C 0x0200 /* Center/LFE pins are MCI sources */#define AC97_AD1986_OMS_MC 0x0400 /* Mix of MIC and C/LFE pins */ /* are MIC sources */#define AC97_AD1986_OMS_ML 0x0500 /* MIX of MIC and LINE_IN pins */ /* are MIC sources */#define AC97_AD1986_OMS_LC 0x0600 /* MIX of LINE_IN and C/LFE pins */ /* are MIC sources */#define AC97_AD1986_OMS_MLC 0x0700 /* MIX of MIC, LINE_IN, C/LFE pins */ /* are MIC sources */static int snd_ac97_ad198x_spdif_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo){ static char *texts[2] = { "AC-Link", "A/D Converter" }; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; uinfo->value.enumerated.items = 2; if (uinfo->value.enumerated.item > 1) uinfo->value.enumerated.item = 1; strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); return 0;}static int snd_ac97_ad198x_spdif_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol); unsigned short val; val = ac97->regs[AC97_AD_SERIAL_CFG]; ucontrol->value.enumerated.item[0] = (val >> 2) & 1; return 0;}static int snd_ac97_ad198x_spdif_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol); unsigned short val; if (ucontrol->value.enumerated.item[0] > 1) return -EINVAL; val = ucontrol->value.enumerated.item[0] << 2; return snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x0004, val);}static const struct snd_kcontrol_new snd_ac97_ad198x_spdif_source = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", .info = snd_ac97_ad198x_spdif_source_info, .get = snd_ac97_ad198x_spdif_source_get, .put = snd_ac97_ad198x_spdif_source_put,};static int patch_ad198x_post_spdif(struct snd_ac97 * ac97){ return patch_build_controls(ac97, &snd_ac97_ad198x_spdif_source, 1);}static const struct snd_kcontrol_new snd_ac97_ad1981x_jack_sense[] = { AC97_SINGLE("Headphone Jack Sense", AC97_AD_JACK_SPDIF, 11, 1, 0), AC97_SINGLE("Line Jack Sense", AC97_AD_JACK_SPDIF, 12, 1, 0),};/* black list to avoid HP/Line jack-sense controls * (SS vendor << 16 | device) */static unsigned int ad1981_jacks_blacklist[] = { 0x10140523, /* Thinkpad R40 */ 0x10140534, /* Thinkpad X31 */ 0x10140537, /* Thinkpad T41p */ 0x10140554, /* Thinkpad T42p/R50p */ 0x10140567, /* Thinkpad T43p 2668-G7U */ 0x10140581, /* Thinkpad X41-2527 */ 0x104380b0, /* Asus A7V8X-MX */ 0x11790241, /* Toshiba Satellite A-15 S127 */ 0x144dc01a, /* Samsung NP-X20C004/SEG */ 0 /* end */};static int c
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -