]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - sound/soc/intel/boards/bytcr_rt5651.c
ASoC: Intel: boards: use helper to get codec_dai
[mirror_ubuntu-bionic-kernel.git] / sound / soc / intel / boards / bytcr_rt5651.c
CommitLineData
2bd5bd15
PLB
1/*
2 * bytcr_rt5651.c - ASoc Machine driver for Intel Byt CR platform
3 * (derived from bytcr_rt5640.c)
4 *
5 * Copyright (C) 2015 Intel Corp
6 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; version 2 of the License.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
18 */
19
20#include <linux/init.h>
21#include <linux/module.h>
22#include <linux/platform_device.h>
23#include <linux/acpi.h>
02c0a3b3 24#include <linux/clk.h>
2bd5bd15
PLB
25#include <linux/device.h>
26#include <linux/dmi.h>
27#include <linux/slab.h>
02c0a3b3 28#include <asm/platform_sst_audio.h>
2bd5bd15
PLB
29#include <sound/pcm.h>
30#include <sound/pcm_params.h>
31#include <sound/soc.h>
32#include <sound/jack.h>
33#include "../../codecs/rt5651.h"
34#include "../atom/sst-atom-controls.h"
02c0a3b3
PLB
35#include "../common/sst-acpi.h"
36
37enum {
38 BYT_RT5651_DMIC_MAP,
39 BYT_RT5651_IN1_MAP,
40};
41
42#define BYT_RT5651_MAP(quirk) ((quirk) & GENMASK(7, 0))
43#define BYT_RT5651_DMIC_EN BIT(16)
44#define BYT_RT5651_MCLK_EN BIT(17)
45#define BYT_RT5651_MCLK_25MHZ BIT(18)
46
47struct byt_rt5651_private {
48 struct clk *mclk;
49};
50
51static unsigned long byt_rt5651_quirk = BYT_RT5651_DMIC_MAP |
52 BYT_RT5651_DMIC_EN |
53 BYT_RT5651_MCLK_EN;
54
55static void log_quirks(struct device *dev)
56{
57 if (BYT_RT5651_MAP(byt_rt5651_quirk) == BYT_RT5651_DMIC_MAP)
58 dev_info(dev, "quirk DMIC_MAP enabled");
59 if (BYT_RT5651_MAP(byt_rt5651_quirk) == BYT_RT5651_IN1_MAP)
60 dev_info(dev, "quirk IN1_MAP enabled");
61 if (byt_rt5651_quirk & BYT_RT5651_DMIC_EN)
62 dev_info(dev, "quirk DMIC enabled");
63 if (byt_rt5651_quirk & BYT_RT5651_MCLK_EN)
64 dev_info(dev, "quirk MCLK_EN enabled");
65 if (byt_rt5651_quirk & BYT_RT5651_MCLK_25MHZ)
66 dev_info(dev, "quirk MCLK_25MHZ enabled");
67}
68
69#define BYT_CODEC_DAI1 "rt5651-aif1"
70
02c0a3b3
PLB
71static int platform_clock_control(struct snd_soc_dapm_widget *w,
72 struct snd_kcontrol *k, int event)
73{
74 struct snd_soc_dapm_context *dapm = w->dapm;
75 struct snd_soc_card *card = dapm->card;
76 struct snd_soc_dai *codec_dai;
77 struct byt_rt5651_private *priv = snd_soc_card_get_drvdata(card);
78 int ret;
79
dfb6ec7a 80 codec_dai = snd_soc_card_get_codec_dai(card, BYT_CODEC_DAI1);
02c0a3b3
PLB
81 if (!codec_dai) {
82 dev_err(card->dev,
83 "Codec dai not found; Unable to set platform clock\n");
84 return -EIO;
85 }
86
87 if (SND_SOC_DAPM_EVENT_ON(event)) {
88 if (byt_rt5651_quirk & BYT_RT5651_MCLK_EN) {
89 ret = clk_prepare_enable(priv->mclk);
90 if (ret < 0) {
91 dev_err(card->dev,
92 "could not configure MCLK state");
93 return ret;
94 }
95 }
96 ret = snd_soc_dai_set_sysclk(codec_dai, RT5651_SCLK_S_PLL1,
97 48000 * 512,
98 SND_SOC_CLOCK_IN);
99 } else {
100 /*
101 * Set codec clock source to internal clock before
102 * turning off the platform clock. Codec needs clock
103 * for Jack detection and button press
104 */
105 ret = snd_soc_dai_set_sysclk(codec_dai, RT5651_SCLK_S_RCCLK,
106 48000 * 512,
107 SND_SOC_CLOCK_IN);
108 if (!ret)
109 if (byt_rt5651_quirk & BYT_RT5651_MCLK_EN)
110 clk_disable_unprepare(priv->mclk);
111 }
112
113 if (ret < 0) {
114 dev_err(card->dev, "can't set codec sysclk: %d\n", ret);
115 return ret;
116 }
117
118 return 0;
119}
2bd5bd15
PLB
120
121static const struct snd_soc_dapm_widget byt_rt5651_widgets[] = {
122 SND_SOC_DAPM_HP("Headphone", NULL),
123 SND_SOC_DAPM_MIC("Headset Mic", NULL),
124 SND_SOC_DAPM_MIC("Internal Mic", NULL),
125 SND_SOC_DAPM_SPK("Speaker", NULL),
02c0a3b3
PLB
126 SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
127 platform_clock_control, SND_SOC_DAPM_PRE_PMU |
128 SND_SOC_DAPM_POST_PMD),
129
2bd5bd15
PLB
130};
131
132static const struct snd_soc_dapm_route byt_rt5651_audio_map[] = {
02c0a3b3
PLB
133 {"Headphone", NULL, "Platform Clock"},
134 {"Headset Mic", NULL, "Platform Clock"},
135 {"Internal Mic", NULL, "Platform Clock"},
136 {"Speaker", NULL, "Platform Clock"},
137
2bd5bd15
PLB
138 {"AIF1 Playback", NULL, "ssp2 Tx"},
139 {"ssp2 Tx", NULL, "codec_out0"},
140 {"ssp2 Tx", NULL, "codec_out1"},
141 {"codec_in0", NULL, "ssp2 Rx"},
142 {"codec_in1", NULL, "ssp2 Rx"},
143 {"ssp2 Rx", NULL, "AIF1 Capture"},
144
145 {"Headset Mic", NULL, "micbias1"}, /* lowercase for rt5651 */
146 {"IN2P", NULL, "Headset Mic"},
147 {"Headphone", NULL, "HPOL"},
148 {"Headphone", NULL, "HPOR"},
149 {"Speaker", NULL, "LOUTL"},
150 {"Speaker", NULL, "LOUTR"},
151};
152
6356c78c
PLB
153static const struct snd_soc_dapm_route byt_rt5651_intmic_dmic_map[] = {
154 {"DMIC L1", NULL, "Internal Mic"},
155 {"DMIC R1", NULL, "Internal Mic"},
2bd5bd15
PLB
156};
157
158static const struct snd_soc_dapm_route byt_rt5651_intmic_in1_map[] = {
159 {"Internal Mic", NULL, "micbias1"},
160 {"IN1P", NULL, "Internal Mic"},
161};
162
2bd5bd15
PLB
163static const struct snd_kcontrol_new byt_rt5651_controls[] = {
164 SOC_DAPM_PIN_SWITCH("Headphone"),
165 SOC_DAPM_PIN_SWITCH("Headset Mic"),
166 SOC_DAPM_PIN_SWITCH("Internal Mic"),
167 SOC_DAPM_PIN_SWITCH("Speaker"),
168};
169
170static int byt_rt5651_aif1_hw_params(struct snd_pcm_substream *substream,
171 struct snd_pcm_hw_params *params)
172{
173 struct snd_soc_pcm_runtime *rtd = substream->private_data;
174 struct snd_soc_dai *codec_dai = rtd->codec_dai;
175 int ret;
176
177 snd_soc_dai_set_bclk_ratio(codec_dai, 50);
178
179 ret = snd_soc_dai_set_sysclk(codec_dai, RT5651_SCLK_S_PLL1,
180 params_rate(params) * 512,
181 SND_SOC_CLOCK_IN);
182 if (ret < 0) {
183 dev_err(rtd->dev, "can't set codec clock %d\n", ret);
184 return ret;
185 }
186
02c0a3b3
PLB
187 if (!(byt_rt5651_quirk & BYT_RT5651_MCLK_EN)) {
188 /* 2x25 bit slots on SSP2 */
189 ret = snd_soc_dai_set_pll(codec_dai, 0,
190 RT5651_PLL1_S_BCLK1,
191 params_rate(params) * 50,
192 params_rate(params) * 512);
193 } else {
194 if (byt_rt5651_quirk & BYT_RT5651_MCLK_25MHZ) {
195 ret = snd_soc_dai_set_pll(codec_dai, 0,
196 RT5651_PLL1_S_MCLK,
197 25000000,
198 params_rate(params) * 512);
199 } else {
200 ret = snd_soc_dai_set_pll(codec_dai, 0,
201 RT5651_PLL1_S_MCLK,
202 19200000,
203 params_rate(params) * 512);
204 }
205 }
206
2bd5bd15
PLB
207 if (ret < 0) {
208 dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
209 return ret;
210 }
211
212 return 0;
213}
214
02c0a3b3
PLB
215static int byt_rt5651_quirk_cb(const struct dmi_system_id *id)
216{
217 byt_rt5651_quirk = (unsigned long)id->driver_data;
218 return 1;
219}
220
2bd5bd15 221static const struct dmi_system_id byt_rt5651_quirk_table[] = {
02c0a3b3
PLB
222 {
223 .callback = byt_rt5651_quirk_cb,
224 .matches = {
225 DMI_MATCH(DMI_SYS_VENDOR, "Circuitco"),
226 DMI_MATCH(DMI_PRODUCT_NAME, "Minnowboard Max B3 PLATFORM"),
227 },
228 .driver_data = (void *)(BYT_RT5651_DMIC_MAP |
229 BYT_RT5651_DMIC_EN),
230 },
2bd5bd15
PLB
231 {}
232};
233
234static int byt_rt5651_init(struct snd_soc_pcm_runtime *runtime)
235{
2bd5bd15 236 struct snd_soc_card *card = runtime->card;
02c0a3b3 237 struct byt_rt5651_private *priv = snd_soc_card_get_drvdata(card);
2bd5bd15
PLB
238 const struct snd_soc_dapm_route *custom_map;
239 int num_routes;
02c0a3b3 240 int ret;
2bd5bd15
PLB
241
242 card->dapm.idle_bias_off = true;
243
2bd5bd15
PLB
244 switch (BYT_RT5651_MAP(byt_rt5651_quirk)) {
245 case BYT_RT5651_IN1_MAP:
246 custom_map = byt_rt5651_intmic_in1_map;
247 num_routes = ARRAY_SIZE(byt_rt5651_intmic_in1_map);
248 break;
2bd5bd15 249 default:
6356c78c
PLB
250 custom_map = byt_rt5651_intmic_dmic_map;
251 num_routes = ARRAY_SIZE(byt_rt5651_intmic_dmic_map);
2bd5bd15 252 }
6356c78c
PLB
253 ret = snd_soc_dapm_add_routes(&card->dapm, custom_map, num_routes);
254 if (ret)
255 return ret;
2bd5bd15
PLB
256
257 ret = snd_soc_add_card_controls(card, byt_rt5651_controls,
258 ARRAY_SIZE(byt_rt5651_controls));
259 if (ret) {
260 dev_err(card->dev, "unable to add card controls\n");
261 return ret;
262 }
263 snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone");
264 snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker");
265
02c0a3b3
PLB
266 if (byt_rt5651_quirk & BYT_RT5651_MCLK_EN) {
267 /*
268 * The firmware might enable the clock at
269 * boot (this information may or may not
270 * be reflected in the enable clock register).
271 * To change the rate we must disable the clock
272 * first to cover these cases. Due to common
273 * clock framework restrictions that do not allow
274 * to disable a clock that has not been enabled,
275 * we need to enable the clock first.
276 */
277 ret = clk_prepare_enable(priv->mclk);
278 if (!ret)
279 clk_disable_unprepare(priv->mclk);
280
281 if (byt_rt5651_quirk & BYT_RT5651_MCLK_25MHZ)
282 ret = clk_set_rate(priv->mclk, 25000000);
283 else
284 ret = clk_set_rate(priv->mclk, 19200000);
285
286 if (ret)
287 dev_err(card->dev, "unable to set MCLK rate\n");
288 }
289
2bd5bd15
PLB
290 return ret;
291}
292
293static const struct snd_soc_pcm_stream byt_rt5651_dai_params = {
294 .formats = SNDRV_PCM_FMTBIT_S24_LE,
295 .rate_min = 48000,
296 .rate_max = 48000,
297 .channels_min = 2,
298 .channels_max = 2,
299};
300
301static int byt_rt5651_codec_fixup(struct snd_soc_pcm_runtime *rtd,
302 struct snd_pcm_hw_params *params)
303{
304 struct snd_interval *rate = hw_param_interval(params,
305 SNDRV_PCM_HW_PARAM_RATE);
306 struct snd_interval *channels = hw_param_interval(params,
307 SNDRV_PCM_HW_PARAM_CHANNELS);
308 int ret;
309
310 /* The DSP will covert the FE rate to 48k, stereo, 24bits */
311 rate->min = rate->max = 48000;
312 channels->min = channels->max = 2;
313
314 /* set SSP2 to 24-bit */
315 params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
316
317 /*
318 * Default mode for SSP configuration is TDM 4 slot, override config
319 * with explicit setting to I2S 2ch 24-bit. The word length is set with
320 * dai_set_tdm_slot() since there is no other API exposed
321 */
322 ret = snd_soc_dai_set_fmt(rtd->cpu_dai,
323 SND_SOC_DAIFMT_I2S |
f12f5c84 324 SND_SOC_DAIFMT_NB_NF |
2bd5bd15
PLB
325 SND_SOC_DAIFMT_CBS_CFS
326 );
327
328 if (ret < 0) {
329 dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
330 return ret;
331 }
332
333 ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24);
334 if (ret < 0) {
335 dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);
336 return ret;
337 }
338
339 return 0;
340}
341
1ebb4d9d 342static const unsigned int rates_48000[] = {
2bd5bd15
PLB
343 48000,
344};
345
1ebb4d9d 346static const struct snd_pcm_hw_constraint_list constraints_48000 = {
2bd5bd15
PLB
347 .count = ARRAY_SIZE(rates_48000),
348 .list = rates_48000,
349};
350
351static int byt_rt5651_aif1_startup(struct snd_pcm_substream *substream)
352{
353 return snd_pcm_hw_constraint_list(substream->runtime, 0,
354 SNDRV_PCM_HW_PARAM_RATE,
355 &constraints_48000);
356}
357
9b6fdef6 358static const struct snd_soc_ops byt_rt5651_aif1_ops = {
2bd5bd15
PLB
359 .startup = byt_rt5651_aif1_startup,
360};
361
9b6fdef6 362static const struct snd_soc_ops byt_rt5651_be_ssp2_ops = {
2bd5bd15
PLB
363 .hw_params = byt_rt5651_aif1_hw_params,
364};
365
366static struct snd_soc_dai_link byt_rt5651_dais[] = {
367 [MERR_DPCM_AUDIO] = {
368 .name = "Audio Port",
369 .stream_name = "Audio",
370 .cpu_dai_name = "media-cpu-dai",
371 .codec_dai_name = "snd-soc-dummy-dai",
372 .codec_name = "snd-soc-dummy",
373 .platform_name = "sst-mfld-platform",
2bd5bd15
PLB
374 .nonatomic = true,
375 .dynamic = 1,
376 .dpcm_playback = 1,
377 .dpcm_capture = 1,
378 .ops = &byt_rt5651_aif1_ops,
379 },
380 [MERR_DPCM_DEEP_BUFFER] = {
381 .name = "Deep-Buffer Audio Port",
382 .stream_name = "Deep-Buffer Audio",
383 .cpu_dai_name = "deepbuffer-cpu-dai",
384 .codec_dai_name = "snd-soc-dummy-dai",
385 .codec_name = "snd-soc-dummy",
386 .platform_name = "sst-mfld-platform",
2bd5bd15
PLB
387 .nonatomic = true,
388 .dynamic = 1,
389 .dpcm_playback = 1,
390 .ops = &byt_rt5651_aif1_ops,
391 },
392 [MERR_DPCM_COMPR] = {
393 .name = "Compressed Port",
394 .stream_name = "Compress",
395 .cpu_dai_name = "compress-cpu-dai",
396 .codec_dai_name = "snd-soc-dummy-dai",
397 .codec_name = "snd-soc-dummy",
398 .platform_name = "sst-mfld-platform",
399 },
400 /* CODEC<->CODEC link */
401 /* back ends */
402 {
403 .name = "SSP2-Codec",
2f0ad491 404 .id = 1,
2bd5bd15
PLB
405 .cpu_dai_name = "ssp2-port",
406 .platform_name = "sst-mfld-platform",
407 .no_pcm = 1,
408 .codec_dai_name = "rt5651-aif1",
409 .codec_name = "i2c-10EC5651:00",
410 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
411 | SND_SOC_DAIFMT_CBS_CFS,
412 .be_hw_params_fixup = byt_rt5651_codec_fixup,
413 .ignore_suspend = 1,
414 .nonatomic = true,
415 .dpcm_playback = 1,
416 .dpcm_capture = 1,
417 .init = byt_rt5651_init,
418 .ops = &byt_rt5651_be_ssp2_ops,
419 },
420};
421
422/* SoC card */
423static struct snd_soc_card byt_rt5651_card = {
424 .name = "bytcr-rt5651",
425 .owner = THIS_MODULE,
426 .dai_link = byt_rt5651_dais,
427 .num_links = ARRAY_SIZE(byt_rt5651_dais),
428 .dapm_widgets = byt_rt5651_widgets,
429 .num_dapm_widgets = ARRAY_SIZE(byt_rt5651_widgets),
430 .dapm_routes = byt_rt5651_audio_map,
431 .num_dapm_routes = ARRAY_SIZE(byt_rt5651_audio_map),
432 .fully_routed = true,
433};
434
02c0a3b3
PLB
435static char byt_rt5651_codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */
436
2bd5bd15
PLB
437static int snd_byt_rt5651_mc_probe(struct platform_device *pdev)
438{
02c0a3b3
PLB
439 struct byt_rt5651_private *priv;
440 struct sst_acpi_mach *mach;
441 const char *i2c_name = NULL;
2bd5bd15 442 int ret_val = 0;
02c0a3b3
PLB
443 int dai_index;
444 int i;
445
446 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC);
447 if (!priv)
448 return -ENOMEM;
2bd5bd15
PLB
449
450 /* register the soc card */
451 byt_rt5651_card.dev = &pdev->dev;
452
02c0a3b3
PLB
453 mach = byt_rt5651_card.dev->platform_data;
454 snd_soc_card_set_drvdata(&byt_rt5651_card, priv);
455
456 /* fix index of codec dai */
457 dai_index = MERR_DPCM_COMPR + 1;
458 for (i = 0; i < ARRAY_SIZE(byt_rt5651_dais); i++) {
459 if (!strcmp(byt_rt5651_dais[i].codec_name, "i2c-10EC5651:00")) {
460 dai_index = i;
461 break;
462 }
463 }
464
465 /* fixup codec name based on HID */
466 i2c_name = sst_acpi_find_name_from_hid(mach->id);
467 if (i2c_name) {
468 snprintf(byt_rt5651_codec_name, sizeof(byt_rt5651_codec_name),
469 "%s%s", "i2c-", i2c_name);
470
471 byt_rt5651_dais[dai_index].codec_name = byt_rt5651_codec_name;
472 }
473
474 /* check quirks before creating card */
475 dmi_check_system(byt_rt5651_quirk_table);
476 log_quirks(&pdev->dev);
477
478 if (byt_rt5651_quirk & BYT_RT5651_MCLK_EN) {
479 priv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3");
480 if (IS_ERR(priv->mclk)) {
481 dev_err(&pdev->dev,
482 "Failed to get MCLK from pmc_plt_clk_3: %ld\n",
483 PTR_ERR(priv->mclk));
484 /*
485 * Fall back to bit clock usage for -ENOENT (clock not
486 * available likely due to missing dependencies), bail
487 * for all other errors, including -EPROBE_DEFER
488 */
489 if (ret_val != -ENOENT)
490 return ret_val;
491 byt_rt5651_quirk &= ~BYT_RT5651_MCLK_EN;
492 }
493 }
494
2bd5bd15
PLB
495 ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_rt5651_card);
496
497 if (ret_val) {
498 dev_err(&pdev->dev, "devm_snd_soc_register_card failed %d\n",
499 ret_val);
500 return ret_val;
501 }
502 platform_set_drvdata(pdev, &byt_rt5651_card);
503 return ret_val;
504}
505
506static struct platform_driver snd_byt_rt5651_mc_driver = {
507 .driver = {
508 .name = "bytcr_rt5651",
2bd5bd15
PLB
509 },
510 .probe = snd_byt_rt5651_mc_probe,
511};
512
513module_platform_driver(snd_byt_rt5651_mc_driver);
514
515MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver for RT5651");
516MODULE_AUTHOR("Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>");
517MODULE_LICENSE("GPL v2");
518MODULE_ALIAS("platform:bytcr_rt5651");