Linux Audio

Check our new training course

Loading...
v6.13.7
  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");
v6.2
  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
 16const 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};
 21EXPORT_SYMBOL_NS(max_98373_dapm_routes, SND_SOC_INTEL_SOF_MAXIM_COMMON);
 22
 23static 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
 34struct 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};
 44EXPORT_SYMBOL_NS(max_98373_components, SND_SOC_INTEL_SOF_MAXIM_COMMON);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 45
 46static 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
 66int 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}
109EXPORT_SYMBOL_NS(max_98373_trigger, SND_SOC_INTEL_SOF_MAXIM_COMMON);
110
111struct snd_soc_ops max_98373_ops = {
112	.hw_params = max_98373_hw_params,
113	.trigger = max_98373_trigger,
114};
115EXPORT_SYMBOL_NS(max_98373_ops, SND_SOC_INTEL_SOF_MAXIM_COMMON);
116
117int 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}
128EXPORT_SYMBOL_NS(max_98373_spk_codec_init, SND_SOC_INTEL_SOF_MAXIM_COMMON);
 
 
 
 
 
 
 
 
129
130void 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}
135EXPORT_SYMBOL_NS(max_98373_set_codec_conf, SND_SOC_INTEL_SOF_MAXIM_COMMON);
136
137/*
138 * Maxim MAX98390
139 */
140static 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
146static 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
151static 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
156static 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
162static 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
173static 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
192struct 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};
202EXPORT_SYMBOL_NS(max_98390_components, SND_SOC_INTEL_SOF_MAXIM_COMMON);
203
204struct 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};
222EXPORT_SYMBOL_NS(max_98390_4spk_components, SND_SOC_INTEL_SOF_MAXIM_COMMON);
 
 
 
 
 
 
 
 
 
223
224static 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
258int 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}
297EXPORT_SYMBOL_NS(max_98390_spk_codec_init, SND_SOC_INTEL_SOF_MAXIM_COMMON);
298
299const struct snd_soc_ops max_98390_ops = {
300	.hw_params = max_98390_hw_params,
301};
302EXPORT_SYMBOL_NS(max_98390_ops, SND_SOC_INTEL_SOF_MAXIM_COMMON);
303
304void 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}
314EXPORT_SYMBOL_NS(max_98390_set_codec_conf, SND_SOC_INTEL_SOF_MAXIM_COMMON);
315
316/*
317 * Maxim MAX98357A/MAX98360A
318 */
319static const struct snd_kcontrol_new max_98357a_kcontrols[] = {
320	SOC_DAPM_PIN_SWITCH("Spk"),
321};
322
323static const struct snd_soc_dapm_widget max_98357a_dapm_widgets[] = {
324	SND_SOC_DAPM_SPK("Spk", NULL),
325};
326
327static const struct snd_soc_dapm_route max_98357a_dapm_routes[] = {
328	/* speaker */
329	{"Spk", NULL, "Speaker"},
330};
331
332static 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
339static 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
346static 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
375void 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}
381EXPORT_SYMBOL_NS(max_98357a_dai_link, SND_SOC_INTEL_SOF_MAXIM_COMMON);
382
383void 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}
389EXPORT_SYMBOL_NS(max_98360a_dai_link, SND_SOC_INTEL_SOF_MAXIM_COMMON);
390
391MODULE_DESCRIPTION("ASoC Intel SOF Maxim helpers");
392MODULE_LICENSE("GPL");