pmaports/device/testing/linux-samsung-i927/0057-ASoC-tegra_wm8994-New-driver-based-on-WM8903.patch
Oliver Smith 64035ac463
device/*: move to device/testing/* (!1063)
Prepare for better device categorization by moving everything to testing
subdir first.

[skip-ci]: chicken-egg problem: passing pmaports CI depends on pmbootstrap MR
				depends on this MR

Related: postmarketos#16
2020-03-14 08:35:32 +01:00

459 lines
13 KiB
Diff

From c96dc7817d36eedb85d8ac3b8775e4d9803f8467 Mon Sep 17 00:00:00 2001
From: Sergey Larin <cerg2010cerg2010@mail.ru>
Date: Sun, 14 Jul 2019 20:22:44 +0300
Subject: [PATCH] ASoC: tegra_wm8994: New driver based on WM8903
This fully routed driver resolves issues with suspend/resume state not being
correctly restored. It also provides headphone detection and internal/external
mic switching (however, mic detection is probably broken).
Signed-off-by: Sergey Larin <cerg2010cerg2010@mail.ru>
---
sound/soc/tegra/tegra_wm8994.c | 295 +++++++++++++++++++++------------
1 file changed, 188 insertions(+), 107 deletions(-)
diff --git a/sound/soc/tegra/tegra_wm8994.c b/sound/soc/tegra/tegra_wm8994.c
index 87cb36df3bcf..5e59b3b34f72 100755
--- a/sound/soc/tegra/tegra_wm8994.c
+++ b/sound/soc/tegra/tegra_wm8994.c
@@ -1,8 +1,10 @@
/*
* tegra_wm8994.c - Tegra machine ASoC driver for boards using WM8994 codec.
*
+ * Author: Sergey Larin <cerg2010cerg2010@mail.ru>
+ * Based on driver for wm8994 by:
* Author: Stephen Warren <swarren@nvidia.com>
- * Copyright (C) 2010-2011 - NVIDIA, Inc.
+ * Copyright (C) 2010-2012 - NVIDIA, Inc.
*
* Based on code copyright/by:
*
@@ -28,29 +30,29 @@
*
*/
-#include <asm/mach-types.h>
-
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/gpio.h>
-#include <linux/of.h>
+#include <linux/of_gpio.h>
#include <sound/core.h>
+#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
-#include "tegra_pcm.h"
-#include "tegra_asoc_utils.h"
+#include "../codecs/wm8994.h"
-#ifdef CONFIG_ARCH_TEGRA_2x_SOC
-#include "tegra20_das.h"
-#endif
+#include "tegra_asoc_utils.h"
#define DRV_NAME "tegra-snd-wm8994"
struct tegra_wm8994 {
+ int gpio_hp_det;
+ int gpio_ear_sel;
+ int gpio_int_mic_en;
+ int gpio_ext_mic_en;
struct tegra_asoc_utils_data util_data;
};
@@ -59,10 +61,9 @@ static int tegra_wm8994_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_card *card = rtd->card;
struct tegra_wm8994 *machine = snd_soc_card_get_drvdata(card);
- int srate, mclk, i2s_daifmt;
+ int srate, mclk;
int err;
srate = params_rate(params);
@@ -82,104 +83,152 @@ static int tegra_wm8994_hw_params(struct snd_pcm_substream *substream,
err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk);
if (err < 0) {
- if (!(machine->util_data.set_mclk % mclk))
- mclk = machine->util_data.set_mclk;
- else {
- dev_err(card->dev, "Can't configure clocks\n");
- return err;
- }
- }
-
- i2s_daifmt = SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS;
-
- /* Use DSP mode for mono on Tegra20 */
- // if ((params_channels(params) != 2) &&
- // (machine_is_ventana() || machine_is_harmony() ||
- // machine_is_kaen() || machine_is_aebl()))
- // i2s_daifmt |= SND_SOC_DAIFMT_DSP_A;
- // else
- i2s_daifmt |= SND_SOC_DAIFMT_I2S;
-
- err = snd_soc_dai_set_fmt(codec_dai, i2s_daifmt);
- if (err < 0) {
- dev_err(card->dev, "codec_dai fmt not set\n");
+ dev_err(card->dev, "Can't configure clocks\n");
return err;
}
- err = snd_soc_dai_set_fmt(cpu_dai, i2s_daifmt);
- if (err < 0) {
- dev_err(card->dev, "cpu_dai fmt not set\n");
- return err;
- }
- /* Need to check clk id */
- err = snd_soc_dai_set_sysclk(codec_dai, 1, mclk,
+ err = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_MCLK1, mclk,
SND_SOC_CLOCK_IN);
if (err < 0) {
dev_err(card->dev, "codec_dai clock not set\n");
return err;
}
-#ifdef CONFIG_ARCH_TEGRA_2x_SOC
- err = tegra20_das_connect_dac_to_dap(TEGRA20_DAS_DAP_SEL_DAC1,
- TEGRA20_DAS_DAP_ID_1);
- if (err < 0) {
- dev_err(card->dev, "failed to set dap-dac path\n");
- return err;
- }
-
- err = tegra20_das_connect_dap_to_dac(TEGRA20_DAS_DAP_ID_1,
- TEGRA20_DAS_DAP_SEL_DAC1);
- if (err < 0) {
- dev_err(card->dev, "failed to set dac-dap path\n");
- return err;
- }
-#endif
return 0;
}
-static int tegra_hw_free(struct snd_pcm_substream *substream)
+static const struct snd_soc_ops tegra_wm8994_ops = {
+ .hw_params = tegra_wm8994_hw_params,
+};
+
+static struct snd_soc_jack tegra_wm8994_hp_jack;
+
+static struct snd_soc_jack_pin tegra_wm8994_hp_jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+};
+
+static struct snd_soc_jack_gpio tegra_wm8994_hp_jack_gpio = {
+ .name = "headphone detect",
+ .report = SND_JACK_HEADPHONE,
+ .debounce_time = 150,
+ .invert = 1,
+};
+
+static struct snd_soc_jack tegra_wm8994_mic_jack;
+
+static struct snd_soc_jack_pin tegra_wm8994_mic_jack_pins[] = {
+ {
+ .pin = "Mic Jack",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static int tegra_wm8994_event_ext_mic(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
{
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct snd_soc_card *card = dapm->card;
+ struct tegra_wm8994 *machine = snd_soc_card_get_drvdata(card);
+
+ pr_info("EXT MIC event: %s", SND_SOC_DAPM_EVENT_ON(event) ? "on" : "off");
+
+ if (gpio_is_valid(machine->gpio_ext_mic_en))
+ {
+ gpio_set_value_cansleep(machine->gpio_ext_mic_en,
+ SND_SOC_DAPM_EVENT_ON(event));
+ }
+
+ if (gpio_is_valid(machine->gpio_int_mic_en))
+ {
+ gpio_set_value_cansleep(machine->gpio_int_mic_en,
+ !SND_SOC_DAPM_EVENT_ON(event));
+ }
+
+ /* Internal/external mic switch */
+ if (gpio_is_valid(machine->gpio_ear_sel))
+ {
+ gpio_set_value_cansleep(machine->gpio_ear_sel,
+ SND_SOC_DAPM_EVENT_ON(event));
+ }
+
return 0;
}
-static struct snd_soc_ops tegra_wm8994_ops = {
- .hw_params = tegra_wm8994_hw_params,
- .hw_free = tegra_hw_free,
+static const struct snd_soc_dapm_widget tegra_wm8994_dapm_widgets[] = {
+ SND_SOC_DAPM_SPK("Int Spk", NULL),
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Mic Jack", tegra_wm8994_event_ext_mic),
+ SND_SOC_DAPM_SPK("Earpiece Spk", NULL),
+};
+
+static const struct snd_kcontrol_new tegra_wm8994_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Int Spk"),
};
static int tegra_wm8994_init(struct snd_soc_pcm_runtime *rtd)
{
- pr_info("%s\n", __func__);
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_component *component = codec_dai->component;
+ struct snd_soc_card *card = rtd->card;
+ struct tegra_wm8994 *machine = snd_soc_card_get_drvdata(card);
+
+ if (gpio_is_valid(machine->gpio_hp_det)) {
+ tegra_wm8994_hp_jack_gpio.gpio = machine->gpio_hp_det;
+ snd_soc_card_jack_new(rtd->card, "Headphone Jack",
+ SND_JACK_HEADPHONE, &tegra_wm8994_hp_jack,
+ tegra_wm8994_hp_jack_pins,
+ ARRAY_SIZE(tegra_wm8994_hp_jack_pins));
+ snd_soc_jack_add_gpios(&tegra_wm8994_hp_jack,
+ 1,
+ &tegra_wm8994_hp_jack_gpio);
+ }
+
+ snd_soc_card_jack_new(rtd->card, "Mic Jack", SND_JACK_MICROPHONE,
+ &tegra_wm8994_mic_jack,
+ tegra_wm8994_mic_jack_pins,
+ ARRAY_SIZE(tegra_wm8994_mic_jack_pins));
+ wm8994_mic_detect(component, &tegra_wm8994_mic_jack, 1);
+
return 0;
}
static int tegra_wm8994_remove(struct snd_soc_card *card)
{
- pr_info("%s\n", __func__);
+ struct snd_soc_pcm_runtime *rtd =
+ snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_component *component = codec_dai->component;
+
+ wm8994_mic_detect(component, NULL, 1);
+
return 0;
}
-static struct snd_soc_dai_link tegra_wm8994_dai[] = {
- {
- .name = "WM8994",
- .stream_name = "WM8994 PCM",
- .codec_dai_name = "wm8994-aif1",
- .init = tegra_wm8994_init,
-
- // .codec_name = "WM8994 I2C Codec.8-001b",
- // .platform_name = "tegra-pcm-audio",
- // .cpu_dai_name = "tegra20-i2s.0",
- // .codec_dai_name = "WM8994 PAIFRX",
- .ops = &tegra_wm8994_ops,
- },
+static struct snd_soc_dai_link tegra_wm8994_dai = {
+ .name = "WM8994",
+ .stream_name = "WM8994 PCM",
+ .codec_dai_name = "wm8994-aif1",
+ .init = tegra_wm8994_init,
+ .ops = &tegra_wm8994_ops,
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
};
static struct snd_soc_card snd_soc_tegra_wm8994 = {
.name = "tegra-wm8994",
- .dai_link = tegra_wm8994_dai,
- .num_links = ARRAY_SIZE(tegra_wm8994_dai),
+ .owner = THIS_MODULE,
+ .dai_link = &tegra_wm8994_dai,
+ .num_links = 1,
.remove = tegra_wm8994_remove,
+ .controls = tegra_wm8994_controls,
+ .num_controls = ARRAY_SIZE(tegra_wm8994_controls),
+ .dapm_widgets = tegra_wm8994_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tegra_wm8994_dapm_widgets),
+ .fully_routed = true,
};
static int tegra_wm8994_driver_probe(struct platform_device *pdev)
@@ -189,59 +238,95 @@ static int tegra_wm8994_driver_probe(struct platform_device *pdev)
struct tegra_wm8994 *machine;
int ret;
- pr_info("%s\n", __func__);
-
- if (!pdev->dev.platform_data && !pdev->dev.of_node) {
- dev_err(&pdev->dev, "No platform data supplied\n");
- return -EINVAL;
- }
-
machine = devm_kzalloc(&pdev->dev, sizeof(struct tegra_wm8994),
- GFP_KERNEL);
- if (!machine) {
- dev_err(&pdev->dev, "Can't allocate tegra_wm8994 struct\n");
- ret = -ENOMEM;
- goto err;
- }
+ GFP_KERNEL);
+ if (!machine)
+ return -ENOMEM;
card->dev = &pdev->dev;
- platform_set_drvdata(pdev, card);
snd_soc_card_set_drvdata(card, machine);
- // Parse device tree nodes
+ machine->gpio_ear_sel = of_get_named_gpio(np, "nvidia,ear-sel-gpios",
+ 0);
+ if (machine->gpio_ear_sel == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ if (gpio_is_valid(machine->gpio_ear_sel)) {
+ ret = devm_gpio_request_one(&pdev->dev, machine->gpio_ear_sel,
+ GPIOF_OUT_INIT_HIGH, "ear_sel");
+ if (ret) {
+ dev_err(card->dev, "cannot get ear_sel gpio\n");
+ return ret;
+ }
+ gpio_set_value_cansleep(machine->gpio_ear_sel, 0);
+ }
- // ...
+ machine->gpio_hp_det = of_get_named_gpio(np, "nvidia,hp-det-gpios", 0);
+ if (machine->gpio_hp_det == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ machine->gpio_int_mic_en = of_get_named_gpio(np,
+ "nvidia,int-mic-en-gpios", 0);
+ if (machine->gpio_int_mic_en == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ if (gpio_is_valid(machine->gpio_int_mic_en)) {
+ /* Disable int mic; enable signal is active-high */
+ ret = devm_gpio_request_one(&pdev->dev,
+ machine->gpio_int_mic_en,
+ GPIOF_OUT_INIT_LOW, "int_mic_en");
+ if (ret) {
+ dev_err(card->dev, "cannot get int_mic_en gpio\n");
+ return ret;
+ }
+ gpio_set_value_cansleep(machine->gpio_int_mic_en, 1);
+ }
+
+ machine->gpio_ext_mic_en = of_get_named_gpio(np,
+ "nvidia,ext-mic-en-gpios", 0);
+ if (machine->gpio_ext_mic_en == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ if (gpio_is_valid(machine->gpio_ext_mic_en)) {
+ /* Enable ext mic; enable signal is active-low */
+ ret = devm_gpio_request_one(&pdev->dev,
+ machine->gpio_ext_mic_en,
+ GPIOF_OUT_INIT_LOW, "ext_mic_en");
+ if (ret) {
+ dev_err(card->dev, "cannot get ext_mic_en gpio\n");
+ return ret;
+ }
+ gpio_set_value_cansleep(machine->gpio_ext_mic_en, 0);
+ }
ret = snd_soc_of_parse_card_name(card, "nvidia,model");
if (ret)
goto err;
+ ret = snd_soc_of_parse_audio_routing(card, "nvidia,audio-routing");
+ if (ret)
+ goto err;
- tegra_wm8994_dai[0].codec_of_node = of_parse_phandle(np,
+ tegra_wm8994_dai.codec_of_node = of_parse_phandle(np,
"nvidia,audio-codec", 0);
- if (!tegra_wm8994_dai[0].codec_of_node) {
+ if (!tegra_wm8994_dai.codec_of_node) {
dev_err(&pdev->dev,
"Property 'nvidia,audio-codec' missing or invalid\n");
ret = -EINVAL;
goto err;
}
- tegra_wm8994_dai[0].cpu_of_node = of_parse_phandle(np,
+ tegra_wm8994_dai.cpu_of_node = of_parse_phandle(np,
"nvidia,i2s-controller", 0);
- if (!tegra_wm8994_dai[0].cpu_of_node) {
+ if (!tegra_wm8994_dai.cpu_of_node) {
dev_err(&pdev->dev,
"Property 'nvidia,i2s-controller' missing or invalid\n");
ret = -EINVAL;
goto err;
}
- tegra_wm8994_dai[0].platform_of_node = tegra_wm8994_dai[0].cpu_of_node;
+ tegra_wm8994_dai.platform_of_node = tegra_wm8994_dai.cpu_of_node;
+
ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev);
- if (ret) {
- dev_err(&pdev->dev, "tegra_asoc_utils_init failed (%d)\n",
- ret);
+ if (ret)
goto err;
- }
ret = snd_soc_register_card(card);
if (ret) {
@@ -250,9 +335,6 @@ static int tegra_wm8994_driver_probe(struct platform_device *pdev)
goto err_fini_utils;
}
- pr_info("%s: probed\n", __func__);
-
-
return 0;
err_fini_utils:
@@ -265,6 +347,7 @@ static int tegra_wm8994_driver_remove(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
struct tegra_wm8994 *machine = snd_soc_card_get_drvdata(card);
+
snd_soc_unregister_card(card);
tegra_asoc_utils_fini(&machine->util_data);
@@ -280,17 +363,15 @@ static const struct of_device_id tegra_wm8994_of_match[] = {
static struct platform_driver tegra_wm8994_driver = {
.driver = {
.name = DRV_NAME,
- .owner = THIS_MODULE,
.pm = &snd_soc_pm_ops,
.of_match_table = tegra_wm8994_of_match,
},
.probe = tegra_wm8994_driver_probe,
.remove = tegra_wm8994_driver_remove,
};
-
module_platform_driver(tegra_wm8994_driver);
-MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_AUTHOR("Sergey Larin <cerg2010cerg2010@mail.ru>");
MODULE_DESCRIPTION("Tegra+WM8994 machine ASoC driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRV_NAME);
--
2.22.0