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