diff options
Diffstat (limited to 'sound/pci/ac97/ac97_patch.c')
-rw-r--r-- | sound/pci/ac97/ac97_patch.c | 129 |
1 files changed, 113 insertions, 16 deletions
diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index 2da89810ca1..0746e9ccc20 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -669,6 +669,7 @@ AC97_SINGLE("Mic 1 Volume", AC97_MIC, 8, 31, 1), AC97_SINGLE("Mic 2 Volume", AC97_MIC, 0, 31, 1), AC97_SINGLE("Mic 20dB Boost Switch", AC97_MIC, 7, 1, 0), +AC97_SINGLE("Master Left Inv Switch", AC97_MASTER, 6, 1, 0), AC97_SINGLE("Master ZC Switch", AC97_MASTER, 7, 1, 0), AC97_SINGLE("Headphone ZC Switch", AC97_HEADPHONE, 7, 1, 0), AC97_SINGLE("Mono ZC Switch", AC97_MASTER_MONO, 7, 1, 0), @@ -1971,6 +1972,9 @@ static int snd_ac97_ad1888_lohpsel_get(struct snd_kcontrol *kcontrol, struct snd val = ac97->regs[AC97_AD_MISC]; ucontrol->value.integer.value[0] = !(val & AC97_AD198X_LOSEL); + if (ac97->spec.ad18xx.lo_as_master) + ucontrol->value.integer.value[0] = + !ucontrol->value.integer.value[0]; return 0; } @@ -1979,8 +1983,10 @@ static int snd_ac97_ad1888_lohpsel_put(struct snd_kcontrol *kcontrol, struct snd struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol); unsigned short val; - val = !ucontrol->value.integer.value[0] - ? (AC97_AD198X_LOSEL | AC97_AD198X_HPSEL) : 0; + val = !ucontrol->value.integer.value[0]; + if (ac97->spec.ad18xx.lo_as_master) + val = !val; + val = val ? (AC97_AD198X_LOSEL | AC97_AD198X_HPSEL) : 0; return snd_ac97_update_bits(ac97, AC97_AD_MISC, AC97_AD198X_LOSEL | AC97_AD198X_HPSEL, val); } @@ -2031,7 +2037,7 @@ static void ad1888_update_jacks(struct snd_ac97 *ac97) { unsigned short val = 0; /* clear LODIS if shared jack is to be used for Surround out */ - if (is_shared_linein(ac97)) + if (!ac97->spec.ad18xx.lo_as_master && is_shared_linein(ac97)) val |= (1 << 12); /* clear CLDIS if shared jack is to be used for C/LFE out */ if (is_shared_micin(ac97)) @@ -2067,9 +2073,13 @@ static const struct snd_kcontrol_new snd_ac97_ad1888_controls[] = { static int patch_ad1888_specific(struct snd_ac97 *ac97) { - /* rename 0x04 as "Master" and 0x02 as "Master Surround" */ - snd_ac97_rename_vol_ctl(ac97, "Master Playback", "Master Surround Playback"); - snd_ac97_rename_vol_ctl(ac97, "Headphone Playback", "Master Playback"); + if (!ac97->spec.ad18xx.lo_as_master) { + /* rename 0x04 as "Master" and 0x02 as "Master Surround" */ + snd_ac97_rename_vol_ctl(ac97, "Master Playback", + "Master Surround Playback"); + snd_ac97_rename_vol_ctl(ac97, "Headphone Playback", + "Master Playback"); + } return patch_build_controls(ac97, snd_ac97_ad1888_controls, ARRAY_SIZE(snd_ac97_ad1888_controls)); } @@ -2088,16 +2098,27 @@ static int patch_ad1888(struct snd_ac97 * ac97) patch_ad1881(ac97); ac97->build_ops = &patch_ad1888_build_ops; - /* Switch FRONT/SURROUND LINE-OUT/HP-OUT default connection */ - /* it seems that most vendors connect line-out connector to headphone out of AC'97 */ + + /* + * LO can be used as a real line-out on some devices, + * and we need to revert the front/surround mixer switches + */ + if (ac97->subsystem_vendor == 0x1043 && + ac97->subsystem_device == 0x1193) /* ASUS A9T laptop */ + ac97->spec.ad18xx.lo_as_master = 1; + + misc = snd_ac97_read(ac97, AC97_AD_MISC); /* AD-compatible mode */ /* Stereo mutes enabled */ - misc = snd_ac97_read(ac97, AC97_AD_MISC); - snd_ac97_write_cache(ac97, AC97_AD_MISC, misc | - AC97_AD198X_LOSEL | - AC97_AD198X_HPSEL | - AC97_AD198X_MSPLT | - AC97_AD198X_AC97NC); + misc |= AC97_AD198X_MSPLT | AC97_AD198X_AC97NC; + if (!ac97->spec.ad18xx.lo_as_master) + /* Switch FRONT/SURROUND LINE-OUT/HP-OUT default connection */ + /* it seems that most vendors connect line-out connector to + * headphone out of AC'97 + */ + misc |= AC97_AD198X_LOSEL | AC97_AD198X_HPSEL; + + snd_ac97_write_cache(ac97, AC97_AD_MISC, misc); ac97->flags |= AC97_STEREO_MUTES; return 0; } @@ -3332,8 +3353,66 @@ AC97_SINGLE("Downmix LFE and Center to Front", 0x5a, 12, 1, 0), AC97_SINGLE("Downmix Surround to Front", 0x5a, 11, 1, 0), }; +static const char *slave_vols_vt1616[] = { + "Front Playback Volume", + "Surround Playback Volume", + "Center Playback Volume", + "LFE Playback Volume", + NULL +}; + +static const char *slave_sws_vt1616[] = { + "Front Playback Switch", + "Surround Playback Switch", + "Center Playback Switch", + "LFE Playback Switch", + NULL +}; + +/* find a mixer control element with the given name */ +static struct snd_kcontrol *snd_ac97_find_mixer_ctl(struct snd_ac97 *ac97, + const char *name) +{ + struct snd_ctl_elem_id id; + memset(&id, 0, sizeof(id)); + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(id.name, name); + return snd_ctl_find_id(ac97->bus->card, &id); +} + +/* create a virtual master control and add slaves */ +int snd_ac97_add_vmaster(struct snd_ac97 *ac97, char *name, + const unsigned int *tlv, const char **slaves) +{ + struct snd_kcontrol *kctl; + const char **s; + int err; + + kctl = snd_ctl_make_virtual_master(name, tlv); + if (!kctl) + return -ENOMEM; + err = snd_ctl_add(ac97->bus->card, kctl); + if (err < 0) + return err; + + for (s = slaves; *s; s++) { + struct snd_kcontrol *sctl; + + sctl = snd_ac97_find_mixer_ctl(ac97, *s); + if (!sctl) { + snd_printdd("Cannot find slave %s, skipped\n", *s); + continue; + } + err = snd_ctl_add_slave(kctl, sctl); + if (err < 0) + return err; + } + return 0; +} + static int patch_vt1616_specific(struct snd_ac97 * ac97) { + struct snd_kcontrol *kctl; int err; if (snd_ac97_try_bit(ac97, 0x5a, 9)) @@ -3341,6 +3420,24 @@ static int patch_vt1616_specific(struct snd_ac97 * ac97) return err; if ((err = patch_build_controls(ac97, &snd_ac97_controls_vt1616[1], ARRAY_SIZE(snd_ac97_controls_vt1616) - 1)) < 0) return err; + + /* There is already a misnamed master switch. Rename it. */ + kctl = snd_ac97_find_mixer_ctl(ac97, "Master Playback Volume"); + if (!kctl) + return -EINVAL; + + snd_ac97_rename_vol_ctl(ac97, "Master Playback", "Front Playback"); + + err = snd_ac97_add_vmaster(ac97, "Master Playback Volume", + kctl->tlv.p, slave_vols_vt1616); + if (err < 0) + return err; + + err = snd_ac97_add_vmaster(ac97, "Master Playback Switch", + NULL, slave_sws_vt1616); + if (err < 0) + return err; + return 0; } @@ -3613,7 +3710,7 @@ static int patch_ucb1400(struct snd_ac97 * ac97) { ac97->build_ops = &patch_ucb1400_ops; /* enable headphone driver and smart low power mode by default */ - snd_ac97_write(ac97, 0x6a, 0x0050); - snd_ac97_write(ac97, 0x6c, 0x0030); + snd_ac97_write_cache(ac97, 0x6a, 0x0050); + snd_ac97_write_cache(ac97, 0x6c, 0x0030); return 0; } |