1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19#include <linux/module.h>
20#include <sound/soc.h>
21#include <linux/delay.h>
22#include <linux/gpio.h>
23#include <linux/pinctrl/consumer.h>
24#include <linux/of_gpio.h>
25
26#include "mt2701-afe-common.h"
27
28struct mt2701_cs42448_private {
29 int i2s1_in_mux;
30 int i2s1_in_mux_gpio_sel_1;
31 int i2s1_in_mux_gpio_sel_2;
32};
33
34static const char * const i2sin_mux_switch_text[] = {
35 "ADC_SDOUT2",
36 "ADC_SDOUT3",
37 "I2S_IN_1",
38 "I2S_IN_2",
39};
40
41static const struct soc_enum i2sin_mux_enum =
42 SOC_ENUM_SINGLE_EXT(4, i2sin_mux_switch_text);
43
44static int mt2701_cs42448_i2sin1_mux_get(struct snd_kcontrol *kcontrol,
45 struct snd_ctl_elem_value *ucontrol)
46{
47 struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
48 struct mt2701_cs42448_private *priv = snd_soc_card_get_drvdata(card);
49
50 ucontrol->value.integer.value[0] = priv->i2s1_in_mux;
51 return 0;
52}
53
54static int mt2701_cs42448_i2sin1_mux_set(struct snd_kcontrol *kcontrol,
55 struct snd_ctl_elem_value *ucontrol)
56{
57 struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
58 struct mt2701_cs42448_private *priv = snd_soc_card_get_drvdata(card);
59
60 if (ucontrol->value.integer.value[0] == priv->i2s1_in_mux)
61 return 0;
62
63 switch (ucontrol->value.integer.value[0]) {
64 case 0:
65 gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 0);
66 gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 0);
67 break;
68 case 1:
69 gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 1);
70 gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 0);
71 break;
72 case 2:
73 gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 0);
74 gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 1);
75 break;
76 case 3:
77 gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 1);
78 gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 1);
79 break;
80 default:
81 dev_warn(card->dev, "%s invalid setting\n", __func__);
82 }
83
84 priv->i2s1_in_mux = ucontrol->value.integer.value[0];
85 return 0;
86}
87
88static const struct snd_soc_dapm_widget
89 mt2701_cs42448_asoc_card_dapm_widgets[] = {
90 SND_SOC_DAPM_LINE("Line Out Jack", NULL),
91 SND_SOC_DAPM_MIC("AMIC", NULL),
92 SND_SOC_DAPM_LINE("Tuner In", NULL),
93 SND_SOC_DAPM_LINE("Satellite Tuner In", NULL),
94 SND_SOC_DAPM_LINE("AUX In", NULL),
95};
96
97static const struct snd_kcontrol_new mt2701_cs42448_controls[] = {
98 SOC_DAPM_PIN_SWITCH("Line Out Jack"),
99 SOC_DAPM_PIN_SWITCH("AMIC"),
100 SOC_DAPM_PIN_SWITCH("Tuner In"),
101 SOC_DAPM_PIN_SWITCH("Satellite Tuner In"),
102 SOC_DAPM_PIN_SWITCH("AUX In"),
103 SOC_ENUM_EXT("I2SIN1_MUX_Switch", i2sin_mux_enum,
104 mt2701_cs42448_i2sin1_mux_get,
105 mt2701_cs42448_i2sin1_mux_set),
106};
107
108static const unsigned int mt2701_cs42448_sampling_rates[] = {48000};
109
110static const struct snd_pcm_hw_constraint_list mt2701_cs42448_constraints_rates = {
111 .count = ARRAY_SIZE(mt2701_cs42448_sampling_rates),
112 .list = mt2701_cs42448_sampling_rates,
113 .mask = 0,
114};
115
116static int mt2701_cs42448_fe_ops_startup(struct snd_pcm_substream *substream)
117{
118 int err;
119
120 err = snd_pcm_hw_constraint_list(substream->runtime, 0,
121 SNDRV_PCM_HW_PARAM_RATE,
122 &mt2701_cs42448_constraints_rates);
123 if (err < 0) {
124 dev_err(substream->pcm->card->dev,
125 "%s snd_pcm_hw_constraint_list failed: 0x%x\n",
126 __func__, err);
127 return err;
128 }
129 return 0;
130}
131
132static const struct snd_soc_ops mt2701_cs42448_48k_fe_ops = {
133 .startup = mt2701_cs42448_fe_ops_startup,
134};
135
136static int mt2701_cs42448_be_ops_hw_params(struct snd_pcm_substream *substream,
137 struct snd_pcm_hw_params *params)
138{
139 struct snd_soc_pcm_runtime *rtd = substream->private_data;
140 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
141 struct snd_soc_dai *codec_dai = rtd->codec_dai;
142 unsigned int mclk_rate;
143 unsigned int rate = params_rate(params);
144 unsigned int div_mclk_over_bck = rate > 192000 ? 2 : 4;
145 unsigned int div_bck_over_lrck = 64;
146
147 mclk_rate = rate * div_bck_over_lrck * div_mclk_over_bck;
148
149
150 snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_rate, SND_SOC_CLOCK_OUT);
151
152
153 snd_soc_dai_set_sysclk(codec_dai, 0, mclk_rate, SND_SOC_CLOCK_IN);
154
155 return 0;
156}
157
158static struct snd_soc_ops mt2701_cs42448_be_ops = {
159 .hw_params = mt2701_cs42448_be_ops_hw_params
160};
161
162enum {
163 DAI_LINK_FE_MULTI_CH_OUT,
164 DAI_LINK_FE_PCM0_IN,
165 DAI_LINK_FE_PCM1_IN,
166 DAI_LINK_FE_BT_OUT,
167 DAI_LINK_FE_BT_IN,
168 DAI_LINK_BE_I2S0,
169 DAI_LINK_BE_I2S1,
170 DAI_LINK_BE_I2S2,
171 DAI_LINK_BE_I2S3,
172 DAI_LINK_BE_MRG_BT,
173};
174
175static struct snd_soc_dai_link mt2701_cs42448_dai_links[] = {
176
177 [DAI_LINK_FE_MULTI_CH_OUT] = {
178 .name = "mt2701-cs42448-multi-ch-out",
179 .stream_name = "mt2701-cs42448-multi-ch-out",
180 .cpu_dai_name = "PCM_multi",
181 .codec_name = "snd-soc-dummy",
182 .codec_dai_name = "snd-soc-dummy-dai",
183 .trigger = {SND_SOC_DPCM_TRIGGER_POST,
184 SND_SOC_DPCM_TRIGGER_POST},
185 .ops = &mt2701_cs42448_48k_fe_ops,
186 .dynamic = 1,
187 .dpcm_playback = 1,
188 },
189 [DAI_LINK_FE_PCM0_IN] = {
190 .name = "mt2701-cs42448-pcm0",
191 .stream_name = "mt2701-cs42448-pcm0-data-UL",
192 .cpu_dai_name = "PCM0",
193 .codec_name = "snd-soc-dummy",
194 .codec_dai_name = "snd-soc-dummy-dai",
195 .trigger = {SND_SOC_DPCM_TRIGGER_POST,
196 SND_SOC_DPCM_TRIGGER_POST},
197 .ops = &mt2701_cs42448_48k_fe_ops,
198 .dynamic = 1,
199 .dpcm_capture = 1,
200 },
201 [DAI_LINK_FE_PCM1_IN] = {
202 .name = "mt2701-cs42448-pcm1-data-UL",
203 .stream_name = "mt2701-cs42448-pcm1-data-UL",
204 .cpu_dai_name = "PCM1",
205 .codec_name = "snd-soc-dummy",
206 .codec_dai_name = "snd-soc-dummy-dai",
207 .trigger = {SND_SOC_DPCM_TRIGGER_POST,
208 SND_SOC_DPCM_TRIGGER_POST},
209 .ops = &mt2701_cs42448_48k_fe_ops,
210 .dynamic = 1,
211 .dpcm_capture = 1,
212 },
213 [DAI_LINK_FE_BT_OUT] = {
214 .name = "mt2701-cs42448-pcm-BT-out",
215 .stream_name = "mt2701-cs42448-pcm-BT",
216 .cpu_dai_name = "PCM_BT_DL",
217 .codec_name = "snd-soc-dummy",
218 .codec_dai_name = "snd-soc-dummy-dai",
219 .trigger = {SND_SOC_DPCM_TRIGGER_POST,
220 SND_SOC_DPCM_TRIGGER_POST},
221 .dynamic = 1,
222 .dpcm_playback = 1,
223 },
224 [DAI_LINK_FE_BT_IN] = {
225 .name = "mt2701-cs42448-pcm-BT-in",
226 .stream_name = "mt2701-cs42448-pcm-BT",
227 .cpu_dai_name = "PCM_BT_UL",
228 .codec_name = "snd-soc-dummy",
229 .codec_dai_name = "snd-soc-dummy-dai",
230 .trigger = {SND_SOC_DPCM_TRIGGER_POST,
231 SND_SOC_DPCM_TRIGGER_POST},
232 .dynamic = 1,
233 .dpcm_capture = 1,
234 },
235
236 [DAI_LINK_BE_I2S0] = {
237 .name = "mt2701-cs42448-I2S0",
238 .cpu_dai_name = "I2S0",
239 .no_pcm = 1,
240 .codec_dai_name = "cs42448",
241 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
242 | SND_SOC_DAIFMT_GATED,
243 .ops = &mt2701_cs42448_be_ops,
244 .dpcm_playback = 1,
245 .dpcm_capture = 1,
246 },
247 [DAI_LINK_BE_I2S1] = {
248 .name = "mt2701-cs42448-I2S1",
249 .cpu_dai_name = "I2S1",
250 .no_pcm = 1,
251 .codec_dai_name = "cs42448",
252 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
253 | SND_SOC_DAIFMT_GATED,
254 .ops = &mt2701_cs42448_be_ops,
255 .dpcm_playback = 1,
256 .dpcm_capture = 1,
257 },
258 [DAI_LINK_BE_I2S2] = {
259 .name = "mt2701-cs42448-I2S2",
260 .cpu_dai_name = "I2S2",
261 .no_pcm = 1,
262 .codec_dai_name = "cs42448",
263 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
264 | SND_SOC_DAIFMT_GATED,
265 .ops = &mt2701_cs42448_be_ops,
266 .dpcm_playback = 1,
267 .dpcm_capture = 1,
268 },
269 [DAI_LINK_BE_I2S3] = {
270 .name = "mt2701-cs42448-I2S3",
271 .cpu_dai_name = "I2S3",
272 .no_pcm = 1,
273 .codec_dai_name = "cs42448",
274 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
275 | SND_SOC_DAIFMT_GATED,
276 .ops = &mt2701_cs42448_be_ops,
277 .dpcm_playback = 1,
278 .dpcm_capture = 1,
279 },
280 [DAI_LINK_BE_MRG_BT] = {
281 .name = "mt2701-cs42448-MRG-BT",
282 .cpu_dai_name = "MRG BT",
283 .no_pcm = 1,
284 .codec_dai_name = "bt-sco-pcm-wb",
285 .dpcm_playback = 1,
286 .dpcm_capture = 1,
287 },
288};
289
290static struct snd_soc_card mt2701_cs42448_soc_card = {
291 .name = "mt2701-cs42448",
292 .owner = THIS_MODULE,
293 .dai_link = mt2701_cs42448_dai_links,
294 .num_links = ARRAY_SIZE(mt2701_cs42448_dai_links),
295 .controls = mt2701_cs42448_controls,
296 .num_controls = ARRAY_SIZE(mt2701_cs42448_controls),
297 .dapm_widgets = mt2701_cs42448_asoc_card_dapm_widgets,
298 .num_dapm_widgets = ARRAY_SIZE(mt2701_cs42448_asoc_card_dapm_widgets),
299};
300
301static int mt2701_cs42448_machine_probe(struct platform_device *pdev)
302{
303 struct snd_soc_card *card = &mt2701_cs42448_soc_card;
304 int ret;
305 int i;
306 struct device_node *platform_node, *codec_node, *codec_node_bt_mrg;
307 struct mt2701_cs42448_private *priv =
308 devm_kzalloc(&pdev->dev, sizeof(struct mt2701_cs42448_private),
309 GFP_KERNEL);
310 struct device *dev = &pdev->dev;
311
312 if (!priv)
313 return -ENOMEM;
314
315 platform_node = of_parse_phandle(pdev->dev.of_node,
316 "mediatek,platform", 0);
317 if (!platform_node) {
318 dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");
319 return -EINVAL;
320 }
321 for (i = 0; i < card->num_links; i++) {
322 if (mt2701_cs42448_dai_links[i].platform_name)
323 continue;
324 mt2701_cs42448_dai_links[i].platform_of_node = platform_node;
325 }
326
327 card->dev = dev;
328
329 codec_node = of_parse_phandle(pdev->dev.of_node,
330 "mediatek,audio-codec", 0);
331 if (!codec_node) {
332 dev_err(&pdev->dev,
333 "Property 'audio-codec' missing or invalid\n");
334 return -EINVAL;
335 }
336 for (i = 0; i < card->num_links; i++) {
337 if (mt2701_cs42448_dai_links[i].codec_name)
338 continue;
339 mt2701_cs42448_dai_links[i].codec_of_node = codec_node;
340 }
341
342 codec_node_bt_mrg = of_parse_phandle(pdev->dev.of_node,
343 "mediatek,audio-codec-bt-mrg", 0);
344 if (!codec_node_bt_mrg) {
345 dev_err(&pdev->dev,
346 "Property 'audio-codec-bt-mrg' missing or invalid\n");
347 return -EINVAL;
348 }
349 mt2701_cs42448_dai_links[DAI_LINK_BE_MRG_BT].codec_of_node
350 = codec_node_bt_mrg;
351
352 ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
353 if (ret) {
354 dev_err(&pdev->dev, "failed to parse audio-routing: %d\n", ret);
355 return ret;
356 }
357
358 priv->i2s1_in_mux_gpio_sel_1 =
359 of_get_named_gpio(dev->of_node, "i2s1-in-sel-gpio1", 0);
360 if (gpio_is_valid(priv->i2s1_in_mux_gpio_sel_1)) {
361 ret = devm_gpio_request(dev, priv->i2s1_in_mux_gpio_sel_1,
362 "i2s1_in_mux_gpio_sel_1");
363 if (ret)
364 dev_warn(&pdev->dev, "%s devm_gpio_request fail %d\n",
365 __func__, ret);
366 gpio_direction_output(priv->i2s1_in_mux_gpio_sel_1, 0);
367 }
368
369 priv->i2s1_in_mux_gpio_sel_2 =
370 of_get_named_gpio(dev->of_node, "i2s1-in-sel-gpio2", 0);
371 if (gpio_is_valid(priv->i2s1_in_mux_gpio_sel_2)) {
372 ret = devm_gpio_request(dev, priv->i2s1_in_mux_gpio_sel_2,
373 "i2s1_in_mux_gpio_sel_2");
374 if (ret)
375 dev_warn(&pdev->dev, "%s devm_gpio_request fail2 %d\n",
376 __func__, ret);
377 gpio_direction_output(priv->i2s1_in_mux_gpio_sel_2, 0);
378 }
379 snd_soc_card_set_drvdata(card, priv);
380
381 ret = devm_snd_soc_register_card(&pdev->dev, card);
382
383 if (ret)
384 dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
385 __func__, ret);
386 return ret;
387}
388
389#ifdef CONFIG_OF
390static const struct of_device_id mt2701_cs42448_machine_dt_match[] = {
391 {.compatible = "mediatek,mt2701-cs42448-machine",},
392 {}
393};
394#endif
395
396static struct platform_driver mt2701_cs42448_machine = {
397 .driver = {
398 .name = "mt2701-cs42448",
399 #ifdef CONFIG_OF
400 .of_match_table = mt2701_cs42448_machine_dt_match,
401 #endif
402 },
403 .probe = mt2701_cs42448_machine_probe,
404};
405
406module_platform_driver(mt2701_cs42448_machine);
407
408
409MODULE_DESCRIPTION("MT2701 CS42448 ALSA SoC machine driver");
410MODULE_AUTHOR("Ir Lian <ir.lian@mediatek.com>");
411MODULE_LICENSE("GPL v2");
412MODULE_ALIAS("mt2701 cs42448 soc card");
413