Make headphone work on Huawei Matebook D15
Merge series from Mauro Carvalho Chehab <mchehab@kernel.org>: Huawei Matebook D15 uses two different GPIOs are used to control the output: - gpio0 controls the speaker output; - gpio1 controls the headphone output. Changing both at the same time cause spurious events that are mis-interpreted as input events, causing troubles on apps. So, a delay is needed before turning on such gpios. Also, the headset microphone is connected to MIC1, instead of MIC2 port. With this patch, plugging a headphone causes a jack event to trigger the speaker supply, powering down the speaker and powering up the headphone output. Removing the headphone also triggers the power supply, powering up the speaker and powering down the headphone. The headset microphone also works.
This commit is contained in:
commit
aa70527d36
|
@ -27,9 +27,11 @@
|
|||
#define SOF_ES8336_SSP_CODEC(quirk) ((quirk) & GENMASK(3, 0))
|
||||
#define SOF_ES8336_SSP_CODEC_MASK (GENMASK(3, 0))
|
||||
|
||||
#define SOF_ES8336_TGL_GPIO_QUIRK BIT(4)
|
||||
#define SOF_ES8336_SPEAKERS_EN_GPIO1_QUIRK BIT(4)
|
||||
#define SOF_ES8336_ENABLE_DMIC BIT(5)
|
||||
#define SOF_ES8336_JD_INVERTED BIT(6)
|
||||
#define SOF_ES8336_HEADPHONE_GPIO BIT(7)
|
||||
#define SOC_ES8336_HEADSET_MIC1 BIT(8)
|
||||
|
||||
static unsigned long quirk;
|
||||
|
||||
|
@ -39,7 +41,7 @@ MODULE_PARM_DESC(quirk, "Board-specific quirk override");
|
|||
|
||||
struct sof_es8336_private {
|
||||
struct device *codec_dev;
|
||||
struct gpio_desc *gpio_pa;
|
||||
struct gpio_desc *gpio_speakers, *gpio_headphone;
|
||||
struct snd_soc_jack jack;
|
||||
struct list_head hdmi_pcm_list;
|
||||
bool speaker_en;
|
||||
|
@ -51,19 +53,31 @@ struct sof_hdmi_pcm {
|
|||
int device;
|
||||
};
|
||||
|
||||
static const struct acpi_gpio_params pa_enable_gpio = { 0, 0, true };
|
||||
static const struct acpi_gpio_mapping acpi_es8336_gpios[] = {
|
||||
{ "pa-enable-gpios", &pa_enable_gpio, 1 },
|
||||
static const struct acpi_gpio_params enable_gpio0 = { 0, 0, true };
|
||||
static const struct acpi_gpio_params enable_gpio1 = { 1, 0, true };
|
||||
|
||||
static const struct acpi_gpio_mapping acpi_speakers_enable_gpio0[] = {
|
||||
{ "speakers-enable-gpios", &enable_gpio0, 1 },
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct acpi_gpio_params quirk_pa_enable_gpio = { 1, 0, true };
|
||||
static const struct acpi_gpio_mapping quirk_acpi_es8336_gpios[] = {
|
||||
{ "pa-enable-gpios", &quirk_pa_enable_gpio, 1 },
|
||||
static const struct acpi_gpio_mapping acpi_speakers_enable_gpio1[] = {
|
||||
{ "speakers-enable-gpios", &enable_gpio1, 1 },
|
||||
};
|
||||
|
||||
static const struct acpi_gpio_mapping acpi_enable_both_gpios[] = {
|
||||
{ "speakers-enable-gpios", &enable_gpio0, 1 },
|
||||
{ "headphone-enable-gpios", &enable_gpio1, 1 },
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct acpi_gpio_mapping *gpio_mapping = acpi_es8336_gpios;
|
||||
static const struct acpi_gpio_mapping acpi_enable_both_gpios_rev_order[] = {
|
||||
{ "speakers-enable-gpios", &enable_gpio1, 1 },
|
||||
{ "headphone-enable-gpios", &enable_gpio0, 1 },
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct acpi_gpio_mapping *gpio_mapping = acpi_speakers_enable_gpio0;
|
||||
|
||||
static void log_quirks(struct device *dev)
|
||||
{
|
||||
|
@ -71,10 +85,14 @@ static void log_quirks(struct device *dev)
|
|||
dev_info(dev, "quirk SSP%ld\n", SOF_ES8336_SSP_CODEC(quirk));
|
||||
if (quirk & SOF_ES8336_ENABLE_DMIC)
|
||||
dev_info(dev, "quirk DMIC enabled\n");
|
||||
if (quirk & SOF_ES8336_TGL_GPIO_QUIRK)
|
||||
dev_info(dev, "quirk TGL GPIO enabled\n");
|
||||
if (quirk & SOF_ES8336_SPEAKERS_EN_GPIO1_QUIRK)
|
||||
dev_info(dev, "Speakers GPIO1 quirk enabled\n");
|
||||
if (quirk & SOF_ES8336_HEADPHONE_GPIO)
|
||||
dev_info(dev, "quirk headphone GPIO enabled\n");
|
||||
if (quirk & SOF_ES8336_JD_INVERTED)
|
||||
dev_info(dev, "quirk JD inverted enabled\n");
|
||||
if (quirk & SOC_ES8336_HEADSET_MIC1)
|
||||
dev_info(dev, "quirk headset at mic1 port enabled\n");
|
||||
}
|
||||
|
||||
static int sof_es8316_speaker_power_event(struct snd_soc_dapm_widget *w,
|
||||
|
@ -83,12 +101,23 @@ static int sof_es8316_speaker_power_event(struct snd_soc_dapm_widget *w,
|
|||
struct snd_soc_card *card = w->dapm->card;
|
||||
struct sof_es8336_private *priv = snd_soc_card_get_drvdata(card);
|
||||
|
||||
if (SND_SOC_DAPM_EVENT_ON(event))
|
||||
priv->speaker_en = false;
|
||||
else
|
||||
priv->speaker_en = true;
|
||||
if (priv->speaker_en == !SND_SOC_DAPM_EVENT_ON(event))
|
||||
return 0;
|
||||
|
||||
gpiod_set_value_cansleep(priv->gpio_pa, priv->speaker_en);
|
||||
priv->speaker_en = !SND_SOC_DAPM_EVENT_ON(event);
|
||||
|
||||
if (SND_SOC_DAPM_EVENT_ON(event))
|
||||
msleep(70);
|
||||
|
||||
gpiod_set_value_cansleep(priv->gpio_speakers, priv->speaker_en);
|
||||
|
||||
if (!(quirk & SOF_ES8336_HEADPHONE_GPIO))
|
||||
return 0;
|
||||
|
||||
if (SND_SOC_DAPM_EVENT_ON(event))
|
||||
msleep(70);
|
||||
|
||||
gpiod_set_value_cansleep(priv->gpio_headphone, priv->speaker_en);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -114,18 +143,23 @@ static const struct snd_soc_dapm_route sof_es8316_audio_map[] = {
|
|||
|
||||
/*
|
||||
* There is no separate speaker output instead the speakers are muxed to
|
||||
* the HP outputs. The mux is controlled by the "Speaker Power" supply.
|
||||
* the HP outputs. The mux is controlled Speaker and/or headphone switch.
|
||||
*/
|
||||
{"Speaker", NULL, "HPOL"},
|
||||
{"Speaker", NULL, "HPOR"},
|
||||
{"Speaker", NULL, "Speaker Power"},
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route sof_es8316_intmic_in1_map[] = {
|
||||
static const struct snd_soc_dapm_route sof_es8316_headset_mic2_map[] = {
|
||||
{"MIC1", NULL, "Internal Mic"},
|
||||
{"MIC2", NULL, "Headset Mic"},
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route sof_es8316_headset_mic1_map[] = {
|
||||
{"MIC2", NULL, "Internal Mic"},
|
||||
{"MIC1", NULL, "Headset Mic"},
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route dmic_map[] = {
|
||||
/* digital mics */
|
||||
{"DMic", NULL, "SoC DMIC"},
|
||||
|
@ -199,8 +233,13 @@ static int sof_es8316_init(struct snd_soc_pcm_runtime *runtime)
|
|||
|
||||
card->dapm.idle_bias_off = true;
|
||||
|
||||
custom_map = sof_es8316_intmic_in1_map;
|
||||
num_routes = ARRAY_SIZE(sof_es8316_intmic_in1_map);
|
||||
if (quirk & SOC_ES8336_HEADSET_MIC1) {
|
||||
custom_map = sof_es8316_headset_mic1_map;
|
||||
num_routes = ARRAY_SIZE(sof_es8316_headset_mic1_map);
|
||||
} else {
|
||||
custom_map = sof_es8316_headset_mic2_map;
|
||||
num_routes = ARRAY_SIZE(sof_es8316_headset_mic2_map);
|
||||
}
|
||||
|
||||
ret = snd_soc_dapm_add_routes(&card->dapm, custom_map, num_routes);
|
||||
if (ret)
|
||||
|
@ -233,8 +272,14 @@ static int sof_es8336_quirk_cb(const struct dmi_system_id *id)
|
|||
{
|
||||
quirk = (unsigned long)id->driver_data;
|
||||
|
||||
if (quirk & SOF_ES8336_TGL_GPIO_QUIRK)
|
||||
gpio_mapping = quirk_acpi_es8336_gpios;
|
||||
if (quirk & SOF_ES8336_HEADPHONE_GPIO) {
|
||||
if (quirk & SOF_ES8336_SPEAKERS_EN_GPIO1_QUIRK)
|
||||
gpio_mapping = acpi_enable_both_gpios;
|
||||
else
|
||||
gpio_mapping = acpi_enable_both_gpios_rev_order;
|
||||
} else if (quirk & SOF_ES8336_SPEAKERS_EN_GPIO1_QUIRK) {
|
||||
gpio_mapping = acpi_speakers_enable_gpio1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -257,7 +302,16 @@ static const struct dmi_system_id sof_es8336_quirk_table[] = {
|
|||
DMI_MATCH(DMI_SYS_VENDOR, "IP3 tech"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "WN1"),
|
||||
},
|
||||
.driver_data = (void *)(SOF_ES8336_TGL_GPIO_QUIRK)
|
||||
.driver_data = (void *)(SOF_ES8336_SPEAKERS_EN_GPIO1_QUIRK)
|
||||
},
|
||||
{
|
||||
.callback = sof_es8336_quirk_cb,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "BOHB-WAX9-PCB-B2"),
|
||||
},
|
||||
.driver_data = (void *)(SOF_ES8336_HEADPHONE_GPIO |
|
||||
SOC_ES8336_HEADSET_MIC1)
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
@ -585,10 +639,17 @@ static int sof_es8336_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
dev_warn(codec_dev, "unable to add GPIO mapping table\n");
|
||||
|
||||
priv->gpio_pa = gpiod_get_optional(codec_dev, "pa-enable", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(priv->gpio_pa)) {
|
||||
ret = dev_err_probe(dev, PTR_ERR(priv->gpio_pa),
|
||||
"could not get pa-enable GPIO\n");
|
||||
priv->gpio_speakers = gpiod_get_optional(codec_dev, "speakers-enable", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(priv->gpio_speakers)) {
|
||||
ret = dev_err_probe(dev, PTR_ERR(priv->gpio_speakers),
|
||||
"could not get speakers-enable GPIO\n");
|
||||
goto err_put_codec;
|
||||
}
|
||||
|
||||
priv->gpio_headphone = gpiod_get_optional(codec_dev, "headphone-enable", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(priv->gpio_headphone)) {
|
||||
ret = dev_err_probe(dev, PTR_ERR(priv->gpio_headphone),
|
||||
"could not get headphone-enable GPIO\n");
|
||||
goto err_put_codec;
|
||||
}
|
||||
|
||||
|
@ -604,7 +665,7 @@ static int sof_es8336_probe(struct platform_device *pdev)
|
|||
|
||||
ret = devm_snd_soc_register_card(dev, card);
|
||||
if (ret) {
|
||||
gpiod_put(priv->gpio_pa);
|
||||
gpiod_put(priv->gpio_speakers);
|
||||
dev_err(dev, "snd_soc_register_card failed: %d\n", ret);
|
||||
goto err_put_codec;
|
||||
}
|
||||
|
@ -622,7 +683,7 @@ static int sof_es8336_remove(struct platform_device *pdev)
|
|||
struct snd_soc_card *card = platform_get_drvdata(pdev);
|
||||
struct sof_es8336_private *priv = snd_soc_card_get_drvdata(card);
|
||||
|
||||
gpiod_put(priv->gpio_pa);
|
||||
gpiod_put(priv->gpio_speakers);
|
||||
device_remove_software_node(priv->codec_dev);
|
||||
put_device(priv->codec_dev);
|
||||
|
||||
|
|
Loading…
Reference in New Issue