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