1 // SPDX-License-Identifier: GPL-2.0-only
2 // Copyright(c) 2021 Intel Corporation.
3
4 /*
5 * Intel SOF Machine Driver with Cirrus Logic CS42L42 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/regulator/consumer.h>
13 #include <linux/dmi.h>
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/sof.h>
20 #include <sound/soc-acpi.h>
21 #include <dt-bindings/sound/cs42l42.h>
22 #include "../common/soc-intel-quirks.h"
23 #include "sof_board_helpers.h"
24 #include "sof_maxim_common.h"
25 #include "sof_ssp_common.h"
26
27 #define SOF_CS42L42_SSP_CODEC(quirk) ((quirk) & GENMASK(2, 0))
28 #define SOF_CS42L42_SSP_CODEC_MASK (GENMASK(2, 0))
29 #define SOF_CS42L42_SSP_AMP_SHIFT 4
30 #define SOF_CS42L42_SSP_AMP_MASK (GENMASK(6, 4))
31 #define SOF_CS42L42_SSP_AMP(quirk) \
32 (((quirk) << SOF_CS42L42_SSP_AMP_SHIFT) & SOF_CS42L42_SSP_AMP_MASK)
33 #define SOF_CS42L42_NUM_HDMIDEV_SHIFT 7
34 #define SOF_CS42L42_NUM_HDMIDEV_MASK (GENMASK(9, 7))
35 #define SOF_CS42L42_NUM_HDMIDEV(quirk) \
36 (((quirk) << SOF_CS42L42_NUM_HDMIDEV_SHIFT) & SOF_CS42L42_NUM_HDMIDEV_MASK)
37 #define SOF_CS42L42_DAILINK_SHIFT 10
38 #define SOF_CS42L42_DAILINK_MASK (GENMASK(24, 10))
39 #define SOF_CS42L42_DAILINK(link1, link2, link3, link4, link5) \
40 ((((link1) | ((link2) << 3) | ((link3) << 6) | ((link4) << 9) | ((link5) << 12)) << SOF_CS42L42_DAILINK_SHIFT) & SOF_CS42L42_DAILINK_MASK)
41 #define SOF_BT_OFFLOAD_PRESENT BIT(25)
42 #define SOF_CS42L42_SSP_BT_SHIFT 26
43 #define SOF_CS42L42_SSP_BT_MASK (GENMASK(28, 26))
44 #define SOF_CS42L42_SSP_BT(quirk) \
45 (((quirk) << SOF_CS42L42_SSP_BT_SHIFT) & SOF_CS42L42_SSP_BT_MASK)
46
47 enum {
48 LINK_NONE = 0,
49 LINK_HP = 1,
50 LINK_SPK = 2,
51 LINK_DMIC = 3,
52 LINK_HDMI = 4,
53 LINK_BT = 5,
54 };
55
56 static struct snd_soc_jack_pin jack_pins[] = {
57 {
58 .pin = "Headphone Jack",
59 .mask = SND_JACK_HEADPHONE,
60 },
61 {
62 .pin = "Headset Mic",
63 .mask = SND_JACK_MICROPHONE,
64 },
65 };
66
67 /* Default: SSP2 */
68 static unsigned long sof_cs42l42_quirk = SOF_CS42L42_SSP_CODEC(2);
69
sof_cs42l42_init(struct snd_soc_pcm_runtime * rtd)70 static int sof_cs42l42_init(struct snd_soc_pcm_runtime *rtd)
71 {
72 struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card);
73 struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
74 struct snd_soc_jack *jack = &ctx->headset_jack;
75 int ret;
76
77 /*
78 * Headset buttons map to the google Reference headset.
79 * These can be configured by userspace.
80 */
81 ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
82 SND_JACK_HEADSET | SND_JACK_BTN_0 |
83 SND_JACK_BTN_1 | SND_JACK_BTN_2 |
84 SND_JACK_BTN_3,
85 jack,
86 jack_pins,
87 ARRAY_SIZE(jack_pins));
88 if (ret) {
89 dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
90 return ret;
91 }
92
93 snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
94 snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOLUMEUP);
95 snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN);
96 snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOICECOMMAND);
97
98 ret = snd_soc_component_set_jack(component, jack, NULL);
99 if (ret) {
100 dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret);
101 return ret;
102 }
103
104 return ret;
105 };
106
sof_cs42l42_exit(struct snd_soc_pcm_runtime * rtd)107 static void sof_cs42l42_exit(struct snd_soc_pcm_runtime *rtd)
108 {
109 struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
110
111 snd_soc_component_set_jack(component, NULL, NULL);
112 }
113
sof_cs42l42_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)114 static int sof_cs42l42_hw_params(struct snd_pcm_substream *substream,
115 struct snd_pcm_hw_params *params)
116 {
117 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
118 struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
119 int clk_freq, ret;
120
121 clk_freq = sof_dai_get_bclk(rtd); /* BCLK freq */
122
123 if (clk_freq <= 0) {
124 dev_err(rtd->dev, "get bclk freq failed: %d\n", clk_freq);
125 return -EINVAL;
126 }
127
128 /* Configure sysclk for codec */
129 ret = snd_soc_dai_set_sysclk(codec_dai, 0,
130 clk_freq, SND_SOC_CLOCK_IN);
131 if (ret < 0)
132 dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", ret);
133
134 return ret;
135 }
136
137 static const struct snd_soc_ops sof_cs42l42_ops = {
138 .hw_params = sof_cs42l42_hw_params,
139 };
140
sof_card_late_probe(struct snd_soc_card * card)141 static int sof_card_late_probe(struct snd_soc_card *card)
142 {
143 return sof_intel_board_card_late_probe(card);
144 }
145
146 static const struct snd_kcontrol_new sof_controls[] = {
147 SOC_DAPM_PIN_SWITCH("Headphone Jack"),
148 SOC_DAPM_PIN_SWITCH("Headset Mic"),
149 };
150
151 static const struct snd_soc_dapm_widget sof_widgets[] = {
152 SND_SOC_DAPM_HP("Headphone Jack", NULL),
153 SND_SOC_DAPM_MIC("Headset Mic", NULL),
154 };
155
156 static const struct snd_soc_dapm_route sof_map[] = {
157 /* HP jack connectors - unknown if we have jack detection */
158 {"Headphone Jack", NULL, "HP"},
159
160 /* other jacks */
161 {"HS", NULL, "Headset Mic"},
162 };
163
164 /* sof audio machine driver for cs42l42 codec */
165 static struct snd_soc_card sof_audio_card_cs42l42 = {
166 .name = "cs42l42", /* the sof- prefix is added by the core */
167 .owner = THIS_MODULE,
168 .controls = sof_controls,
169 .num_controls = ARRAY_SIZE(sof_controls),
170 .dapm_widgets = sof_widgets,
171 .num_dapm_widgets = ARRAY_SIZE(sof_widgets),
172 .dapm_routes = sof_map,
173 .num_dapm_routes = ARRAY_SIZE(sof_map),
174 .fully_routed = true,
175 .late_probe = sof_card_late_probe,
176 };
177
178 static struct snd_soc_dai_link_component cs42l42_component[] = {
179 {
180 .name = "i2c-10134242:00",
181 .dai_name = "cs42l42",
182 }
183 };
184
185 static struct snd_soc_dai_link *
sof_card_dai_links_create(struct device * dev,enum sof_ssp_codec amp_type,int ssp_codec,int ssp_amp,int ssp_bt,int dmic_be_num,int hdmi_num,bool idisp_codec)186 sof_card_dai_links_create(struct device *dev, enum sof_ssp_codec amp_type,
187 int ssp_codec, int ssp_amp, int ssp_bt,
188 int dmic_be_num, int hdmi_num, bool idisp_codec)
189 {
190 struct snd_soc_dai_link *links;
191 int ret;
192 int id = 0;
193 int link_seq;
194 int i;
195
196 links = devm_kcalloc(dev, sof_audio_card_cs42l42.num_links,
197 sizeof(struct snd_soc_dai_link), GFP_KERNEL);
198 if (!links)
199 goto devm_err;
200
201 link_seq = (sof_cs42l42_quirk & SOF_CS42L42_DAILINK_MASK) >> SOF_CS42L42_DAILINK_SHIFT;
202
203 while (link_seq) {
204 int link_type = link_seq & 0x07;
205
206 switch (link_type) {
207 case LINK_HP:
208 ret = sof_intel_board_set_codec_link(dev, &links[id], id,
209 CODEC_CS42L42,
210 ssp_codec);
211 if (ret) {
212 dev_err(dev, "fail to create hp codec dai links, ret %d\n",
213 ret);
214 goto devm_err;
215 }
216
217 /* codec-specific fields */
218 links[id].codecs = cs42l42_component;
219 links[id].num_codecs = ARRAY_SIZE(cs42l42_component);
220 links[id].init = sof_cs42l42_init;
221 links[id].exit = sof_cs42l42_exit;
222 links[id].ops = &sof_cs42l42_ops;
223
224 id++;
225 break;
226 case LINK_SPK:
227 if (amp_type != CODEC_NONE) {
228 ret = sof_intel_board_set_ssp_amp_link(dev,
229 &links[id],
230 id,
231 amp_type,
232 ssp_amp);
233 if (ret) {
234 dev_err(dev, "fail to create spk amp dai links, ret %d\n",
235 ret);
236 goto devm_err;
237 }
238
239 /* codec-specific fields */
240 switch (amp_type) {
241 case CODEC_MAX98357A:
242 max_98357a_dai_link(&links[id]);
243 break;
244 case CODEC_MAX98360A:
245 max_98360a_dai_link(&links[id]);
246 break;
247 default:
248 dev_err(dev, "invalid amp type %d\n",
249 amp_type);
250 goto devm_err;
251 }
252
253 id++;
254 }
255 break;
256 case LINK_DMIC:
257 if (dmic_be_num > 0) {
258 /* at least we have dmic01 */
259 ret = sof_intel_board_set_dmic_link(dev,
260 &links[id],
261 id,
262 SOF_DMIC_01);
263 if (ret) {
264 dev_err(dev, "fail to create dmic01 link, ret %d\n",
265 ret);
266 goto devm_err;
267 }
268
269 id++;
270 }
271
272 if (dmic_be_num > 1) {
273 /* set up 2 BE links at most */
274 ret = sof_intel_board_set_dmic_link(dev,
275 &links[id],
276 id,
277 SOF_DMIC_16K);
278 if (ret) {
279 dev_err(dev, "fail to create dmic16k link, ret %d\n",
280 ret);
281 goto devm_err;
282 }
283
284 id++;
285 }
286 break;
287 case LINK_HDMI:
288 for (i = 1; i <= hdmi_num; i++) {
289 ret = sof_intel_board_set_intel_hdmi_link(dev,
290 &links[id],
291 id, i,
292 idisp_codec);
293 if (ret) {
294 dev_err(dev, "fail to create hdmi link, ret %d\n",
295 ret);
296 goto devm_err;
297 }
298
299 id++;
300 }
301 break;
302 case LINK_BT:
303 if (sof_cs42l42_quirk & SOF_BT_OFFLOAD_PRESENT) {
304 ret = sof_intel_board_set_bt_link(dev,
305 &links[id], id,
306 ssp_bt);
307 if (ret) {
308 dev_err(dev, "fail to create bt offload dai links, ret %d\n",
309 ret);
310 goto devm_err;
311 }
312
313 id++;
314 }
315 break;
316 case LINK_NONE:
317 /* caught here if it's not used as terminator in macro */
318 default:
319 dev_err(dev, "invalid link type %d\n", link_type);
320 goto devm_err;
321 }
322
323 link_seq >>= 3;
324 }
325
326 return links;
327 devm_err:
328 return NULL;
329 }
330
sof_audio_probe(struct platform_device * pdev)331 static int sof_audio_probe(struct platform_device *pdev)
332 {
333 struct snd_soc_acpi_mach *mach = pdev->dev.platform_data;
334 struct snd_soc_dai_link *dai_links;
335 struct sof_card_private *ctx;
336 int ret;
337
338 ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
339 if (!ctx)
340 return -ENOMEM;
341
342 if (pdev->id_entry && pdev->id_entry->driver_data)
343 sof_cs42l42_quirk = (unsigned long)pdev->id_entry->driver_data;
344
345 ctx->codec_type = sof_ssp_detect_codec_type(&pdev->dev);
346 ctx->amp_type = sof_ssp_detect_amp_type(&pdev->dev);
347
348 if (soc_intel_is_glk()) {
349 ctx->dmic_be_num = 1;
350 ctx->hdmi_num = 3;
351 } else {
352 ctx->dmic_be_num = 2;
353 ctx->hdmi_num = (sof_cs42l42_quirk & SOF_CS42L42_NUM_HDMIDEV_MASK) >>
354 SOF_CS42L42_NUM_HDMIDEV_SHIFT;
355 /* default number of HDMI DAI's */
356 if (!ctx->hdmi_num)
357 ctx->hdmi_num = 3;
358 }
359
360 if (mach->mach_params.codec_mask & IDISP_CODEC_MASK)
361 ctx->hdmi.idisp_codec = true;
362
363 dev_dbg(&pdev->dev, "sof_cs42l42_quirk = %lx\n", sof_cs42l42_quirk);
364
365 /* port number of peripherals attached to ssp interface */
366 ctx->ssp_bt = (sof_cs42l42_quirk & SOF_CS42L42_SSP_BT_MASK) >>
367 SOF_CS42L42_SSP_BT_SHIFT;
368
369 ctx->ssp_amp = (sof_cs42l42_quirk & SOF_CS42L42_SSP_AMP_MASK) >>
370 SOF_CS42L42_SSP_AMP_SHIFT;
371
372 ctx->ssp_codec = sof_cs42l42_quirk & SOF_CS42L42_SSP_CODEC_MASK;
373
374 /* compute number of dai links */
375 sof_audio_card_cs42l42.num_links = 1 + ctx->dmic_be_num + ctx->hdmi_num;
376
377 if (ctx->amp_type != CODEC_NONE)
378 sof_audio_card_cs42l42.num_links++;
379 if (sof_cs42l42_quirk & SOF_BT_OFFLOAD_PRESENT) {
380 ctx->bt_offload_present = true;
381 sof_audio_card_cs42l42.num_links++;
382 }
383
384 dai_links = sof_card_dai_links_create(&pdev->dev, ctx->amp_type,
385 ctx->ssp_codec, ctx->ssp_amp,
386 ctx->ssp_bt, ctx->dmic_be_num,
387 ctx->hdmi_num,
388 ctx->hdmi.idisp_codec);
389 if (!dai_links)
390 return -ENOMEM;
391
392 sof_audio_card_cs42l42.dai_link = dai_links;
393
394 sof_audio_card_cs42l42.dev = &pdev->dev;
395
396 /* set platform name for each dailink */
397 ret = snd_soc_fixup_dai_links_platform_name(&sof_audio_card_cs42l42,
398 mach->mach_params.platform);
399 if (ret)
400 return ret;
401
402 snd_soc_card_set_drvdata(&sof_audio_card_cs42l42, ctx);
403
404 return devm_snd_soc_register_card(&pdev->dev,
405 &sof_audio_card_cs42l42);
406 }
407
408 static const struct platform_device_id board_ids[] = {
409 {
410 .name = "glk_cs4242_mx98357a",
411 .driver_data = (kernel_ulong_t)(SOF_CS42L42_SSP_CODEC(2) |
412 SOF_CS42L42_SSP_AMP(1)) |
413 SOF_CS42L42_DAILINK(LINK_SPK, LINK_HP, LINK_DMIC, LINK_HDMI, LINK_NONE),
414 },
415 {
416 .name = "jsl_cs4242_mx98360a",
417 .driver_data = (kernel_ulong_t)(SOF_CS42L42_SSP_CODEC(0) |
418 SOF_CS42L42_SSP_AMP(1)) |
419 SOF_CS42L42_DAILINK(LINK_HP, LINK_DMIC, LINK_HDMI, LINK_SPK, LINK_NONE),
420 },
421 {
422 .name = "adl_mx98360a_cs4242",
423 .driver_data = (kernel_ulong_t)(SOF_CS42L42_SSP_CODEC(0) |
424 SOF_CS42L42_SSP_AMP(1) |
425 SOF_CS42L42_NUM_HDMIDEV(4) |
426 SOF_BT_OFFLOAD_PRESENT |
427 SOF_CS42L42_SSP_BT(2) |
428 SOF_CS42L42_DAILINK(LINK_HP, LINK_DMIC, LINK_HDMI, LINK_SPK, LINK_BT)),
429 },
430 { }
431 };
432 MODULE_DEVICE_TABLE(platform, board_ids);
433
434 static struct platform_driver sof_audio = {
435 .probe = sof_audio_probe,
436 .driver = {
437 .name = "sof_cs42l42",
438 .pm = &snd_soc_pm_ops,
439 },
440 .id_table = board_ids,
441 };
442 module_platform_driver(sof_audio)
443
444 /* Module information */
445 MODULE_DESCRIPTION("SOF Audio Machine driver for CS42L42");
446 MODULE_AUTHOR("Brent Lu <brent.lu@intel.com>");
447 MODULE_LICENSE("GPL");
448 MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS);
449 MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_MAXIM_COMMON);
450 MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_SSP_COMMON);
451