xref: /linux/sound/soc/intel/boards/sof_cs42l42.c (revision 79ac11393328fb1717d17c12e3c0eef0e9fa0647)
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 
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 
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 
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 
141 static struct snd_soc_dai_link_component platform_component[] = {
142 	{
143 		/* name might be overridden during probe */
144 		.name = "0000:00:1f.3"
145 	}
146 };
147 
148 static int sof_card_late_probe(struct snd_soc_card *card)
149 {
150 	return sof_intel_board_card_late_probe(card);
151 }
152 
153 static const struct snd_kcontrol_new sof_controls[] = {
154 	SOC_DAPM_PIN_SWITCH("Headphone Jack"),
155 	SOC_DAPM_PIN_SWITCH("Headset Mic"),
156 };
157 
158 static const struct snd_soc_dapm_widget sof_widgets[] = {
159 	SND_SOC_DAPM_HP("Headphone Jack", NULL),
160 	SND_SOC_DAPM_MIC("Headset Mic", NULL),
161 };
162 
163 static const struct snd_soc_dapm_route sof_map[] = {
164 	/* HP jack connectors - unknown if we have jack detection */
165 	{"Headphone Jack", NULL, "HP"},
166 
167 	/* other jacks */
168 	{"HS", NULL, "Headset Mic"},
169 };
170 
171 /* sof audio machine driver for cs42l42 codec */
172 static struct snd_soc_card sof_audio_card_cs42l42 = {
173 	.name = "cs42l42", /* the sof- prefix is added by the core */
174 	.owner = THIS_MODULE,
175 	.controls = sof_controls,
176 	.num_controls = ARRAY_SIZE(sof_controls),
177 	.dapm_widgets = sof_widgets,
178 	.num_dapm_widgets = ARRAY_SIZE(sof_widgets),
179 	.dapm_routes = sof_map,
180 	.num_dapm_routes = ARRAY_SIZE(sof_map),
181 	.fully_routed = true,
182 	.late_probe = sof_card_late_probe,
183 };
184 
185 static struct snd_soc_dai_link_component cs42l42_component[] = {
186 	{
187 		.name = "i2c-10134242:00",
188 		.dai_name = "cs42l42",
189 	}
190 };
191 
192 static int create_spk_amp_dai_links(struct device *dev,
193 				    struct snd_soc_dai_link *links,
194 				    struct snd_soc_dai_link_component *cpus,
195 				    int *id, enum sof_ssp_codec amp_type,
196 				    int ssp_amp)
197 {
198 	int ret = 0;
199 
200 	/* speaker amp */
201 	if (amp_type == CODEC_NONE)
202 		return 0;
203 
204 	links[*id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec",
205 					 ssp_amp);
206 	if (!links[*id].name) {
207 		ret = -ENOMEM;
208 		goto devm_err;
209 	}
210 
211 	links[*id].id = *id;
212 
213 	switch (amp_type) {
214 	case CODEC_MAX98357A:
215 		max_98357a_dai_link(&links[*id]);
216 		break;
217 	case CODEC_MAX98360A:
218 		max_98360a_dai_link(&links[*id]);
219 		break;
220 	default:
221 		dev_err(dev, "invalid amp type %d\n", amp_type);
222 		return -EINVAL;
223 	}
224 
225 	links[*id].platforms = platform_component;
226 	links[*id].num_platforms = ARRAY_SIZE(platform_component);
227 	links[*id].dpcm_playback = 1;
228 	/* firmware-generated echo reference */
229 	links[*id].dpcm_capture = 1;
230 
231 	links[*id].no_pcm = 1;
232 	links[*id].cpus = &cpus[*id];
233 	links[*id].num_cpus = 1;
234 
235 	links[*id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
236 						   "SSP%d Pin", ssp_amp);
237 	if (!links[*id].cpus->dai_name) {
238 		ret = -ENOMEM;
239 		goto devm_err;
240 	}
241 
242 	(*id)++;
243 
244 devm_err:
245 	return ret;
246 }
247 
248 static int create_hp_codec_dai_links(struct device *dev,
249 				     struct snd_soc_dai_link *links,
250 				     struct snd_soc_dai_link_component *cpus,
251 				     int *id, int ssp_codec)
252 {
253 	/* codec SSP */
254 	links[*id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec",
255 					 ssp_codec);
256 	if (!links[*id].name)
257 		goto devm_err;
258 
259 	links[*id].id = *id;
260 	links[*id].codecs = cs42l42_component;
261 	links[*id].num_codecs = ARRAY_SIZE(cs42l42_component);
262 	links[*id].platforms = platform_component;
263 	links[*id].num_platforms = ARRAY_SIZE(platform_component);
264 	links[*id].init = sof_cs42l42_init;
265 	links[*id].exit = sof_cs42l42_exit;
266 	links[*id].ops = &sof_cs42l42_ops;
267 	links[*id].dpcm_playback = 1;
268 	links[*id].dpcm_capture = 1;
269 	links[*id].no_pcm = 1;
270 	links[*id].cpus = &cpus[*id];
271 	links[*id].num_cpus = 1;
272 
273 	links[*id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
274 						   "SSP%d Pin",
275 						   ssp_codec);
276 	if (!links[*id].cpus->dai_name)
277 		goto devm_err;
278 
279 	(*id)++;
280 
281 	return 0;
282 
283 devm_err:
284 	return -ENOMEM;
285 }
286 
287 static int create_bt_offload_dai_links(struct device *dev,
288 				       struct snd_soc_dai_link *links,
289 				       struct snd_soc_dai_link_component *cpus,
290 				       int *id, int ssp_bt)
291 {
292 	/* bt offload */
293 	if (!(sof_cs42l42_quirk & SOF_BT_OFFLOAD_PRESENT))
294 		return 0;
295 
296 	links[*id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-BT",
297 					 ssp_bt);
298 	if (!links[*id].name)
299 		goto devm_err;
300 
301 	links[*id].id = *id;
302 	links[*id].codecs = &snd_soc_dummy_dlc;
303 	links[*id].num_codecs = 1;
304 	links[*id].platforms = platform_component;
305 	links[*id].num_platforms = ARRAY_SIZE(platform_component);
306 
307 	links[*id].dpcm_playback = 1;
308 	links[*id].dpcm_capture = 1;
309 	links[*id].no_pcm = 1;
310 	links[*id].cpus = &cpus[*id];
311 	links[*id].num_cpus = 1;
312 
313 	links[*id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
314 						   "SSP%d Pin",
315 						   ssp_bt);
316 	if (!links[*id].cpus->dai_name)
317 		goto devm_err;
318 
319 	(*id)++;
320 
321 	return 0;
322 
323 devm_err:
324 	return -ENOMEM;
325 }
326 
327 static struct snd_soc_dai_link *
328 sof_card_dai_links_create(struct device *dev, enum sof_ssp_codec amp_type,
329 			  int ssp_codec, int ssp_amp, int ssp_bt,
330 			  int dmic_be_num, int hdmi_num, bool idisp_codec)
331 {
332 	struct snd_soc_dai_link_component *cpus;
333 	struct snd_soc_dai_link *links;
334 	int ret;
335 	int id = 0;
336 	int link_seq;
337 	int i;
338 
339 	links = devm_kcalloc(dev, sof_audio_card_cs42l42.num_links,
340 			    sizeof(struct snd_soc_dai_link), GFP_KERNEL);
341 	cpus = devm_kcalloc(dev, sof_audio_card_cs42l42.num_links,
342 			    sizeof(struct snd_soc_dai_link_component), GFP_KERNEL);
343 	if (!links || !cpus)
344 		goto devm_err;
345 
346 	link_seq = (sof_cs42l42_quirk & SOF_CS42L42_DAILINK_MASK) >> SOF_CS42L42_DAILINK_SHIFT;
347 
348 	while (link_seq) {
349 		int link_type = link_seq & 0x07;
350 
351 		switch (link_type) {
352 		case LINK_HP:
353 			ret = create_hp_codec_dai_links(dev, links, cpus, &id, ssp_codec);
354 			if (ret < 0) {
355 				dev_err(dev, "fail to create hp codec dai links, ret %d\n",
356 					ret);
357 				goto devm_err;
358 			}
359 			break;
360 		case LINK_SPK:
361 			ret = create_spk_amp_dai_links(dev, links, cpus, &id,
362 						       amp_type, ssp_amp);
363 			if (ret < 0) {
364 				dev_err(dev, "fail to create spk amp dai links, ret %d\n",
365 					ret);
366 				goto devm_err;
367 			}
368 			break;
369 		case LINK_DMIC:
370 			if (dmic_be_num > 0) {
371 				/* at least we have dmic01 */
372 				ret = sof_intel_board_set_dmic_link(dev,
373 								    &links[id],
374 								    id,
375 								    SOF_DMIC_01);
376 				if (ret) {
377 					dev_err(dev, "fail to create dmic01 link, ret %d\n",
378 						ret);
379 					goto devm_err;
380 				}
381 
382 				id++;
383 			}
384 
385 			if (dmic_be_num > 1) {
386 				/* set up 2 BE links at most */
387 				ret = sof_intel_board_set_dmic_link(dev,
388 								    &links[id],
389 								    id,
390 								    SOF_DMIC_16K);
391 				if (ret) {
392 					dev_err(dev, "fail to create dmic16k link, ret %d\n",
393 						ret);
394 					goto devm_err;
395 				}
396 
397 				id++;
398 			}
399 			break;
400 		case LINK_HDMI:
401 			for (i = 1; i <= hdmi_num; i++) {
402 				ret = sof_intel_board_set_intel_hdmi_link(dev,
403 									  &links[id],
404 									  id, i,
405 									  idisp_codec);
406 				if (ret) {
407 					dev_err(dev, "fail to create hdmi link, ret %d\n",
408 						ret);
409 					goto devm_err;
410 				}
411 
412 				id++;
413 			}
414 			break;
415 		case LINK_BT:
416 			ret = create_bt_offload_dai_links(dev, links, cpus, &id, ssp_bt);
417 			if (ret < 0) {
418 				dev_err(dev, "fail to create bt offload dai links, ret %d\n",
419 					ret);
420 				goto devm_err;
421 			}
422 			break;
423 		case LINK_NONE:
424 			/* caught here if it's not used as terminator in macro */
425 		default:
426 			dev_err(dev, "invalid link type %d\n", link_type);
427 			goto devm_err;
428 		}
429 
430 		link_seq >>= 3;
431 	}
432 
433 	return links;
434 devm_err:
435 	return NULL;
436 }
437 
438 static int sof_audio_probe(struct platform_device *pdev)
439 {
440 	struct snd_soc_acpi_mach *mach = pdev->dev.platform_data;
441 	struct snd_soc_dai_link *dai_links;
442 	struct sof_card_private *ctx;
443 	int ret, ssp_bt, ssp_amp, ssp_codec;
444 
445 	ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
446 	if (!ctx)
447 		return -ENOMEM;
448 
449 	if (pdev->id_entry && pdev->id_entry->driver_data)
450 		sof_cs42l42_quirk = (unsigned long)pdev->id_entry->driver_data;
451 
452 	ctx->codec_type = sof_ssp_detect_codec_type(&pdev->dev);
453 	ctx->amp_type = sof_ssp_detect_amp_type(&pdev->dev);
454 
455 	if (soc_intel_is_glk()) {
456 		ctx->dmic_be_num = 1;
457 		ctx->hdmi_num = 3;
458 	} else {
459 		ctx->dmic_be_num = 2;
460 		ctx->hdmi_num = (sof_cs42l42_quirk & SOF_CS42L42_NUM_HDMIDEV_MASK) >>
461 			 SOF_CS42L42_NUM_HDMIDEV_SHIFT;
462 		/* default number of HDMI DAI's */
463 		if (!ctx->hdmi_num)
464 			ctx->hdmi_num = 3;
465 	}
466 
467 	if (mach->mach_params.codec_mask & IDISP_CODEC_MASK)
468 		ctx->hdmi.idisp_codec = true;
469 
470 	dev_dbg(&pdev->dev, "sof_cs42l42_quirk = %lx\n", sof_cs42l42_quirk);
471 
472 	ssp_bt = (sof_cs42l42_quirk & SOF_CS42L42_SSP_BT_MASK) >>
473 			SOF_CS42L42_SSP_BT_SHIFT;
474 
475 	ssp_amp = (sof_cs42l42_quirk & SOF_CS42L42_SSP_AMP_MASK) >>
476 			SOF_CS42L42_SSP_AMP_SHIFT;
477 
478 	ssp_codec = sof_cs42l42_quirk & SOF_CS42L42_SSP_CODEC_MASK;
479 
480 	/* compute number of dai links */
481 	sof_audio_card_cs42l42.num_links = 1 + ctx->dmic_be_num + ctx->hdmi_num;
482 
483 	if (ctx->amp_type != CODEC_NONE)
484 		sof_audio_card_cs42l42.num_links++;
485 	if (sof_cs42l42_quirk & SOF_BT_OFFLOAD_PRESENT)
486 		sof_audio_card_cs42l42.num_links++;
487 
488 	dai_links = sof_card_dai_links_create(&pdev->dev, ctx->amp_type,
489 					      ssp_codec, ssp_amp, ssp_bt,
490 					      ctx->dmic_be_num, ctx->hdmi_num,
491 					      ctx->hdmi.idisp_codec);
492 	if (!dai_links)
493 		return -ENOMEM;
494 
495 	sof_audio_card_cs42l42.dai_link = dai_links;
496 
497 	sof_audio_card_cs42l42.dev = &pdev->dev;
498 
499 	/* set platform name for each dailink */
500 	ret = snd_soc_fixup_dai_links_platform_name(&sof_audio_card_cs42l42,
501 						    mach->mach_params.platform);
502 	if (ret)
503 		return ret;
504 
505 	snd_soc_card_set_drvdata(&sof_audio_card_cs42l42, ctx);
506 
507 	return devm_snd_soc_register_card(&pdev->dev,
508 					  &sof_audio_card_cs42l42);
509 }
510 
511 static const struct platform_device_id board_ids[] = {
512 	{
513 		.name = "glk_cs4242_mx98357a",
514 		.driver_data = (kernel_ulong_t)(SOF_CS42L42_SSP_CODEC(2) |
515 					SOF_CS42L42_SSP_AMP(1)) |
516 					SOF_CS42L42_DAILINK(LINK_SPK, LINK_HP, LINK_DMIC, LINK_HDMI, LINK_NONE),
517 	},
518 	{
519 		.name = "jsl_cs4242_mx98360a",
520 		.driver_data = (kernel_ulong_t)(SOF_CS42L42_SSP_CODEC(0) |
521 					SOF_CS42L42_SSP_AMP(1)) |
522 					SOF_CS42L42_DAILINK(LINK_HP, LINK_DMIC, LINK_HDMI, LINK_SPK, LINK_NONE),
523 	},
524 	{
525 		.name = "adl_mx98360a_cs4242",
526 		.driver_data = (kernel_ulong_t)(SOF_CS42L42_SSP_CODEC(0) |
527 				SOF_CS42L42_SSP_AMP(1) |
528 				SOF_CS42L42_NUM_HDMIDEV(4) |
529 				SOF_BT_OFFLOAD_PRESENT |
530 				SOF_CS42L42_SSP_BT(2) |
531 				SOF_CS42L42_DAILINK(LINK_HP, LINK_DMIC, LINK_HDMI, LINK_SPK, LINK_BT)),
532 	},
533 	{ }
534 };
535 MODULE_DEVICE_TABLE(platform, board_ids);
536 
537 static struct platform_driver sof_audio = {
538 	.probe = sof_audio_probe,
539 	.driver = {
540 		.name = "sof_cs42l42",
541 		.pm = &snd_soc_pm_ops,
542 	},
543 	.id_table = board_ids,
544 };
545 module_platform_driver(sof_audio)
546 
547 /* Module information */
548 MODULE_DESCRIPTION("SOF Audio Machine driver for CS42L42");
549 MODULE_AUTHOR("Brent Lu <brent.lu@intel.com>");
550 MODULE_LICENSE("GPL");
551 MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS);
552 MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_MAXIM_COMMON);
553 MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_SSP_COMMON);
554