1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * mt8173-rt5650.c  --  MT8173 machine driver with RT5650 codecs
4  *
5  * Copyright (c) 2016 MediaTek Inc.
6  * Author: Koro Chen <koro.chen@mediatek.com>
7  */
8 
9 #include <linux/module.h>
10 #include <linux/gpio.h>
11 #include <linux/of_gpio.h>
12 #include <sound/soc.h>
13 #include <sound/jack.h>
14 #include <sound/hdmi-codec.h>
15 #include "../../codecs/rt5645.h"
16 
17 #define MCLK_FOR_CODECS		12288000
18 
19 enum mt8173_rt5650_mclk {
20 	MT8173_RT5650_MCLK_EXTERNAL = 0,
21 	MT8173_RT5650_MCLK_INTERNAL,
22 };
23 
24 struct mt8173_rt5650_platform_data {
25 	enum mt8173_rt5650_mclk pll_from;
26 	/* 0 = external oscillator; 1 = internal source from mt8173 */
27 };
28 
29 static struct mt8173_rt5650_platform_data mt8173_rt5650_priv = {
30 	.pll_from = MT8173_RT5650_MCLK_EXTERNAL,
31 };
32 
33 static const struct snd_soc_dapm_widget mt8173_rt5650_widgets[] = {
34 	SND_SOC_DAPM_SPK("Speaker", NULL),
35 	SND_SOC_DAPM_MIC("Int Mic", NULL),
36 	SND_SOC_DAPM_HP("Headphone", NULL),
37 	SND_SOC_DAPM_MIC("Headset Mic", NULL),
38 };
39 
40 static const struct snd_soc_dapm_route mt8173_rt5650_routes[] = {
41 	{"Speaker", NULL, "SPOL"},
42 	{"Speaker", NULL, "SPOR"},
43 	{"DMIC L1", NULL, "Int Mic"},
44 	{"DMIC R1", NULL, "Int Mic"},
45 	{"Headphone", NULL, "HPOL"},
46 	{"Headphone", NULL, "HPOR"},
47 	{"IN1P", NULL, "Headset Mic"},
48 	{"IN1N", NULL, "Headset Mic"},
49 };
50 
51 static const struct snd_kcontrol_new mt8173_rt5650_controls[] = {
52 	SOC_DAPM_PIN_SWITCH("Speaker"),
53 	SOC_DAPM_PIN_SWITCH("Int Mic"),
54 	SOC_DAPM_PIN_SWITCH("Headphone"),
55 	SOC_DAPM_PIN_SWITCH("Headset Mic"),
56 };
57 
58 static int mt8173_rt5650_hw_params(struct snd_pcm_substream *substream,
59 				   struct snd_pcm_hw_params *params)
60 {
61 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
62 	unsigned int mclk_clock;
63 	struct snd_soc_dai *codec_dai;
64 	int i, ret;
65 
66 	switch (mt8173_rt5650_priv.pll_from) {
67 	case MT8173_RT5650_MCLK_EXTERNAL:
68 		/* mclk = 12.288M */
69 		mclk_clock = MCLK_FOR_CODECS;
70 		break;
71 	case MT8173_RT5650_MCLK_INTERNAL:
72 		/* mclk = sampling rate*256 */
73 		mclk_clock = params_rate(params) * 256;
74 		break;
75 	default:
76 		/* mclk = 12.288M */
77 		mclk_clock = MCLK_FOR_CODECS;
78 		break;
79 	}
80 
81 	for_each_rtd_codec_dais(rtd, i, codec_dai) {
82 		/* pll from mclk */
83 		ret = snd_soc_dai_set_pll(codec_dai, 0, 0, mclk_clock,
84 					  params_rate(params) * 512);
85 		if (ret)
86 			return ret;
87 
88 		/* sysclk from pll */
89 		ret = snd_soc_dai_set_sysclk(codec_dai, 1,
90 					     params_rate(params) * 512,
91 					     SND_SOC_CLOCK_IN);
92 		if (ret)
93 			return ret;
94 	}
95 	return 0;
96 }
97 
98 static const struct snd_soc_ops mt8173_rt5650_ops = {
99 	.hw_params = mt8173_rt5650_hw_params,
100 };
101 
102 static struct snd_soc_jack mt8173_rt5650_jack, mt8173_rt5650_hdmi_jack;
103 
104 static int mt8173_rt5650_init(struct snd_soc_pcm_runtime *runtime)
105 {
106 	struct snd_soc_card *card = runtime->card;
107 	struct snd_soc_component *component = asoc_rtd_to_codec(runtime, 0)->component;
108 	const char *codec_capture_dai = asoc_rtd_to_codec(runtime, 1)->name;
109 	int ret;
110 
111 	rt5645_sel_asrc_clk_src(component,
112 				RT5645_DA_STEREO_FILTER,
113 				RT5645_CLK_SEL_I2S1_ASRC);
114 
115 	if (!strcmp(codec_capture_dai, "rt5645-aif1")) {
116 		rt5645_sel_asrc_clk_src(component,
117 					RT5645_AD_STEREO_FILTER,
118 					RT5645_CLK_SEL_I2S1_ASRC);
119 	} else if (!strcmp(codec_capture_dai, "rt5645-aif2")) {
120 		rt5645_sel_asrc_clk_src(component,
121 					RT5645_AD_STEREO_FILTER,
122 					RT5645_CLK_SEL_I2S2_ASRC);
123 	} else {
124 		dev_warn(card->dev,
125 			 "Only one dai codec found in DTS, enabled rt5645 AD filter\n");
126 		rt5645_sel_asrc_clk_src(component,
127 					RT5645_AD_STEREO_FILTER,
128 					RT5645_CLK_SEL_I2S1_ASRC);
129 	}
130 
131 	/* enable jack detection */
132 	ret = snd_soc_card_jack_new(card, "Headset Jack",
133 				    SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
134 				    SND_JACK_BTN_0 | SND_JACK_BTN_1 |
135 				    SND_JACK_BTN_2 | SND_JACK_BTN_3,
136 				    &mt8173_rt5650_jack, NULL, 0);
137 	if (ret) {
138 		dev_err(card->dev, "Can't new Headset Jack %d\n", ret);
139 		return ret;
140 	}
141 
142 	return rt5645_set_jack_detect(component,
143 				      &mt8173_rt5650_jack,
144 				      &mt8173_rt5650_jack,
145 				      &mt8173_rt5650_jack);
146 }
147 
148 static int mt8173_rt5650_hdmi_init(struct snd_soc_pcm_runtime *rtd)
149 {
150 	int ret;
151 
152 	ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT,
153 				    &mt8173_rt5650_hdmi_jack, NULL, 0);
154 	if (ret)
155 		return ret;
156 
157 	return hdmi_codec_set_jack_detect(asoc_rtd_to_codec(rtd, 0)->component,
158 					  &mt8173_rt5650_hdmi_jack);
159 }
160 
161 enum {
162 	DAI_LINK_PLAYBACK,
163 	DAI_LINK_CAPTURE,
164 	DAI_LINK_HDMI,
165 	DAI_LINK_CODEC_I2S,
166 	DAI_LINK_HDMI_I2S,
167 };
168 
169 SND_SOC_DAILINK_DEFS(playback,
170 	DAILINK_COMP_ARRAY(COMP_CPU("DL1")),
171 	DAILINK_COMP_ARRAY(COMP_DUMMY()),
172 	DAILINK_COMP_ARRAY(COMP_EMPTY()));
173 
174 SND_SOC_DAILINK_DEFS(capture,
175 	DAILINK_COMP_ARRAY(COMP_CPU("VUL")),
176 	DAILINK_COMP_ARRAY(COMP_DUMMY()),
177 	DAILINK_COMP_ARRAY(COMP_EMPTY()));
178 
179 SND_SOC_DAILINK_DEFS(hdmi_pcm,
180 	DAILINK_COMP_ARRAY(COMP_CPU("HDMI")),
181 	DAILINK_COMP_ARRAY(COMP_DUMMY()),
182 	DAILINK_COMP_ARRAY(COMP_EMPTY()));
183 
184 SND_SOC_DAILINK_DEFS(codec,
185 	DAILINK_COMP_ARRAY(COMP_CPU("I2S")),
186 	DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "rt5645-aif1"), /* Playback */
187 			   COMP_CODEC(NULL, "rt5645-aif1")),/* Capture */
188 	DAILINK_COMP_ARRAY(COMP_EMPTY()));
189 
190 SND_SOC_DAILINK_DEFS(hdmi_be,
191 	DAILINK_COMP_ARRAY(COMP_CPU("HDMIO")),
192 	DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "i2s-hifi")),
193 	DAILINK_COMP_ARRAY(COMP_EMPTY()));
194 
195 /* Digital audio interface glue - connects codec <---> CPU */
196 static struct snd_soc_dai_link mt8173_rt5650_dais[] = {
197 	/* Front End DAI links */
198 	[DAI_LINK_PLAYBACK] = {
199 		.name = "rt5650 Playback",
200 		.stream_name = "rt5650 Playback",
201 		.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
202 		.dynamic = 1,
203 		.dpcm_playback = 1,
204 		SND_SOC_DAILINK_REG(playback),
205 	},
206 	[DAI_LINK_CAPTURE] = {
207 		.name = "rt5650 Capture",
208 		.stream_name = "rt5650 Capture",
209 		.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
210 		.dynamic = 1,
211 		.dpcm_capture = 1,
212 		SND_SOC_DAILINK_REG(capture),
213 	},
214 	[DAI_LINK_HDMI] = {
215 		.name = "HDMI",
216 		.stream_name = "HDMI PCM",
217 		.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
218 		.dynamic = 1,
219 		.dpcm_playback = 1,
220 		SND_SOC_DAILINK_REG(hdmi_pcm),
221 	},
222 	/* Back End DAI links */
223 	[DAI_LINK_CODEC_I2S] = {
224 		.name = "Codec",
225 		.no_pcm = 1,
226 		.init = mt8173_rt5650_init,
227 		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
228 			   SND_SOC_DAIFMT_CBS_CFS,
229 		.ops = &mt8173_rt5650_ops,
230 		.ignore_pmdown_time = 1,
231 		.dpcm_playback = 1,
232 		.dpcm_capture = 1,
233 		SND_SOC_DAILINK_REG(codec),
234 	},
235 	[DAI_LINK_HDMI_I2S] = {
236 		.name = "HDMI BE",
237 		.no_pcm = 1,
238 		.dpcm_playback = 1,
239 		.init = mt8173_rt5650_hdmi_init,
240 		SND_SOC_DAILINK_REG(hdmi_be),
241 	},
242 };
243 
244 static struct snd_soc_card mt8173_rt5650_card = {
245 	.name = "mtk-rt5650",
246 	.owner = THIS_MODULE,
247 	.dai_link = mt8173_rt5650_dais,
248 	.num_links = ARRAY_SIZE(mt8173_rt5650_dais),
249 	.controls = mt8173_rt5650_controls,
250 	.num_controls = ARRAY_SIZE(mt8173_rt5650_controls),
251 	.dapm_widgets = mt8173_rt5650_widgets,
252 	.num_dapm_widgets = ARRAY_SIZE(mt8173_rt5650_widgets),
253 	.dapm_routes = mt8173_rt5650_routes,
254 	.num_dapm_routes = ARRAY_SIZE(mt8173_rt5650_routes),
255 };
256 
257 static int mt8173_rt5650_dev_probe(struct platform_device *pdev)
258 {
259 	struct snd_soc_card *card = &mt8173_rt5650_card;
260 	struct device_node *platform_node;
261 	struct device_node *np;
262 	const char *codec_capture_dai;
263 	struct snd_soc_dai_link *dai_link;
264 	int i, ret;
265 
266 	platform_node = of_parse_phandle(pdev->dev.of_node,
267 					 "mediatek,platform", 0);
268 	if (!platform_node) {
269 		dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");
270 		return -EINVAL;
271 	}
272 
273 	for_each_card_prelinks(card, i, dai_link) {
274 		if (dai_link->platforms->name)
275 			continue;
276 		dai_link->platforms->of_node = platform_node;
277 	}
278 
279 	mt8173_rt5650_dais[DAI_LINK_CODEC_I2S].codecs[0].of_node =
280 		of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 0);
281 	if (!mt8173_rt5650_dais[DAI_LINK_CODEC_I2S].codecs[0].of_node) {
282 		dev_err(&pdev->dev,
283 			"Property 'audio-codec' missing or invalid\n");
284 		return -EINVAL;
285 	}
286 	mt8173_rt5650_dais[DAI_LINK_CODEC_I2S].codecs[1].of_node =
287 		mt8173_rt5650_dais[DAI_LINK_CODEC_I2S].codecs[0].of_node;
288 
289 	np = of_get_child_by_name(pdev->dev.of_node, "codec-capture");
290 	if (np) {
291 		ret = snd_soc_of_get_dai_name(np, &codec_capture_dai);
292 		of_node_put(np);
293 		if (ret < 0) {
294 			dev_err(&pdev->dev,
295 				"%s codec_capture_dai name fail %d\n",
296 				__func__, ret);
297 			return ret;
298 		}
299 		mt8173_rt5650_dais[DAI_LINK_CODEC_I2S].codecs[1].dai_name =
300 			codec_capture_dai;
301 	}
302 
303 	if (device_property_present(&pdev->dev, "mediatek,mclk")) {
304 		ret = device_property_read_u32(&pdev->dev,
305 					       "mediatek,mclk",
306 					       &mt8173_rt5650_priv.pll_from);
307 		if (ret) {
308 			dev_err(&pdev->dev,
309 				"%s snd_soc_register_card fail %d\n",
310 				__func__, ret);
311 		}
312 	}
313 
314 	mt8173_rt5650_dais[DAI_LINK_HDMI_I2S].codecs->of_node =
315 		of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 1);
316 	if (!mt8173_rt5650_dais[DAI_LINK_HDMI_I2S].codecs->of_node) {
317 		dev_err(&pdev->dev,
318 			"Property 'audio-codec' missing or invalid\n");
319 		return -EINVAL;
320 	}
321 	card->dev = &pdev->dev;
322 
323 	ret = devm_snd_soc_register_card(&pdev->dev, card);
324 	if (ret)
325 		dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
326 			__func__, ret);
327 	return ret;
328 }
329 
330 static const struct of_device_id mt8173_rt5650_dt_match[] = {
331 	{ .compatible = "mediatek,mt8173-rt5650", },
332 	{ }
333 };
334 MODULE_DEVICE_TABLE(of, mt8173_rt5650_dt_match);
335 
336 static struct platform_driver mt8173_rt5650_driver = {
337 	.driver = {
338 		   .name = "mtk-rt5650",
339 		   .of_match_table = mt8173_rt5650_dt_match,
340 #ifdef CONFIG_PM
341 		   .pm = &snd_soc_pm_ops,
342 #endif
343 	},
344 	.probe = mt8173_rt5650_dev_probe,
345 };
346 
347 module_platform_driver(mt8173_rt5650_driver);
348 
349 /* Module information */
350 MODULE_DESCRIPTION("MT8173 RT5650 SoC machine driver");
351 MODULE_AUTHOR("Koro Chen <koro.chen@mediatek.com>");
352 MODULE_LICENSE("GPL v2");
353 MODULE_ALIAS("platform:mtk-rt5650");
354 
355