File Index
Files and directories
Sign in
FILE

adau7118.c

File preview with syntax highlighting.

linux-6.19.14/sound/soc/codecs/adau7118.c C
  1// SPDX-License-Identifier: GPL-2.0
  2//
  3// Analog Devices ADAU7118 8 channel PDM-to-I2S/TDM Converter driver
  4//
  5// Copyright 2019 Analog Devices Inc.
  6
  7#include <linux/bitfield.h>
  8#include <linux/module.h>
  9#include <linux/regmap.h>
 10#include <linux/regulator/consumer.h>
 11#include <sound/pcm_params.h>
 12#include <sound/soc.h>
 13
 14#include "adau7118.h"
 15
 16#define ADAU7118_DEC_RATIO_MASK		GENMASK(1, 0)
 17#define ADAU7118_DEC_RATIO(x)		FIELD_PREP(ADAU7118_DEC_RATIO_MASK, x)
 18#define ADAU7118_CLK_MAP_MASK		GENMASK(7, 4)
 19#define ADAU7118_SLOT_WIDTH_MASK	GENMASK(5, 4)
 20#define ADAU7118_SLOT_WIDTH(x)		FIELD_PREP(ADAU7118_SLOT_WIDTH_MASK, x)
 21#define ADAU7118_TRISTATE_MASK		BIT(6)
 22#define ADAU7118_TRISTATE(x)		FIELD_PREP(ADAU7118_TRISTATE_MASK, x)
 23#define ADAU7118_DATA_FMT_MASK		GENMASK(3, 1)
 24#define ADAU7118_DATA_FMT(x)		FIELD_PREP(ADAU7118_DATA_FMT_MASK, x)
 25#define ADAU7118_SAI_MODE_MASK		BIT(0)
 26#define ADAU7118_SAI_MODE(x)		FIELD_PREP(ADAU7118_SAI_MODE_MASK, x)
 27#define ADAU7118_LRCLK_BCLK_POL_MASK	GENMASK(1, 0)
 28#define ADAU7118_LRCLK_BCLK_POL(x) \
 29				FIELD_PREP(ADAU7118_LRCLK_BCLK_POL_MASK, x)
 30#define ADAU7118_SPT_SLOT_MASK		GENMASK(7, 4)
 31#define ADAU7118_SPT_SLOT(x)		FIELD_PREP(ADAU7118_SPT_SLOT_MASK, x)
 32#define ADAU7118_FULL_SOFT_R_MASK	BIT(1)
 33#define ADAU7118_FULL_SOFT_R(x)		FIELD_PREP(ADAU7118_FULL_SOFT_R_MASK, x)
 34
 35struct adau7118_data {
 36	struct regmap *map;
 37	struct device *dev;
 38	struct regulator *iovdd;
 39	struct regulator *dvdd;
 40	u32 slot_width;
 41	u32 slots;
 42	bool hw_mode;
 43	bool right_j;
 44};
 45
 46/* Input Enable */
 47static const struct snd_kcontrol_new adau7118_dapm_pdm_control[4] = {
 48	SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 0, 1, 0),
 49	SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 1, 1, 0),
 50	SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 2, 1, 0),
 51	SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 3, 1, 0),
 52};
 53
 54static const struct snd_soc_dapm_widget adau7118_widgets_sw[] = {
 55	/* Input Enable Switches */
 56	SND_SOC_DAPM_SWITCH("PDM0", SND_SOC_NOPM, 0, 0,
 57			    &adau7118_dapm_pdm_control[0]),
 58	SND_SOC_DAPM_SWITCH("PDM1", SND_SOC_NOPM, 0, 0,
 59			    &adau7118_dapm_pdm_control[1]),
 60	SND_SOC_DAPM_SWITCH("PDM2", SND_SOC_NOPM, 0, 0,
 61			    &adau7118_dapm_pdm_control[2]),
 62	SND_SOC_DAPM_SWITCH("PDM3", SND_SOC_NOPM, 0, 0,
 63			    &adau7118_dapm_pdm_control[3]),
 64
 65	/* PDM Clocks */
 66	SND_SOC_DAPM_SUPPLY("PDM_CLK0", ADAU7118_REG_ENABLES, 4, 0, NULL, 0),
 67	SND_SOC_DAPM_SUPPLY("PDM_CLK1", ADAU7118_REG_ENABLES, 5, 0, NULL, 0),
 68
 69	/* Output channels */
 70	SND_SOC_DAPM_AIF_OUT("AIF1TX1", "Capture", 0, ADAU7118_REG_SPT_CX(0),
 71			     0, 0),
 72	SND_SOC_DAPM_AIF_OUT("AIF1TX2", "Capture", 0, ADAU7118_REG_SPT_CX(1),
 73			     0, 0),
 74	SND_SOC_DAPM_AIF_OUT("AIF1TX3", "Capture", 0, ADAU7118_REG_SPT_CX(2),
 75			     0, 0),
 76	SND_SOC_DAPM_AIF_OUT("AIF1TX4", "Capture", 0, ADAU7118_REG_SPT_CX(3),
 77			     0, 0),
 78	SND_SOC_DAPM_AIF_OUT("AIF1TX5", "Capture", 0, ADAU7118_REG_SPT_CX(4),
 79			     0, 0),
 80	SND_SOC_DAPM_AIF_OUT("AIF1TX6", "Capture", 0, ADAU7118_REG_SPT_CX(5),
 81			     0, 0),
 82	SND_SOC_DAPM_AIF_OUT("AIF1TX7", "Capture", 0, ADAU7118_REG_SPT_CX(6),
 83			     0, 0),
 84	SND_SOC_DAPM_AIF_OUT("AIF1TX8", "Capture", 0, ADAU7118_REG_SPT_CX(7),
 85			     0, 0),
 86};
 87
 88static const struct snd_soc_dapm_route adau7118_routes_sw[] = {
 89	{ "PDM0", "Capture Switch", "PDM_DAT0" },
 90	{ "PDM1", "Capture Switch", "PDM_DAT1" },
 91	{ "PDM2", "Capture Switch", "PDM_DAT2" },
 92	{ "PDM3", "Capture Switch", "PDM_DAT3" },
 93	{ "AIF1TX1", NULL, "PDM0" },
 94	{ "AIF1TX2", NULL, "PDM0" },
 95	{ "AIF1TX3", NULL, "PDM1" },
 96	{ "AIF1TX4", NULL, "PDM1" },
 97	{ "AIF1TX5", NULL, "PDM2" },
 98	{ "AIF1TX6", NULL, "PDM2" },
 99	{ "AIF1TX7", NULL, "PDM3" },
100	{ "AIF1TX8", NULL, "PDM3" },
101	{ "Capture", NULL, "PDM_CLK0" },
102	{ "Capture", NULL, "PDM_CLK1" },
103};
104
105static const struct snd_soc_dapm_widget adau7118_widgets_hw[] = {
106	SND_SOC_DAPM_AIF_OUT("AIF1TX", "Capture", 0, SND_SOC_NOPM, 0, 0),
107};
108
109static const struct snd_soc_dapm_route adau7118_routes_hw[] = {
110	{ "AIF1TX", NULL, "PDM_DAT0" },
111	{ "AIF1TX", NULL, "PDM_DAT1" },
112	{ "AIF1TX", NULL, "PDM_DAT2" },
113	{ "AIF1TX", NULL, "PDM_DAT3" },
114};
115
116static const struct snd_soc_dapm_widget adau7118_widgets[] = {
117	SND_SOC_DAPM_INPUT("PDM_DAT0"),
118	SND_SOC_DAPM_INPUT("PDM_DAT1"),
119	SND_SOC_DAPM_INPUT("PDM_DAT2"),
120	SND_SOC_DAPM_INPUT("PDM_DAT3"),
121};
122
123static int adau7118_set_channel_map(struct snd_soc_dai *dai,
124				    unsigned int tx_num,
125				    const unsigned int *tx_slot,
126				    unsigned int rx_num,
127				    const unsigned int *rx_slot)
128{
129	struct adau7118_data *st =
130		snd_soc_component_get_drvdata(dai->component);
131	int chan, ret;
132
133	dev_dbg(st->dev, "Set channel map, %d", tx_num);
134
135	for (chan = 0; chan < tx_num; chan++) {
136		ret = snd_soc_component_update_bits(dai->component,
137					ADAU7118_REG_SPT_CX(chan),
138					ADAU7118_SPT_SLOT_MASK,
139					ADAU7118_SPT_SLOT(tx_slot[chan]));
140		if (ret < 0)
141			return ret;
142	}
143
144	return 0;
145}
146
147static int adau7118_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
148{
149	struct adau7118_data *st =
150		snd_soc_component_get_drvdata(dai->component);
151	int ret = 0;
152	u32 regval;
153
154	dev_dbg(st->dev, "Set format, fmt:%d\n", fmt);
155
156	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
157	case SND_SOC_DAIFMT_I2S:
158		ret = snd_soc_component_update_bits(dai->component,
159						    ADAU7118_REG_SPT_CTRL1,
160						    ADAU7118_DATA_FMT_MASK,
161						    ADAU7118_DATA_FMT(0));
162		break;
163	case SND_SOC_DAIFMT_LEFT_J:
164		ret = snd_soc_component_update_bits(dai->component,
165						    ADAU7118_REG_SPT_CTRL1,
166						    ADAU7118_DATA_FMT_MASK,
167						    ADAU7118_DATA_FMT(1));
168		break;
169	case SND_SOC_DAIFMT_RIGHT_J:
170		st->right_j = true;
171		break;
172	case SND_SOC_DAIFMT_DSP_A:
173		ret = snd_soc_component_update_bits(dai->component,
174						    ADAU7118_REG_SPT_CTRL1,
175						    ADAU7118_DATA_FMT_MASK,
176						    ADAU7118_DATA_FMT(1));
177		break;
178	default:
179		dev_err(st->dev, "Invalid format %d",
180			fmt & SND_SOC_DAIFMT_FORMAT_MASK);
181		return -EINVAL;
182	}
183
184	if (ret < 0)
185		return ret;
186
187	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
188	case SND_SOC_DAIFMT_NB_NF:
189		regval = ADAU7118_LRCLK_BCLK_POL(0);
190		break;
191	case SND_SOC_DAIFMT_NB_IF:
192		regval = ADAU7118_LRCLK_BCLK_POL(2);
193		break;
194	case SND_SOC_DAIFMT_IB_NF:
195		regval = ADAU7118_LRCLK_BCLK_POL(1);
196		break;
197	case SND_SOC_DAIFMT_IB_IF:
198		regval = ADAU7118_LRCLK_BCLK_POL(3);
199		break;
200	default:
201		dev_err(st->dev, "Invalid Inv mask %d",
202			fmt & SND_SOC_DAIFMT_INV_MASK);
203		return -EINVAL;
204	}
205
206	ret = snd_soc_component_update_bits(dai->component,
207					    ADAU7118_REG_SPT_CTRL2,
208					    ADAU7118_LRCLK_BCLK_POL_MASK,
209					    regval);
210	if (ret < 0)
211		return ret;
212
213	return 0;
214}
215
216static int adau7118_set_tristate(struct snd_soc_dai *dai, int tristate)
217{
218	struct adau7118_data *st =
219		snd_soc_component_get_drvdata(dai->component);
220	int ret;
221
222	dev_dbg(st->dev, "Set tristate, %d\n", tristate);
223
224	ret = snd_soc_component_update_bits(dai->component,
225					    ADAU7118_REG_SPT_CTRL1,
226					    ADAU7118_TRISTATE_MASK,
227					    ADAU7118_TRISTATE(tristate));
228	if (ret < 0)
229		return ret;
230
231	return 0;
232}
233
234static int adau7118_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
235				 unsigned int rx_mask, int slots,
236				 int slot_width)
237{
238	struct adau7118_data *st =
239		snd_soc_component_get_drvdata(dai->component);
240	int ret = 0;
241	u32 regval;
242
243	dev_dbg(st->dev, "Set tdm, slots:%d width:%d\n", slots, slot_width);
244
245	switch (slot_width) {
246	case 32:
247		regval = ADAU7118_SLOT_WIDTH(0);
248		break;
249	case 24:
250		regval = ADAU7118_SLOT_WIDTH(2);
251		break;
252	case 16:
253		regval = ADAU7118_SLOT_WIDTH(1);
254		break;
255	default:
256		dev_err(st->dev, "Invalid slot width:%d\n", slot_width);
257		return -EINVAL;
258	}
259
260	ret = snd_soc_component_update_bits(dai->component,
261					    ADAU7118_REG_SPT_CTRL1,
262					    ADAU7118_SLOT_WIDTH_MASK, regval);
263	if (ret < 0)
264		return ret;
265
266	st->slot_width = slot_width;
267	st->slots = slots;
268
269	return 0;
270}
271
272static int adau7118_hw_params(struct snd_pcm_substream *substream,
273			      struct snd_pcm_hw_params *params,
274			      struct snd_soc_dai *dai)
275{
276	struct adau7118_data *st =
277		snd_soc_component_get_drvdata(dai->component);
278	u32 data_width = params_width(params), slots_width;
279	int ret;
280	u32 regval;
281
282	if (!st->slots) {
283		/* set stereo mode */
284		ret = snd_soc_component_update_bits(dai->component,
285						    ADAU7118_REG_SPT_CTRL1,
286						    ADAU7118_SAI_MODE_MASK,
287						    ADAU7118_SAI_MODE(0));
288		if (ret < 0)
289			return ret;
290
291		slots_width = 32;
292	} else {
293		slots_width = st->slot_width;
294	}
295
296	if (data_width > slots_width) {
297		dev_err(st->dev, "Invalid data_width:%d, slots_width:%d",
298			data_width, slots_width);
299		return -EINVAL;
300	}
301
302	if (st->right_j) {
303		switch (slots_width - data_width) {
304		case 8:
305			/* delay bclck by 8 */
306			regval = ADAU7118_DATA_FMT(2);
307			break;
308		case 12:
309			/* delay bclck by 12 */
310			regval = ADAU7118_DATA_FMT(3);
311			break;
312		case 16:
313			/* delay bclck by 16 */
314			regval = ADAU7118_DATA_FMT(4);
315			break;
316		default:
317			dev_err(st->dev,
318				"Cannot set right_j setting, slot_w:%d, data_w:%d\n",
319					slots_width, data_width);
320			return -EINVAL;
321		}
322
323		ret = snd_soc_component_update_bits(dai->component,
324						    ADAU7118_REG_SPT_CTRL1,
325						    ADAU7118_DATA_FMT_MASK,
326						    regval);
327		if (ret < 0)
328			return ret;
329	}
330
331	return 0;
332}
333
334static int adau7118_set_bias_level(struct snd_soc_component *component,
335				   enum snd_soc_bias_level level)
336{
337	struct adau7118_data *st = snd_soc_component_get_drvdata(component);
338	struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
339	int ret = 0;
340
341	dev_dbg(st->dev, "Set bias level %d\n", level);
342
343	switch (level) {
344	case SND_SOC_BIAS_ON:
345	case SND_SOC_BIAS_PREPARE:
346		break;
347
348	case SND_SOC_BIAS_STANDBY:
349		if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
350			/* power on */
351			ret = regulator_enable(st->iovdd);
352			if (ret)
353				return ret;
354
355			/* there's no timing constraints before enabling dvdd */
356			ret = regulator_enable(st->dvdd);
357			if (ret) {
358				regulator_disable(st->iovdd);
359				return ret;
360			}
361
362			if (st->hw_mode)
363				return 0;
364
365			regcache_cache_only(st->map, false);
366			/* sync cache */
367			ret = snd_soc_component_cache_sync(component);
368		}
369		break;
370	case SND_SOC_BIAS_OFF:
371		/* power off */
372		ret = regulator_disable(st->dvdd);
373		if (ret)
374			return ret;
375
376		ret = regulator_disable(st->iovdd);
377		if (ret)
378			return ret;
379
380		if (st->hw_mode)
381			return 0;
382
383		/* cache only */
384		regcache_mark_dirty(st->map);
385		regcache_cache_only(st->map, true);
386
387		break;
388	}
389
390	return ret;
391}
392
393static int adau7118_component_probe(struct snd_soc_component *component)
394{
395	struct adau7118_data *st = snd_soc_component_get_drvdata(component);
396	struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
397	int ret = 0;
398
399	if (st->hw_mode) {
400		ret = snd_soc_dapm_new_controls(dapm, adau7118_widgets_hw,
401					ARRAY_SIZE(adau7118_widgets_hw));
402		if (ret)
403			return ret;
404
405		ret = snd_soc_dapm_add_routes(dapm, adau7118_routes_hw,
406					      ARRAY_SIZE(adau7118_routes_hw));
407	} else {
408		snd_soc_component_init_regmap(component, st->map);
409		ret = snd_soc_dapm_new_controls(dapm, adau7118_widgets_sw,
410					ARRAY_SIZE(adau7118_widgets_sw));
411		if (ret)
412			return ret;
413
414		ret = snd_soc_dapm_add_routes(dapm, adau7118_routes_sw,
415					      ARRAY_SIZE(adau7118_routes_sw));
416	}
417
418	return ret;
419}
420
421static const struct snd_soc_dai_ops adau7118_ops = {
422	.hw_params = adau7118_hw_params,
423	.set_channel_map = adau7118_set_channel_map,
424	.set_fmt = adau7118_set_fmt,
425	.set_tdm_slot = adau7118_set_tdm_slot,
426	.set_tristate = adau7118_set_tristate,
427};
428
429static struct snd_soc_dai_driver adau7118_dai = {
430	.name = "adau7118-hifi-capture",
431	.capture = {
432		.stream_name = "Capture",
433		.channels_min = 1,
434		.channels_max = 8,
435		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |
436			SNDRV_PCM_FMTBIT_S20_LE | SNDRV_PCM_FMTBIT_S24_LE |
437			SNDRV_PCM_FMTBIT_S24_3LE,
438		.rates = SNDRV_PCM_RATE_CONTINUOUS,
439		.rate_min = 4000,
440		.rate_max = 192000,
441		.sig_bits = 24,
442	},
443};
444
445static const struct snd_soc_component_driver adau7118_component_driver = {
446	.probe			= adau7118_component_probe,
447	.set_bias_level		= adau7118_set_bias_level,
448	.dapm_widgets		= adau7118_widgets,
449	.num_dapm_widgets	= ARRAY_SIZE(adau7118_widgets),
450	.use_pmdown_time	= 1,
451	.endianness		= 1,
452};
453
454static int adau7118_regulator_setup(struct adau7118_data *st)
455{
456	st->iovdd = devm_regulator_get(st->dev, "iovdd");
457	if (IS_ERR(st->iovdd)) {
458		dev_err(st->dev, "Could not get iovdd: %ld\n",
459			PTR_ERR(st->iovdd));
460		return PTR_ERR(st->iovdd);
461	}
462
463	st->dvdd = devm_regulator_get(st->dev, "dvdd");
464	if (IS_ERR(st->dvdd)) {
465		dev_err(st->dev, "Could not get dvdd: %ld\n",
466			PTR_ERR(st->dvdd));
467		return PTR_ERR(st->dvdd);
468	}
469	/* just assume the device is in reset */
470	if (!st->hw_mode) {
471		regcache_mark_dirty(st->map);
472		regcache_cache_only(st->map, true);
473	}
474
475	return 0;
476}
477
478static int adau7118_parset_dt(const struct adau7118_data *st)
479{
480	int ret;
481	u32 dec_ratio = 0;
482	/* 4 inputs */
483	u32 clk_map[4], regval;
484
485	if (st->hw_mode)
486		return 0;
487
488	ret = device_property_read_u32(st->dev, "adi,decimation-ratio",
489				       &dec_ratio);
490	if (!ret) {
491		switch (dec_ratio) {
492		case 64:
493			regval = ADAU7118_DEC_RATIO(0);
494			break;
495		case 32:
496			regval = ADAU7118_DEC_RATIO(1);
497			break;
498		case 16:
499			regval = ADAU7118_DEC_RATIO(2);
500			break;
501		default:
502			dev_err(st->dev, "Invalid dec ratio: %u", dec_ratio);
503			return -EINVAL;
504		}
505
506		ret = regmap_update_bits(st->map,
507					 ADAU7118_REG_DEC_RATIO_CLK_MAP,
508					 ADAU7118_DEC_RATIO_MASK, regval);
509		if (ret)
510			return ret;
511	}
512
513	ret = device_property_read_u32_array(st->dev, "adi,pdm-clk-map",
514					     clk_map, ARRAY_SIZE(clk_map));
515	if (!ret) {
516		int pdm;
517		u32 _clk_map = 0;
518
519		for (pdm = 0; pdm < ARRAY_SIZE(clk_map); pdm++)
520			_clk_map |= (clk_map[pdm] << (pdm + 4));
521
522		ret = regmap_update_bits(st->map,
523					 ADAU7118_REG_DEC_RATIO_CLK_MAP,
524					 ADAU7118_CLK_MAP_MASK, _clk_map);
525		if (ret)
526			return ret;
527	}
528
529	return 0;
530}
531
532int adau7118_probe(struct device *dev, struct regmap *map, bool hw_mode)
533{
534	struct adau7118_data *st;
535	int ret;
536
537	st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
538	if (!st)
539		return -ENOMEM;
540
541	st->dev = dev;
542	st->hw_mode = hw_mode;
543	dev_set_drvdata(dev, st);
544
545	if (!hw_mode) {
546		st->map = map;
547		adau7118_dai.ops = &adau7118_ops;
548		/*
549		 * Perform a full soft reset. This will set all register's
550		 * with their reset values.
551		 */
552		ret = regmap_update_bits(map, ADAU7118_REG_RESET,
553					 ADAU7118_FULL_SOFT_R_MASK,
554					 ADAU7118_FULL_SOFT_R(1));
555		if (ret)
556			return ret;
557	}
558
559	ret = adau7118_parset_dt(st);
560	if (ret)
561		return ret;
562
563	ret = adau7118_regulator_setup(st);
564	if (ret)
565		return ret;
566
567	return devm_snd_soc_register_component(dev,
568					       &adau7118_component_driver,
569					       &adau7118_dai, 1);
570}
571EXPORT_SYMBOL_GPL(adau7118_probe);
572
573MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>");
574MODULE_DESCRIPTION("ADAU7118 8 channel PDM-to-I2S/TDM Converter driver");
575MODULE_LICENSE("GPL");
~/ Read-only file preview.