xref: /openbmc/linux/sound/soc/qcom/sc7180.c (revision 816ffd28002651a469e86d1118a225862e392ecd)
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