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
{
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
[] = {
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
;
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
)
63 switch (ucontrol
->value
.integer
.value
[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);
69 gpio_set_value(priv
->i2s1_in_mux_gpio_sel_1
, 1);
70 gpio_set_value(priv
->i2s1_in_mux_gpio_sel_2
, 0);
73 gpio_set_value(priv
->i2s1_in_mux_gpio_sel_1
, 0);
74 gpio_set_value(priv
->i2s1_in_mux_gpio_sel_2
, 1);
77 gpio_set_value(priv
->i2s1_in_mux_gpio_sel_1
, 1);
78 gpio_set_value(priv
->i2s1_in_mux_gpio_sel_2
, 1);
81 dev_warn(card
->dev
, "%s invalid setting\n", __func__
);
84 priv
->i2s1_in_mux
= ucontrol
->value
.integer
.value
[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
,
116 static int mt2701_cs42448_fe_ops_startup(struct snd_pcm_substream
*substream
)
120 err
= snd_pcm_hw_constraint_list(substream
->runtime
, 0,
121 SNDRV_PCM_HW_PARAM_RATE
,
122 &mt2701_cs42448_constraints_rates
);
124 dev_err(substream
->pcm
->card
->dev
,
125 "%s snd_pcm_hw_constraint_list failed: 0x%x\n",
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
;
150 snd_soc_dai_set_sysclk(cpu_dai
, 0, mclk_rate
, SND_SOC_CLOCK_OUT
);
153 snd_soc_dai_set_sysclk(codec_dai
, 0, mclk_rate
, SND_SOC_CLOCK_IN
);
158 static struct snd_soc_ops mt2701_cs42448_be_ops
= {
159 .hw_params
= mt2701_cs42448_be_ops_hw_params
163 DAI_LINK_FE_MULTI_CH_OUT
,
175 static struct snd_soc_dai_link mt2701_cs42448_dai_links
[] = {
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
,
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
,
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
,
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
},
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
},
236 [DAI_LINK_BE_I2S0
] = {
237 .name
= "mt2701-cs42448-I2S0",
238 .cpu_dai_name
= "I2S0",
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
,
247 [DAI_LINK_BE_I2S1
] = {
248 .name
= "mt2701-cs42448-I2S1",
249 .cpu_dai_name
= "I2S1",
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
,
258 [DAI_LINK_BE_I2S2
] = {
259 .name
= "mt2701-cs42448-I2S2",
260 .cpu_dai_name
= "I2S2",
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
,
269 [DAI_LINK_BE_I2S3
] = {
270 .name
= "mt2701-cs42448-I2S3",
271 .cpu_dai_name
= "I2S3",
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
,
280 [DAI_LINK_BE_MRG_BT
] = {
281 .name
= "mt2701-cs42448-MRG-BT",
282 .cpu_dai_name
= "MRG BT",
284 .codec_dai_name
= "bt-sco-pcm-wb",
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
;
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
),
310 struct device
*dev
= &pdev
->dev
;
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");
321 for (i
= 0; i
< card
->num_links
; i
++) {
322 if (mt2701_cs42448_dai_links
[i
].platform_name
)
324 mt2701_cs42448_dai_links
[i
].platform_of_node
= platform_node
;
329 codec_node
= of_parse_phandle(pdev
->dev
.of_node
,
330 "mediatek,audio-codec", 0);
333 "Property 'audio-codec' missing or invalid\n");
336 for (i
= 0; i
< card
->num_links
; i
++) {
337 if (mt2701_cs42448_dai_links
[i
].codec_name
)
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
) {
346 "Property 'audio-codec-bt-mrg' missing or invalid\n");
349 mt2701_cs42448_dai_links
[DAI_LINK_BE_MRG_BT
].codec_of_node
352 ret
= snd_soc_of_parse_audio_routing(card
, "audio-routing");
354 dev_err(&pdev
->dev
, "failed to parse audio-routing: %d\n", 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");
364 dev_warn(&pdev
->dev
, "%s devm_gpio_request fail %d\n",
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");
375 dev_warn(&pdev
->dev
, "%s devm_gpio_request fail2 %d\n",
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
);
384 dev_err(&pdev
->dev
, "%s snd_soc_register_card fail %d\n",
390 static const struct of_device_id mt2701_cs42448_machine_dt_match
[] = {
391 {.compatible
= "mediatek,mt2701-cs42448-machine",},
396 static struct platform_driver mt2701_cs42448_machine
= {
398 .name
= "mt2701-cs42448",
400 .of_match_table
= mt2701_cs42448_machine_dt_match
,
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");