xref: /linux/sound/soc/intel/boards/sof_da7219.c (revision 2aa680df68062e4e0c356ec2aa7100c13654907b)
1 // SPDX-License-Identifier: GPL-2.0-only
2 // Copyright(c) 2019 Intel Corporation.
3 
4 /*
5  * Intel SOF Machine driver for Dialog headphone codec
6  */
7 
8 #include <linux/input.h>
9 #include <linux/module.h>
10 #include <sound/jack.h>
11 #include <sound/pcm.h>
12 #include <sound/pcm_params.h>
13 #include <linux/platform_device.h>
14 #include <sound/soc.h>
15 #include <sound/soc-acpi.h>
16 #include <sound/sof.h>
17 #include "../../codecs/da7219.h"
18 #include "sof_board_helpers.h"
19 #include "sof_maxim_common.h"
20 
21 /* Driver-specific board quirks: from bit 0 to 7 */
22 #define SOF_DA7219_GLK_BOARD			BIT(0)
23 #define SOF_DA7219_CML_BOARD			BIT(1)
24 #define SOF_DA7219_JSL_BOARD			BIT(2)
25 #define SOF_DA7219_MCLK_EN			BIT(3)
26 
27 #define DIALOG_CODEC_DAI	"da7219-hifi"
28 
29 static int platform_clock_control(struct snd_soc_dapm_widget *w,
30 				  struct snd_kcontrol *k, int  event)
31 {
32 	struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
33 	struct sof_card_private *ctx = snd_soc_card_get_drvdata(card);
34 	struct snd_soc_dai *codec_dai;
35 	int ret = 0;
36 
37 	if (ctx->da7219.pll_bypass)
38 		return ret;
39 
40 	/* PLL SRM mode */
41 	codec_dai = snd_soc_card_get_codec_dai(card, DIALOG_CODEC_DAI);
42 	if (!codec_dai) {
43 		dev_err(card->dev, "Codec dai not found; Unable to set/unset codec pll\n");
44 		return -EIO;
45 	}
46 
47 	if (SND_SOC_DAPM_EVENT_OFF(event)) {
48 		ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_MCLK,
49 					  0, 0);
50 		if (ret)
51 			dev_err(card->dev, "failed to stop PLL: %d\n", ret);
52 	} else if (SND_SOC_DAPM_EVENT_ON(event)) {
53 		dev_dbg(card->dev, "pll srm mode\n");
54 
55 		ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_PLL_SRM,
56 					  0, DA7219_PLL_FREQ_OUT_98304);
57 		if (ret)
58 			dev_err(card->dev, "failed to start PLL: %d\n", ret);
59 	}
60 
61 	return ret;
62 }
63 
64 static const struct snd_kcontrol_new controls[] = {
65 	SOC_DAPM_PIN_SWITCH("Headphone Jack"),
66 	SOC_DAPM_PIN_SWITCH("Headset Mic"),
67 	SOC_DAPM_PIN_SWITCH("Line Out"),
68 };
69 
70 static const struct snd_soc_dapm_widget widgets[] = {
71 	SND_SOC_DAPM_HP("Headphone Jack", NULL),
72 	SND_SOC_DAPM_MIC("Headset Mic", NULL),
73 	SND_SOC_DAPM_LINE("Line Out", NULL),
74 
75 	SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
76 			    platform_clock_control, SND_SOC_DAPM_POST_PMD |
77 			    SND_SOC_DAPM_PRE_PMU),
78 };
79 
80 static const struct snd_soc_dapm_route audio_map[] = {
81 	{ "Headphone Jack", NULL, "HPL" },
82 	{ "Headphone Jack", NULL, "HPR" },
83 
84 	{ "MIC", NULL, "Headset Mic" },
85 
86 	{ "Headphone Jack", NULL, "Platform Clock" },
87 	{ "Headset Mic", NULL, "Platform Clock" },
88 	{ "Line Out", NULL, "Platform Clock" },
89 };
90 
91 static struct snd_soc_jack_pin jack_pins[] = {
92 	{
93 		.pin    = "Headphone Jack",
94 		.mask   = SND_JACK_HEADPHONE,
95 	},
96 	{
97 		.pin    = "Headset Mic",
98 		.mask   = SND_JACK_MICROPHONE,
99 	},
100 	{
101 		.pin    = "Line Out",
102 		.mask   = SND_JACK_LINEOUT,
103 	},
104 };
105 
106 static int da7219_codec_init(struct snd_soc_pcm_runtime *rtd)
107 {
108 	struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card);
109 	struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
110 	struct snd_soc_component *component = codec_dai->component;
111 	struct snd_soc_jack *jack = &ctx->headset_jack;
112 	int mclk_rate, ret;
113 
114 	mclk_rate = sof_dai_get_mclk(rtd);
115 	if (mclk_rate <= 0) {
116 		dev_err(rtd->dev, "invalid mclk freq %d\n", mclk_rate);
117 		return -EINVAL;
118 	}
119 
120 	ret = snd_soc_dai_set_sysclk(codec_dai, DA7219_CLKSRC_MCLK, mclk_rate,
121 				     SND_SOC_CLOCK_IN);
122 	if (ret) {
123 		dev_err(rtd->dev, "fail to set sysclk, ret %d\n", ret);
124 		return ret;
125 	}
126 
127 	/*
128 	 * Use PLL bypass mode if MCLK is available, be sure to set the
129 	 * frequency of MCLK to 12.288 or 24.576MHz on topology side.
130 	 */
131 	if (ctx->da7219.mclk_en &&
132 	    (mclk_rate == 12288000 || mclk_rate == 24576000)) {
133 		/* PLL bypass mode */
134 		dev_dbg(rtd->dev, "pll bypass mode, mclk rate %d\n", mclk_rate);
135 
136 		ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_MCLK, 0, 0);
137 		if (ret) {
138 			dev_err(rtd->dev, "fail to set pll, ret %d\n", ret);
139 			return ret;
140 		}
141 
142 		ctx->da7219.pll_bypass = true;
143 	}
144 
145 	/*
146 	 * Headset buttons map to the google Reference headset.
147 	 * These can be configured by userspace.
148 	 */
149 	ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
150 					 SND_JACK_HEADSET | SND_JACK_BTN_0 |
151 					 SND_JACK_BTN_1 | SND_JACK_BTN_2 |
152 					 SND_JACK_BTN_3 | SND_JACK_LINEOUT,
153 					 jack, jack_pins, ARRAY_SIZE(jack_pins));
154 	if (ret) {
155 		dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
156 		return ret;
157 	}
158 
159 	snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
160 	snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOLUMEUP);
161 	snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN);
162 	snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOICECOMMAND);
163 
164 	ret = snd_soc_component_set_jack(component, jack, NULL);
165 	if (ret) {
166 		dev_err(rtd->dev, "fail to set component jack, ret %d\n", ret);
167 		return ret;
168 	}
169 
170 	return ret;
171 }
172 
173 static void da7219_codec_exit(struct snd_soc_pcm_runtime *rtd)
174 {
175 	struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
176 
177 	snd_soc_component_set_jack(component, NULL, NULL);
178 }
179 
180 static int card_late_probe(struct snd_soc_card *card)
181 {
182 	struct sof_card_private *ctx = snd_soc_card_get_drvdata(card);
183 	struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
184 	int err;
185 
186 	if (ctx->amp_type == CODEC_MAX98373) {
187 		/* Disable Left and Right Spk pin after boot */
188 		snd_soc_dapm_disable_pin(dapm, "Left Spk");
189 		snd_soc_dapm_disable_pin(dapm, "Right Spk");
190 		err = snd_soc_dapm_sync(dapm);
191 		if (err < 0)
192 			return err;
193 	}
194 
195 	return sof_intel_board_card_late_probe(card);
196 }
197 
198 static struct snd_soc_card card_da7219 = {
199 	.name = "da7219", /* the sof- prefix is added by the core */
200 	.owner = THIS_MODULE,
201 	.controls = controls,
202 	.num_controls = ARRAY_SIZE(controls),
203 	.dapm_widgets = widgets,
204 	.num_dapm_widgets = ARRAY_SIZE(widgets),
205 	.dapm_routes = audio_map,
206 	.num_dapm_routes = ARRAY_SIZE(audio_map),
207 	.fully_routed = true,
208 	.late_probe = card_late_probe,
209 };
210 
211 static struct snd_soc_dai_link_component da7219_component[] = {
212 	{
213 		.name = "i2c-DLGS7219:00",
214 		.dai_name = DIALOG_CODEC_DAI,
215 	}
216 };
217 
218 static int
219 sof_card_dai_links_create(struct device *dev, struct snd_soc_card *card,
220 			  struct sof_card_private *ctx)
221 {
222 	int ret;
223 
224 	ret = sof_intel_board_set_dai_link(dev, card, ctx);
225 	if (ret)
226 		return ret;
227 
228 	if (!ctx->codec_link) {
229 		dev_err(dev, "codec link not available");
230 		return -EINVAL;
231 	}
232 
233 	/* codec-specific fields for headphone codec */
234 	ctx->codec_link->codecs = da7219_component;
235 	ctx->codec_link->num_codecs = ARRAY_SIZE(da7219_component);
236 	ctx->codec_link->init = da7219_codec_init;
237 	ctx->codec_link->exit = da7219_codec_exit;
238 
239 	if (ctx->amp_type == CODEC_NONE)
240 		return 0;
241 
242 	if (!ctx->amp_link) {
243 		dev_err(dev, "amp link not available");
244 		return -EINVAL;
245 	}
246 
247 	/* codec-specific fields for speaker amplifier */
248 	switch (ctx->amp_type) {
249 	case CODEC_MAX98357A:
250 		max_98357a_dai_link(ctx->amp_link);
251 		break;
252 	case CODEC_MAX98360A:
253 		max_98360a_dai_link(ctx->amp_link);
254 		break;
255 	case CODEC_MAX98373:
256 		max_98373_dai_link(dev, ctx->amp_link);
257 		break;
258 	case CODEC_MAX98390:
259 		max_98390_dai_link(dev, ctx->amp_link);
260 		break;
261 	default:
262 		dev_err(dev, "invalid amp type %d\n", ctx->amp_type);
263 		return -EINVAL;
264 	}
265 
266 	return 0;
267 }
268 
269 #define GLK_LINK_ORDER	SOF_LINK_ORDER(SOF_LINK_AMP,         \
270 					SOF_LINK_CODEC,      \
271 					SOF_LINK_DMIC01,     \
272 					SOF_LINK_IDISP_HDMI, \
273 					SOF_LINK_NONE,       \
274 					SOF_LINK_NONE,       \
275 					SOF_LINK_NONE)
276 
277 #define CML_LINK_ORDER	SOF_LINK_ORDER(SOF_LINK_AMP,         \
278 					SOF_LINK_CODEC,      \
279 					SOF_LINK_DMIC01,     \
280 					SOF_LINK_IDISP_HDMI, \
281 					SOF_LINK_DMIC16K,    \
282 					SOF_LINK_NONE,       \
283 					SOF_LINK_NONE)
284 
285 #define JSL_LINK_ORDER	SOF_LINK_ORDER(SOF_LINK_AMP,         \
286 					SOF_LINK_CODEC,      \
287 					SOF_LINK_DMIC01,     \
288 					SOF_LINK_IDISP_HDMI, \
289 					SOF_LINK_DMIC16K,    \
290 					SOF_LINK_NONE,       \
291 					SOF_LINK_NONE)
292 
293 static int audio_probe(struct platform_device *pdev)
294 {
295 	struct snd_soc_acpi_mach *mach = pdev->dev.platform_data;
296 	struct sof_card_private *ctx;
297 	char *card_name;
298 	unsigned long board_quirk = 0;
299 	int ret;
300 
301 	if (pdev->id_entry && pdev->id_entry->driver_data)
302 		board_quirk = (unsigned long)pdev->id_entry->driver_data;
303 
304 	dev_dbg(&pdev->dev, "board_quirk = %lx\n", board_quirk);
305 
306 	/* initialize ctx with board quirk */
307 	ctx = sof_intel_board_get_ctx(&pdev->dev, board_quirk);
308 	if (!ctx)
309 		return -ENOMEM;
310 
311 	if (mach->mach_params.codec_mask & IDISP_CODEC_MASK)
312 		ctx->hdmi.idisp_codec = true;
313 
314 	if (board_quirk & SOF_DA7219_GLK_BOARD) {
315 		/* dmic16k not support */
316 		ctx->dmic_be_num = 1;
317 
318 		/* overwrite the DAI link order for GLK boards */
319 		ctx->link_order_overwrite = GLK_LINK_ORDER;
320 
321 		/* backward-compatible with existing devices */
322 		switch (ctx->amp_type) {
323 		case CODEC_MAX98357A:
324 			card_name = devm_kstrdup(&pdev->dev, "glkda7219max",
325 						 GFP_KERNEL);
326 			if (!card_name)
327 				return -ENOMEM;
328 
329 			card_da7219.name = card_name;
330 			break;
331 		default:
332 			break;
333 		}
334 	} else if (board_quirk & SOF_DA7219_CML_BOARD) {
335 		/* overwrite the DAI link order for CML boards */
336 		ctx->link_order_overwrite = CML_LINK_ORDER;
337 
338 		/* backward-compatible with existing devices */
339 		switch (ctx->amp_type) {
340 		case CODEC_MAX98357A:
341 			card_name = devm_kstrdup(&pdev->dev, "cmlda7219max",
342 						 GFP_KERNEL);
343 			if (!card_name)
344 				return -ENOMEM;
345 
346 			card_da7219.name = card_name;
347 			break;
348 		case CODEC_MAX98390:
349 			card_name = devm_kstrdup(&pdev->dev,
350 						 "cml_max98390_da7219",
351 						 GFP_KERNEL);
352 			if (!card_name)
353 				return -ENOMEM;
354 
355 			card_da7219.name = card_name;
356 			break;
357 		default:
358 			break;
359 		}
360 	} else if (board_quirk & SOF_DA7219_JSL_BOARD) {
361 		/* overwrite the DAI link order for JSL boards */
362 		ctx->link_order_overwrite = JSL_LINK_ORDER;
363 
364 		/* backward-compatible with existing devices */
365 		switch (ctx->amp_type) {
366 		case CODEC_MAX98360A:
367 			card_name = devm_kstrdup(&pdev->dev, "da7219max98360a",
368 						 GFP_KERNEL);
369 			if (!card_name)
370 				return -ENOMEM;
371 
372 			card_da7219.name = card_name;
373 			break;
374 		case CODEC_MAX98373:
375 			card_name = devm_kstrdup(&pdev->dev, "da7219max",
376 						 GFP_KERNEL);
377 			if (!card_name)
378 				return -ENOMEM;
379 
380 			card_da7219.name = card_name;
381 			break;
382 		default:
383 			break;
384 		}
385 	}
386 
387 	if (board_quirk & SOF_DA7219_MCLK_EN)
388 		ctx->da7219.mclk_en = true;
389 
390 	/* update dai_link */
391 	ret = sof_card_dai_links_create(&pdev->dev, &card_da7219, ctx);
392 	if (ret)
393 		return ret;
394 
395 	/* update codec_conf */
396 	switch (ctx->amp_type) {
397 	case CODEC_MAX98373:
398 		max_98373_set_codec_conf(&card_da7219);
399 		break;
400 	case CODEC_MAX98390:
401 		max_98390_set_codec_conf(&pdev->dev, &card_da7219);
402 		break;
403 	case CODEC_MAX98357A:
404 	case CODEC_MAX98360A:
405 	case CODEC_NONE:
406 		/* no codec conf required */
407 		break;
408 	default:
409 		dev_err(&pdev->dev, "invalid amp type %d\n", ctx->amp_type);
410 		return -EINVAL;
411 	}
412 
413 	card_da7219.dev = &pdev->dev;
414 
415 	ret = snd_soc_fixup_dai_links_platform_name(&card_da7219,
416 						    mach->mach_params.platform);
417 	if (ret)
418 		return ret;
419 
420 	snd_soc_card_set_drvdata(&card_da7219, ctx);
421 
422 	return devm_snd_soc_register_card(&pdev->dev, &card_da7219);
423 }
424 
425 static const struct platform_device_id board_ids[] = {
426 	{
427 		.name = "glk_da7219_def",
428 		.driver_data = (kernel_ulong_t)(SOF_DA7219_GLK_BOARD |
429 					SOF_SSP_PORT_CODEC(2) |
430 					SOF_SSP_PORT_AMP(1)),
431 	},
432 	{
433 		.name = "cml_da7219_def",
434 		.driver_data = (kernel_ulong_t)(SOF_DA7219_CML_BOARD |
435 					SOF_SSP_PORT_CODEC(0) |
436 					SOF_SSP_PORT_AMP(1)),
437 	},
438 	{
439 		.name = "jsl_da7219_def",
440 		.driver_data = (kernel_ulong_t)(SOF_DA7219_JSL_BOARD |
441 					SOF_SSP_PORT_CODEC(0) |
442 					SOF_SSP_PORT_AMP(1)),
443 	},
444 	{
445 		.name = "adl_da7219_def",
446 		.driver_data = (kernel_ulong_t)(SOF_DA7219_MCLK_EN |
447 					SOF_SSP_PORT_CODEC(0) |
448 					SOF_SSP_PORT_AMP(1) |
449 					SOF_NUM_IDISP_HDMI(4) |
450 					SOF_SSP_PORT_BT_OFFLOAD(2) |
451 					SOF_BT_OFFLOAD_PRESENT),
452 	},
453 	{
454 		.name = "rpl_da7219_def",
455 		.driver_data = (kernel_ulong_t)(SOF_DA7219_MCLK_EN |
456 					SOF_SSP_PORT_CODEC(0) |
457 					SOF_SSP_PORT_AMP(1) |
458 					SOF_NUM_IDISP_HDMI(4) |
459 					SOF_SSP_PORT_BT_OFFLOAD(2) |
460 					SOF_BT_OFFLOAD_PRESENT),
461 	},
462 	{
463 		.name = "mtl_da7219_def",
464 		.driver_data = (kernel_ulong_t)(SOF_DA7219_MCLK_EN |
465 					SOF_SSP_PORT_CODEC(2) |
466 					SOF_SSP_PORT_AMP(0) |
467 					SOF_SSP_PORT_BT_OFFLOAD(1) |
468 					SOF_BT_OFFLOAD_PRESENT),
469 	},
470 	{ }
471 };
472 MODULE_DEVICE_TABLE(platform, board_ids);
473 
474 static struct platform_driver audio = {
475 	.probe = audio_probe,
476 	.driver = {
477 		.name = "sof_da7219",
478 		.pm = &snd_soc_pm_ops,
479 	},
480 	.id_table = board_ids,
481 };
482 module_platform_driver(audio)
483 
484 /* Module information */
485 MODULE_DESCRIPTION("ASoC Intel(R) SOF Machine driver for Dialog codec");
486 MODULE_AUTHOR("Yong Zhi <yong.zhi@intel.com>");
487 MODULE_AUTHOR("Brent Lu <brent.lu@intel.com>");
488 MODULE_LICENSE("GPL v2");
489 MODULE_IMPORT_NS("SND_SOC_INTEL_SOF_BOARD_HELPERS");
490 MODULE_IMPORT_NS("SND_SOC_INTEL_SOF_MAXIM_COMMON");
491