]> asedeno.scripts.mit.edu Git - linux.git/blob - sound/soc/intel/boards/sof_rt5682.c
cifs: fix GlobalMid_Lock bug in cifs_reconnect
[linux.git] / sound / soc / intel / boards / sof_rt5682.c
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>
12 #include <linux/dmi.h>
13 #include <asm/cpu_device_id.h>
14 #include <asm/intel-family.h>
15 #include <sound/core.h>
16 #include <sound/jack.h>
17 #include <sound/pcm.h>
18 #include <sound/pcm_params.h>
19 #include <sound/soc.h>
20 #include <sound/rt5682.h>
21 #include <sound/soc-acpi.h>
22 #include "../../codecs/rt5682.h"
23 #include "../../codecs/hdac_hdmi.h"
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)
32 #define SOF_RT5682_SSP_AMP(quirk)               ((quirk) & GENMASK(8, 6))
33 #define SOF_RT5682_SSP_AMP_MASK                 (GENMASK(8, 6))
34 #define SOF_RT5682_SSP_AMP_SHIFT                6
35
36 /* Default: MCLK on, MCLK 19.2M, SSP0  */
37 static unsigned long sof_rt5682_quirk = SOF_RT5682_MCLK_EN |
38                                         SOF_RT5682_SSP_CODEC(0);
39
40 static int is_legacy_cpu;
41
42 static struct snd_soc_jack sof_hdmi[3];
43
44 struct sof_hdmi_pcm {
45         struct list_head head;
46         struct snd_soc_dai *codec_dai;
47         int device;
48 };
49
50 struct sof_card_private {
51         struct snd_soc_jack sof_headset;
52         struct list_head hdmi_pcm_list;
53 };
54
55 static int sof_rt5682_quirk_cb(const struct dmi_system_id *id)
56 {
57         sof_rt5682_quirk = (unsigned long)id->driver_data;
58         return 1;
59 }
60
61 static const struct dmi_system_id sof_rt5682_quirk_table[] = {
62         {
63                 .callback = sof_rt5682_quirk_cb,
64                 .matches = {
65                         DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
66                         DMI_MATCH(DMI_PRODUCT_NAME, "WhiskeyLake Client"),
67                 },
68                 .driver_data = (void *)(SOF_RT5682_MCLK_EN |
69                                         SOF_RT5682_MCLK_24MHZ |
70                                         SOF_RT5682_SSP_CODEC(1)),
71         },
72         {
73                 .callback = sof_rt5682_quirk_cb,
74                 .matches = {
75                         DMI_MATCH(DMI_SYS_VENDOR, "Google"),
76                         DMI_MATCH(DMI_PRODUCT_NAME, "Hatch"),
77                 },
78                 .driver_data = (void *)(SOF_RT5682_MCLK_EN |
79                                         SOF_RT5682_MCLK_24MHZ |
80                                         SOF_RT5682_SSP_CODEC(0) |
81                                         SOF_SPEAKER_AMP_PRESENT |
82                                         SOF_RT5682_SSP_AMP(1)),
83         },
84         {
85                 .callback = sof_rt5682_quirk_cb,
86                 .matches = {
87                         DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
88                         DMI_MATCH(DMI_PRODUCT_NAME, "Ice Lake Client"),
89                 },
90                 .driver_data = (void *)(SOF_RT5682_MCLK_EN |
91                                         SOF_RT5682_SSP_CODEC(0)),
92         },
93         {}
94 };
95
96 static int sof_hdmi_init(struct snd_soc_pcm_runtime *rtd)
97 {
98         struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card);
99         struct snd_soc_dai *dai = rtd->codec_dai;
100         struct sof_hdmi_pcm *pcm;
101
102         pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
103         if (!pcm)
104                 return -ENOMEM;
105
106         /* dai_link id is 1:1 mapped to the PCM device */
107         pcm->device = rtd->dai_link->id;
108         pcm->codec_dai = dai;
109
110         list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
111
112         return 0;
113 }
114
115 static int sof_rt5682_codec_init(struct snd_soc_pcm_runtime *rtd)
116 {
117         struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card);
118         struct snd_soc_component *component = rtd->codec_dai->component;
119         struct snd_soc_jack *jack;
120         int ret;
121
122         /* need to enable ASRC function for 24MHz mclk rate */
123         if ((sof_rt5682_quirk & SOF_RT5682_MCLK_EN) &&
124             (sof_rt5682_quirk & SOF_RT5682_MCLK_24MHZ)) {
125                 rt5682_sel_asrc_clk_src(component, RT5682_DA_STEREO1_FILTER |
126                                         RT5682_AD_STEREO1_FILTER,
127                                         RT5682_CLK_SEL_I2S1_ASRC);
128         }
129
130         /*
131          * Headset buttons map to the google Reference headset.
132          * These can be configured by userspace.
133          */
134         ret = snd_soc_card_jack_new(rtd->card, "Headset Jack",
135                                     SND_JACK_HEADSET | SND_JACK_BTN_0 |
136                                     SND_JACK_BTN_1 | SND_JACK_BTN_2 |
137                                     SND_JACK_BTN_3,
138                                     &ctx->sof_headset, NULL, 0);
139         if (ret) {
140                 dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
141                 return ret;
142         }
143
144         jack = &ctx->sof_headset;
145
146         snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
147         snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOLUMEUP);
148         snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN);
149         snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOICECOMMAND);
150         ret = snd_soc_component_set_jack(component, jack, NULL);
151
152         if (ret) {
153                 dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret);
154                 return ret;
155         }
156
157         return ret;
158 };
159
160 static int sof_rt5682_hw_params(struct snd_pcm_substream *substream,
161                                 struct snd_pcm_hw_params *params)
162 {
163         struct snd_soc_pcm_runtime *rtd = substream->private_data;
164         struct snd_soc_dai *codec_dai = rtd->codec_dai;
165         int clk_id, clk_freq, pll_out, ret;
166
167         if (sof_rt5682_quirk & SOF_RT5682_MCLK_EN) {
168                 clk_id = RT5682_PLL1_S_MCLK;
169                 if (sof_rt5682_quirk & SOF_RT5682_MCLK_24MHZ)
170                         clk_freq = 24000000;
171                 else
172                         clk_freq = 19200000;
173         } else {
174                 clk_id = RT5682_PLL1_S_BCLK1;
175                 clk_freq = params_rate(params) * 50;
176         }
177
178         pll_out = params_rate(params) * 512;
179
180         ret = snd_soc_dai_set_pll(codec_dai, 0, clk_id, clk_freq, pll_out);
181         if (ret < 0)
182                 dev_err(rtd->dev, "snd_soc_dai_set_pll err = %d\n", ret);
183
184         /* Configure sysclk for codec */
185         ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL1,
186                                      pll_out, SND_SOC_CLOCK_IN);
187         if (ret < 0)
188                 dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", ret);
189
190         /*
191          * slot_width should equal or large than data length, set them
192          * be the same
193          */
194         ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x0, 0x0, 2,
195                                        params_width(params));
196         if (ret < 0) {
197                 dev_err(rtd->dev, "set TDM slot err:%d\n", ret);
198                 return ret;
199         }
200
201         return ret;
202 }
203
204 static struct snd_soc_ops sof_rt5682_ops = {
205         .hw_params = sof_rt5682_hw_params,
206 };
207
208 static struct snd_soc_dai_link_component platform_component[] = {
209         {
210                 /* name might be overridden during probe */
211                 .name = "0000:00:1f.3"
212         }
213 };
214
215 static int sof_card_late_probe(struct snd_soc_card *card)
216 {
217         struct sof_card_private *ctx = snd_soc_card_get_drvdata(card);
218         struct snd_soc_component *component = NULL;
219         char jack_name[NAME_SIZE];
220         struct sof_hdmi_pcm *pcm;
221         int err = 0;
222         int i = 0;
223
224         /* HDMI is not supported by SOF on Baytrail/CherryTrail */
225         if (is_legacy_cpu)
226                 return 0;
227
228         list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) {
229                 component = pcm->codec_dai->component;
230                 snprintf(jack_name, sizeof(jack_name),
231                          "HDMI/DP, pcm=%d Jack", pcm->device);
232                 err = snd_soc_card_jack_new(card, jack_name,
233                                             SND_JACK_AVOUT, &sof_hdmi[i],
234                                             NULL, 0);
235
236                 if (err)
237                         return err;
238
239                 err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device,
240                                           &sof_hdmi[i]);
241                 if (err < 0)
242                         return err;
243
244                 i++;
245         }
246         if (!component)
247                 return -EINVAL;
248
249         return hdac_hdmi_jack_port_init(component, &card->dapm);
250 }
251
252 static const struct snd_kcontrol_new sof_controls[] = {
253         SOC_DAPM_PIN_SWITCH("Headphone Jack"),
254         SOC_DAPM_PIN_SWITCH("Headset Mic"),
255         SOC_DAPM_PIN_SWITCH("Spk"),
256 };
257
258 static const struct snd_soc_dapm_widget sof_widgets[] = {
259         SND_SOC_DAPM_HP("Headphone Jack", NULL),
260         SND_SOC_DAPM_MIC("Headset Mic", NULL),
261         SND_SOC_DAPM_SPK("Spk", NULL),
262 };
263
264 static const struct snd_soc_dapm_route sof_map[] = {
265         /* HP jack connectors - unknown if we have jack detection */
266         { "Headphone Jack", NULL, "HPOL" },
267         { "Headphone Jack", NULL, "HPOR" },
268
269         /* other jacks */
270         { "IN1P", NULL, "Headset Mic" },
271
272 };
273
274 static const struct snd_soc_dapm_route speaker_map[] = {
275         /* speaker */
276         { "Spk", NULL, "Speaker" },
277 };
278
279 static int speaker_codec_init(struct snd_soc_pcm_runtime *rtd)
280 {
281         struct snd_soc_card *card = rtd->card;
282         int ret;
283
284         ret = snd_soc_dapm_add_routes(&card->dapm, speaker_map,
285                                       ARRAY_SIZE(speaker_map));
286
287         if (ret)
288                 dev_err(rtd->dev, "Speaker map addition failed: %d\n", ret);
289         return ret;
290 }
291
292 /* sof audio machine driver for rt5682 codec */
293 static struct snd_soc_card sof_audio_card_rt5682 = {
294         .name = "sof_rt5682",
295         .owner = THIS_MODULE,
296         .controls = sof_controls,
297         .num_controls = ARRAY_SIZE(sof_controls),
298         .dapm_widgets = sof_widgets,
299         .num_dapm_widgets = ARRAY_SIZE(sof_widgets),
300         .dapm_routes = sof_map,
301         .num_dapm_routes = ARRAY_SIZE(sof_map),
302         .fully_routed = true,
303         .late_probe = sof_card_late_probe,
304 };
305
306 static const struct x86_cpu_id legacy_cpi_ids[] = {
307         { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_SILVERMONT }, /* Baytrail */
308         { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_AIRMONT }, /* Cherrytrail */
309         {}
310 };
311
312 static struct snd_soc_dai_link_component rt5682_component[] = {
313         {
314                 .name = "i2c-10EC5682:00",
315                 .dai_name = "rt5682-aif1",
316         }
317 };
318
319 static struct snd_soc_dai_link_component dmic_component[] = {
320         {
321                 .name = "dmic-codec",
322                 .dai_name = "dmic-hifi",
323         }
324 };
325
326 static struct snd_soc_dai_link_component max98357a_component[] = {
327         {
328                 .name = "MX98357A:00",
329                 .dai_name = "HiFi",
330         }
331 };
332
333 static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
334                                                           int ssp_codec,
335                                                           int ssp_amp,
336                                                           int dmic_num,
337                                                           int hdmi_num)
338 {
339         struct snd_soc_dai_link_component *idisp_components;
340         struct snd_soc_dai_link *links;
341         int i, id = 0;
342
343         links = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link) *
344                              sof_audio_card_rt5682.num_links, GFP_KERNEL);
345         if (!links)
346                 goto devm_err;
347
348         /* codec SSP */
349         links[id].name = devm_kasprintf(dev, GFP_KERNEL,
350                                         "SSP%d-Codec", ssp_codec);
351         if (!links[id].name)
352                 goto devm_err;
353
354         links[id].id = id;
355         links[id].codecs = rt5682_component;
356         links[id].num_codecs = ARRAY_SIZE(rt5682_component);
357         links[id].platforms = platform_component;
358         links[id].num_platforms = ARRAY_SIZE(platform_component);
359         links[id].init = sof_rt5682_codec_init;
360         links[id].ops = &sof_rt5682_ops;
361         links[id].nonatomic = true;
362         links[id].dpcm_playback = 1;
363         links[id].dpcm_capture = 1;
364         links[id].no_pcm = 1;
365         if (is_legacy_cpu) {
366                 links[id].cpu_dai_name = devm_kasprintf(dev, GFP_KERNEL,
367                                                         "ssp%d-port",
368                                                         ssp_codec);
369                 if (!links[id].cpu_dai_name)
370                         goto devm_err;
371         } else {
372                 /*
373                  * Currently, On SKL+ platforms MCLK will be turned off in sof
374                  * runtime suspended, and it will go into runtime suspended
375                  * right after playback is stop. However, rt5682 will output
376                  * static noise if sysclk turns off during playback. Set
377                  * ignore_pmdown_time to power down rt5682 immediately and
378                  * avoid the noise.
379                  * It can be removed once we can control MCLK by driver.
380                  */
381                 links[id].ignore_pmdown_time = 1;
382                 links[id].cpu_dai_name = devm_kasprintf(dev, GFP_KERNEL,
383                                                         "SSP%d Pin",
384                                                         ssp_codec);
385                 if (!links[id].cpu_dai_name)
386                         goto devm_err;
387         }
388         id++;
389
390         /* dmic */
391         for (i = 1; i <= dmic_num; i++) {
392                 links[id].name = devm_kasprintf(dev, GFP_KERNEL,
393                                                 "dmic%02d", i);
394                 if (!links[id].name)
395                         goto devm_err;
396
397                 links[id].id = id;
398                 links[id].cpu_dai_name = devm_kasprintf(dev, GFP_KERNEL,
399                                                         "DMIC%02d Pin", i);
400                 if (!links[id].cpu_dai_name)
401                         goto devm_err;
402
403                 links[id].codecs = dmic_component;
404                 links[id].num_codecs = ARRAY_SIZE(dmic_component);
405                 links[id].platforms = platform_component;
406                 links[id].num_platforms = ARRAY_SIZE(platform_component);
407                 links[id].ignore_suspend = 1;
408                 links[id].dpcm_capture = 1;
409                 links[id].no_pcm = 1;
410                 id++;
411         }
412
413         /* HDMI */
414         if (hdmi_num > 0) {
415                 idisp_components = devm_kzalloc(dev,
416                                    sizeof(struct snd_soc_dai_link_component) *
417                                    hdmi_num, GFP_KERNEL);
418                 if (!idisp_components)
419                         goto devm_err;
420         }
421         for (i = 1; i <= hdmi_num; i++) {
422                 links[id].name = devm_kasprintf(dev, GFP_KERNEL,
423                                                 "iDisp%d", i);
424                 if (!links[id].name)
425                         goto devm_err;
426
427                 links[id].id = id;
428                 links[id].cpu_dai_name = devm_kasprintf(dev, GFP_KERNEL,
429                                                         "iDisp%d Pin", i);
430                 if (!links[id].cpu_dai_name)
431                         goto devm_err;
432
433                 idisp_components[i - 1].name = "ehdaudio0D2";
434                 idisp_components[i - 1].dai_name = devm_kasprintf(dev,
435                                                                   GFP_KERNEL,
436                                                                   "intel-hdmi-hifi%d",
437                                                                   i);
438                 if (!idisp_components[i - 1].dai_name)
439                         goto devm_err;
440
441                 links[id].codecs = &idisp_components[i - 1];
442                 links[id].num_codecs = 1;
443                 links[id].platforms = platform_component;
444                 links[id].num_platforms = ARRAY_SIZE(platform_component);
445                 links[id].init = sof_hdmi_init;
446                 links[id].dpcm_playback = 1;
447                 links[id].no_pcm = 1;
448                 id++;
449         }
450
451         /* speaker amp */
452         if (sof_rt5682_quirk & SOF_SPEAKER_AMP_PRESENT) {
453                 links[id].name = devm_kasprintf(dev, GFP_KERNEL,
454                                                 "SSP%d-Codec", ssp_amp);
455                 if (!links[id].name)
456                         goto devm_err;
457
458                 links[id].id = id;
459                 links[id].codecs = max98357a_component;
460                 links[id].num_codecs = ARRAY_SIZE(max98357a_component);
461                 links[id].platforms = platform_component;
462                 links[id].num_platforms = ARRAY_SIZE(platform_component);
463                 links[id].init = speaker_codec_init,
464                 links[id].nonatomic = true;
465                 links[id].dpcm_playback = 1;
466                 links[id].no_pcm = 1;
467                 if (is_legacy_cpu) {
468                         links[id].cpu_dai_name = devm_kasprintf(dev, GFP_KERNEL,
469                                                                 "ssp%d-port",
470                                                                 ssp_amp);
471                         if (!links[id].cpu_dai_name)
472                                 goto devm_err;
473
474                 } else {
475                         links[id].cpu_dai_name = devm_kasprintf(dev, GFP_KERNEL,
476                                                                 "SSP%d Pin",
477                                                                 ssp_amp);
478                         if (!links[id].cpu_dai_name)
479                                 goto devm_err;
480                 }
481         }
482
483         return links;
484 devm_err:
485         return NULL;
486 }
487
488 static int sof_audio_probe(struct platform_device *pdev)
489 {
490         struct snd_soc_dai_link *dai_links;
491         struct snd_soc_acpi_mach *mach;
492         struct sof_card_private *ctx;
493         int dmic_num, hdmi_num;
494         int ret, ssp_amp, ssp_codec;
495
496         ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC);
497         if (!ctx)
498                 return -ENOMEM;
499
500         if (x86_match_cpu(legacy_cpi_ids)) {
501                 is_legacy_cpu = 1;
502                 dmic_num = 0;
503                 hdmi_num = 0;
504                 /* default quirk for legacy cpu */
505                 sof_rt5682_quirk = SOF_RT5682_SSP_CODEC(2);
506         } else {
507                 dmic_num = 1;
508                 hdmi_num = 3;
509         }
510
511         dmi_check_system(sof_rt5682_quirk_table);
512
513         dev_dbg(&pdev->dev, "sof_rt5682_quirk = %lx\n", sof_rt5682_quirk);
514
515         ssp_amp = (sof_rt5682_quirk & SOF_RT5682_SSP_AMP_MASK) >>
516                         SOF_RT5682_SSP_AMP_SHIFT;
517
518         ssp_codec = sof_rt5682_quirk & SOF_RT5682_SSP_CODEC_MASK;
519
520         /* compute number of dai links */
521         sof_audio_card_rt5682.num_links = 1 + dmic_num + hdmi_num;
522         if (sof_rt5682_quirk & SOF_SPEAKER_AMP_PRESENT)
523                 sof_audio_card_rt5682.num_links++;
524
525         dai_links = sof_card_dai_links_create(&pdev->dev, ssp_codec, ssp_amp,
526                                               dmic_num, hdmi_num);
527         if (!dai_links)
528                 return -ENOMEM;
529
530         sof_audio_card_rt5682.dai_link = dai_links;
531
532         INIT_LIST_HEAD(&ctx->hdmi_pcm_list);
533
534         sof_audio_card_rt5682.dev = &pdev->dev;
535         mach = (&pdev->dev)->platform_data;
536
537         /* set platform name for each dailink */
538         ret = snd_soc_fixup_dai_links_platform_name(&sof_audio_card_rt5682,
539                                                     mach->mach_params.platform);
540         if (ret)
541                 return ret;
542
543         snd_soc_card_set_drvdata(&sof_audio_card_rt5682, ctx);
544
545         return devm_snd_soc_register_card(&pdev->dev,
546                                           &sof_audio_card_rt5682);
547 }
548
549 static struct platform_driver sof_audio = {
550         .probe = sof_audio_probe,
551         .driver = {
552                 .name = "sof_rt5682",
553                 .pm = &snd_soc_pm_ops,
554         },
555 };
556 module_platform_driver(sof_audio)
557
558 /* Module information */
559 MODULE_DESCRIPTION("SOF Audio Machine driver");
560 MODULE_AUTHOR("Bard Liao <bard.liao@intel.com>");
561 MODULE_AUTHOR("Sathya Prakash M R <sathya.prakash.m.r@intel.com>");
562 MODULE_LICENSE("GPL v2");
563 MODULE_ALIAS("platform:sof_rt5682");