19e3ecb5bSAjit Pandey // SPDX-License-Identifier: GPL-2.0-only
29e3ecb5bSAjit Pandey //
39e3ecb5bSAjit Pandey // Copyright (c) 2020, The Linux Foundation. All rights reserved.
49e3ecb5bSAjit Pandey //
59e3ecb5bSAjit Pandey // sc7180.c -- ALSA SoC Machine driver for SC7180
69e3ecb5bSAjit Pandey
79e3ecb5bSAjit Pandey #include <dt-bindings/sound/sc7180-lpass.h>
83cfbf07cSAjye Huang #include <linux/gpio.h>
93cfbf07cSAjye Huang #include <linux/gpio/consumer.h>
109e3ecb5bSAjit Pandey #include <linux/module.h>
119e3ecb5bSAjit Pandey #include <linux/of_device.h>
129e3ecb5bSAjit Pandey #include <linux/platform_device.h>
139e3ecb5bSAjit Pandey #include <sound/core.h>
149e3ecb5bSAjit Pandey #include <sound/jack.h>
159e3ecb5bSAjit Pandey #include <sound/pcm.h>
169e3ecb5bSAjit Pandey #include <sound/soc.h>
179e3ecb5bSAjit Pandey #include <uapi/linux/input-event-codes.h>
189e3ecb5bSAjit Pandey
199e3ecb5bSAjit Pandey #include "../codecs/rt5682.h"
20425c5fceSlvzhaoxiong #include "../codecs/rt5682s.h"
219e3ecb5bSAjit Pandey #include "common.h"
229e3ecb5bSAjit Pandey #include "lpass.h"
239e3ecb5bSAjit Pandey
249e3ecb5bSAjit Pandey #define DEFAULT_MCLK_RATE 19200000
259e3ecb5bSAjit Pandey #define RT5682_PLL1_FREQ (48000 * 512)
269e3ecb5bSAjit Pandey
279e3ecb5bSAjit Pandey #define DRIVER_NAME "SC7180"
289e3ecb5bSAjit Pandey
299e3ecb5bSAjit Pandey struct sc7180_snd_data {
309e3ecb5bSAjit Pandey struct snd_soc_card card;
319e3ecb5bSAjit Pandey u32 pri_mi2s_clk_count;
329e3ecb5bSAjit Pandey struct snd_soc_jack hs_jack;
339e3ecb5bSAjit Pandey struct snd_soc_jack hdmi_jack;
343cfbf07cSAjye Huang struct gpio_desc *dmic_sel;
353cfbf07cSAjye Huang int dmic_switch;
369e3ecb5bSAjit Pandey };
379e3ecb5bSAjit Pandey
sc7180_jack_free(struct snd_jack * jack)389e3ecb5bSAjit Pandey static void sc7180_jack_free(struct snd_jack *jack)
399e3ecb5bSAjit Pandey {
409e3ecb5bSAjit Pandey struct snd_soc_component *component = jack->private_data;
419e3ecb5bSAjit Pandey
429e3ecb5bSAjit Pandey snd_soc_component_set_jack(component, NULL, NULL);
439e3ecb5bSAjit Pandey }
449e3ecb5bSAjit Pandey
45883bfefcSAlper Nebi Yasak static struct snd_soc_jack_pin sc7180_jack_pins[] = {
46883bfefcSAlper Nebi Yasak {
47883bfefcSAlper Nebi Yasak .pin = "Headphone Jack",
48883bfefcSAlper Nebi Yasak .mask = SND_JACK_HEADPHONE,
49883bfefcSAlper Nebi Yasak },
50883bfefcSAlper Nebi Yasak {
51883bfefcSAlper Nebi Yasak .pin = "Headset Mic",
52883bfefcSAlper Nebi Yasak .mask = SND_JACK_MICROPHONE,
53883bfefcSAlper Nebi Yasak },
54883bfefcSAlper Nebi Yasak };
55883bfefcSAlper Nebi Yasak
sc7180_headset_init(struct snd_soc_pcm_runtime * rtd)569e3ecb5bSAjit Pandey static int sc7180_headset_init(struct snd_soc_pcm_runtime *rtd)
579e3ecb5bSAjit Pandey {
589e3ecb5bSAjit Pandey struct snd_soc_card *card = rtd->card;
599e3ecb5bSAjit Pandey struct sc7180_snd_data *pdata = snd_soc_card_get_drvdata(card);
60*841361d8SKuninori Morimoto struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
619e3ecb5bSAjit Pandey struct snd_soc_component *component = codec_dai->component;
629e3ecb5bSAjit Pandey struct snd_jack *jack;
639e3ecb5bSAjit Pandey int rval;
649e3ecb5bSAjit Pandey
65883bfefcSAlper Nebi Yasak rval = snd_soc_card_jack_new_pins(card, "Headset Jack",
669e3ecb5bSAjit Pandey SND_JACK_HEADSET |
679e3ecb5bSAjit Pandey SND_JACK_HEADPHONE |
689e3ecb5bSAjit Pandey SND_JACK_BTN_0 | SND_JACK_BTN_1 |
699e3ecb5bSAjit Pandey SND_JACK_BTN_2 | SND_JACK_BTN_3,
70883bfefcSAlper Nebi Yasak &pdata->hs_jack,
71883bfefcSAlper Nebi Yasak sc7180_jack_pins,
72883bfefcSAlper Nebi Yasak ARRAY_SIZE(sc7180_jack_pins));
739e3ecb5bSAjit Pandey
749e3ecb5bSAjit Pandey if (rval < 0) {
759e3ecb5bSAjit Pandey dev_err(card->dev, "Unable to add Headset Jack\n");
769e3ecb5bSAjit Pandey return rval;
779e3ecb5bSAjit Pandey }
789e3ecb5bSAjit Pandey
799e3ecb5bSAjit Pandey jack = pdata->hs_jack.jack;
809e3ecb5bSAjit Pandey
819e3ecb5bSAjit Pandey snd_jack_set_key(jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
829e3ecb5bSAjit Pandey snd_jack_set_key(jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
839e3ecb5bSAjit Pandey snd_jack_set_key(jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
849e3ecb5bSAjit Pandey snd_jack_set_key(jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
859e3ecb5bSAjit Pandey
869e3ecb5bSAjit Pandey jack->private_data = component;
879e3ecb5bSAjit Pandey jack->private_free = sc7180_jack_free;
889e3ecb5bSAjit Pandey
899e3ecb5bSAjit Pandey return snd_soc_component_set_jack(component, &pdata->hs_jack, NULL);
909e3ecb5bSAjit Pandey }
919e3ecb5bSAjit Pandey
sc7180_hdmi_init(struct snd_soc_pcm_runtime * rtd)929e3ecb5bSAjit Pandey static int sc7180_hdmi_init(struct snd_soc_pcm_runtime *rtd)
939e3ecb5bSAjit Pandey {
949e3ecb5bSAjit Pandey struct snd_soc_card *card = rtd->card;
959e3ecb5bSAjit Pandey struct sc7180_snd_data *pdata = snd_soc_card_get_drvdata(card);
96*841361d8SKuninori Morimoto struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
979e3ecb5bSAjit Pandey struct snd_soc_component *component = codec_dai->component;
989e3ecb5bSAjit Pandey struct snd_jack *jack;
999e3ecb5bSAjit Pandey int rval;
1009e3ecb5bSAjit Pandey
1019e3ecb5bSAjit Pandey rval = snd_soc_card_jack_new(
1029e3ecb5bSAjit Pandey card, "HDMI Jack",
1039e3ecb5bSAjit Pandey SND_JACK_LINEOUT,
10419aed2d6SAkihiko Odaki &pdata->hdmi_jack);
1059e3ecb5bSAjit Pandey
1069e3ecb5bSAjit Pandey if (rval < 0) {
1079e3ecb5bSAjit Pandey dev_err(card->dev, "Unable to add HDMI Jack\n");
1089e3ecb5bSAjit Pandey return rval;
1099e3ecb5bSAjit Pandey }
1109e3ecb5bSAjit Pandey
1119e3ecb5bSAjit Pandey jack = pdata->hdmi_jack.jack;
1129e3ecb5bSAjit Pandey jack->private_data = component;
1139e3ecb5bSAjit Pandey jack->private_free = sc7180_jack_free;
1149e3ecb5bSAjit Pandey
1159e3ecb5bSAjit Pandey return snd_soc_component_set_jack(component, &pdata->hdmi_jack, NULL);
1169e3ecb5bSAjit Pandey }
1179e3ecb5bSAjit Pandey
sc7180_init(struct snd_soc_pcm_runtime * rtd)1189e3ecb5bSAjit Pandey static int sc7180_init(struct snd_soc_pcm_runtime *rtd)
1199e3ecb5bSAjit Pandey {
120*841361d8SKuninori Morimoto struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
1219e3ecb5bSAjit Pandey
1229e3ecb5bSAjit Pandey switch (cpu_dai->id) {
1239e3ecb5bSAjit Pandey case MI2S_PRIMARY:
1249e3ecb5bSAjit Pandey return sc7180_headset_init(rtd);
1259e3ecb5bSAjit Pandey case MI2S_SECONDARY:
1269e3ecb5bSAjit Pandey return 0;
1279e3ecb5bSAjit Pandey case LPASS_DP_RX:
1289e3ecb5bSAjit Pandey return sc7180_hdmi_init(rtd);
1299e3ecb5bSAjit Pandey default:
1309e3ecb5bSAjit Pandey dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
1319e3ecb5bSAjit Pandey cpu_dai->id);
1329e3ecb5bSAjit Pandey return -EINVAL;
1339e3ecb5bSAjit Pandey }
1349e3ecb5bSAjit Pandey return 0;
1359e3ecb5bSAjit Pandey }
1369e3ecb5bSAjit Pandey
sc7180_snd_startup(struct snd_pcm_substream * substream)1379e3ecb5bSAjit Pandey static int sc7180_snd_startup(struct snd_pcm_substream *substream)
1389e3ecb5bSAjit Pandey {
1399e3ecb5bSAjit Pandey struct snd_soc_pcm_runtime *rtd = substream->private_data;
1409e3ecb5bSAjit Pandey struct snd_soc_card *card = rtd->card;
1419e3ecb5bSAjit Pandey struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card);
142*841361d8SKuninori Morimoto struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
143*841361d8SKuninori Morimoto struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
144425c5fceSlvzhaoxiong int pll_id, pll_source, pll_in, pll_out, clk_id, ret;
145425c5fceSlvzhaoxiong
146833a94aaSJudy Hsiao if (!strcmp(codec_dai->name, "rt5682-aif1")) {
147425c5fceSlvzhaoxiong pll_source = RT5682_PLL1_S_MCLK;
148425c5fceSlvzhaoxiong pll_id = 0;
149425c5fceSlvzhaoxiong clk_id = RT5682_SCLK_S_PLL1;
150425c5fceSlvzhaoxiong pll_out = RT5682_PLL1_FREQ;
151425c5fceSlvzhaoxiong pll_in = DEFAULT_MCLK_RATE;
152833a94aaSJudy Hsiao } else if (!strcmp(codec_dai->name, "rt5682s-aif1")) {
153425c5fceSlvzhaoxiong pll_source = RT5682S_PLL_S_MCLK;
154425c5fceSlvzhaoxiong pll_id = RT5682S_PLL2;
155425c5fceSlvzhaoxiong clk_id = RT5682S_SCLK_S_PLL2;
156425c5fceSlvzhaoxiong pll_out = RT5682_PLL1_FREQ;
157425c5fceSlvzhaoxiong pll_in = DEFAULT_MCLK_RATE;
158425c5fceSlvzhaoxiong }
1599e3ecb5bSAjit Pandey
1609e3ecb5bSAjit Pandey switch (cpu_dai->id) {
1619e3ecb5bSAjit Pandey case MI2S_PRIMARY:
1629e3ecb5bSAjit Pandey if (++data->pri_mi2s_clk_count == 1) {
1639e3ecb5bSAjit Pandey snd_soc_dai_set_sysclk(cpu_dai,
1649e3ecb5bSAjit Pandey LPASS_MCLK0,
1659e3ecb5bSAjit Pandey DEFAULT_MCLK_RATE,
1669e3ecb5bSAjit Pandey SNDRV_PCM_STREAM_PLAYBACK);
1679e3ecb5bSAjit Pandey }
1689e3ecb5bSAjit Pandey
1699e3ecb5bSAjit Pandey snd_soc_dai_set_fmt(codec_dai,
1701148e16bSCharles Keepax SND_SOC_DAIFMT_BC_FC |
1719e3ecb5bSAjit Pandey SND_SOC_DAIFMT_NB_NF |
1729e3ecb5bSAjit Pandey SND_SOC_DAIFMT_I2S);
1739e3ecb5bSAjit Pandey
1749e3ecb5bSAjit Pandey /* Configure PLL1 for codec */
175425c5fceSlvzhaoxiong ret = snd_soc_dai_set_pll(codec_dai, pll_id, pll_source,
176425c5fceSlvzhaoxiong pll_in, pll_out);
1779e3ecb5bSAjit Pandey if (ret) {
1789e3ecb5bSAjit Pandey dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
1799e3ecb5bSAjit Pandey return ret;
1809e3ecb5bSAjit Pandey }
1819e3ecb5bSAjit Pandey
1829e3ecb5bSAjit Pandey /* Configure sysclk for codec */
183425c5fceSlvzhaoxiong ret = snd_soc_dai_set_sysclk(codec_dai, clk_id, pll_out,
1849e3ecb5bSAjit Pandey SND_SOC_CLOCK_IN);
1859e3ecb5bSAjit Pandey if (ret)
1869e3ecb5bSAjit Pandey dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n",
1879e3ecb5bSAjit Pandey ret);
1889e3ecb5bSAjit Pandey
1899e3ecb5bSAjit Pandey break;
1909e3ecb5bSAjit Pandey case MI2S_SECONDARY:
1919e3ecb5bSAjit Pandey break;
1929e3ecb5bSAjit Pandey case LPASS_DP_RX:
1939e3ecb5bSAjit Pandey break;
1949e3ecb5bSAjit Pandey default:
1959e3ecb5bSAjit Pandey dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
1969e3ecb5bSAjit Pandey cpu_dai->id);
1979e3ecb5bSAjit Pandey return -EINVAL;
1989e3ecb5bSAjit Pandey }
1999e3ecb5bSAjit Pandey return 0;
2009e3ecb5bSAjit Pandey }
2019e3ecb5bSAjit Pandey
dmic_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2023cfbf07cSAjye Huang static int dmic_get(struct snd_kcontrol *kcontrol,
2033cfbf07cSAjye Huang struct snd_ctl_elem_value *ucontrol)
2043cfbf07cSAjye Huang {
2053cfbf07cSAjye Huang struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
2063cfbf07cSAjye Huang struct sc7180_snd_data *data = snd_soc_card_get_drvdata(dapm->card);
2073cfbf07cSAjye Huang
2083cfbf07cSAjye Huang ucontrol->value.integer.value[0] = data->dmic_switch;
2093cfbf07cSAjye Huang return 0;
2103cfbf07cSAjye Huang }
2113cfbf07cSAjye Huang
dmic_set(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2123cfbf07cSAjye Huang static int dmic_set(struct snd_kcontrol *kcontrol,
2133cfbf07cSAjye Huang struct snd_ctl_elem_value *ucontrol)
2143cfbf07cSAjye Huang {
2153cfbf07cSAjye Huang struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
2163cfbf07cSAjye Huang struct sc7180_snd_data *data = snd_soc_card_get_drvdata(dapm->card);
2173cfbf07cSAjye Huang
2183cfbf07cSAjye Huang data->dmic_switch = ucontrol->value.integer.value[0];
2193cfbf07cSAjye Huang gpiod_set_value(data->dmic_sel, data->dmic_switch);
2203cfbf07cSAjye Huang return 0;
2213cfbf07cSAjye Huang }
2223cfbf07cSAjye Huang
sc7180_snd_shutdown(struct snd_pcm_substream * substream)2239e3ecb5bSAjit Pandey static void sc7180_snd_shutdown(struct snd_pcm_substream *substream)
2249e3ecb5bSAjit Pandey {
2259e3ecb5bSAjit Pandey struct snd_soc_pcm_runtime *rtd = substream->private_data;
2269e3ecb5bSAjit Pandey struct snd_soc_card *card = rtd->card;
2279e3ecb5bSAjit Pandey struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card);
228*841361d8SKuninori Morimoto struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
2299e3ecb5bSAjit Pandey
2309e3ecb5bSAjit Pandey switch (cpu_dai->id) {
2319e3ecb5bSAjit Pandey case MI2S_PRIMARY:
2329e3ecb5bSAjit Pandey if (--data->pri_mi2s_clk_count == 0) {
2339e3ecb5bSAjit Pandey snd_soc_dai_set_sysclk(cpu_dai,
2349e3ecb5bSAjit Pandey LPASS_MCLK0,
2359e3ecb5bSAjit Pandey 0,
2369e3ecb5bSAjit Pandey SNDRV_PCM_STREAM_PLAYBACK);
2379e3ecb5bSAjit Pandey }
2389e3ecb5bSAjit Pandey break;
2399e3ecb5bSAjit Pandey case MI2S_SECONDARY:
2409e3ecb5bSAjit Pandey break;
2419e3ecb5bSAjit Pandey case LPASS_DP_RX:
2429e3ecb5bSAjit Pandey break;
2439e3ecb5bSAjit Pandey default:
2449e3ecb5bSAjit Pandey dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
2459e3ecb5bSAjit Pandey cpu_dai->id);
2469e3ecb5bSAjit Pandey break;
2479e3ecb5bSAjit Pandey }
2489e3ecb5bSAjit Pandey }
2499e3ecb5bSAjit Pandey
sc7180_adau7002_init(struct snd_soc_pcm_runtime * rtd)250e936619bSxuyuqing static int sc7180_adau7002_init(struct snd_soc_pcm_runtime *rtd)
251e936619bSxuyuqing {
252*841361d8SKuninori Morimoto struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
253e936619bSxuyuqing
254e936619bSxuyuqing switch (cpu_dai->id) {
255e936619bSxuyuqing case MI2S_PRIMARY:
256e936619bSxuyuqing return 0;
257e936619bSxuyuqing case MI2S_SECONDARY:
258e936619bSxuyuqing return 0;
259e936619bSxuyuqing case LPASS_DP_RX:
260e936619bSxuyuqing return sc7180_hdmi_init(rtd);
261e936619bSxuyuqing default:
262e936619bSxuyuqing dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
263e936619bSxuyuqing cpu_dai->id);
264e936619bSxuyuqing return -EINVAL;
265e936619bSxuyuqing }
266e936619bSxuyuqing return 0;
267e936619bSxuyuqing }
268e936619bSxuyuqing
sc7180_adau7002_snd_startup(struct snd_pcm_substream * substream)269e936619bSxuyuqing static int sc7180_adau7002_snd_startup(struct snd_pcm_substream *substream)
270e936619bSxuyuqing {
271e936619bSxuyuqing struct snd_soc_pcm_runtime *rtd = substream->private_data;
272*841361d8SKuninori Morimoto struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
273*841361d8SKuninori Morimoto struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
2747f2c63d6Sxuyuqing struct snd_pcm_runtime *runtime = substream->runtime;
275e936619bSxuyuqing
276e936619bSxuyuqing switch (cpu_dai->id) {
277e936619bSxuyuqing case MI2S_PRIMARY:
278e936619bSxuyuqing snd_soc_dai_set_fmt(codec_dai,
279e936619bSxuyuqing SND_SOC_DAIFMT_CBS_CFS |
280e936619bSxuyuqing SND_SOC_DAIFMT_NB_NF |
281e936619bSxuyuqing SND_SOC_DAIFMT_I2S);
2827f2c63d6Sxuyuqing runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE;
2837f2c63d6Sxuyuqing snd_pcm_hw_constraint_msbits(runtime, 0, 32, 32);
284e936619bSxuyuqing
285e936619bSxuyuqing break;
286e936619bSxuyuqing case MI2S_SECONDARY:
287e936619bSxuyuqing break;
288e936619bSxuyuqing case LPASS_DP_RX:
289e936619bSxuyuqing break;
290e936619bSxuyuqing default:
291e936619bSxuyuqing dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
292e936619bSxuyuqing cpu_dai->id);
293e936619bSxuyuqing return -EINVAL;
294e936619bSxuyuqing }
295e936619bSxuyuqing return 0;
296e936619bSxuyuqing }
297e936619bSxuyuqing
2989e3ecb5bSAjit Pandey static const struct snd_soc_ops sc7180_ops = {
2999e3ecb5bSAjit Pandey .startup = sc7180_snd_startup,
3009e3ecb5bSAjit Pandey .shutdown = sc7180_snd_shutdown,
3019e3ecb5bSAjit Pandey };
3029e3ecb5bSAjit Pandey
303e936619bSxuyuqing static const struct snd_soc_ops sc7180_adau7002_ops = {
304e936619bSxuyuqing .startup = sc7180_adau7002_snd_startup,
305e936619bSxuyuqing };
306e936619bSxuyuqing
3079e3ecb5bSAjit Pandey static const struct snd_soc_dapm_widget sc7180_snd_widgets[] = {
3089e3ecb5bSAjit Pandey SND_SOC_DAPM_HP("Headphone Jack", NULL),
3099e3ecb5bSAjit Pandey SND_SOC_DAPM_MIC("Headset Mic", NULL),
3109e3ecb5bSAjit Pandey };
3119e3ecb5bSAjit Pandey
312883bfefcSAlper Nebi Yasak static const struct snd_kcontrol_new sc7180_snd_controls[] = {
313883bfefcSAlper Nebi Yasak SOC_DAPM_PIN_SWITCH("Headphone Jack"),
314883bfefcSAlper Nebi Yasak SOC_DAPM_PIN_SWITCH("Headset Mic"),
315883bfefcSAlper Nebi Yasak };
316883bfefcSAlper Nebi Yasak
317e936619bSxuyuqing static const struct snd_soc_dapm_widget sc7180_adau7002_snd_widgets[] = {
318e936619bSxuyuqing SND_SOC_DAPM_MIC("DMIC", NULL),
319e936619bSxuyuqing };
320e936619bSxuyuqing
3213cfbf07cSAjye Huang static const char * const dmic_mux_text[] = {
3223cfbf07cSAjye Huang "Front Mic",
3233cfbf07cSAjye Huang "Rear Mic",
3243cfbf07cSAjye Huang };
3253cfbf07cSAjye Huang
3263cfbf07cSAjye Huang static SOC_ENUM_SINGLE_DECL(sc7180_dmic_enum,
3273cfbf07cSAjye Huang SND_SOC_NOPM, 0, dmic_mux_text);
3283cfbf07cSAjye Huang
3293cfbf07cSAjye Huang static const struct snd_kcontrol_new sc7180_dmic_mux_control =
3303cfbf07cSAjye Huang SOC_DAPM_ENUM_EXT("DMIC Select Mux", sc7180_dmic_enum,
3313cfbf07cSAjye Huang dmic_get, dmic_set);
3323cfbf07cSAjye Huang
3333cfbf07cSAjye Huang static const struct snd_soc_dapm_widget sc7180_snd_dual_mic_widgets[] = {
3343cfbf07cSAjye Huang SND_SOC_DAPM_HP("Headphone Jack", NULL),
3353cfbf07cSAjye Huang SND_SOC_DAPM_MIC("Headset Mic", NULL),
3363cfbf07cSAjye Huang SND_SOC_DAPM_MIC("DMIC", NULL),
3373cfbf07cSAjye Huang SND_SOC_DAPM_MUX("Dmic Mux", SND_SOC_NOPM, 0, 0, &sc7180_dmic_mux_control),
3383cfbf07cSAjye Huang };
3393cfbf07cSAjye Huang
340883bfefcSAlper Nebi Yasak static const struct snd_kcontrol_new sc7180_snd_dual_mic_controls[] = {
341883bfefcSAlper Nebi Yasak SOC_DAPM_PIN_SWITCH("Headphone Jack"),
342883bfefcSAlper Nebi Yasak SOC_DAPM_PIN_SWITCH("Headset Mic"),
343883bfefcSAlper Nebi Yasak };
344883bfefcSAlper Nebi Yasak
3453cfbf07cSAjye Huang static const struct snd_soc_dapm_route sc7180_snd_dual_mic_audio_route[] = {
3463cfbf07cSAjye Huang {"Dmic Mux", "Front Mic", "DMIC"},
3473cfbf07cSAjye Huang {"Dmic Mux", "Rear Mic", "DMIC"},
3483cfbf07cSAjye Huang };
3493cfbf07cSAjye Huang
sc7180_snd_platform_probe(struct platform_device * pdev)3509e3ecb5bSAjit Pandey static int sc7180_snd_platform_probe(struct platform_device *pdev)
3519e3ecb5bSAjit Pandey {
3529e3ecb5bSAjit Pandey struct snd_soc_card *card;
3539e3ecb5bSAjit Pandey struct sc7180_snd_data *data;
3549e3ecb5bSAjit Pandey struct device *dev = &pdev->dev;
355e936619bSxuyuqing struct snd_soc_dai_link *link;
3569e3ecb5bSAjit Pandey int ret;
357e936619bSxuyuqing int i;
3587141f25fSDan Carpenter bool no_headphone = false;
3599e3ecb5bSAjit Pandey
3609e3ecb5bSAjit Pandey /* Allocate the private data */
3619e3ecb5bSAjit Pandey data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
3629e3ecb5bSAjit Pandey if (!data)
3639e3ecb5bSAjit Pandey return -ENOMEM;
3649e3ecb5bSAjit Pandey
3659e3ecb5bSAjit Pandey card = &data->card;
3669e3ecb5bSAjit Pandey snd_soc_card_set_drvdata(card, data);
3679e3ecb5bSAjit Pandey
3689e3ecb5bSAjit Pandey card->owner = THIS_MODULE;
3699e3ecb5bSAjit Pandey card->driver_name = DRIVER_NAME;
3709e3ecb5bSAjit Pandey card->dev = dev;
3719e3ecb5bSAjit Pandey card->dapm_widgets = sc7180_snd_widgets;
3729e3ecb5bSAjit Pandey card->num_dapm_widgets = ARRAY_SIZE(sc7180_snd_widgets);
373883bfefcSAlper Nebi Yasak card->controls = sc7180_snd_controls;
374883bfefcSAlper Nebi Yasak card->num_controls = ARRAY_SIZE(sc7180_snd_controls);
3759e3ecb5bSAjit Pandey
3763cfbf07cSAjye Huang if (of_property_read_bool(dev->of_node, "dmic-gpios")) {
3773cfbf07cSAjye Huang card->dapm_widgets = sc7180_snd_dual_mic_widgets,
3783cfbf07cSAjye Huang card->num_dapm_widgets = ARRAY_SIZE(sc7180_snd_dual_mic_widgets),
379883bfefcSAlper Nebi Yasak card->controls = sc7180_snd_dual_mic_controls,
380883bfefcSAlper Nebi Yasak card->num_controls = ARRAY_SIZE(sc7180_snd_dual_mic_controls),
3813cfbf07cSAjye Huang card->dapm_routes = sc7180_snd_dual_mic_audio_route,
3823cfbf07cSAjye Huang card->num_dapm_routes = ARRAY_SIZE(sc7180_snd_dual_mic_audio_route),
3833cfbf07cSAjye Huang data->dmic_sel = devm_gpiod_get(&pdev->dev, "dmic", GPIOD_OUT_LOW);
3843cfbf07cSAjye Huang if (IS_ERR(data->dmic_sel)) {
3853cfbf07cSAjye Huang dev_err(&pdev->dev, "DMIC gpio failed err=%ld\n", PTR_ERR(data->dmic_sel));
3863cfbf07cSAjye Huang return PTR_ERR(data->dmic_sel);
3873cfbf07cSAjye Huang }
3883cfbf07cSAjye Huang }
3893cfbf07cSAjye Huang
390e936619bSxuyuqing if (of_device_is_compatible(dev->of_node, "google,sc7180-coachz")) {
391e936619bSxuyuqing no_headphone = true;
392e936619bSxuyuqing card->dapm_widgets = sc7180_adau7002_snd_widgets;
393e936619bSxuyuqing card->num_dapm_widgets = ARRAY_SIZE(sc7180_adau7002_snd_widgets);
394e936619bSxuyuqing }
395e936619bSxuyuqing
3969e3ecb5bSAjit Pandey ret = qcom_snd_parse_of(card);
3979e3ecb5bSAjit Pandey if (ret)
3989e3ecb5bSAjit Pandey return ret;
3999e3ecb5bSAjit Pandey
400e936619bSxuyuqing for_each_card_prelinks(card, i, link) {
401e936619bSxuyuqing if (no_headphone) {
402e936619bSxuyuqing link->ops = &sc7180_adau7002_ops;
403e936619bSxuyuqing link->init = sc7180_adau7002_init;
404e936619bSxuyuqing } else {
405e936619bSxuyuqing link->ops = &sc7180_ops;
406e936619bSxuyuqing link->init = sc7180_init;
407e936619bSxuyuqing }
408e936619bSxuyuqing }
4099e3ecb5bSAjit Pandey
4109e3ecb5bSAjit Pandey return devm_snd_soc_register_card(dev, card);
4119e3ecb5bSAjit Pandey }
4129e3ecb5bSAjit Pandey
4139e3ecb5bSAjit Pandey static const struct of_device_id sc7180_snd_device_id[] = {
4149e3ecb5bSAjit Pandey {.compatible = "google,sc7180-trogdor"},
415e936619bSxuyuqing {.compatible = "google,sc7180-coachz"},
4169e3ecb5bSAjit Pandey {},
4179e3ecb5bSAjit Pandey };
4189e3ecb5bSAjit Pandey MODULE_DEVICE_TABLE(of, sc7180_snd_device_id);
4199e3ecb5bSAjit Pandey
4209e3ecb5bSAjit Pandey static struct platform_driver sc7180_snd_driver = {
4219e3ecb5bSAjit Pandey .probe = sc7180_snd_platform_probe,
4229e3ecb5bSAjit Pandey .driver = {
4239e3ecb5bSAjit Pandey .name = "msm-snd-sc7180",
4249e3ecb5bSAjit Pandey .of_match_table = sc7180_snd_device_id,
425b2fc3029SCheng-Yi Chiang .pm = &snd_soc_pm_ops,
4269e3ecb5bSAjit Pandey },
4279e3ecb5bSAjit Pandey };
4289e3ecb5bSAjit Pandey module_platform_driver(sc7180_snd_driver);
4299e3ecb5bSAjit Pandey
4309e3ecb5bSAjit Pandey MODULE_DESCRIPTION("sc7180 ASoC Machine Driver");
4319e3ecb5bSAjit Pandey MODULE_LICENSE("GPL v2");
432