1 // SPDX-License-Identifier: GPL-2.0-only
2 //
3 // Copyright(c) 2020 Intel Corporation. All rights reserved.
4 #include <linux/module.h>
5 #include <linux/string.h>
6 #include <sound/pcm.h>
7 #include <sound/soc.h>
8 #include <sound/soc-acpi.h>
9 #include <sound/soc-dai.h>
10 #include <sound/soc-dapm.h>
11 #include <uapi/sound/asound.h>
12 #include "sof_maxim_common.h"
13 
14 #define MAX_98373_PIN_NAME 16
15 
16 const struct snd_soc_dapm_route max_98373_dapm_routes[] = {
17 	/* speaker */
18 	{ "Left Spk", NULL, "Left BE_OUT" },
19 	{ "Right Spk", NULL, "Right BE_OUT" },
20 };
21 EXPORT_SYMBOL_NS(max_98373_dapm_routes, SND_SOC_INTEL_SOF_MAXIM_COMMON);
22 
23 static struct snd_soc_codec_conf max_98373_codec_conf[] = {
24 	{
25 		.dlc = COMP_CODEC_CONF(MAX_98373_DEV0_NAME),
26 		.name_prefix = "Right",
27 	},
28 	{
29 		.dlc = COMP_CODEC_CONF(MAX_98373_DEV1_NAME),
30 		.name_prefix = "Left",
31 	},
32 };
33 
34 struct snd_soc_dai_link_component max_98373_components[] = {
35 	{  /* For Right */
36 		.name = MAX_98373_DEV0_NAME,
37 		.dai_name = MAX_98373_CODEC_DAI,
38 	},
39 	{  /* For Left */
40 		.name = MAX_98373_DEV1_NAME,
41 		.dai_name = MAX_98373_CODEC_DAI,
42 	},
43 };
44 EXPORT_SYMBOL_NS(max_98373_components, SND_SOC_INTEL_SOF_MAXIM_COMMON);
45 
max_98373_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)46 static int max_98373_hw_params(struct snd_pcm_substream *substream,
47 			       struct snd_pcm_hw_params *params)
48 {
49 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
50 	struct snd_soc_dai *codec_dai;
51 	int j;
52 
53 	for_each_rtd_codec_dais(rtd, j, codec_dai) {
54 		if (!strcmp(codec_dai->component->name, MAX_98373_DEV0_NAME)) {
55 			/* DEV0 tdm slot configuration */
56 			snd_soc_dai_set_tdm_slot(codec_dai, 0x03, 3, 8, 32);
57 		}
58 		if (!strcmp(codec_dai->component->name, MAX_98373_DEV1_NAME)) {
59 			/* DEV1 tdm slot configuration */
60 			snd_soc_dai_set_tdm_slot(codec_dai, 0x0C, 3, 8, 32);
61 		}
62 	}
63 	return 0;
64 }
65 
max_98373_trigger(struct snd_pcm_substream * substream,int cmd)66 int max_98373_trigger(struct snd_pcm_substream *substream, int cmd)
67 {
68 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
69 	struct snd_soc_dai *codec_dai;
70 	struct snd_soc_dai *cpu_dai;
71 	int j;
72 	int ret = 0;
73 
74 	/* set spk pin by playback only */
75 	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
76 		return 0;
77 
78 	cpu_dai = asoc_rtd_to_cpu(rtd, 0);
79 	for_each_rtd_codec_dais(rtd, j, codec_dai) {
80 		struct snd_soc_dapm_context *dapm =
81 				snd_soc_component_get_dapm(cpu_dai->component);
82 		char pin_name[MAX_98373_PIN_NAME];
83 
84 		snprintf(pin_name, ARRAY_SIZE(pin_name), "%s Spk",
85 			 codec_dai->component->name_prefix);
86 
87 		switch (cmd) {
88 		case SNDRV_PCM_TRIGGER_START:
89 		case SNDRV_PCM_TRIGGER_RESUME:
90 		case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
91 			ret = snd_soc_dapm_enable_pin(dapm, pin_name);
92 			if (!ret)
93 				snd_soc_dapm_sync(dapm);
94 			break;
95 		case SNDRV_PCM_TRIGGER_STOP:
96 		case SNDRV_PCM_TRIGGER_SUSPEND:
97 		case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
98 			ret = snd_soc_dapm_disable_pin(dapm, pin_name);
99 			if (!ret)
100 				snd_soc_dapm_sync(dapm);
101 			break;
102 		default:
103 			break;
104 		}
105 	}
106 
107 	return ret;
108 }
109 EXPORT_SYMBOL_NS(max_98373_trigger, SND_SOC_INTEL_SOF_MAXIM_COMMON);
110 
111 struct snd_soc_ops max_98373_ops = {
112 	.hw_params = max_98373_hw_params,
113 	.trigger = max_98373_trigger,
114 };
115 EXPORT_SYMBOL_NS(max_98373_ops, SND_SOC_INTEL_SOF_MAXIM_COMMON);
116 
max_98373_spk_codec_init(struct snd_soc_pcm_runtime * rtd)117 int max_98373_spk_codec_init(struct snd_soc_pcm_runtime *rtd)
118 {
119 	struct snd_soc_card *card = rtd->card;
120 	int ret;
121 
122 	ret = snd_soc_dapm_add_routes(&card->dapm, max_98373_dapm_routes,
123 				      ARRAY_SIZE(max_98373_dapm_routes));
124 	if (ret)
125 		dev_err(rtd->dev, "Speaker map addition failed: %d\n", ret);
126 	return ret;
127 }
128 EXPORT_SYMBOL_NS(max_98373_spk_codec_init, SND_SOC_INTEL_SOF_MAXIM_COMMON);
129 
max_98373_set_codec_conf(struct snd_soc_card * card)130 void max_98373_set_codec_conf(struct snd_soc_card *card)
131 {
132 	card->codec_conf = max_98373_codec_conf;
133 	card->num_configs = ARRAY_SIZE(max_98373_codec_conf);
134 }
135 EXPORT_SYMBOL_NS(max_98373_set_codec_conf, SND_SOC_INTEL_SOF_MAXIM_COMMON);
136 
137 /*
138  * Maxim MAX98390
139  */
140 static const struct snd_soc_dapm_route max_98390_dapm_routes[] = {
141 	/* speaker */
142 	{ "Left Spk", NULL, "Left BE_OUT" },
143 	{ "Right Spk", NULL, "Right BE_OUT" },
144 };
145 
146 static const struct snd_kcontrol_new max_98390_tt_kcontrols[] = {
147 	SOC_DAPM_PIN_SWITCH("TL Spk"),
148 	SOC_DAPM_PIN_SWITCH("TR Spk"),
149 };
150 
151 static const struct snd_soc_dapm_widget max_98390_tt_dapm_widgets[] = {
152 	SND_SOC_DAPM_SPK("TL Spk", NULL),
153 	SND_SOC_DAPM_SPK("TR Spk", NULL),
154 };
155 
156 static const struct snd_soc_dapm_route max_98390_tt_dapm_routes[] = {
157 	/* Tweeter speaker */
158 	{ "TL Spk", NULL, "Tweeter Left BE_OUT" },
159 	{ "TR Spk", NULL, "Tweeter Right BE_OUT" },
160 };
161 
162 static struct snd_soc_codec_conf max_98390_codec_conf[] = {
163 	{
164 		.dlc = COMP_CODEC_CONF(MAX_98390_DEV0_NAME),
165 		.name_prefix = "Right",
166 	},
167 	{
168 		.dlc = COMP_CODEC_CONF(MAX_98390_DEV1_NAME),
169 		.name_prefix = "Left",
170 	},
171 };
172 
173 static struct snd_soc_codec_conf max_98390_4spk_codec_conf[] = {
174 	{
175 		.dlc = COMP_CODEC_CONF(MAX_98390_DEV0_NAME),
176 		.name_prefix = "Right",
177 	},
178 	{
179 		.dlc = COMP_CODEC_CONF(MAX_98390_DEV1_NAME),
180 		.name_prefix = "Left",
181 	},
182 	{
183 		.dlc = COMP_CODEC_CONF(MAX_98390_DEV2_NAME),
184 		.name_prefix = "Tweeter Right",
185 	},
186 	{
187 		.dlc = COMP_CODEC_CONF(MAX_98390_DEV3_NAME),
188 		.name_prefix = "Tweeter Left",
189 	},
190 };
191 
192 struct snd_soc_dai_link_component max_98390_components[] = {
193 	{
194 		.name = MAX_98390_DEV0_NAME,
195 		.dai_name = MAX_98390_CODEC_DAI,
196 	},
197 	{
198 		.name = MAX_98390_DEV1_NAME,
199 		.dai_name = MAX_98390_CODEC_DAI,
200 	},
201 };
202 EXPORT_SYMBOL_NS(max_98390_components, SND_SOC_INTEL_SOF_MAXIM_COMMON);
203 
204 struct snd_soc_dai_link_component max_98390_4spk_components[] = {
205 	{
206 		.name = MAX_98390_DEV0_NAME,
207 		.dai_name = MAX_98390_CODEC_DAI,
208 	},
209 	{
210 		.name = MAX_98390_DEV1_NAME,
211 		.dai_name = MAX_98390_CODEC_DAI,
212 	},
213 	{
214 		.name = MAX_98390_DEV2_NAME,
215 		.dai_name = MAX_98390_CODEC_DAI,
216 	},
217 	{
218 		.name = MAX_98390_DEV3_NAME,
219 		.dai_name = MAX_98390_CODEC_DAI,
220 	},
221 };
222 EXPORT_SYMBOL_NS(max_98390_4spk_components, SND_SOC_INTEL_SOF_MAXIM_COMMON);
223 
max_98390_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)224 static int max_98390_hw_params(struct snd_pcm_substream *substream,
225 			       struct snd_pcm_hw_params *params)
226 {
227 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
228 	struct snd_soc_dai *codec_dai;
229 	int i;
230 
231 	for_each_rtd_codec_dais(rtd, i, codec_dai) {
232 		if (i >= ARRAY_SIZE(max_98390_4spk_components)) {
233 			dev_err(codec_dai->dev, "invalid codec index %d\n", i);
234 			return -ENODEV;
235 		}
236 
237 		if (!strcmp(codec_dai->component->name, MAX_98390_DEV0_NAME)) {
238 			/* DEV0 tdm slot configuration Right */
239 			snd_soc_dai_set_tdm_slot(codec_dai, 0x01, 3, 4, 32);
240 		}
241 		if (!strcmp(codec_dai->component->name, MAX_98390_DEV1_NAME)) {
242 			/* DEV1 tdm slot configuration Left */
243 			snd_soc_dai_set_tdm_slot(codec_dai, 0x02, 3, 4, 32);
244 		}
245 
246 		if (!strcmp(codec_dai->component->name, MAX_98390_DEV2_NAME)) {
247 			/* DEVi2 tdm slot configuration Tweeter Right */
248 			snd_soc_dai_set_tdm_slot(codec_dai, 0x04, 3, 4, 32);
249 		}
250 		if (!strcmp(codec_dai->component->name, MAX_98390_DEV3_NAME)) {
251 			/* DEV3 tdm slot configuration Tweeter Left */
252 			snd_soc_dai_set_tdm_slot(codec_dai, 0x08, 3, 4, 32);
253 		}
254 	}
255 	return 0;
256 }
257 
max_98390_spk_codec_init(struct snd_soc_pcm_runtime * rtd)258 int max_98390_spk_codec_init(struct snd_soc_pcm_runtime *rtd)
259 {
260 	struct snd_soc_card *card = rtd->card;
261 	int ret;
262 
263 	/* add regular speakers dapm route */
264 	ret = snd_soc_dapm_add_routes(&card->dapm, max_98390_dapm_routes,
265 				      ARRAY_SIZE(max_98390_dapm_routes));
266 	if (ret) {
267 		dev_err(rtd->dev, "unable to add Left/Right Speaker dapm, ret %d\n", ret);
268 		return ret;
269 	}
270 
271 	/* add widgets/controls/dapm for tweeter speakers */
272 	if (acpi_dev_present("MX98390", "3", -1)) {
273 		ret = snd_soc_dapm_new_controls(&card->dapm, max_98390_tt_dapm_widgets,
274 						ARRAY_SIZE(max_98390_tt_dapm_widgets));
275 
276 		if (ret) {
277 			dev_err(rtd->dev, "unable to add tweeter dapm controls, ret %d\n", ret);
278 			/* Don't need to add routes if widget addition failed */
279 			return ret;
280 		}
281 
282 		ret = snd_soc_add_card_controls(card, max_98390_tt_kcontrols,
283 						ARRAY_SIZE(max_98390_tt_kcontrols));
284 		if (ret) {
285 			dev_err(rtd->dev, "unable to add tweeter card controls, ret %d\n", ret);
286 			return ret;
287 		}
288 
289 		ret = snd_soc_dapm_add_routes(&card->dapm, max_98390_tt_dapm_routes,
290 					      ARRAY_SIZE(max_98390_tt_dapm_routes));
291 		if (ret)
292 			dev_err(rtd->dev,
293 				"unable to add Tweeter Left/Right Speaker dapm, ret %d\n", ret);
294 	}
295 	return ret;
296 }
297 EXPORT_SYMBOL_NS(max_98390_spk_codec_init, SND_SOC_INTEL_SOF_MAXIM_COMMON);
298 
299 const struct snd_soc_ops max_98390_ops = {
300 	.hw_params = max_98390_hw_params,
301 };
302 EXPORT_SYMBOL_NS(max_98390_ops, SND_SOC_INTEL_SOF_MAXIM_COMMON);
303 
max_98390_set_codec_conf(struct snd_soc_card * card,int ch)304 void max_98390_set_codec_conf(struct snd_soc_card *card, int ch)
305 {
306 	if (ch == ARRAY_SIZE(max_98390_4spk_codec_conf)) {
307 		card->codec_conf = max_98390_4spk_codec_conf;
308 		card->num_configs = ARRAY_SIZE(max_98390_4spk_codec_conf);
309 	} else {
310 		card->codec_conf = max_98390_codec_conf;
311 		card->num_configs = ARRAY_SIZE(max_98390_codec_conf);
312 	}
313 }
314 EXPORT_SYMBOL_NS(max_98390_set_codec_conf, SND_SOC_INTEL_SOF_MAXIM_COMMON);
315 
316 /*
317  * Maxim MAX98357A/MAX98360A
318  */
319 static const struct snd_kcontrol_new max_98357a_kcontrols[] = {
320 	SOC_DAPM_PIN_SWITCH("Spk"),
321 };
322 
323 static const struct snd_soc_dapm_widget max_98357a_dapm_widgets[] = {
324 	SND_SOC_DAPM_SPK("Spk", NULL),
325 };
326 
327 static const struct snd_soc_dapm_route max_98357a_dapm_routes[] = {
328 	/* speaker */
329 	{"Spk", NULL, "Speaker"},
330 };
331 
332 static struct snd_soc_dai_link_component max_98357a_components[] = {
333 	{
334 		.name = MAX_98357A_DEV0_NAME,
335 		.dai_name = MAX_98357A_CODEC_DAI,
336 	}
337 };
338 
339 static struct snd_soc_dai_link_component max_98360a_components[] = {
340 	{
341 		.name = MAX_98360A_DEV0_NAME,
342 		.dai_name = MAX_98357A_CODEC_DAI,
343 	}
344 };
345 
max_98357a_init(struct snd_soc_pcm_runtime * rtd)346 static int max_98357a_init(struct snd_soc_pcm_runtime *rtd)
347 {
348 	struct snd_soc_card *card = rtd->card;
349 	int ret;
350 
351 	ret = snd_soc_dapm_new_controls(&card->dapm, max_98357a_dapm_widgets,
352 					ARRAY_SIZE(max_98357a_dapm_widgets));
353 	if (ret) {
354 		dev_err(rtd->dev, "unable to add dapm controls, ret %d\n", ret);
355 		/* Don't need to add routes if widget addition failed */
356 		return ret;
357 	}
358 
359 	ret = snd_soc_add_card_controls(card, max_98357a_kcontrols,
360 					ARRAY_SIZE(max_98357a_kcontrols));
361 	if (ret) {
362 		dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret);
363 		return ret;
364 	}
365 
366 	ret = snd_soc_dapm_add_routes(&card->dapm, max_98357a_dapm_routes,
367 				      ARRAY_SIZE(max_98357a_dapm_routes));
368 
369 	if (ret)
370 		dev_err(rtd->dev, "unable to add dapm routes, ret %d\n", ret);
371 
372 	return ret;
373 }
374 
max_98357a_dai_link(struct snd_soc_dai_link * link)375 void max_98357a_dai_link(struct snd_soc_dai_link *link)
376 {
377 	link->codecs = max_98357a_components;
378 	link->num_codecs = ARRAY_SIZE(max_98357a_components);
379 	link->init = max_98357a_init;
380 }
381 EXPORT_SYMBOL_NS(max_98357a_dai_link, SND_SOC_INTEL_SOF_MAXIM_COMMON);
382 
max_98360a_dai_link(struct snd_soc_dai_link * link)383 void max_98360a_dai_link(struct snd_soc_dai_link *link)
384 {
385 	link->codecs = max_98360a_components;
386 	link->num_codecs = ARRAY_SIZE(max_98360a_components);
387 	link->init = max_98357a_init;
388 }
389 EXPORT_SYMBOL_NS(max_98360a_dai_link, SND_SOC_INTEL_SOF_MAXIM_COMMON);
390 
391 MODULE_DESCRIPTION("ASoC Intel SOF Maxim helpers");
392 MODULE_LICENSE("GPL");
393