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