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