Linux 4.16.11
[linux/fpc-iii.git] / sound / soc / mediatek / mt2701 / mt2701-cs42448.c
blob70f61d53fe05760cc20a1c8cb73acf5c7902cd8d
1 /*
2 * mt2701-cs42448.c -- MT2701 CS42448 ALSA SoC machine driver
4 * Copyright (c) 2016 MediaTek Inc.
5 * Author: Ir Lian <ir.lian@mediatek.com>
6 * Garlic Tseng <garlic.tseng@mediatek.com>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 and
11 * only version 2 as published by the Free Software Foundation.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
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>
26 #include "mt2701-afe-common.h"
28 struct mt2701_cs42448_private {
29 int i2s1_in_mux;
30 int i2s1_in_mux_gpio_sel_1;
31 int i2s1_in_mux_gpio_sel_2;
34 static const char * const i2sin_mux_switch_text[] = {
35 "ADC_SDOUT2",
36 "ADC_SDOUT3",
37 "I2S_IN_1",
38 "I2S_IN_2",
41 static const struct soc_enum i2sin_mux_enum =
42 SOC_ENUM_SINGLE_EXT(4, i2sin_mux_switch_text);
44 static int mt2701_cs42448_i2sin1_mux_get(struct snd_kcontrol *kcontrol,
45 struct snd_ctl_elem_value *ucontrol)
47 struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
48 struct mt2701_cs42448_private *priv = snd_soc_card_get_drvdata(card);
50 ucontrol->value.integer.value[0] = priv->i2s1_in_mux;
51 return 0;
54 static int mt2701_cs42448_i2sin1_mux_set(struct snd_kcontrol *kcontrol,
55 struct snd_ctl_elem_value *ucontrol)
57 struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
58 struct mt2701_cs42448_private *priv = snd_soc_card_get_drvdata(card);
60 if (ucontrol->value.integer.value[0] == priv->i2s1_in_mux)
61 return 0;
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__);
84 priv->i2s1_in_mux = ucontrol->value.integer.value[0];
85 return 0;
88 static 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),
97 static 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),
108 static const unsigned int mt2701_cs42448_sampling_rates[] = {48000};
110 static 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,
116 static int mt2701_cs42448_fe_ops_startup(struct snd_pcm_substream *substream)
118 int err;
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;
129 return 0;
132 static const struct snd_soc_ops mt2701_cs42448_48k_fe_ops = {
133 .startup = mt2701_cs42448_fe_ops_startup,
136 static int mt2701_cs42448_be_ops_hw_params(struct snd_pcm_substream *substream,
137 struct snd_pcm_hw_params *params)
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;
147 mclk_rate = rate * div_bck_over_lrck * div_mclk_over_bck;
149 /* mt2701 mclk */
150 snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_rate, SND_SOC_CLOCK_OUT);
152 /* codec mclk */
153 snd_soc_dai_set_sysclk(codec_dai, 0, mclk_rate, SND_SOC_CLOCK_IN);
155 return 0;
158 static struct snd_soc_ops mt2701_cs42448_be_ops = {
159 .hw_params = mt2701_cs42448_be_ops_hw_params
162 enum {
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,
175 static struct snd_soc_dai_link mt2701_cs42448_dai_links[] = {
176 /* FE */
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,
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,
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,
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,
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,
235 /* BE */
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,
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,
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,
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,
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,
290 static 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),
301 static int mt2701_cs42448_machine_probe(struct platform_device *pdev)
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;
312 if (!priv)
313 return -ENOMEM;
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;
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;
327 card->dev = dev;
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;
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;
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;
349 mt2701_cs42448_dai_links[DAI_LINK_BE_MRG_BT].codec_of_node
350 = codec_node_bt_mrg;
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;
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);
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);
379 snd_soc_card_set_drvdata(card, priv);
381 ret = devm_snd_soc_register_card(&pdev->dev, card);
383 if (ret)
384 dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
385 __func__, ret);
386 return ret;
389 #ifdef CONFIG_OF
390 static const struct of_device_id mt2701_cs42448_machine_dt_match[] = {
391 {.compatible = "mediatek,mt2701-cs42448-machine",},
394 #endif
396 static 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
403 .probe = mt2701_cs42448_machine_probe,
406 module_platform_driver(mt2701_cs42448_machine);
408 /* Module information */
409 MODULE_DESCRIPTION("MT2701 CS42448 ALSA SoC machine driver");
410 MODULE_AUTHOR("Ir Lian <ir.lian@mediatek.com>");
411 MODULE_LICENSE("GPL v2");
412 MODULE_ALIAS("mt2701 cs42448 soc card");