Commit | Line | Data |
---|---|---|
f70abd75 B |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // Copyright(c) 2019 Intel Corporation. | |
3 | ||
4 | /* | |
5 | * Intel SOF Machine Driver with Realtek rt5682 Codec | |
6 | * and speaker codec MAX98357A | |
7 | */ | |
8 | #include <linux/i2c.h> | |
9 | #include <linux/input.h> | |
10 | #include <linux/module.h> | |
11 | #include <linux/platform_device.h> | |
1eed6bc0 | 12 | #include <linux/clk.h> |
f70abd75 | 13 | #include <linux/dmi.h> |
f70abd75 B |
14 | #include <sound/core.h> |
15 | #include <sound/jack.h> | |
16 | #include <sound/pcm.h> | |
17 | #include <sound/pcm_params.h> | |
18 | #include <sound/soc.h> | |
19 | #include <sound/rt5682.h> | |
20 | #include <sound/soc-acpi.h> | |
21 | #include "../../codecs/rt5682.h" | |
22 | #include "../../codecs/hdac_hdmi.h" | |
536cfd2f | 23 | #include "../common/soc-intel-quirks.h" |
f70abd75 B |
24 | |
25 | #define NAME_SIZE 32 | |
26 | ||
27 | #define SOF_RT5682_SSP_CODEC(quirk) ((quirk) & GENMASK(2, 0)) | |
28 | #define SOF_RT5682_SSP_CODEC_MASK (GENMASK(2, 0)) | |
29 | #define SOF_RT5682_MCLK_EN BIT(3) | |
30 | #define SOF_RT5682_MCLK_24MHZ BIT(4) | |
31 | #define SOF_SPEAKER_AMP_PRESENT BIT(5) | |
f70abd75 | 32 | #define SOF_RT5682_SSP_AMP_SHIFT 6 |
df936613 SP |
33 | #define SOF_RT5682_SSP_AMP_MASK (GENMASK(8, 6)) |
34 | #define SOF_RT5682_SSP_AMP(quirk) \ | |
35 | (((quirk) << SOF_RT5682_SSP_AMP_SHIFT) & SOF_RT5682_SSP_AMP_MASK) | |
1eed6bc0 | 36 | #define SOF_RT5682_MCLK_BYTCHT_EN BIT(9) |
f70abd75 B |
37 | |
38 | /* Default: MCLK on, MCLK 19.2M, SSP0 */ | |
39 | static unsigned long sof_rt5682_quirk = SOF_RT5682_MCLK_EN | | |
40 | SOF_RT5682_SSP_CODEC(0); | |
41 | ||
42 | static int is_legacy_cpu; | |
43 | ||
44 | static struct snd_soc_jack sof_hdmi[3]; | |
45 | ||
46 | struct sof_hdmi_pcm { | |
47 | struct list_head head; | |
48 | struct snd_soc_dai *codec_dai; | |
49 | int device; | |
50 | }; | |
51 | ||
52 | struct sof_card_private { | |
1eed6bc0 | 53 | struct clk *mclk; |
f70abd75 B |
54 | struct snd_soc_jack sof_headset; |
55 | struct list_head hdmi_pcm_list; | |
56 | }; | |
57 | ||
58 | static int sof_rt5682_quirk_cb(const struct dmi_system_id *id) | |
59 | { | |
60 | sof_rt5682_quirk = (unsigned long)id->driver_data; | |
61 | return 1; | |
62 | } | |
63 | ||
64 | static const struct dmi_system_id sof_rt5682_quirk_table[] = { | |
1eed6bc0 XZ |
65 | { |
66 | .callback = sof_rt5682_quirk_cb, | |
67 | .matches = { | |
68 | DMI_MATCH(DMI_SYS_VENDOR, "Circuitco"), | |
69 | DMI_MATCH(DMI_PRODUCT_NAME, "Minnowboard Max"), | |
70 | }, | |
71 | .driver_data = (void *)(SOF_RT5682_SSP_CODEC(2)), | |
72 | }, | |
73 | { | |
74 | .callback = sof_rt5682_quirk_cb, | |
75 | .matches = { | |
76 | DMI_MATCH(DMI_SYS_VENDOR, "AAEON"), | |
77 | DMI_MATCH(DMI_PRODUCT_NAME, "UP-CHT01"), | |
78 | }, | |
79 | .driver_data = (void *)(SOF_RT5682_SSP_CODEC(2)), | |
80 | }, | |
f70abd75 B |
81 | { |
82 | .callback = sof_rt5682_quirk_cb, | |
83 | .matches = { | |
84 | DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), | |
85 | DMI_MATCH(DMI_PRODUCT_NAME, "WhiskeyLake Client"), | |
86 | }, | |
87 | .driver_data = (void *)(SOF_RT5682_MCLK_EN | | |
88 | SOF_RT5682_MCLK_24MHZ | | |
89 | SOF_RT5682_SSP_CODEC(1)), | |
90 | }, | |
91 | { | |
92 | .callback = sof_rt5682_quirk_cb, | |
93 | .matches = { | |
1a3fb5d3 | 94 | DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Hatch"), |
f70abd75 B |
95 | }, |
96 | .driver_data = (void *)(SOF_RT5682_MCLK_EN | | |
97 | SOF_RT5682_MCLK_24MHZ | | |
98 | SOF_RT5682_SSP_CODEC(0) | | |
99 | SOF_SPEAKER_AMP_PRESENT | | |
100 | SOF_RT5682_SSP_AMP(1)), | |
101 | }, | |
102 | { | |
103 | .callback = sof_rt5682_quirk_cb, | |
104 | .matches = { | |
105 | DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), | |
106 | DMI_MATCH(DMI_PRODUCT_NAME, "Ice Lake Client"), | |
107 | }, | |
108 | .driver_data = (void *)(SOF_RT5682_MCLK_EN | | |
109 | SOF_RT5682_SSP_CODEC(0)), | |
110 | }, | |
111 | {} | |
112 | }; | |
113 | ||
114 | static int sof_hdmi_init(struct snd_soc_pcm_runtime *rtd) | |
115 | { | |
116 | struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card); | |
117 | struct snd_soc_dai *dai = rtd->codec_dai; | |
118 | struct sof_hdmi_pcm *pcm; | |
119 | ||
120 | pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); | |
121 | if (!pcm) | |
122 | return -ENOMEM; | |
123 | ||
124 | /* dai_link id is 1:1 mapped to the PCM device */ | |
125 | pcm->device = rtd->dai_link->id; | |
126 | pcm->codec_dai = dai; | |
127 | ||
128 | list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); | |
129 | ||
130 | return 0; | |
131 | } | |
132 | ||
133 | static int sof_rt5682_codec_init(struct snd_soc_pcm_runtime *rtd) | |
134 | { | |
135 | struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card); | |
136 | struct snd_soc_component *component = rtd->codec_dai->component; | |
137 | struct snd_soc_jack *jack; | |
138 | int ret; | |
139 | ||
140 | /* need to enable ASRC function for 24MHz mclk rate */ | |
141 | if ((sof_rt5682_quirk & SOF_RT5682_MCLK_EN) && | |
142 | (sof_rt5682_quirk & SOF_RT5682_MCLK_24MHZ)) { | |
143 | rt5682_sel_asrc_clk_src(component, RT5682_DA_STEREO1_FILTER | | |
144 | RT5682_AD_STEREO1_FILTER, | |
145 | RT5682_CLK_SEL_I2S1_ASRC); | |
146 | } | |
147 | ||
1eed6bc0 XZ |
148 | if (sof_rt5682_quirk & SOF_RT5682_MCLK_BYTCHT_EN) { |
149 | /* | |
150 | * The firmware might enable the clock at | |
151 | * boot (this information may or may not | |
152 | * be reflected in the enable clock register). | |
153 | * To change the rate we must disable the clock | |
154 | * first to cover these cases. Due to common | |
155 | * clock framework restrictions that do not allow | |
156 | * to disable a clock that has not been enabled, | |
157 | * we need to enable the clock first. | |
158 | */ | |
159 | ret = clk_prepare_enable(ctx->mclk); | |
160 | if (!ret) | |
161 | clk_disable_unprepare(ctx->mclk); | |
162 | ||
163 | ret = clk_set_rate(ctx->mclk, 19200000); | |
164 | ||
165 | if (ret) | |
166 | dev_err(rtd->dev, "unable to set MCLK rate\n"); | |
167 | } | |
168 | ||
f70abd75 B |
169 | /* |
170 | * Headset buttons map to the google Reference headset. | |
171 | * These can be configured by userspace. | |
172 | */ | |
173 | ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", | |
174 | SND_JACK_HEADSET | SND_JACK_BTN_0 | | |
175 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | | |
176 | SND_JACK_BTN_3, | |
177 | &ctx->sof_headset, NULL, 0); | |
178 | if (ret) { | |
179 | dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret); | |
180 | return ret; | |
181 | } | |
182 | ||
183 | jack = &ctx->sof_headset; | |
184 | ||
185 | snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); | |
d5952f34 SP |
186 | snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); |
187 | snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP); | |
188 | snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); | |
f70abd75 B |
189 | ret = snd_soc_component_set_jack(component, jack, NULL); |
190 | ||
191 | if (ret) { | |
192 | dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret); | |
193 | return ret; | |
194 | } | |
195 | ||
196 | return ret; | |
197 | }; | |
198 | ||
199 | static int sof_rt5682_hw_params(struct snd_pcm_substream *substream, | |
200 | struct snd_pcm_hw_params *params) | |
201 | { | |
202 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
1eed6bc0 | 203 | struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card); |
f70abd75 B |
204 | struct snd_soc_dai *codec_dai = rtd->codec_dai; |
205 | int clk_id, clk_freq, pll_out, ret; | |
206 | ||
207 | if (sof_rt5682_quirk & SOF_RT5682_MCLK_EN) { | |
1eed6bc0 XZ |
208 | if (sof_rt5682_quirk & SOF_RT5682_MCLK_BYTCHT_EN) { |
209 | ret = clk_prepare_enable(ctx->mclk); | |
210 | if (ret < 0) { | |
211 | dev_err(rtd->dev, | |
212 | "could not configure MCLK state"); | |
213 | return ret; | |
214 | } | |
215 | } | |
216 | ||
f70abd75 B |
217 | clk_id = RT5682_PLL1_S_MCLK; |
218 | if (sof_rt5682_quirk & SOF_RT5682_MCLK_24MHZ) | |
219 | clk_freq = 24000000; | |
220 | else | |
221 | clk_freq = 19200000; | |
222 | } else { | |
223 | clk_id = RT5682_PLL1_S_BCLK1; | |
224 | clk_freq = params_rate(params) * 50; | |
225 | } | |
226 | ||
227 | pll_out = params_rate(params) * 512; | |
228 | ||
229 | ret = snd_soc_dai_set_pll(codec_dai, 0, clk_id, clk_freq, pll_out); | |
230 | if (ret < 0) | |
231 | dev_err(rtd->dev, "snd_soc_dai_set_pll err = %d\n", ret); | |
232 | ||
233 | /* Configure sysclk for codec */ | |
234 | ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL1, | |
235 | pll_out, SND_SOC_CLOCK_IN); | |
236 | if (ret < 0) | |
237 | dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", ret); | |
238 | ||
239 | /* | |
240 | * slot_width should equal or large than data length, set them | |
241 | * be the same | |
242 | */ | |
243 | ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x0, 0x0, 2, | |
244 | params_width(params)); | |
245 | if (ret < 0) { | |
246 | dev_err(rtd->dev, "set TDM slot err:%d\n", ret); | |
247 | return ret; | |
248 | } | |
249 | ||
250 | return ret; | |
251 | } | |
252 | ||
253 | static struct snd_soc_ops sof_rt5682_ops = { | |
254 | .hw_params = sof_rt5682_hw_params, | |
255 | }; | |
256 | ||
257 | static struct snd_soc_dai_link_component platform_component[] = { | |
258 | { | |
259 | /* name might be overridden during probe */ | |
260 | .name = "0000:00:1f.3" | |
261 | } | |
262 | }; | |
263 | ||
264 | static int sof_card_late_probe(struct snd_soc_card *card) | |
265 | { | |
266 | struct sof_card_private *ctx = snd_soc_card_get_drvdata(card); | |
267 | struct snd_soc_component *component = NULL; | |
268 | char jack_name[NAME_SIZE]; | |
269 | struct sof_hdmi_pcm *pcm; | |
270 | int err = 0; | |
271 | int i = 0; | |
272 | ||
273 | /* HDMI is not supported by SOF on Baytrail/CherryTrail */ | |
274 | if (is_legacy_cpu) | |
275 | return 0; | |
276 | ||
277 | list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { | |
278 | component = pcm->codec_dai->component; | |
279 | snprintf(jack_name, sizeof(jack_name), | |
280 | "HDMI/DP, pcm=%d Jack", pcm->device); | |
281 | err = snd_soc_card_jack_new(card, jack_name, | |
282 | SND_JACK_AVOUT, &sof_hdmi[i], | |
283 | NULL, 0); | |
284 | ||
285 | if (err) | |
286 | return err; | |
287 | ||
288 | err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device, | |
289 | &sof_hdmi[i]); | |
290 | if (err < 0) | |
291 | return err; | |
292 | ||
293 | i++; | |
294 | } | |
295 | if (!component) | |
296 | return -EINVAL; | |
297 | ||
298 | return hdac_hdmi_jack_port_init(component, &card->dapm); | |
299 | } | |
300 | ||
301 | static const struct snd_kcontrol_new sof_controls[] = { | |
302 | SOC_DAPM_PIN_SWITCH("Headphone Jack"), | |
303 | SOC_DAPM_PIN_SWITCH("Headset Mic"), | |
304 | SOC_DAPM_PIN_SWITCH("Spk"), | |
305 | }; | |
306 | ||
307 | static const struct snd_soc_dapm_widget sof_widgets[] = { | |
308 | SND_SOC_DAPM_HP("Headphone Jack", NULL), | |
309 | SND_SOC_DAPM_MIC("Headset Mic", NULL), | |
310 | SND_SOC_DAPM_SPK("Spk", NULL), | |
4413adc4 BL |
311 | }; |
312 | ||
313 | static const struct snd_soc_dapm_widget dmic_widgets[] = { | |
8afd1a99 | 314 | SND_SOC_DAPM_MIC("SoC DMIC", NULL), |
f70abd75 B |
315 | }; |
316 | ||
317 | static const struct snd_soc_dapm_route sof_map[] = { | |
318 | /* HP jack connectors - unknown if we have jack detection */ | |
319 | { "Headphone Jack", NULL, "HPOL" }, | |
320 | { "Headphone Jack", NULL, "HPOR" }, | |
321 | ||
322 | /* other jacks */ | |
323 | { "IN1P", NULL, "Headset Mic" }, | |
f70abd75 B |
324 | }; |
325 | ||
326 | static const struct snd_soc_dapm_route speaker_map[] = { | |
327 | /* speaker */ | |
328 | { "Spk", NULL, "Speaker" }, | |
329 | }; | |
330 | ||
4413adc4 BL |
331 | static const struct snd_soc_dapm_route dmic_map[] = { |
332 | /* digital mics */ | |
333 | {"DMic", NULL, "SoC DMIC"}, | |
334 | }; | |
335 | ||
f70abd75 B |
336 | static int speaker_codec_init(struct snd_soc_pcm_runtime *rtd) |
337 | { | |
338 | struct snd_soc_card *card = rtd->card; | |
339 | int ret; | |
340 | ||
341 | ret = snd_soc_dapm_add_routes(&card->dapm, speaker_map, | |
342 | ARRAY_SIZE(speaker_map)); | |
343 | ||
344 | if (ret) | |
345 | dev_err(rtd->dev, "Speaker map addition failed: %d\n", ret); | |
346 | return ret; | |
347 | } | |
348 | ||
4413adc4 BL |
349 | static int dmic_init(struct snd_soc_pcm_runtime *rtd) |
350 | { | |
351 | struct snd_soc_card *card = rtd->card; | |
352 | int ret; | |
353 | ||
354 | ret = snd_soc_dapm_new_controls(&card->dapm, dmic_widgets, | |
355 | ARRAY_SIZE(dmic_widgets)); | |
356 | if (ret) { | |
357 | dev_err(card->dev, "DMic widget addition failed: %d\n", ret); | |
358 | /* Don't need to add routes if widget addition failed */ | |
359 | return ret; | |
360 | } | |
361 | ||
362 | ret = snd_soc_dapm_add_routes(&card->dapm, dmic_map, | |
363 | ARRAY_SIZE(dmic_map)); | |
364 | ||
365 | if (ret) | |
366 | dev_err(card->dev, "DMic map addition failed: %d\n", ret); | |
367 | ||
368 | return ret; | |
369 | } | |
370 | ||
f70abd75 B |
371 | /* sof audio machine driver for rt5682 codec */ |
372 | static struct snd_soc_card sof_audio_card_rt5682 = { | |
373 | .name = "sof_rt5682", | |
374 | .owner = THIS_MODULE, | |
375 | .controls = sof_controls, | |
376 | .num_controls = ARRAY_SIZE(sof_controls), | |
377 | .dapm_widgets = sof_widgets, | |
378 | .num_dapm_widgets = ARRAY_SIZE(sof_widgets), | |
379 | .dapm_routes = sof_map, | |
380 | .num_dapm_routes = ARRAY_SIZE(sof_map), | |
381 | .fully_routed = true, | |
382 | .late_probe = sof_card_late_probe, | |
383 | }; | |
384 | ||
f70abd75 B |
385 | static struct snd_soc_dai_link_component rt5682_component[] = { |
386 | { | |
387 | .name = "i2c-10EC5682:00", | |
388 | .dai_name = "rt5682-aif1", | |
389 | } | |
390 | }; | |
391 | ||
392 | static struct snd_soc_dai_link_component dmic_component[] = { | |
393 | { | |
394 | .name = "dmic-codec", | |
395 | .dai_name = "dmic-hifi", | |
396 | } | |
397 | }; | |
398 | ||
399 | static struct snd_soc_dai_link_component max98357a_component[] = { | |
400 | { | |
401 | .name = "MX98357A:00", | |
402 | .dai_name = "HiFi", | |
403 | } | |
404 | }; | |
405 | ||
406 | static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, | |
407 | int ssp_codec, | |
408 | int ssp_amp, | |
e711223a | 409 | int dmic_be_num, |
f70abd75 B |
410 | int hdmi_num) |
411 | { | |
412 | struct snd_soc_dai_link_component *idisp_components; | |
e1684073 | 413 | struct snd_soc_dai_link_component *cpus; |
f70abd75 B |
414 | struct snd_soc_dai_link *links; |
415 | int i, id = 0; | |
416 | ||
417 | links = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link) * | |
418 | sof_audio_card_rt5682.num_links, GFP_KERNEL); | |
e1684073 KM |
419 | cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component) * |
420 | sof_audio_card_rt5682.num_links, GFP_KERNEL); | |
421 | if (!links || !cpus) | |
f70abd75 B |
422 | goto devm_err; |
423 | ||
424 | /* codec SSP */ | |
425 | links[id].name = devm_kasprintf(dev, GFP_KERNEL, | |
426 | "SSP%d-Codec", ssp_codec); | |
427 | if (!links[id].name) | |
428 | goto devm_err; | |
429 | ||
430 | links[id].id = id; | |
431 | links[id].codecs = rt5682_component; | |
432 | links[id].num_codecs = ARRAY_SIZE(rt5682_component); | |
433 | links[id].platforms = platform_component; | |
434 | links[id].num_platforms = ARRAY_SIZE(platform_component); | |
435 | links[id].init = sof_rt5682_codec_init; | |
436 | links[id].ops = &sof_rt5682_ops; | |
437 | links[id].nonatomic = true; | |
438 | links[id].dpcm_playback = 1; | |
439 | links[id].dpcm_capture = 1; | |
440 | links[id].no_pcm = 1; | |
e1684073 KM |
441 | links[id].cpus = &cpus[id]; |
442 | links[id].num_cpus = 1; | |
f70abd75 | 443 | if (is_legacy_cpu) { |
e1684073 KM |
444 | links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, |
445 | "ssp%d-port", | |
446 | ssp_codec); | |
447 | if (!links[id].cpus->dai_name) | |
f70abd75 B |
448 | goto devm_err; |
449 | } else { | |
450 | /* | |
451 | * Currently, On SKL+ platforms MCLK will be turned off in sof | |
452 | * runtime suspended, and it will go into runtime suspended | |
453 | * right after playback is stop. However, rt5682 will output | |
454 | * static noise if sysclk turns off during playback. Set | |
455 | * ignore_pmdown_time to power down rt5682 immediately and | |
456 | * avoid the noise. | |
457 | * It can be removed once we can control MCLK by driver. | |
458 | */ | |
459 | links[id].ignore_pmdown_time = 1; | |
e1684073 KM |
460 | links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, |
461 | "SSP%d Pin", | |
462 | ssp_codec); | |
463 | if (!links[id].cpus->dai_name) | |
f70abd75 B |
464 | goto devm_err; |
465 | } | |
466 | id++; | |
467 | ||
468 | /* dmic */ | |
e711223a KJ |
469 | if (dmic_be_num > 0) { |
470 | /* at least we have dmic01 */ | |
471 | links[id].name = "dmic01"; | |
472 | links[id].cpus = &cpus[id]; | |
473 | links[id].cpus->dai_name = "DMIC01 Pin"; | |
4413adc4 | 474 | links[id].init = dmic_init; |
e711223a KJ |
475 | if (dmic_be_num > 1) { |
476 | /* set up 2 BE links at most */ | |
477 | links[id + 1].name = "dmic16k"; | |
478 | links[id + 1].cpus = &cpus[id + 1]; | |
479 | links[id + 1].cpus->dai_name = "DMIC16k Pin"; | |
480 | dmic_be_num = 2; | |
481 | } | |
482 | } | |
f70abd75 | 483 | |
e711223a | 484 | for (i = 0; i < dmic_be_num; i++) { |
f70abd75 | 485 | links[id].id = id; |
e1684073 | 486 | links[id].num_cpus = 1; |
f70abd75 B |
487 | links[id].codecs = dmic_component; |
488 | links[id].num_codecs = ARRAY_SIZE(dmic_component); | |
489 | links[id].platforms = platform_component; | |
490 | links[id].num_platforms = ARRAY_SIZE(platform_component); | |
491 | links[id].ignore_suspend = 1; | |
492 | links[id].dpcm_capture = 1; | |
493 | links[id].no_pcm = 1; | |
494 | id++; | |
495 | } | |
496 | ||
497 | /* HDMI */ | |
498 | if (hdmi_num > 0) { | |
499 | idisp_components = devm_kzalloc(dev, | |
500 | sizeof(struct snd_soc_dai_link_component) * | |
501 | hdmi_num, GFP_KERNEL); | |
502 | if (!idisp_components) | |
503 | goto devm_err; | |
504 | } | |
505 | for (i = 1; i <= hdmi_num; i++) { | |
506 | links[id].name = devm_kasprintf(dev, GFP_KERNEL, | |
507 | "iDisp%d", i); | |
508 | if (!links[id].name) | |
509 | goto devm_err; | |
510 | ||
511 | links[id].id = id; | |
e1684073 KM |
512 | links[id].cpus = &cpus[id]; |
513 | links[id].num_cpus = 1; | |
514 | links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, | |
515 | "iDisp%d Pin", i); | |
516 | if (!links[id].cpus->dai_name) | |
f70abd75 B |
517 | goto devm_err; |
518 | ||
519 | idisp_components[i - 1].name = "ehdaudio0D2"; | |
520 | idisp_components[i - 1].dai_name = devm_kasprintf(dev, | |
521 | GFP_KERNEL, | |
522 | "intel-hdmi-hifi%d", | |
523 | i); | |
524 | if (!idisp_components[i - 1].dai_name) | |
525 | goto devm_err; | |
526 | ||
527 | links[id].codecs = &idisp_components[i - 1]; | |
528 | links[id].num_codecs = 1; | |
529 | links[id].platforms = platform_component; | |
530 | links[id].num_platforms = ARRAY_SIZE(platform_component); | |
531 | links[id].init = sof_hdmi_init; | |
532 | links[id].dpcm_playback = 1; | |
533 | links[id].no_pcm = 1; | |
534 | id++; | |
535 | } | |
536 | ||
537 | /* speaker amp */ | |
538 | if (sof_rt5682_quirk & SOF_SPEAKER_AMP_PRESENT) { | |
539 | links[id].name = devm_kasprintf(dev, GFP_KERNEL, | |
540 | "SSP%d-Codec", ssp_amp); | |
541 | if (!links[id].name) | |
542 | goto devm_err; | |
543 | ||
544 | links[id].id = id; | |
545 | links[id].codecs = max98357a_component; | |
546 | links[id].num_codecs = ARRAY_SIZE(max98357a_component); | |
547 | links[id].platforms = platform_component; | |
548 | links[id].num_platforms = ARRAY_SIZE(platform_component); | |
549 | links[id].init = speaker_codec_init, | |
550 | links[id].nonatomic = true; | |
551 | links[id].dpcm_playback = 1; | |
552 | links[id].no_pcm = 1; | |
e1684073 KM |
553 | links[id].cpus = &cpus[id]; |
554 | links[id].num_cpus = 1; | |
f70abd75 | 555 | if (is_legacy_cpu) { |
e1684073 KM |
556 | links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, |
557 | "ssp%d-port", | |
558 | ssp_amp); | |
559 | if (!links[id].cpus->dai_name) | |
f70abd75 B |
560 | goto devm_err; |
561 | ||
562 | } else { | |
e1684073 KM |
563 | links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, |
564 | "SSP%d Pin", | |
565 | ssp_amp); | |
566 | if (!links[id].cpus->dai_name) | |
f70abd75 B |
567 | goto devm_err; |
568 | } | |
569 | } | |
570 | ||
571 | return links; | |
572 | devm_err: | |
573 | return NULL; | |
574 | } | |
575 | ||
576 | static int sof_audio_probe(struct platform_device *pdev) | |
577 | { | |
578 | struct snd_soc_dai_link *dai_links; | |
579 | struct snd_soc_acpi_mach *mach; | |
580 | struct sof_card_private *ctx; | |
e711223a | 581 | int dmic_be_num, hdmi_num; |
f70abd75 B |
582 | int ret, ssp_amp, ssp_codec; |
583 | ||
281c443f | 584 | ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); |
f70abd75 B |
585 | if (!ctx) |
586 | return -ENOMEM; | |
587 | ||
536cfd2f | 588 | if (soc_intel_is_byt() || soc_intel_is_cht()) { |
f70abd75 | 589 | is_legacy_cpu = 1; |
e711223a | 590 | dmic_be_num = 0; |
f70abd75 B |
591 | hdmi_num = 0; |
592 | /* default quirk for legacy cpu */ | |
1eed6bc0 XZ |
593 | sof_rt5682_quirk = SOF_RT5682_MCLK_EN | |
594 | SOF_RT5682_MCLK_BYTCHT_EN | | |
595 | SOF_RT5682_SSP_CODEC(2); | |
f70abd75 | 596 | } else { |
e711223a | 597 | dmic_be_num = 2; |
f70abd75 B |
598 | hdmi_num = 3; |
599 | } | |
600 | ||
601 | dmi_check_system(sof_rt5682_quirk_table); | |
602 | ||
1eed6bc0 XZ |
603 | /* need to get main clock from pmc */ |
604 | if (sof_rt5682_quirk & SOF_RT5682_MCLK_BYTCHT_EN) { | |
605 | ctx->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3"); | |
e5f0d490 CY |
606 | if (IS_ERR(ctx->mclk)) { |
607 | ret = PTR_ERR(ctx->mclk); | |
608 | ||
609 | dev_err(&pdev->dev, | |
610 | "Failed to get MCLK from pmc_plt_clk_3: %d\n", | |
611 | ret); | |
612 | return ret; | |
613 | } | |
614 | ||
1eed6bc0 XZ |
615 | ret = clk_prepare_enable(ctx->mclk); |
616 | if (ret < 0) { | |
617 | dev_err(&pdev->dev, | |
618 | "could not configure MCLK state"); | |
619 | return ret; | |
620 | } | |
621 | } | |
622 | ||
f70abd75 B |
623 | dev_dbg(&pdev->dev, "sof_rt5682_quirk = %lx\n", sof_rt5682_quirk); |
624 | ||
625 | ssp_amp = (sof_rt5682_quirk & SOF_RT5682_SSP_AMP_MASK) >> | |
626 | SOF_RT5682_SSP_AMP_SHIFT; | |
627 | ||
628 | ssp_codec = sof_rt5682_quirk & SOF_RT5682_SSP_CODEC_MASK; | |
629 | ||
630 | /* compute number of dai links */ | |
e711223a | 631 | sof_audio_card_rt5682.num_links = 1 + dmic_be_num + hdmi_num; |
b385256e | 632 | |
f70abd75 B |
633 | if (sof_rt5682_quirk & SOF_SPEAKER_AMP_PRESENT) |
634 | sof_audio_card_rt5682.num_links++; | |
635 | ||
636 | dai_links = sof_card_dai_links_create(&pdev->dev, ssp_codec, ssp_amp, | |
e711223a | 637 | dmic_be_num, hdmi_num); |
f70abd75 B |
638 | if (!dai_links) |
639 | return -ENOMEM; | |
640 | ||
641 | sof_audio_card_rt5682.dai_link = dai_links; | |
642 | ||
643 | INIT_LIST_HEAD(&ctx->hdmi_pcm_list); | |
644 | ||
645 | sof_audio_card_rt5682.dev = &pdev->dev; | |
646 | mach = (&pdev->dev)->platform_data; | |
647 | ||
648 | /* set platform name for each dailink */ | |
649 | ret = snd_soc_fixup_dai_links_platform_name(&sof_audio_card_rt5682, | |
650 | mach->mach_params.platform); | |
651 | if (ret) | |
652 | return ret; | |
653 | ||
654 | snd_soc_card_set_drvdata(&sof_audio_card_rt5682, ctx); | |
655 | ||
656 | return devm_snd_soc_register_card(&pdev->dev, | |
657 | &sof_audio_card_rt5682); | |
658 | } | |
659 | ||
6ba5041c JU |
660 | static int sof_rt5682_remove(struct platform_device *pdev) |
661 | { | |
662 | struct snd_soc_card *card = platform_get_drvdata(pdev); | |
663 | struct snd_soc_component *component = NULL; | |
664 | ||
665 | for_each_card_components(card, component) { | |
666 | if (!strcmp(component->name, rt5682_component[0].name)) { | |
667 | snd_soc_component_set_jack(component, NULL, NULL); | |
668 | break; | |
669 | } | |
670 | } | |
671 | ||
672 | return 0; | |
673 | } | |
674 | ||
f70abd75 B |
675 | static struct platform_driver sof_audio = { |
676 | .probe = sof_audio_probe, | |
6ba5041c | 677 | .remove = sof_rt5682_remove, |
f70abd75 B |
678 | .driver = { |
679 | .name = "sof_rt5682", | |
680 | .pm = &snd_soc_pm_ops, | |
681 | }, | |
682 | }; | |
683 | module_platform_driver(sof_audio) | |
684 | ||
685 | /* Module information */ | |
686 | MODULE_DESCRIPTION("SOF Audio Machine driver"); | |
687 | MODULE_AUTHOR("Bard Liao <bard.liao@intel.com>"); | |
688 | MODULE_AUTHOR("Sathya Prakash M R <sathya.prakash.m.r@intel.com>"); | |
689 | MODULE_LICENSE("GPL v2"); | |
690 | MODULE_ALIAS("platform:sof_rt5682"); |