Loading...
1// SPDX-License-Identifier: GPL-2.0-only
2//
3// Copyright(c) 2020 Intel Corporation
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 <sound/sof.h>
13#include <uapi/sound/asound.h>
14#include "../common/soc-intel-quirks.h"
15#include "sof_maxim_common.h"
16
17/*
18 * Common structures and functions
19 */
20static const struct snd_kcontrol_new maxim_2spk_kcontrols[] = {
21 SOC_DAPM_PIN_SWITCH("Left Spk"),
22 SOC_DAPM_PIN_SWITCH("Right Spk"),
23
24};
25
26static const struct snd_soc_dapm_widget maxim_2spk_widgets[] = {
27 SND_SOC_DAPM_SPK("Left Spk", NULL),
28 SND_SOC_DAPM_SPK("Right Spk", NULL),
29};
30
31/* helper function to get the number of specific codec */
32static unsigned int get_num_codecs(const char *hid)
33{
34 struct acpi_device *adev;
35 unsigned int dev_num = 0;
36
37 for_each_acpi_dev_match(adev, hid, NULL, -1)
38 dev_num++;
39
40 return dev_num;
41}
42
43/*
44 * Maxim MAX98373
45 */
46#define MAX_98373_PIN_NAME 16
47
48static const struct snd_soc_dapm_route max_98373_dapm_routes[] = {
49 /* speaker */
50 { "Left Spk", NULL, "Left BE_OUT" },
51 { "Right Spk", NULL, "Right BE_OUT" },
52};
53
54static struct snd_soc_codec_conf max_98373_codec_conf[] = {
55 {
56 .dlc = COMP_CODEC_CONF(MAX_98373_DEV0_NAME),
57 .name_prefix = "Right",
58 },
59 {
60 .dlc = COMP_CODEC_CONF(MAX_98373_DEV1_NAME),
61 .name_prefix = "Left",
62 },
63};
64
65static struct snd_soc_dai_link_component max_98373_components[] = {
66 { /* For Right */
67 .name = MAX_98373_DEV0_NAME,
68 .dai_name = MAX_98373_CODEC_DAI,
69 },
70 { /* For Left */
71 .name = MAX_98373_DEV1_NAME,
72 .dai_name = MAX_98373_CODEC_DAI,
73 },
74};
75
76/*
77 * According to the definition of 'DAI Sel Mux' mixer in max98373.c, rx mask
78 * should choose two channels from TDM slots, the LSB of rx mask is left channel
79 * and the other one is right channel.
80 */
81static const struct {
82 unsigned int rx;
83} max_98373_tdm_mask[] = {
84 {.rx = 0x3},
85 {.rx = 0x3},
86};
87
88/*
89 * The tx mask indicates which channel(s) contains output IV-sense data and
90 * others should set to Hi-Z. Here we get the channel number from codec's ACPI
91 * device property "maxim,vmon-slot-no" and "maxim,imon-slot-no" to generate the
92 * mask. Refer to the max98373_slot_config() function in max98373.c codec driver.
93 */
94static unsigned int max_98373_get_tx_mask(struct device *dev)
95{
96 int vmon_slot;
97 int imon_slot;
98
99 if (device_property_read_u32(dev, "maxim,vmon-slot-no", &vmon_slot))
100 vmon_slot = 0;
101
102 if (device_property_read_u32(dev, "maxim,imon-slot-no", &imon_slot))
103 imon_slot = 1;
104
105 dev_dbg(dev, "vmon_slot %d imon_slot %d\n", vmon_slot, imon_slot);
106
107 return (0x1 << vmon_slot) | (0x1 << imon_slot);
108}
109
110static int max_98373_hw_params(struct snd_pcm_substream *substream,
111 struct snd_pcm_hw_params *params)
112{
113 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
114 struct snd_soc_dai_link *dai_link = rtd->dai_link;
115 struct snd_soc_dai *codec_dai;
116 int i;
117 int tdm_slots;
118 unsigned int tx_mask;
119 unsigned int tx_mask_used = 0x0;
120 int ret = 0;
121
122 for_each_rtd_codec_dais(rtd, i, codec_dai) {
123 if (i >= ARRAY_SIZE(max_98373_tdm_mask)) {
124 dev_err(codec_dai->dev, "only 2 amps are supported\n");
125 return -EINVAL;
126 }
127
128 switch (dai_link->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
129 case SND_SOC_DAIFMT_DSP_A:
130 case SND_SOC_DAIFMT_DSP_B:
131 /* get the tplg configured tdm slot number */
132 tdm_slots = sof_dai_get_tdm_slots(rtd);
133 if (tdm_slots <= 0) {
134 dev_err(rtd->dev, "invalid tdm slots %d\n",
135 tdm_slots);
136 return -EINVAL;
137 }
138
139 /* get the tx mask from ACPI device properties */
140 tx_mask = max_98373_get_tx_mask(codec_dai->dev);
141 if (!tx_mask)
142 return -EINVAL;
143
144 if (tx_mask & tx_mask_used) {
145 dev_err(codec_dai->dev, "invalid tx mask 0x%x, used 0x%x\n",
146 tx_mask, tx_mask_used);
147 return -EINVAL;
148 }
149
150 tx_mask_used |= tx_mask;
151
152 /*
153 * check if tdm slot number is too small for channel
154 * allocation
155 */
156 if (fls(tx_mask) > tdm_slots) {
157 dev_err(codec_dai->dev, "slot mismatch, tx %d slots %d\n",
158 fls(tx_mask), tdm_slots);
159 return -EINVAL;
160 }
161
162 if (fls(max_98373_tdm_mask[i].rx) > tdm_slots) {
163 dev_err(codec_dai->dev, "slot mismatch, rx %d slots %d\n",
164 fls(max_98373_tdm_mask[i].rx), tdm_slots);
165 return -EINVAL;
166 }
167
168 dev_dbg(codec_dai->dev, "set tdm slot: tx 0x%x rx 0x%x slots %d width %d\n",
169 tx_mask, max_98373_tdm_mask[i].rx,
170 tdm_slots, params_width(params));
171
172 ret = snd_soc_dai_set_tdm_slot(codec_dai, tx_mask,
173 max_98373_tdm_mask[i].rx,
174 tdm_slots,
175 params_width(params));
176 if (ret < 0) {
177 dev_err(codec_dai->dev, "fail to set tdm slot, ret %d\n",
178 ret);
179 return ret;
180 }
181 break;
182 default:
183 dev_dbg(codec_dai->dev, "codec is in I2S mode\n");
184 break;
185 }
186 }
187 return 0;
188}
189
190static int max_98373_trigger(struct snd_pcm_substream *substream, int cmd)
191{
192 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
193 struct snd_soc_dai *codec_dai;
194 struct snd_soc_dai *cpu_dai;
195 int j;
196 int ret = 0;
197
198 /* set spk pin by playback only */
199 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
200 return 0;
201
202 cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
203 for_each_rtd_codec_dais(rtd, j, codec_dai) {
204 struct snd_soc_dapm_context *dapm =
205 snd_soc_component_get_dapm(cpu_dai->component);
206 char pin_name[MAX_98373_PIN_NAME];
207
208 snprintf(pin_name, ARRAY_SIZE(pin_name), "%s Spk",
209 codec_dai->component->name_prefix);
210
211 switch (cmd) {
212 case SNDRV_PCM_TRIGGER_START:
213 case SNDRV_PCM_TRIGGER_RESUME:
214 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
215 ret = snd_soc_dapm_enable_pin(dapm, pin_name);
216 if (!ret)
217 snd_soc_dapm_sync(dapm);
218 break;
219 case SNDRV_PCM_TRIGGER_STOP:
220 case SNDRV_PCM_TRIGGER_SUSPEND:
221 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
222 ret = snd_soc_dapm_disable_pin(dapm, pin_name);
223 if (!ret)
224 snd_soc_dapm_sync(dapm);
225 break;
226 default:
227 break;
228 }
229 }
230
231 return ret;
232}
233
234static const struct snd_soc_ops max_98373_ops = {
235 .hw_params = max_98373_hw_params,
236 .trigger = max_98373_trigger,
237};
238
239static int max_98373_spk_codec_init(struct snd_soc_pcm_runtime *rtd)
240{
241 struct snd_soc_card *card = rtd->card;
242 unsigned int num_codecs = get_num_codecs(MAX_98373_ACPI_HID);
243 int ret;
244
245 switch (num_codecs) {
246 case 2:
247 ret = snd_soc_dapm_new_controls(&card->dapm, maxim_2spk_widgets,
248 ARRAY_SIZE(maxim_2spk_widgets));
249 if (ret) {
250 dev_err(rtd->dev, "fail to add max98373 widgets, ret %d\n",
251 ret);
252 return ret;
253 }
254
255 ret = snd_soc_add_card_controls(card, maxim_2spk_kcontrols,
256 ARRAY_SIZE(maxim_2spk_kcontrols));
257 if (ret) {
258 dev_err(rtd->dev, "fail to add max98373 kcontrols, ret %d\n",
259 ret);
260 return ret;
261 }
262
263 ret = snd_soc_dapm_add_routes(&card->dapm, max_98373_dapm_routes,
264 ARRAY_SIZE(max_98373_dapm_routes));
265 if (ret) {
266 dev_err(rtd->dev, "fail to add max98373 routes, ret %d\n",
267 ret);
268 return ret;
269 }
270 break;
271 default:
272 dev_err(rtd->dev, "max98373: invalid num_codecs %d\n", num_codecs);
273 return -EINVAL;
274 }
275
276 return ret;
277}
278
279void max_98373_dai_link(struct device *dev, struct snd_soc_dai_link *link)
280{
281 link->codecs = max_98373_components;
282 link->num_codecs = ARRAY_SIZE(max_98373_components);
283 link->init = max_98373_spk_codec_init;
284 link->ops = &max_98373_ops;
285}
286EXPORT_SYMBOL_NS(max_98373_dai_link, "SND_SOC_INTEL_SOF_MAXIM_COMMON");
287
288void max_98373_set_codec_conf(struct snd_soc_card *card)
289{
290 card->codec_conf = max_98373_codec_conf;
291 card->num_configs = ARRAY_SIZE(max_98373_codec_conf);
292}
293EXPORT_SYMBOL_NS(max_98373_set_codec_conf, "SND_SOC_INTEL_SOF_MAXIM_COMMON");
294
295/*
296 * Maxim MAX98390
297 */
298static const struct snd_soc_dapm_route max_98390_dapm_routes[] = {
299 /* speaker */
300 { "Left Spk", NULL, "Left BE_OUT" },
301 { "Right Spk", NULL, "Right BE_OUT" },
302};
303
304static const struct snd_kcontrol_new max_98390_tt_kcontrols[] = {
305 SOC_DAPM_PIN_SWITCH("TL Spk"),
306 SOC_DAPM_PIN_SWITCH("TR Spk"),
307};
308
309static const struct snd_soc_dapm_widget max_98390_tt_dapm_widgets[] = {
310 SND_SOC_DAPM_SPK("TL Spk", NULL),
311 SND_SOC_DAPM_SPK("TR Spk", NULL),
312};
313
314static const struct snd_soc_dapm_route max_98390_tt_dapm_routes[] = {
315 /* Tweeter speaker */
316 { "TL Spk", NULL, "Tweeter Left BE_OUT" },
317 { "TR Spk", NULL, "Tweeter Right BE_OUT" },
318};
319
320static struct snd_soc_codec_conf max_98390_cml_codec_conf[] = {
321 {
322 .dlc = COMP_CODEC_CONF(MAX_98390_DEV0_NAME),
323 .name_prefix = "Left",
324 },
325 {
326 .dlc = COMP_CODEC_CONF(MAX_98390_DEV1_NAME),
327 .name_prefix = "Right",
328 },
329};
330
331static struct snd_soc_codec_conf max_98390_codec_conf[] = {
332 {
333 .dlc = COMP_CODEC_CONF(MAX_98390_DEV0_NAME),
334 .name_prefix = "Right",
335 },
336 {
337 .dlc = COMP_CODEC_CONF(MAX_98390_DEV1_NAME),
338 .name_prefix = "Left",
339 },
340 {
341 .dlc = COMP_CODEC_CONF(MAX_98390_DEV2_NAME),
342 .name_prefix = "Tweeter Right",
343 },
344 {
345 .dlc = COMP_CODEC_CONF(MAX_98390_DEV3_NAME),
346 .name_prefix = "Tweeter Left",
347 },
348};
349
350static struct snd_soc_dai_link_component max_98390_components[] = {
351 {
352 .name = MAX_98390_DEV0_NAME,
353 .dai_name = MAX_98390_CODEC_DAI,
354 },
355 {
356 .name = MAX_98390_DEV1_NAME,
357 .dai_name = MAX_98390_CODEC_DAI,
358 },
359 {
360 .name = MAX_98390_DEV2_NAME,
361 .dai_name = MAX_98390_CODEC_DAI,
362 },
363 {
364 .name = MAX_98390_DEV3_NAME,
365 .dai_name = MAX_98390_CODEC_DAI,
366 },
367};
368
369static const struct {
370 unsigned int tx;
371 unsigned int rx;
372} max_98390_tdm_mask[] = {
373 {.tx = 0x01, .rx = 0x3},
374 {.tx = 0x02, .rx = 0x3},
375 {.tx = 0x04, .rx = 0x3},
376 {.tx = 0x08, .rx = 0x3},
377};
378
379static int max_98390_hw_params(struct snd_pcm_substream *substream,
380 struct snd_pcm_hw_params *params)
381{
382 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
383 struct snd_soc_dai_link *dai_link = rtd->dai_link;
384 struct snd_soc_dai *codec_dai;
385 int i, ret;
386
387 for_each_rtd_codec_dais(rtd, i, codec_dai) {
388 if (i >= ARRAY_SIZE(max_98390_tdm_mask)) {
389 dev_err(codec_dai->dev, "invalid codec index %d\n", i);
390 return -ENODEV;
391 }
392
393 switch (dai_link->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
394 case SND_SOC_DAIFMT_DSP_A:
395 case SND_SOC_DAIFMT_DSP_B:
396 /* 4-slot TDM */
397 ret = snd_soc_dai_set_tdm_slot(codec_dai,
398 max_98390_tdm_mask[i].tx,
399 max_98390_tdm_mask[i].rx,
400 4,
401 params_width(params));
402 if (ret < 0) {
403 dev_err(codec_dai->dev, "fail to set tdm slot, ret %d\n",
404 ret);
405 return ret;
406 }
407 break;
408 default:
409 dev_dbg(codec_dai->dev, "codec is in I2S mode\n");
410 break;
411 }
412 }
413 return 0;
414}
415
416static int max_98390_init(struct snd_soc_pcm_runtime *rtd)
417{
418 struct snd_soc_card *card = rtd->card;
419 unsigned int num_codecs = get_num_codecs(MAX_98390_ACPI_HID);
420 int ret;
421
422 switch (num_codecs) {
423 case 4:
424 /* add widgets/controls/dapm for tweeter speakers */
425 ret = snd_soc_dapm_new_controls(&card->dapm, max_98390_tt_dapm_widgets,
426 ARRAY_SIZE(max_98390_tt_dapm_widgets));
427 if (ret) {
428 dev_err(rtd->dev, "unable to add tweeter dapm widgets, ret %d\n",
429 ret);
430 /* Don't need to add routes if widget addition failed */
431 return ret;
432 }
433
434 ret = snd_soc_add_card_controls(card, max_98390_tt_kcontrols,
435 ARRAY_SIZE(max_98390_tt_kcontrols));
436 if (ret) {
437 dev_err(rtd->dev, "unable to add tweeter controls, ret %d\n",
438 ret);
439 return ret;
440 }
441
442 ret = snd_soc_dapm_add_routes(&card->dapm, max_98390_tt_dapm_routes,
443 ARRAY_SIZE(max_98390_tt_dapm_routes));
444 if (ret) {
445 dev_err(rtd->dev, "unable to add tweeter dapm routes, ret %d\n",
446 ret);
447 return ret;
448 }
449
450 fallthrough;
451 case 2:
452 /* add regular speakers dapm route */
453 ret = snd_soc_dapm_new_controls(&card->dapm, maxim_2spk_widgets,
454 ARRAY_SIZE(maxim_2spk_widgets));
455 if (ret) {
456 dev_err(rtd->dev, "fail to add max98390 woofer widgets, ret %d\n",
457 ret);
458 return ret;
459 }
460
461 ret = snd_soc_add_card_controls(card, maxim_2spk_kcontrols,
462 ARRAY_SIZE(maxim_2spk_kcontrols));
463 if (ret) {
464 dev_err(rtd->dev, "fail to add max98390 woofer kcontrols, ret %d\n",
465 ret);
466 return ret;
467 }
468
469 ret = snd_soc_dapm_add_routes(&card->dapm, max_98390_dapm_routes,
470 ARRAY_SIZE(max_98390_dapm_routes));
471 if (ret) {
472 dev_err(rtd->dev, "unable to add dapm routes, ret %d\n",
473 ret);
474 return ret;
475 }
476 break;
477 default:
478 dev_err(rtd->dev, "invalid codec number %d\n", num_codecs);
479 return -EINVAL;
480 }
481
482 return ret;
483}
484
485static const struct snd_soc_ops max_98390_ops = {
486 .hw_params = max_98390_hw_params,
487};
488
489void max_98390_dai_link(struct device *dev, struct snd_soc_dai_link *link)
490{
491 unsigned int num_codecs = get_num_codecs(MAX_98390_ACPI_HID);
492
493 link->codecs = max_98390_components;
494
495 switch (num_codecs) {
496 case 2:
497 case 4:
498 link->num_codecs = num_codecs;
499 break;
500 default:
501 dev_err(dev, "invalid codec number %d for %s\n", num_codecs,
502 MAX_98390_ACPI_HID);
503 break;
504 }
505
506 link->init = max_98390_init;
507 link->ops = &max_98390_ops;
508}
509EXPORT_SYMBOL_NS(max_98390_dai_link, "SND_SOC_INTEL_SOF_MAXIM_COMMON");
510
511void max_98390_set_codec_conf(struct device *dev, struct snd_soc_card *card)
512{
513 unsigned int num_codecs = get_num_codecs(MAX_98390_ACPI_HID);
514
515 card->codec_conf = max_98390_codec_conf;
516
517 switch (num_codecs) {
518 case 2:
519 if (soc_intel_is_cml())
520 card->codec_conf = max_98390_cml_codec_conf;
521
522 fallthrough;
523 case 4:
524 card->num_configs = num_codecs;
525 break;
526 default:
527 dev_err(dev, "invalid codec number %d for %s\n", num_codecs,
528 MAX_98390_ACPI_HID);
529 break;
530 }
531}
532EXPORT_SYMBOL_NS(max_98390_set_codec_conf, "SND_SOC_INTEL_SOF_MAXIM_COMMON");
533
534/*
535 * Maxim MAX98357A/MAX98360A
536 */
537static const struct snd_kcontrol_new max_98357a_kcontrols[] = {
538 SOC_DAPM_PIN_SWITCH("Spk"),
539};
540
541static const struct snd_soc_dapm_widget max_98357a_dapm_widgets[] = {
542 SND_SOC_DAPM_SPK("Spk", NULL),
543};
544
545static const struct snd_soc_dapm_route max_98357a_dapm_routes[] = {
546 /* speaker */
547 {"Spk", NULL, "Speaker"},
548};
549
550static struct snd_soc_dai_link_component max_98357a_components[] = {
551 {
552 .name = MAX_98357A_DEV0_NAME,
553 .dai_name = MAX_98357A_CODEC_DAI,
554 }
555};
556
557static struct snd_soc_dai_link_component max_98360a_components[] = {
558 {
559 .name = MAX_98360A_DEV0_NAME,
560 .dai_name = MAX_98357A_CODEC_DAI,
561 }
562};
563
564static int max_98357a_init(struct snd_soc_pcm_runtime *rtd)
565{
566 struct snd_soc_card *card = rtd->card;
567 int ret;
568
569 ret = snd_soc_dapm_new_controls(&card->dapm, max_98357a_dapm_widgets,
570 ARRAY_SIZE(max_98357a_dapm_widgets));
571 if (ret) {
572 dev_err(rtd->dev, "unable to add dapm controls, ret %d\n", ret);
573 /* Don't need to add routes if widget addition failed */
574 return ret;
575 }
576
577 ret = snd_soc_add_card_controls(card, max_98357a_kcontrols,
578 ARRAY_SIZE(max_98357a_kcontrols));
579 if (ret) {
580 dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret);
581 return ret;
582 }
583
584 ret = snd_soc_dapm_add_routes(&card->dapm, max_98357a_dapm_routes,
585 ARRAY_SIZE(max_98357a_dapm_routes));
586
587 if (ret)
588 dev_err(rtd->dev, "unable to add dapm routes, ret %d\n", ret);
589
590 return ret;
591}
592
593void max_98357a_dai_link(struct snd_soc_dai_link *link)
594{
595 link->codecs = max_98357a_components;
596 link->num_codecs = ARRAY_SIZE(max_98357a_components);
597 link->init = max_98357a_init;
598}
599EXPORT_SYMBOL_NS(max_98357a_dai_link, "SND_SOC_INTEL_SOF_MAXIM_COMMON");
600
601void max_98360a_dai_link(struct snd_soc_dai_link *link)
602{
603 link->codecs = max_98360a_components;
604 link->num_codecs = ARRAY_SIZE(max_98360a_components);
605 link->init = max_98357a_init;
606}
607EXPORT_SYMBOL_NS(max_98360a_dai_link, "SND_SOC_INTEL_SOF_MAXIM_COMMON");
608
609MODULE_DESCRIPTION("ASoC Intel SOF Maxim helpers");
610MODULE_LICENSE("GPL");
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");