blob: 4e44e5689d6f70d2d6197fe0fa330aa482bdc816 [file] [log] [blame]
Shunli Wang11c02692019-03-29 16:34:46 +08001// SPDX-License-Identifier: GPL-2.0
2//
3// mt8183-mt6358.c --
4// MT8183-MT6358-TS3A227-MAX98357 ALSA SoC machine driver
5//
6// Copyright (c) 2018 MediaTek Inc.
7// Author: Shunli Wang <shunli.wang@mediatek.com>
8
9#include <linux/module.h>
10#include <sound/pcm_params.h>
11#include <sound/soc.h>
12#include <sound/jack.h>
13#include <linux/pinctrl/consumer.h>
14
15#include "mt8183-afe-common.h"
16#include "../../codecs/ts3a227e.h"
17
18static struct snd_soc_jack headset_jack;
19
20/* Headset jack detection DAPM pins */
21static struct snd_soc_jack_pin headset_jack_pins[] = {
22 {
23 .pin = "Headphone",
24 .mask = SND_JACK_HEADPHONE,
25 },
26 {
27 .pin = "Headset Mic",
28 .mask = SND_JACK_MICROPHONE,
29 },
30
31};
32
33static int mt8183_mt6358_i2s_hw_params(struct snd_pcm_substream *substream,
34 struct snd_pcm_hw_params *params)
35{
36 struct snd_soc_pcm_runtime *rtd = substream->private_data;
37 unsigned int rate = params_rate(params);
38 unsigned int mclk_fs_ratio = 128;
39 unsigned int mclk_fs = rate * mclk_fs_ratio;
40
41 return snd_soc_dai_set_sysclk(rtd->cpu_dai,
42 0, mclk_fs, SND_SOC_CLOCK_OUT);
43}
44
45static const struct snd_soc_ops mt8183_mt6358_i2s_ops = {
46 .hw_params = mt8183_mt6358_i2s_hw_params,
47};
48
49static int mt8183_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
50 struct snd_pcm_hw_params *params)
51{
52 dev_dbg(rtd->dev, "%s(), fix format to 32bit\n", __func__);
53
54 /* fix BE i2s format to 32bit, clean param mask first */
55 snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT),
56 0, SNDRV_PCM_FORMAT_LAST);
57
58 params_set_format(params, SNDRV_PCM_FORMAT_S32_LE);
59 return 0;
60}
61
62static const struct snd_soc_dapm_widget
63mt8183_mt6358_ts3a227_max98357_dapm_widgets[] = {
64 SND_SOC_DAPM_OUTPUT("IT6505_8CH"),
65};
66
67static const struct snd_soc_dapm_route
68mt8183_mt6358_ts3a227_max98357_dapm_routes[] = {
69 {"IT6505_8CH", NULL, "TDM"},
70};
71
Tzung-Bi Shih1df1e542019-04-08 18:47:28 +080072static int
73mt8183_mt6358_ts3a227_max98357_bt_sco_startup(
74 struct snd_pcm_substream *substream)
75{
76 static const unsigned int rates[] = {
77 8000, 16000
78 };
79 static const struct snd_pcm_hw_constraint_list constraints_rates = {
80 .count = ARRAY_SIZE(rates),
81 .list = rates,
82 .mask = 0,
83 };
84 static const unsigned int channels[] = {
85 1,
86 };
87 static const struct snd_pcm_hw_constraint_list constraints_channels = {
88 .count = ARRAY_SIZE(channels),
89 .list = channels,
90 .mask = 0,
91 };
92
93 struct snd_pcm_runtime *runtime = substream->runtime;
94
95 snd_pcm_hw_constraint_list(runtime, 0,
96 SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
97 runtime->hw.channels_max = 1;
98 snd_pcm_hw_constraint_list(runtime, 0,
99 SNDRV_PCM_HW_PARAM_CHANNELS,
100 &constraints_channels);
101
102 runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
103 snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16);
104
105 return 0;
106}
107
108static const struct snd_soc_ops mt8183_mt6358_ts3a227_max98357_bt_sco_ops = {
109 .startup = mt8183_mt6358_ts3a227_max98357_bt_sco_startup,
110};
111
Shunli Wang11c02692019-03-29 16:34:46 +0800112static struct snd_soc_dai_link
113mt8183_mt6358_ts3a227_max98357_dai_links[] = {
114 /* FE */
115 {
116 .name = "Playback_1",
117 .stream_name = "Playback_1",
118 .cpu_dai_name = "DL1",
119 .codec_name = "snd-soc-dummy",
120 .codec_dai_name = "snd-soc-dummy-dai",
121 .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
122 SND_SOC_DPCM_TRIGGER_PRE},
123 .dynamic = 1,
124 .dpcm_playback = 1,
125 },
126 {
127 .name = "Playback_2",
128 .stream_name = "Playback_2",
129 .cpu_dai_name = "DL2",
130 .codec_name = "snd-soc-dummy",
131 .codec_dai_name = "snd-soc-dummy-dai",
132 .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
133 SND_SOC_DPCM_TRIGGER_PRE},
134 .dynamic = 1,
135 .dpcm_playback = 1,
Tzung-Bi Shih1df1e542019-04-08 18:47:28 +0800136 .ops = &mt8183_mt6358_ts3a227_max98357_bt_sco_ops,
Shunli Wang11c02692019-03-29 16:34:46 +0800137 },
138 {
139 .name = "Playback_3",
140 .stream_name = "Playback_3",
141 .cpu_dai_name = "DL3",
142 .codec_name = "snd-soc-dummy",
143 .codec_dai_name = "snd-soc-dummy-dai",
144 .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
145 SND_SOC_DPCM_TRIGGER_PRE},
146 .dynamic = 1,
147 .dpcm_playback = 1,
148 },
149 {
150 .name = "Capture_1",
151 .stream_name = "Capture_1",
152 .cpu_dai_name = "UL1",
153 .codec_name = "snd-soc-dummy",
154 .codec_dai_name = "snd-soc-dummy-dai",
155 .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
156 SND_SOC_DPCM_TRIGGER_PRE},
157 .dynamic = 1,
158 .dpcm_capture = 1,
Tzung-Bi Shih1df1e542019-04-08 18:47:28 +0800159 .ops = &mt8183_mt6358_ts3a227_max98357_bt_sco_ops,
Shunli Wang11c02692019-03-29 16:34:46 +0800160 },
161 {
162 .name = "Capture_2",
163 .stream_name = "Capture_2",
164 .cpu_dai_name = "UL2",
165 .codec_name = "snd-soc-dummy",
166 .codec_dai_name = "snd-soc-dummy-dai",
167 .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
168 SND_SOC_DPCM_TRIGGER_PRE},
169 .dynamic = 1,
170 .dpcm_capture = 1,
171 },
172 {
173 .name = "Capture_3",
174 .stream_name = "Capture_3",
175 .cpu_dai_name = "UL3",
176 .codec_name = "snd-soc-dummy",
177 .codec_dai_name = "snd-soc-dummy-dai",
178 .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
179 SND_SOC_DPCM_TRIGGER_PRE},
180 .dynamic = 1,
181 .dpcm_capture = 1,
182 },
183 {
184 .name = "Capture_Mono_1",
185 .stream_name = "Capture_Mono_1",
186 .cpu_dai_name = "UL_MONO_1",
187 .codec_name = "snd-soc-dummy",
188 .codec_dai_name = "snd-soc-dummy-dai",
189 .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
190 SND_SOC_DPCM_TRIGGER_PRE},
191 .dynamic = 1,
192 .dpcm_capture = 1,
193 },
194 {
195 .name = "Playback_HDMI",
196 .stream_name = "Playback_HDMI",
197 .cpu_dai_name = "HDMI",
198 .codec_name = "snd-soc-dummy",
199 .codec_dai_name = "snd-soc-dummy-dai",
200 .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
201 SND_SOC_DPCM_TRIGGER_PRE},
202 .dynamic = 1,
203 .dpcm_playback = 1,
204 },
205 /* BE */
206 {
207 .name = "Primary Codec",
208 .cpu_dai_name = "ADDA",
209 .codec_dai_name = "mt6358-snd-codec-aif1",
210 .codec_name = "mt6358-sound",
211 .no_pcm = 1,
212 .dpcm_playback = 1,
213 .dpcm_capture = 1,
214 .ignore_suspend = 1,
215 },
216 {
217 .name = "PCM 1",
218 .cpu_dai_name = "PCM 1",
219 .codec_name = "snd-soc-dummy",
220 .codec_dai_name = "snd-soc-dummy-dai",
221 .no_pcm = 1,
222 .dpcm_playback = 1,
223 .dpcm_capture = 1,
224 .ignore_suspend = 1,
225 },
226 {
227 .name = "PCM 2",
228 .cpu_dai_name = "PCM 2",
229 .codec_name = "snd-soc-dummy",
230 .codec_dai_name = "snd-soc-dummy-dai",
231 .no_pcm = 1,
232 .dpcm_playback = 1,
233 .dpcm_capture = 1,
234 .ignore_suspend = 1,
235 },
236 {
237 .name = "I2S0",
238 .cpu_dai_name = "I2S0",
239 .codec_dai_name = "bt-sco-pcm",
240 .codec_name = "bt-sco",
241 .no_pcm = 1,
242 .dpcm_capture = 1,
243 .ignore_suspend = 1,
244 .be_hw_params_fixup = mt8183_i2s_hw_params_fixup,
245 .ops = &mt8183_mt6358_i2s_ops,
246 },
247 {
248 .name = "I2S1",
249 .cpu_dai_name = "I2S1",
250 .codec_dai_name = "snd-soc-dummy-dai",
251 .codec_name = "snd-soc-dummy",
252 .no_pcm = 1,
253 .dpcm_playback = 1,
254 .ignore_suspend = 1,
255 .be_hw_params_fixup = mt8183_i2s_hw_params_fixup,
256 .ops = &mt8183_mt6358_i2s_ops,
257 },
258 {
259 .name = "I2S2",
260 .cpu_dai_name = "I2S2",
261 .codec_dai_name = "snd-soc-dummy-dai",
262 .codec_name = "snd-soc-dummy",
263 .no_pcm = 1,
264 .dpcm_capture = 1,
265 .ignore_suspend = 1,
266 .be_hw_params_fixup = mt8183_i2s_hw_params_fixup,
267 .ops = &mt8183_mt6358_i2s_ops,
268 },
269 {
270 .name = "I2S3",
271 .cpu_dai_name = "I2S3",
272 .codec_dai_name = "HiFi",
273 .codec_name = "max98357a",
274 .no_pcm = 1,
275 .dpcm_playback = 1,
276 .ignore_suspend = 1,
277 .be_hw_params_fixup = mt8183_i2s_hw_params_fixup,
278 .ops = &mt8183_mt6358_i2s_ops,
279 },
280 {
281 .name = "I2S5",
282 .cpu_dai_name = "I2S5",
283 .codec_dai_name = "bt-sco-pcm",
284 .codec_name = "bt-sco",
285 .no_pcm = 1,
286 .dpcm_playback = 1,
287 .ignore_suspend = 1,
288 .be_hw_params_fixup = mt8183_i2s_hw_params_fixup,
289 .ops = &mt8183_mt6358_i2s_ops,
290 },
291 {
292 .name = "TDM",
293 .cpu_dai_name = "TDM",
294 .codec_name = "snd-soc-dummy",
295 .codec_dai_name = "snd-soc-dummy-dai",
296 .no_pcm = 1,
297 .dpcm_playback = 1,
298 .ignore_suspend = 1,
299 },
300};
301
302static int
303mt8183_mt6358_ts3a227_max98357_headset_init(struct snd_soc_component *cpnt);
304
305static struct snd_soc_aux_dev mt8183_mt6358_ts3a227_max98357_headset_dev = {
306 .name = "Headset Chip",
307 .init = mt8183_mt6358_ts3a227_max98357_headset_init,
308};
309
310static struct snd_soc_card mt8183_mt6358_ts3a227_max98357_card = {
311 .name = "mt8183_mt6358_ts3a227_max98357",
312 .owner = THIS_MODULE,
313 .dai_link = mt8183_mt6358_ts3a227_max98357_dai_links,
314 .num_links = ARRAY_SIZE(mt8183_mt6358_ts3a227_max98357_dai_links),
315 .aux_dev = &mt8183_mt6358_ts3a227_max98357_headset_dev,
316 .num_aux_devs = 1,
317};
318
319static int
320mt8183_mt6358_ts3a227_max98357_headset_init(struct snd_soc_component *component)
321{
322 int ret;
323
324 /* Enable Headset and 4 Buttons Jack detection */
325 ret = snd_soc_card_jack_new(&mt8183_mt6358_ts3a227_max98357_card,
326 "Headset Jack",
327 SND_JACK_HEADSET |
328 SND_JACK_BTN_0 | SND_JACK_BTN_1 |
329 SND_JACK_BTN_2 | SND_JACK_BTN_3,
330 &headset_jack,
331 headset_jack_pins,
332 ARRAY_SIZE(headset_jack_pins));
333 if (ret)
334 return ret;
335
336 ret = ts3a227e_enable_jack_detect(component, &headset_jack);
337
338 return ret;
339}
340
341static int
342mt8183_mt6358_ts3a227_max98357_dev_probe(struct platform_device *pdev)
343{
344 struct snd_soc_card *card = &mt8183_mt6358_ts3a227_max98357_card;
345 struct device_node *platform_node;
346 struct snd_soc_dai_link *dai_link;
347 struct pinctrl *default_pins;
348 int ret, i;
349
350 card->dev = &pdev->dev;
351
352 platform_node = of_parse_phandle(pdev->dev.of_node,
353 "mediatek,platform", 0);
354 if (!platform_node) {
355 dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");
356 return -EINVAL;
357 }
358
359 for_each_card_prelinks(card, i, dai_link) {
360 /* In the alsa soc-core, the "platform" will be
361 * allocated by devm_kzalloc if null.
362 * There is a special case that registerring
363 * sound card is failed at the first time, but
364 * the "platform" will not null when probe is trying
365 * again. It's not expected normally.
366 */
Anders Roxell2b7bcda2019-04-03 05:05:01 +0200367 dai_link->platforms = NULL;
Shunli Wang11c02692019-03-29 16:34:46 +0800368
369 if (dai_link->platform_name)
370 continue;
371 dai_link->platform_of_node = platform_node;
372 }
373
374 mt8183_mt6358_ts3a227_max98357_headset_dev.codec_of_node =
375 of_parse_phandle(pdev->dev.of_node,
376 "mediatek,headset-codec", 0);
377 if (!mt8183_mt6358_ts3a227_max98357_headset_dev.codec_of_node) {
378 dev_err(&pdev->dev,
379 "Property 'mediatek,headset-codec' missing/invalid\n");
380 return -EINVAL;
381 }
382
383 ret = devm_snd_soc_register_card(&pdev->dev, card);
384 if (ret)
385 dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
386 __func__, ret);
387
388 default_pins =
389 devm_pinctrl_get_select(&pdev->dev, PINCTRL_STATE_DEFAULT);
390 if (IS_ERR(default_pins)) {
391 dev_err(&pdev->dev, "%s set pins failed\n",
392 __func__);
393 return PTR_ERR(default_pins);
394 }
395
396 return ret;
397}
398
399#ifdef CONFIG_OF
400static const struct of_device_id mt8183_mt6358_ts3a227_max98357_dt_match[] = {
401 {.compatible = "mediatek,mt8183_mt6358_ts3a227_max98357",},
402 {}
403};
404#endif
405
406static struct platform_driver mt8183_mt6358_ts3a227_max98357_driver = {
407 .driver = {
408 .name = "mt8183_mt6358_ts3a227_max98357",
Shunli Wang11c02692019-03-29 16:34:46 +0800409#ifdef CONFIG_OF
410 .of_match_table = mt8183_mt6358_ts3a227_max98357_dt_match,
411#endif
412 },
413 .probe = mt8183_mt6358_ts3a227_max98357_dev_probe,
414};
415
416module_platform_driver(mt8183_mt6358_ts3a227_max98357_driver);
417
418/* Module information */
419MODULE_DESCRIPTION("MT8183-MT6358-TS3A227-MAX98357 ALSA SoC machine driver");
420MODULE_AUTHOR("Shunli Wang <shunli.wang@mediatek.com>");
421MODULE_LICENSE("GPL v2");
422MODULE_ALIAS("mt8183_mt6358_ts3a227_max98357 soc card");
423