ALSA: hda - Add dock speaker support for ASUS TX300
authorTakashi Iwai <tiwai@suse.de>
Fri, 6 Sep 2013 13:45:38 +0000 (15:45 +0200)
committerTakashi Iwai <tiwai@suse.de>
Fri, 6 Sep 2013 13:54:42 +0000 (15:54 +0200)
ASUS TX300 has a built-in speaker in the tablet part and in the dock
part, and the tablet speaker is supposed to be unused while the
machine is docked.  The current HD-audio driver, however, doesn't
support the dock speaker, partly because BIOS doesn't set up the pin
for the corresponding output.

But, not only the missing pin config, also the missing unsol event
handling is another issue.  Otherwise the automatic switching via
dock/undock won't work.

Through debugging sessions, we found out that the dock speaker pin is
NID 0x1b, and it generates an unsol event at docking/undocking, the
docking state can be inquired via the normal pin detection verb.
Also, it's turned out that GPIO 2 is needed as an amp.  So, all
materials are ready to cook.

This patch provides the basic dock speaker support with TX300:
- The dock speaker is turned on/off via "Dock Speaker" mixer mute.
- The dock speaker is automatically muted when docked.  This is
  independently from the mixer mute switch, just like the headphone
  auto-mute function.

The implementation is a bit tricky.  Since we want to handle it as a
secondary speaker, we set it up a pin as a speaker with a jack
detection.  Then, the fixup function registers the own unsol callback
for this pin because the standard automute can't handle the thing like
a "speaker jack".  In the own automute hook, we apply the mute of the
tablet speaker in addition by checking the dock state.

Also, the speaker control names are slightly shuffled because the
generic parser doesn't give good names but blindly assumes a bass
speaker as a secondary speaker.

Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=59791
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/pci/hda/patch_realtek.c

index 4a90917..d38d6a0 100644 (file)
@@ -3443,6 +3443,56 @@ static void alc283_fixup_chromebook(struct hda_codec *codec,
        }
 }
 
+/* mute tablet speaker pin (0x14) via dock plugging in addition */
+static void asus_tx300_automute(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       snd_hda_gen_update_outputs(codec);
+       if (snd_hda_jack_detect(codec, 0x1b))
+               spec->gen.mute_bits |= (1ULL << 0x14);
+}
+
+static void alc282_fixup_asus_tx300(struct hda_codec *codec,
+                                   const struct hda_fixup *fix, int action)
+{
+       struct alc_spec *spec = codec->spec;
+       /* TX300 needs to set up GPIO2 for the speaker amp */
+       static const struct hda_verb gpio2_verbs[] = {
+               { 0x01, AC_VERB_SET_GPIO_MASK, 0x04 },
+               { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x04 },
+               { 0x01, AC_VERB_SET_GPIO_DATA, 0x04 },
+               {}
+       };
+       static const struct hda_pintbl dock_pins[] = {
+               { 0x1b, 0x21114000 }, /* dock speaker pin */
+               {}
+       };
+       struct snd_kcontrol *kctl;
+
+       switch (action) {
+       case HDA_FIXUP_ACT_PRE_PROBE:
+               snd_hda_add_verbs(codec, gpio2_verbs);
+               snd_hda_apply_pincfgs(codec, dock_pins);
+               spec->gen.auto_mute_via_amp = 1;
+               spec->gen.automute_hook = asus_tx300_automute;
+               snd_hda_jack_detect_enable_callback(codec, 0x1b,
+                                                   HDA_GEN_HP_EVENT,
+                                                   snd_hda_gen_hp_automute);
+               break;
+       case HDA_FIXUP_ACT_BUILD:
+               /* this is a bit tricky; give more sane names for the main
+                * (tablet) speaker and the dock speaker, respectively
+                */
+               kctl = snd_hda_find_mixer_ctl(codec, "Speaker Playback Switch");
+               if (kctl)
+                       strcpy(kctl->id.name, "Dock Speaker Playback Switch");
+               kctl = snd_hda_find_mixer_ctl(codec, "Bass Speaker Playback Switch");
+               if (kctl)
+                       strcpy(kctl->id.name, "Speaker Playback Switch");
+               break;
+       }
+}
+
 enum {
        ALC269_FIXUP_SONY_VAIO,
        ALC275_FIXUP_SONY_VAIO_GPIO2,
@@ -3480,6 +3530,7 @@ enum {
        ALC269_FIXUP_LIMIT_INT_MIC_BOOST,
        ALC269VB_FIXUP_ORDISSIMO_EVE2,
        ALC283_FIXUP_CHROME_BOOK,
+       ALC282_FIXUP_ASUS_TX300,
 };
 
 static const struct hda_fixup alc269_fixups[] = {
@@ -3735,6 +3786,10 @@ static const struct hda_fixup alc269_fixups[] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = alc283_fixup_chromebook,
        },
+       [ALC282_FIXUP_ASUS_TX300] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = alc282_fixup_asus_tx300,
+       },
 };
 
 static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -3784,6 +3839,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x103c, 0x1983, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x21ed, "HP Falco Chromebook", ALC283_FIXUP_CHROME_BOOK),
        SND_PCI_QUIRK_VENDOR(0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED),
+       SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300),
        SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
        SND_PCI_QUIRK(0x1043, 0x115d, "Asus 1015E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
        SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_DMIC),