diff mbox series

ALSA: hda - bind headset buttons to the headphone jack

Message ID 20210303012346.3290-1-hui.wang@canonical.com (mailing list archive)
State Superseded
Headers show
Series ALSA: hda - bind headset buttons to the headphone jack | expand

Commit Message

Hui Wang March 3, 2021, 1:23 a.m. UTC
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>
---
 sound/pci/hda/hda_jack.c      | 43 ++++++++++++++++++++++++++++++++++-
 sound/pci/hda/hda_jack.h      |  5 ++++
 sound/pci/hda/patch_realtek.c | 23 ++++++++++++++++---
 3 files changed, 67 insertions(+), 4 deletions(-)

Comments

Takashi Iwai March 3, 2021, 8:46 a.m. UTC | #1
On Wed, 03 Mar 2021 02:23:46 +0100,
Hui Wang wrote:
> 
> 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>
> ---
>  sound/pci/hda/hda_jack.c      | 43 ++++++++++++++++++++++++++++++++++-
>  sound/pci/hda/hda_jack.h      |  5 ++++
>  sound/pci/hda/patch_realtek.c | 23 ++++++++++++++++---
>  3 files changed, 67 insertions(+), 4 deletions(-)
> 
> diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c
> index b8b568046592..640b72b598e8 100644
> --- a/sound/pci/hda/hda_jack.c
> +++ b/sound/pci/hda/hda_jack.c
> @@ -388,6 +388,39 @@ 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_report_sync - sync the states of all jacks and report if changed
>   * @codec: the HDA codec
> @@ -651,7 +684,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);
> diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h
> index 8ceaf0ef5df1..339746dd9578 100644
> --- a/sound/pci/hda/hda_jack.h
> +++ b/sound/pci/hda/hda_jack.h
> @@ -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,10 @@ 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);
> +
>  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() */
> diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
> index 52907506e16e..f381474f9403 100644
> --- a/sound/pci/hda/patch_realtek.c
> +++ b/sound/pci/hda/patch_realtek.c
> @@ -3102,6 +3102,16 @@ static void alc_headset_btn_callback(struct hda_codec *codec,
>  	if (jack->unsol_res & (7 << 10))
>  		report |= SND_JACK_BTN_3;
>  
> +	if (jack->jack->key_report_jack) {
> +		struct hda_jack_tbl *report_to =
> +			snd_hda_jack_tbl_get(codec, jack->jack->key_report_jack);
> +
> +		if (report_to) {
> +			report_to->button_state = report;
> +			return;
> +		}
> +	}
> +
>  	jack->jack->button_state = report;
>  }

I think this part should be rather in hda_jack.c, e.g. create a function
snd_hda_jack_set_button_state() and handle the redirection there.
It's not good to change the content in jack->jack externally.


thanks,

Takashi
Hui Wang March 3, 2021, 12:17 p.m. UTC | #2
On 3/3/21 4:46 PM, Takashi Iwai wrote:
> On Wed, 03 Mar 2021 02:23:46 +0100,
> Hui Wang wrote:
>> With the HDA driver, if the headset buttons are supported, an audio
[snip]
>> +
>>   	jack->jack->button_state = report;
>>   }
> I think this part should be rather in hda_jack.c, e.g. create a function
> snd_hda_jack_set_button_state() and handle the redirection there.
> It's not good to change the content in jack->jack externally.

OK, got it. will change it.

Thanks.

>
> thanks,
>
> Takashi
diff mbox series

Patch

diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c
index b8b568046592..640b72b598e8 100644
--- a/sound/pci/hda/hda_jack.c
+++ b/sound/pci/hda/hda_jack.c
@@ -388,6 +388,39 @@  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_report_sync - sync the states of all jacks and report if changed
  * @codec: the HDA codec
@@ -651,7 +684,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);
diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h
index 8ceaf0ef5df1..339746dd9578 100644
--- a/sound/pci/hda/hda_jack.h
+++ b/sound/pci/hda/hda_jack.h
@@ -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,10 @@  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);
+
 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() */
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 52907506e16e..f381474f9403 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -3102,6 +3102,16 @@  static void alc_headset_btn_callback(struct hda_codec *codec,
 	if (jack->unsol_res & (7 << 10))
 		report |= SND_JACK_BTN_3;
 
+	if (jack->jack->key_report_jack) {
+		struct hda_jack_tbl *report_to =
+			snd_hda_jack_tbl_get(codec, jack->jack->key_report_jack);
+
+		if (report_to) {
+			report_to->button_state = report;
+			return;
+		}
+	}
+
 	jack->jack->button_state = report;
 }
 
@@ -3163,16 +3173,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;
 	}