From 63d4641973b3ecb3b377e9088bdeecc3fe9e19ea Mon Sep 17 00:00:00 2001
From: Kailang Yang <kailang@realtek.com>
Date: Wed, 28 Nov 2018 15:32:45 +0800
Subject: [PATCH] ALSA: hda/realtek - Enable headset button support for new
 codec

This patch will enable headset button for new Chrome platform.

Signed-off-by: Kailang Yang <kailang@realtek.com>

diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c
index c499727..f9f55d2 100644
--- a/sound/pci/hda/hda_jack.c
+++ b/sound/pci/hda/hda_jack.c
@@ -110,7 +110,7 @@ EXPORT_SYMBOL_GPL(snd_hda_jack_tbl_get_from_tag);
  * @codec: the HDA codec
  * @nid: pin NID to assign
  */
-static struct hda_jack_tbl *
+struct hda_jack_tbl *
 snd_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid)
 {
 	struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, nid);
@@ -124,6 +124,7 @@ snd_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid)
 	jack->tag = codec->jacktbl.used;
 	return jack;
 }
+EXPORT_SYMBOL_GPL(snd_hda_jack_tbl_new);
 
 void snd_hda_jack_tbl_clear(struct hda_codec *codec)
 {
diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h
index e9814c0..a119f1d 100644
--- a/sound/pci/hda/hda_jack.h
+++ b/sound/pci/hda/hda_jack.h
@@ -47,6 +47,8 @@ struct hda_jack_tbl *
 snd_hda_jack_tbl_get(struct hda_codec *codec, hda_nid_t nid);
 struct hda_jack_tbl *
 snd_hda_jack_tbl_get_from_tag(struct hda_codec *codec, unsigned char tag);
+struct hda_jack_tbl *
+snd_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid);
 
 void snd_hda_jack_tbl_clear(struct hda_codec *codec);
 
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 06f9303..0fcc98b 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -5371,6 +5371,95 @@ static void alc285_fixup_invalidate_dacs(struct hda_codec *codec,
 	snd_hda_override_wcaps(codec, 0x03, 0);
 }
 
+static void alc_headset_btn_unsol_event(struct hda_codec *codec,
+				unsigned int res)
+{
+	struct hda_jack_tbl *jack;
+	int report = 0;
+
+	jack = snd_hda_jack_tbl_get(codec, 0x55);
+	report = SND_JACK_HEADSET;
+	if (jack->nid == 0x55) {
+		if (res & (7 << 13))
+			report |= SND_JACK_BTN_0;
+
+		if (res  & (1 << 16 | 3 << 8))
+			report |= SND_JACK_BTN_1;
+
+		/* Volume up key */
+		if (res & (7 << 23))
+			report |= SND_JACK_BTN_2;
+
+		/* Volume down key */
+		if (res & (7 << 10))
+			report |= SND_JACK_BTN_3;
+
+		report |= report & jack->type;
+		snd_jack_report(jack->jack, report);
+	}
+
+	snd_hda_jack_unsol_event(codec, res);
+}
+
+static int alc_hda_jack_add_key_kctl(struct hda_codec *codec, hda_nid_t nid,
+			  const char *name, int type)
+{
+	struct hda_jack_tbl *jack;
+	int err;
+
+	jack = snd_hda_jack_tbl_new(codec, nid);
+	if (!jack)
+		return 0;
+	if (jack->jack)
+		return 0; /* already created */
+
+	err = snd_jack_new(codec->card, name, type,
+			   &jack->jack, false, false);
+	if (err < 0)
+		return err;
+
+	snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+	snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+	snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+	snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+	jack->type = type;
+	jack->jack->private_data = jack;
+
+	return 0;
+}
+
+
+static void alc_fixup_headset_jack(struct hda_codec *codec,
+				    const struct hda_fixup *fix, int action)
+{
+
+	switch (action) {
+	case HDA_FIXUP_ACT_PRE_PROBE:
+		codec->patch_ops.unsol_event = alc_headset_btn_unsol_event;
+		snd_hda_jack_detect_enable(codec, 0x55);
+		alc_hda_jack_add_key_kctl(codec, 0x55,"Headset Jack", SND_JACK_HEADSET |
+			SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3);
+		break;
+	case HDA_FIXUP_ACT_INIT:
+		switch (codec->core.vendor_id) {
+		case 0x10ec0225:
+		case 0x10ec0295:
+		case 0x10ec0299:
+			alc_write_coef_idx(codec, 0x48, 0x8011);
+			alc_update_coef_idx(codec, 0x49, 0x007f, 0x0045);
+			alc_update_coef_idx(codec, 0x44, 0x007f << 8, 0x0045 << 8);
+			break;
+		case 0x10ec0236:
+		case 0x10ec0256:
+			alc_write_coef_idx(codec, 0x48, 0x8011);
+			alc_update_coef_idx(codec, 0x49, 0x007f, 0x0045);
+			break;
+		}
+		break;
+	}
+}
+
 /* for hda_fixup_thinkpad_acpi() */
 #include "thinkpad_helper.c"
 
@@ -5510,6 +5599,7 @@ enum {
 	ALC221_FIXUP_HP_HEADSET_MIC,
 	ALC285_FIXUP_LENOVO_HEADPHONE_NOISE,
 	ALC295_FIXUP_HP_AUTO_MUTE,
+	ALC225_FIXUP_HEADSET_JACK,
 };
 
 static const struct hda_fixup alc269_fixups[] = {
@@ -6387,6 +6477,10 @@ static const struct hda_fixup alc269_fixups[] = {
 		.type = HDA_FIXUP_FUNC,
 		.v.func = alc_fixup_auto_mute_via_amp,
 	},
+	[ALC225_FIXUP_HEADSET_JACK] = {
+		.type = HDA_FIXUP_FUNC,
+		.v.func = alc_fixup_headset_jack,
+	},
 };
 
 static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -6785,6 +6879,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
 	{.id = ALC255_FIXUP_DUMMY_LINEOUT_VERB, .name = "alc255-dummy-lineout"},
 	{.id = ALC255_FIXUP_DELL_HEADSET_MIC, .name = "alc255-dell-headset"},
 	{.id = ALC295_FIXUP_HP_X360, .name = "alc295-hp-x360"},
+	{.id = ALC225_FIXUP_HEADSET_JACK, .name = "alc-sense-combo"},
 	{}
 };
 #define ALC225_STANDARD_PINS \
