xref: /linux/sound/soc/qcom/sc7180.c (revision 833a94aac572d7f0fe3f51329e0eb9f2884cf665)
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 
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 
459e3ecb5bSAjit Pandey static int sc7180_headset_init(struct snd_soc_pcm_runtime *rtd)
469e3ecb5bSAjit Pandey {
479e3ecb5bSAjit Pandey 	struct snd_soc_card *card = rtd->card;
489e3ecb5bSAjit Pandey 	struct sc7180_snd_data *pdata = snd_soc_card_get_drvdata(card);
499e3ecb5bSAjit Pandey 	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
509e3ecb5bSAjit Pandey 	struct snd_soc_component *component = codec_dai->component;
519e3ecb5bSAjit Pandey 	struct snd_jack *jack;
529e3ecb5bSAjit Pandey 	int rval;
539e3ecb5bSAjit Pandey 
549e3ecb5bSAjit Pandey 	rval = snd_soc_card_jack_new(
559e3ecb5bSAjit Pandey 			card, "Headset Jack",
569e3ecb5bSAjit Pandey 			SND_JACK_HEADSET |
579e3ecb5bSAjit Pandey 			SND_JACK_HEADPHONE |
589e3ecb5bSAjit Pandey 			SND_JACK_BTN_0 | SND_JACK_BTN_1 |
599e3ecb5bSAjit Pandey 			SND_JACK_BTN_2 | SND_JACK_BTN_3,
609e3ecb5bSAjit Pandey 			&pdata->hs_jack, NULL, 0);
619e3ecb5bSAjit Pandey 
629e3ecb5bSAjit Pandey 	if (rval < 0) {
639e3ecb5bSAjit Pandey 		dev_err(card->dev, "Unable to add Headset Jack\n");
649e3ecb5bSAjit Pandey 		return rval;
659e3ecb5bSAjit Pandey 	}
669e3ecb5bSAjit Pandey 
679e3ecb5bSAjit Pandey 	jack = pdata->hs_jack.jack;
689e3ecb5bSAjit Pandey 
699e3ecb5bSAjit Pandey 	snd_jack_set_key(jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
709e3ecb5bSAjit Pandey 	snd_jack_set_key(jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
719e3ecb5bSAjit Pandey 	snd_jack_set_key(jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
729e3ecb5bSAjit Pandey 	snd_jack_set_key(jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
739e3ecb5bSAjit Pandey 
749e3ecb5bSAjit Pandey 	jack->private_data = component;
759e3ecb5bSAjit Pandey 	jack->private_free = sc7180_jack_free;
769e3ecb5bSAjit Pandey 
779e3ecb5bSAjit Pandey 	return snd_soc_component_set_jack(component, &pdata->hs_jack, NULL);
789e3ecb5bSAjit Pandey }
799e3ecb5bSAjit Pandey 
809e3ecb5bSAjit Pandey static int sc7180_hdmi_init(struct snd_soc_pcm_runtime *rtd)
819e3ecb5bSAjit Pandey {
829e3ecb5bSAjit Pandey 	struct snd_soc_card *card = rtd->card;
839e3ecb5bSAjit Pandey 	struct sc7180_snd_data *pdata = snd_soc_card_get_drvdata(card);
849e3ecb5bSAjit Pandey 	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
859e3ecb5bSAjit Pandey 	struct snd_soc_component *component = codec_dai->component;
869e3ecb5bSAjit Pandey 	struct snd_jack *jack;
879e3ecb5bSAjit Pandey 	int rval;
889e3ecb5bSAjit Pandey 
899e3ecb5bSAjit Pandey 	rval = snd_soc_card_jack_new(
909e3ecb5bSAjit Pandey 			card, "HDMI Jack",
919e3ecb5bSAjit Pandey 			SND_JACK_LINEOUT,
929e3ecb5bSAjit Pandey 			&pdata->hdmi_jack, NULL, 0);
939e3ecb5bSAjit Pandey 
949e3ecb5bSAjit Pandey 	if (rval < 0) {
959e3ecb5bSAjit Pandey 		dev_err(card->dev, "Unable to add HDMI Jack\n");
969e3ecb5bSAjit Pandey 		return rval;
979e3ecb5bSAjit Pandey 	}
989e3ecb5bSAjit Pandey 
999e3ecb5bSAjit Pandey 	jack = pdata->hdmi_jack.jack;
1009e3ecb5bSAjit Pandey 	jack->private_data = component;
1019e3ecb5bSAjit Pandey 	jack->private_free = sc7180_jack_free;
1029e3ecb5bSAjit Pandey 
1039e3ecb5bSAjit Pandey 	return snd_soc_component_set_jack(component, &pdata->hdmi_jack, NULL);
1049e3ecb5bSAjit Pandey }
1059e3ecb5bSAjit Pandey 
1069e3ecb5bSAjit Pandey static int sc7180_init(struct snd_soc_pcm_runtime *rtd)
1079e3ecb5bSAjit Pandey {
1089e3ecb5bSAjit Pandey 	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
1099e3ecb5bSAjit Pandey 
1109e3ecb5bSAjit Pandey 	switch (cpu_dai->id) {
1119e3ecb5bSAjit Pandey 	case MI2S_PRIMARY:
1129e3ecb5bSAjit Pandey 		return sc7180_headset_init(rtd);
1139e3ecb5bSAjit Pandey 	case MI2S_SECONDARY:
1149e3ecb5bSAjit Pandey 		return 0;
1159e3ecb5bSAjit Pandey 	case LPASS_DP_RX:
1169e3ecb5bSAjit Pandey 		return sc7180_hdmi_init(rtd);
1179e3ecb5bSAjit Pandey 	default:
1189e3ecb5bSAjit Pandey 		dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
1199e3ecb5bSAjit Pandey 			cpu_dai->id);
1209e3ecb5bSAjit Pandey 		return -EINVAL;
1219e3ecb5bSAjit Pandey 	}
1229e3ecb5bSAjit Pandey 	return 0;
1239e3ecb5bSAjit Pandey }
1249e3ecb5bSAjit Pandey 
1259e3ecb5bSAjit Pandey static int sc7180_snd_startup(struct snd_pcm_substream *substream)
1269e3ecb5bSAjit Pandey {
1279e3ecb5bSAjit Pandey 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
1289e3ecb5bSAjit Pandey 	struct snd_soc_card *card = rtd->card;
1299e3ecb5bSAjit Pandey 	struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card);
1309e3ecb5bSAjit Pandey 	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
1319e3ecb5bSAjit Pandey 	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
132425c5fceSlvzhaoxiong 	int pll_id, pll_source, pll_in, pll_out, clk_id, ret;
133425c5fceSlvzhaoxiong 
134*833a94aaSJudy Hsiao 	if (!strcmp(codec_dai->name, "rt5682-aif1")) {
135425c5fceSlvzhaoxiong 		pll_source = RT5682_PLL1_S_MCLK;
136425c5fceSlvzhaoxiong 		pll_id = 0;
137425c5fceSlvzhaoxiong 		clk_id = RT5682_SCLK_S_PLL1;
138425c5fceSlvzhaoxiong 		pll_out = RT5682_PLL1_FREQ;
139425c5fceSlvzhaoxiong 		pll_in = DEFAULT_MCLK_RATE;
140*833a94aaSJudy Hsiao 	} else if (!strcmp(codec_dai->name, "rt5682s-aif1")) {
141425c5fceSlvzhaoxiong 		pll_source = RT5682S_PLL_S_MCLK;
142425c5fceSlvzhaoxiong 		pll_id = RT5682S_PLL2;
143425c5fceSlvzhaoxiong 		clk_id = RT5682S_SCLK_S_PLL2;
144425c5fceSlvzhaoxiong 		pll_out = RT5682_PLL1_FREQ;
145425c5fceSlvzhaoxiong 		pll_in = DEFAULT_MCLK_RATE;
146425c5fceSlvzhaoxiong 	}
1479e3ecb5bSAjit Pandey 
1489e3ecb5bSAjit Pandey 	switch (cpu_dai->id) {
1499e3ecb5bSAjit Pandey 	case MI2S_PRIMARY:
1509e3ecb5bSAjit Pandey 		if (++data->pri_mi2s_clk_count == 1) {
1519e3ecb5bSAjit Pandey 			snd_soc_dai_set_sysclk(cpu_dai,
1529e3ecb5bSAjit Pandey 					       LPASS_MCLK0,
1539e3ecb5bSAjit Pandey 					       DEFAULT_MCLK_RATE,
1549e3ecb5bSAjit Pandey 					       SNDRV_PCM_STREAM_PLAYBACK);
1559e3ecb5bSAjit Pandey 		}
1569e3ecb5bSAjit Pandey 
1579e3ecb5bSAjit Pandey 		snd_soc_dai_set_fmt(codec_dai,
1589e3ecb5bSAjit Pandey 				    SND_SOC_DAIFMT_CBS_CFS |
1599e3ecb5bSAjit Pandey 				    SND_SOC_DAIFMT_NB_NF |
1609e3ecb5bSAjit Pandey 				    SND_SOC_DAIFMT_I2S);
1619e3ecb5bSAjit Pandey 
1629e3ecb5bSAjit Pandey 		/* Configure PLL1 for codec */
163425c5fceSlvzhaoxiong 		ret = snd_soc_dai_set_pll(codec_dai, pll_id, pll_source,
164425c5fceSlvzhaoxiong 					  pll_in, pll_out);
1659e3ecb5bSAjit Pandey 		if (ret) {
1669e3ecb5bSAjit Pandey 			dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
1679e3ecb5bSAjit Pandey 			return ret;
1689e3ecb5bSAjit Pandey 		}
1699e3ecb5bSAjit Pandey 
1709e3ecb5bSAjit Pandey 		/* Configure sysclk for codec */
171425c5fceSlvzhaoxiong 		ret = snd_soc_dai_set_sysclk(codec_dai, clk_id, pll_out,
1729e3ecb5bSAjit Pandey 					     SND_SOC_CLOCK_IN);
1739e3ecb5bSAjit Pandey 		if (ret)
1749e3ecb5bSAjit Pandey 			dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n",
1759e3ecb5bSAjit Pandey 				ret);
1769e3ecb5bSAjit Pandey 
1779e3ecb5bSAjit Pandey 		break;
1789e3ecb5bSAjit Pandey 	case MI2S_SECONDARY:
1799e3ecb5bSAjit Pandey 		break;
1809e3ecb5bSAjit Pandey 	case LPASS_DP_RX:
1819e3ecb5bSAjit Pandey 		break;
1829e3ecb5bSAjit Pandey 	default:
1839e3ecb5bSAjit Pandey 		dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
1849e3ecb5bSAjit Pandey 			cpu_dai->id);
1859e3ecb5bSAjit Pandey 		return -EINVAL;
1869e3ecb5bSAjit Pandey 	}
1879e3ecb5bSAjit Pandey 	return 0;
1889e3ecb5bSAjit Pandey }
1899e3ecb5bSAjit Pandey 
1903cfbf07cSAjye Huang static int dmic_get(struct snd_kcontrol *kcontrol,
1913cfbf07cSAjye Huang 		    struct snd_ctl_elem_value *ucontrol)
1923cfbf07cSAjye Huang {
1933cfbf07cSAjye Huang 	struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
1943cfbf07cSAjye Huang 	struct sc7180_snd_data *data = snd_soc_card_get_drvdata(dapm->card);
1953cfbf07cSAjye Huang 
1963cfbf07cSAjye Huang 	ucontrol->value.integer.value[0] = data->dmic_switch;
1973cfbf07cSAjye Huang 	return 0;
1983cfbf07cSAjye Huang }
1993cfbf07cSAjye Huang 
2003cfbf07cSAjye Huang static int dmic_set(struct snd_kcontrol *kcontrol,
2013cfbf07cSAjye Huang 		    struct snd_ctl_elem_value *ucontrol)
2023cfbf07cSAjye Huang {
2033cfbf07cSAjye Huang 	struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
2043cfbf07cSAjye Huang 	struct sc7180_snd_data *data = snd_soc_card_get_drvdata(dapm->card);
2053cfbf07cSAjye Huang 
2063cfbf07cSAjye Huang 	data->dmic_switch = ucontrol->value.integer.value[0];
2073cfbf07cSAjye Huang 	gpiod_set_value(data->dmic_sel, data->dmic_switch);
2083cfbf07cSAjye Huang 	return 0;
2093cfbf07cSAjye Huang }
2103cfbf07cSAjye Huang 
2119e3ecb5bSAjit Pandey static void sc7180_snd_shutdown(struct snd_pcm_substream *substream)
2129e3ecb5bSAjit Pandey {
2139e3ecb5bSAjit Pandey 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
2149e3ecb5bSAjit Pandey 	struct snd_soc_card *card = rtd->card;
2159e3ecb5bSAjit Pandey 	struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card);
2169e3ecb5bSAjit Pandey 	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
2179e3ecb5bSAjit Pandey 
2189e3ecb5bSAjit Pandey 	switch (cpu_dai->id) {
2199e3ecb5bSAjit Pandey 	case MI2S_PRIMARY:
2209e3ecb5bSAjit Pandey 		if (--data->pri_mi2s_clk_count == 0) {
2219e3ecb5bSAjit Pandey 			snd_soc_dai_set_sysclk(cpu_dai,
2229e3ecb5bSAjit Pandey 					       LPASS_MCLK0,
2239e3ecb5bSAjit Pandey 					       0,
2249e3ecb5bSAjit Pandey 					       SNDRV_PCM_STREAM_PLAYBACK);
2259e3ecb5bSAjit Pandey 		}
2269e3ecb5bSAjit Pandey 		break;
2279e3ecb5bSAjit Pandey 	case MI2S_SECONDARY:
2289e3ecb5bSAjit Pandey 		break;
2299e3ecb5bSAjit Pandey 	case LPASS_DP_RX:
2309e3ecb5bSAjit Pandey 		break;
2319e3ecb5bSAjit Pandey 	default:
2329e3ecb5bSAjit Pandey 		dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
2339e3ecb5bSAjit Pandey 			cpu_dai->id);
2349e3ecb5bSAjit Pandey 		break;
2359e3ecb5bSAjit Pandey 	}
2369e3ecb5bSAjit Pandey }
2379e3ecb5bSAjit Pandey 
238e936619bSxuyuqing static int sc7180_adau7002_init(struct snd_soc_pcm_runtime *rtd)
239e936619bSxuyuqing {
240e936619bSxuyuqing 	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
241e936619bSxuyuqing 
242e936619bSxuyuqing 	switch (cpu_dai->id) {
243e936619bSxuyuqing 	case MI2S_PRIMARY:
244e936619bSxuyuqing 		return 0;
245e936619bSxuyuqing 	case MI2S_SECONDARY:
246e936619bSxuyuqing 		return 0;
247e936619bSxuyuqing 	case LPASS_DP_RX:
248e936619bSxuyuqing 		return sc7180_hdmi_init(rtd);
249e936619bSxuyuqing 	default:
250e936619bSxuyuqing 		dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
251e936619bSxuyuqing 			cpu_dai->id);
252e936619bSxuyuqing 		return -EINVAL;
253e936619bSxuyuqing 	}
254e936619bSxuyuqing 	return 0;
255e936619bSxuyuqing }
256e936619bSxuyuqing 
257e936619bSxuyuqing static int sc7180_adau7002_snd_startup(struct snd_pcm_substream *substream)
258e936619bSxuyuqing {
259e936619bSxuyuqing 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
260e936619bSxuyuqing 	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
261e936619bSxuyuqing 	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
2627f2c63d6Sxuyuqing 	struct snd_pcm_runtime *runtime = substream->runtime;
263e936619bSxuyuqing 
264e936619bSxuyuqing 	switch (cpu_dai->id) {
265e936619bSxuyuqing 	case MI2S_PRIMARY:
266e936619bSxuyuqing 		snd_soc_dai_set_fmt(codec_dai,
267e936619bSxuyuqing 				    SND_SOC_DAIFMT_CBS_CFS |
268e936619bSxuyuqing 				    SND_SOC_DAIFMT_NB_NF |
269e936619bSxuyuqing 				    SND_SOC_DAIFMT_I2S);
2707f2c63d6Sxuyuqing 		runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE;
2717f2c63d6Sxuyuqing 		snd_pcm_hw_constraint_msbits(runtime, 0, 32, 32);
272e936619bSxuyuqing 
273e936619bSxuyuqing 		break;
274e936619bSxuyuqing 	case MI2S_SECONDARY:
275e936619bSxuyuqing 		break;
276e936619bSxuyuqing 	case LPASS_DP_RX:
277e936619bSxuyuqing 		break;
278e936619bSxuyuqing 	default:
279e936619bSxuyuqing 		dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
280e936619bSxuyuqing 			cpu_dai->id);
281e936619bSxuyuqing 		return -EINVAL;
282e936619bSxuyuqing 	}
283e936619bSxuyuqing 	return 0;
284e936619bSxuyuqing }
285e936619bSxuyuqing 
2869e3ecb5bSAjit Pandey static const struct snd_soc_ops sc7180_ops = {
2879e3ecb5bSAjit Pandey 	.startup = sc7180_snd_startup,
2889e3ecb5bSAjit Pandey 	.shutdown = sc7180_snd_shutdown,
2899e3ecb5bSAjit Pandey };
2909e3ecb5bSAjit Pandey 
291e936619bSxuyuqing static const struct snd_soc_ops sc7180_adau7002_ops = {
292e936619bSxuyuqing 	.startup = sc7180_adau7002_snd_startup,
293e936619bSxuyuqing };
294e936619bSxuyuqing 
2959e3ecb5bSAjit Pandey static const struct snd_soc_dapm_widget sc7180_snd_widgets[] = {
2969e3ecb5bSAjit Pandey 	SND_SOC_DAPM_HP("Headphone Jack", NULL),
2979e3ecb5bSAjit Pandey 	SND_SOC_DAPM_MIC("Headset Mic", NULL),
2989e3ecb5bSAjit Pandey };
2999e3ecb5bSAjit Pandey 
300e936619bSxuyuqing static const struct snd_soc_dapm_widget sc7180_adau7002_snd_widgets[] = {
301e936619bSxuyuqing 	SND_SOC_DAPM_MIC("DMIC", NULL),
302e936619bSxuyuqing };
303e936619bSxuyuqing 
3043cfbf07cSAjye Huang static const char * const dmic_mux_text[] = {
3053cfbf07cSAjye Huang 	"Front Mic",
3063cfbf07cSAjye Huang 	"Rear Mic",
3073cfbf07cSAjye Huang };
3083cfbf07cSAjye Huang 
3093cfbf07cSAjye Huang static SOC_ENUM_SINGLE_DECL(sc7180_dmic_enum,
3103cfbf07cSAjye Huang 			    SND_SOC_NOPM, 0, dmic_mux_text);
3113cfbf07cSAjye Huang 
3123cfbf07cSAjye Huang static const struct snd_kcontrol_new sc7180_dmic_mux_control =
3133cfbf07cSAjye Huang 	SOC_DAPM_ENUM_EXT("DMIC Select Mux", sc7180_dmic_enum,
3143cfbf07cSAjye Huang 			  dmic_get, dmic_set);
3153cfbf07cSAjye Huang 
3163cfbf07cSAjye Huang static const struct snd_soc_dapm_widget sc7180_snd_dual_mic_widgets[] = {
3173cfbf07cSAjye Huang 	SND_SOC_DAPM_HP("Headphone Jack", NULL),
3183cfbf07cSAjye Huang 	SND_SOC_DAPM_MIC("Headset Mic", NULL),
3193cfbf07cSAjye Huang 	SND_SOC_DAPM_MIC("DMIC", NULL),
3203cfbf07cSAjye Huang 	SND_SOC_DAPM_MUX("Dmic Mux", SND_SOC_NOPM, 0, 0, &sc7180_dmic_mux_control),
3213cfbf07cSAjye Huang };
3223cfbf07cSAjye Huang 
3233cfbf07cSAjye Huang static const struct snd_soc_dapm_route sc7180_snd_dual_mic_audio_route[] = {
3243cfbf07cSAjye Huang 	{"Dmic Mux", "Front Mic", "DMIC"},
3253cfbf07cSAjye Huang 	{"Dmic Mux", "Rear Mic", "DMIC"},
3263cfbf07cSAjye Huang };
3273cfbf07cSAjye Huang 
3289e3ecb5bSAjit Pandey static int sc7180_snd_platform_probe(struct platform_device *pdev)
3299e3ecb5bSAjit Pandey {
3309e3ecb5bSAjit Pandey 	struct snd_soc_card *card;
3319e3ecb5bSAjit Pandey 	struct sc7180_snd_data *data;
3329e3ecb5bSAjit Pandey 	struct device *dev = &pdev->dev;
333e936619bSxuyuqing 	struct snd_soc_dai_link *link;
3349e3ecb5bSAjit Pandey 	int ret;
335e936619bSxuyuqing 	int i;
3367141f25fSDan Carpenter 	bool no_headphone = false;
3379e3ecb5bSAjit Pandey 
3389e3ecb5bSAjit Pandey 	/* Allocate the private data */
3399e3ecb5bSAjit Pandey 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
3409e3ecb5bSAjit Pandey 	if (!data)
3419e3ecb5bSAjit Pandey 		return -ENOMEM;
3429e3ecb5bSAjit Pandey 
3439e3ecb5bSAjit Pandey 	card = &data->card;
3449e3ecb5bSAjit Pandey 	snd_soc_card_set_drvdata(card, data);
3459e3ecb5bSAjit Pandey 
3469e3ecb5bSAjit Pandey 	card->owner = THIS_MODULE;
3479e3ecb5bSAjit Pandey 	card->driver_name = DRIVER_NAME;
3489e3ecb5bSAjit Pandey 	card->dev = dev;
3499e3ecb5bSAjit Pandey 	card->dapm_widgets = sc7180_snd_widgets;
3509e3ecb5bSAjit Pandey 	card->num_dapm_widgets = ARRAY_SIZE(sc7180_snd_widgets);
3519e3ecb5bSAjit Pandey 
3523cfbf07cSAjye Huang 	if (of_property_read_bool(dev->of_node, "dmic-gpios")) {
3533cfbf07cSAjye Huang 		card->dapm_widgets = sc7180_snd_dual_mic_widgets,
3543cfbf07cSAjye Huang 		card->num_dapm_widgets = ARRAY_SIZE(sc7180_snd_dual_mic_widgets),
3553cfbf07cSAjye Huang 		card->dapm_routes = sc7180_snd_dual_mic_audio_route,
3563cfbf07cSAjye Huang 		card->num_dapm_routes = ARRAY_SIZE(sc7180_snd_dual_mic_audio_route),
3573cfbf07cSAjye Huang 		data->dmic_sel = devm_gpiod_get(&pdev->dev, "dmic", GPIOD_OUT_LOW);
3583cfbf07cSAjye Huang 		if (IS_ERR(data->dmic_sel)) {
3593cfbf07cSAjye Huang 			dev_err(&pdev->dev, "DMIC gpio failed err=%ld\n", PTR_ERR(data->dmic_sel));
3603cfbf07cSAjye Huang 			return PTR_ERR(data->dmic_sel);
3613cfbf07cSAjye Huang 		}
3623cfbf07cSAjye Huang 	}
3633cfbf07cSAjye Huang 
364e936619bSxuyuqing 	if (of_device_is_compatible(dev->of_node, "google,sc7180-coachz")) {
365e936619bSxuyuqing 		no_headphone = true;
366e936619bSxuyuqing 		card->dapm_widgets = sc7180_adau7002_snd_widgets;
367e936619bSxuyuqing 		card->num_dapm_widgets = ARRAY_SIZE(sc7180_adau7002_snd_widgets);
368e936619bSxuyuqing 	}
369e936619bSxuyuqing 
3709e3ecb5bSAjit Pandey 	ret = qcom_snd_parse_of(card);
3719e3ecb5bSAjit Pandey 	if (ret)
3729e3ecb5bSAjit Pandey 		return ret;
3739e3ecb5bSAjit Pandey 
374e936619bSxuyuqing 	for_each_card_prelinks(card, i, link) {
375e936619bSxuyuqing 		if (no_headphone) {
376e936619bSxuyuqing 			link->ops = &sc7180_adau7002_ops;
377e936619bSxuyuqing 			link->init = sc7180_adau7002_init;
378e936619bSxuyuqing 		} else {
379e936619bSxuyuqing 			link->ops = &sc7180_ops;
380e936619bSxuyuqing 			link->init = sc7180_init;
381e936619bSxuyuqing 		}
382e936619bSxuyuqing 	}
3839e3ecb5bSAjit Pandey 
3849e3ecb5bSAjit Pandey 	return devm_snd_soc_register_card(dev, card);
3859e3ecb5bSAjit Pandey }
3869e3ecb5bSAjit Pandey 
3879e3ecb5bSAjit Pandey static const struct of_device_id sc7180_snd_device_id[]  = {
3889e3ecb5bSAjit Pandey 	{.compatible = "google,sc7180-trogdor"},
389e936619bSxuyuqing 	{.compatible = "google,sc7180-coachz"},
3909e3ecb5bSAjit Pandey 	{},
3919e3ecb5bSAjit Pandey };
3929e3ecb5bSAjit Pandey MODULE_DEVICE_TABLE(of, sc7180_snd_device_id);
3939e3ecb5bSAjit Pandey 
3949e3ecb5bSAjit Pandey static struct platform_driver sc7180_snd_driver = {
3959e3ecb5bSAjit Pandey 	.probe = sc7180_snd_platform_probe,
3969e3ecb5bSAjit Pandey 	.driver = {
3979e3ecb5bSAjit Pandey 		.name = "msm-snd-sc7180",
3989e3ecb5bSAjit Pandey 		.of_match_table = sc7180_snd_device_id,
399b2fc3029SCheng-Yi Chiang 		.pm = &snd_soc_pm_ops,
4009e3ecb5bSAjit Pandey 	},
4019e3ecb5bSAjit Pandey };
4029e3ecb5bSAjit Pandey module_platform_driver(sc7180_snd_driver);
4039e3ecb5bSAjit Pandey 
4049e3ecb5bSAjit Pandey MODULE_DESCRIPTION("sc7180 ASoC Machine Driver");
4059e3ecb5bSAjit Pandey MODULE_LICENSE("GPL v2");
406