Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/codecs/adau7118.c
26451 views
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
35
struct 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 */
47
static 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
54
static 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
88
static 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
105
static 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
109
static 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
116
static 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
123
static 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
147
static 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
216
static 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
234
static 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
272
static 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
334
static 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
int ret = 0;
339
340
dev_dbg(st->dev, "Set bias level %d\n", level);
341
342
switch (level) {
343
case SND_SOC_BIAS_ON:
344
case SND_SOC_BIAS_PREPARE:
345
break;
346
347
case SND_SOC_BIAS_STANDBY:
348
if (snd_soc_component_get_bias_level(component) ==
349
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
393
static 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 =
397
snd_soc_component_get_dapm(component);
398
int ret = 0;
399
400
if (st->hw_mode) {
401
ret = snd_soc_dapm_new_controls(dapm, adau7118_widgets_hw,
402
ARRAY_SIZE(adau7118_widgets_hw));
403
if (ret)
404
return ret;
405
406
ret = snd_soc_dapm_add_routes(dapm, adau7118_routes_hw,
407
ARRAY_SIZE(adau7118_routes_hw));
408
} else {
409
snd_soc_component_init_regmap(component, st->map);
410
ret = snd_soc_dapm_new_controls(dapm, adau7118_widgets_sw,
411
ARRAY_SIZE(adau7118_widgets_sw));
412
if (ret)
413
return ret;
414
415
ret = snd_soc_dapm_add_routes(dapm, adau7118_routes_sw,
416
ARRAY_SIZE(adau7118_routes_sw));
417
}
418
419
return ret;
420
}
421
422
static const struct snd_soc_dai_ops adau7118_ops = {
423
.hw_params = adau7118_hw_params,
424
.set_channel_map = adau7118_set_channel_map,
425
.set_fmt = adau7118_set_fmt,
426
.set_tdm_slot = adau7118_set_tdm_slot,
427
.set_tristate = adau7118_set_tristate,
428
};
429
430
static struct snd_soc_dai_driver adau7118_dai = {
431
.name = "adau7118-hifi-capture",
432
.capture = {
433
.stream_name = "Capture",
434
.channels_min = 1,
435
.channels_max = 8,
436
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |
437
SNDRV_PCM_FMTBIT_S20_LE | SNDRV_PCM_FMTBIT_S24_LE |
438
SNDRV_PCM_FMTBIT_S24_3LE,
439
.rates = SNDRV_PCM_RATE_CONTINUOUS,
440
.rate_min = 4000,
441
.rate_max = 192000,
442
.sig_bits = 24,
443
},
444
};
445
446
static const struct snd_soc_component_driver adau7118_component_driver = {
447
.probe = adau7118_component_probe,
448
.set_bias_level = adau7118_set_bias_level,
449
.dapm_widgets = adau7118_widgets,
450
.num_dapm_widgets = ARRAY_SIZE(adau7118_widgets),
451
.use_pmdown_time = 1,
452
.endianness = 1,
453
};
454
455
static int adau7118_regulator_setup(struct adau7118_data *st)
456
{
457
st->iovdd = devm_regulator_get(st->dev, "iovdd");
458
if (IS_ERR(st->iovdd)) {
459
dev_err(st->dev, "Could not get iovdd: %ld\n",
460
PTR_ERR(st->iovdd));
461
return PTR_ERR(st->iovdd);
462
}
463
464
st->dvdd = devm_regulator_get(st->dev, "dvdd");
465
if (IS_ERR(st->dvdd)) {
466
dev_err(st->dev, "Could not get dvdd: %ld\n",
467
PTR_ERR(st->dvdd));
468
return PTR_ERR(st->dvdd);
469
}
470
/* just assume the device is in reset */
471
if (!st->hw_mode) {
472
regcache_mark_dirty(st->map);
473
regcache_cache_only(st->map, true);
474
}
475
476
return 0;
477
}
478
479
static int adau7118_parset_dt(const struct adau7118_data *st)
480
{
481
int ret;
482
u32 dec_ratio = 0;
483
/* 4 inputs */
484
u32 clk_map[4], regval;
485
486
if (st->hw_mode)
487
return 0;
488
489
ret = device_property_read_u32(st->dev, "adi,decimation-ratio",
490
&dec_ratio);
491
if (!ret) {
492
switch (dec_ratio) {
493
case 64:
494
regval = ADAU7118_DEC_RATIO(0);
495
break;
496
case 32:
497
regval = ADAU7118_DEC_RATIO(1);
498
break;
499
case 16:
500
regval = ADAU7118_DEC_RATIO(2);
501
break;
502
default:
503
dev_err(st->dev, "Invalid dec ratio: %u", dec_ratio);
504
return -EINVAL;
505
}
506
507
ret = regmap_update_bits(st->map,
508
ADAU7118_REG_DEC_RATIO_CLK_MAP,
509
ADAU7118_DEC_RATIO_MASK, regval);
510
if (ret)
511
return ret;
512
}
513
514
ret = device_property_read_u32_array(st->dev, "adi,pdm-clk-map",
515
clk_map, ARRAY_SIZE(clk_map));
516
if (!ret) {
517
int pdm;
518
u32 _clk_map = 0;
519
520
for (pdm = 0; pdm < ARRAY_SIZE(clk_map); pdm++)
521
_clk_map |= (clk_map[pdm] << (pdm + 4));
522
523
ret = regmap_update_bits(st->map,
524
ADAU7118_REG_DEC_RATIO_CLK_MAP,
525
ADAU7118_CLK_MAP_MASK, _clk_map);
526
if (ret)
527
return ret;
528
}
529
530
return 0;
531
}
532
533
int adau7118_probe(struct device *dev, struct regmap *map, bool hw_mode)
534
{
535
struct adau7118_data *st;
536
int ret;
537
538
st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
539
if (!st)
540
return -ENOMEM;
541
542
st->dev = dev;
543
st->hw_mode = hw_mode;
544
dev_set_drvdata(dev, st);
545
546
if (!hw_mode) {
547
st->map = map;
548
adau7118_dai.ops = &adau7118_ops;
549
/*
550
* Perform a full soft reset. This will set all register's
551
* with their reset values.
552
*/
553
ret = regmap_update_bits(map, ADAU7118_REG_RESET,
554
ADAU7118_FULL_SOFT_R_MASK,
555
ADAU7118_FULL_SOFT_R(1));
556
if (ret)
557
return ret;
558
}
559
560
ret = adau7118_parset_dt(st);
561
if (ret)
562
return ret;
563
564
ret = adau7118_regulator_setup(st);
565
if (ret)
566
return ret;
567
568
return devm_snd_soc_register_component(dev,
569
&adau7118_component_driver,
570
&adau7118_dai, 1);
571
}
572
EXPORT_SYMBOL_GPL(adau7118_probe);
573
574
MODULE_AUTHOR("Nuno Sa <[email protected]>");
575
MODULE_DESCRIPTION("ADAU7118 8 channel PDM-to-I2S/TDM Converter driver");
576
MODULE_LICENSE("GPL");
577
578