]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ALSA: hda - bind headset buttons to the headphone jack
authorHui Wang <hui.wang@canonical.com>
Fri, 5 Mar 2021 09:26:08 +0000 (17:26 +0800)
committerTakashi Iwai <tiwai@suse.de>
Fri, 5 Mar 2021 14:18:24 +0000 (15:18 +0100)
With the HDA driver, if the headset buttons are supported, an audio
Jack will be created for them. This audio Jack is a bit confusing to
users since it can't report headphone/mic insertion events but it
claims to support these events.

And in addition, the driver already builds a headphone Jack and a mic
Jack, and most of those buttons are used for headphone playback, so
do some change to bind those buttons to the headphone Jack. After this
change, the key events are generated from NID 0x55, and are reported
to the input layer via headphone jack (NID 0x21).

If there is no headphone Jack, then build an audio jack to support
those buttons same as previously.

Signed-off-by: Hui Wang <hui.wang@canonical.com>
Link: https://lore.kernel.org/r/20210305092608.109599-1-hui.wang@canonical.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/pci/hda/hda_jack.c
sound/pci/hda/hda_jack.h
sound/pci/hda/patch_realtek.c

index b8b568046592a74ba457646aeaaf847820a659f8..30e9ebf1f341cf1f5d0d7df8bbb7e957bf8a50f8 100644 (file)
@@ -388,6 +388,69 @@ int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid,
 }
 EXPORT_SYMBOL_GPL(snd_hda_jack_set_gating_jack);
 
+/**
+ * snd_hda_jack_bind_keymap - bind keys generated from one NID to another jack.
+ * @codec: the HDA codec
+ * @key_nid: key event is generated by this pin NID
+ * @keymap: map of key type and key code
+ * @jack_nid: key reports to the jack of this pin NID
+ *
+ * This function is used in the case of key is generated from one NID while is
+ * reported to the jack of another NID.
+ */
+int snd_hda_jack_bind_keymap(struct hda_codec *codec, hda_nid_t key_nid,
+                            const struct hda_jack_keymap *keymap,
+                            hda_nid_t jack_nid)
+{
+       const struct hda_jack_keymap *map;
+       struct hda_jack_tbl *key_gen = snd_hda_jack_tbl_get(codec, key_nid);
+       struct hda_jack_tbl *report_to = snd_hda_jack_tbl_get(codec, jack_nid);
+
+       WARN_ON(codec->dp_mst);
+
+       if (!key_gen || !report_to || !report_to->jack)
+               return -EINVAL;
+
+       key_gen->key_report_jack = jack_nid;
+
+       if (keymap)
+               for (map = keymap; map->type; map++)
+                       snd_jack_set_key(report_to->jack, map->type, map->key);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_bind_keymap);
+
+/**
+ * snd_hda_jack_set_button_state - report button event to the hda_jack_tbl button_state.
+ * @codec: the HDA codec
+ * @jack_nid: the button event reports to the jack_tbl of this NID
+ * @button_state: the button event captured by codec
+ *
+ * Codec driver calls this function to report the button event.
+ */
+void snd_hda_jack_set_button_state(struct hda_codec *codec, hda_nid_t jack_nid,
+                                  int button_state)
+{
+       struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, jack_nid);
+
+       if (!jack)
+               return;
+
+       if (jack->key_report_jack) {
+               struct hda_jack_tbl *report_to =
+                       snd_hda_jack_tbl_get(codec, jack->key_report_jack);
+
+               if (report_to) {
+                       report_to->button_state = button_state;
+                       return;
+               }
+       }
+
+       jack->button_state = button_state;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_set_button_state);
+
 /**
  * snd_hda_jack_report_sync - sync the states of all jacks and report if changed
  * @codec: the HDA codec
@@ -651,7 +714,15 @@ void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res)
        }
        if (!event)
                return;
-       event->jack_dirty = 1;
+
+       if (event->key_report_jack) {
+               struct hda_jack_tbl *report_to =
+                       snd_hda_jack_tbl_get_mst(codec, event->key_report_jack,
+                                                event->dev_id);
+               if (report_to)
+                       report_to->jack_dirty = 1;
+       } else
+               event->jack_dirty = 1;
 
        call_jack_callback(codec, res, event);
        snd_hda_jack_report_sync(codec);
index 8ceaf0ef5df189d64bd3a121137774e23ffbedbd..2abf7aac243a24708989315fd48f2e6b9700e521 100644 (file)
@@ -40,6 +40,7 @@ struct hda_jack_tbl {
        unsigned int block_report:1;    /* in a transitional state - do not report to userspace */
        hda_nid_t gating_jack;          /* valid when gating jack plugged */
        hda_nid_t gated_jack;           /* gated is dependent on this jack */
+       hda_nid_t key_report_jack;      /* key reports to this jack */
        int type;
        int button_state;
        struct snd_jack *jack;
@@ -99,6 +100,13 @@ snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid,
 int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid,
                                 hda_nid_t gating_nid);
 
+int snd_hda_jack_bind_keymap(struct hda_codec *codec, hda_nid_t key_nid,
+                            const struct hda_jack_keymap *keymap,
+                            hda_nid_t jack_nid);
+
+void snd_hda_jack_set_button_state(struct hda_codec *codec, hda_nid_t jack_nid,
+                                  int button_state);
+
 u32 snd_hda_jack_pin_sense(struct hda_codec *codec, hda_nid_t nid, int dev_id);
 
 /* the jack state returned from snd_hda_jack_detect_state() */
index 4871507cd4bf34a5ccf0c0006e37b7a07914c831..ee8d70afe6360daf80c85541161750db634ec70e 100644 (file)
@@ -3102,7 +3102,7 @@ static void alc_headset_btn_callback(struct hda_codec *codec,
        if (jack->unsol_res & (7 << 10))
                report |= SND_JACK_BTN_3;
 
-       jack->jack->button_state = report;
+       snd_hda_jack_set_button_state(codec, jack->nid, report);
 }
 
 static void alc_disable_headset_jack_key(struct hda_codec *codec)
@@ -3163,16 +3163,23 @@ static void alc_fixup_headset_jack(struct hda_codec *codec,
                                    const struct hda_fixup *fix, int action)
 {
        struct alc_spec *spec = codec->spec;
+       hda_nid_t hp_pin;
 
        switch (action) {
        case HDA_FIXUP_ACT_PRE_PROBE:
                spec->has_hs_key = 1;
                snd_hda_jack_detect_enable_callback(codec, 0x55,
                                                    alc_headset_btn_callback);
-               snd_hda_jack_add_kctl(codec, 0x55, "Headset Jack", false,
-                                     SND_JACK_HEADSET, alc_headset_btn_keymap);
                break;
-       case HDA_FIXUP_ACT_INIT:
+       case HDA_FIXUP_ACT_BUILD:
+               hp_pin = alc_get_hp_pin(spec);
+               if (!hp_pin || snd_hda_jack_bind_keymap(codec, 0x55,
+                                                       alc_headset_btn_keymap,
+                                                       hp_pin))
+                       snd_hda_jack_add_kctl(codec, 0x55, "Headset Jack",
+                                             false, SND_JACK_HEADSET,
+                                             alc_headset_btn_keymap);
+
                alc_enable_headset_jack_key(codec);
                break;
        }