]>
Commit | Line | Data |
---|---|---|
ca514c0f NS |
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, unsigned int *tx_slot, | |
125 | unsigned int rx_num, unsigned int *rx_slot) | |
126 | { | |
127 | struct adau7118_data *st = | |
128 | snd_soc_component_get_drvdata(dai->component); | |
129 | int chan, ret; | |
130 | ||
131 | dev_dbg(st->dev, "Set channel map, %d", tx_num); | |
132 | ||
133 | for (chan = 0; chan < tx_num; chan++) { | |
134 | ret = snd_soc_component_update_bits(dai->component, | |
135 | ADAU7118_REG_SPT_CX(chan), | |
136 | ADAU7118_SPT_SLOT_MASK, | |
137 | ADAU7118_SPT_SLOT(tx_slot[chan])); | |
138 | if (ret < 0) | |
139 | return ret; | |
140 | } | |
141 | ||
142 | return 0; | |
143 | } | |
144 | ||
145 | static int adau7118_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) | |
146 | { | |
147 | struct adau7118_data *st = | |
148 | snd_soc_component_get_drvdata(dai->component); | |
149 | int ret = 0; | |
150 | u32 regval; | |
151 | ||
152 | dev_dbg(st->dev, "Set format, fmt:%d\n", fmt); | |
153 | ||
154 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | |
155 | case SND_SOC_DAIFMT_I2S: | |
156 | ret = snd_soc_component_update_bits(dai->component, | |
157 | ADAU7118_REG_SPT_CTRL1, | |
158 | ADAU7118_DATA_FMT_MASK, | |
159 | ADAU7118_DATA_FMT(0)); | |
160 | break; | |
161 | case SND_SOC_DAIFMT_LEFT_J: | |
162 | ret = snd_soc_component_update_bits(dai->component, | |
163 | ADAU7118_REG_SPT_CTRL1, | |
164 | ADAU7118_DATA_FMT_MASK, | |
165 | ADAU7118_DATA_FMT(1)); | |
166 | break; | |
167 | case SND_SOC_DAIFMT_RIGHT_J: | |
168 | st->right_j = true; | |
169 | break; | |
170 | default: | |
171 | dev_err(st->dev, "Invalid format %d", | |
172 | fmt & SND_SOC_DAIFMT_FORMAT_MASK); | |
173 | return -EINVAL; | |
174 | } | |
175 | ||
176 | if (ret < 0) | |
177 | return ret; | |
178 | ||
179 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | |
180 | case SND_SOC_DAIFMT_NB_NF: | |
181 | regval = ADAU7118_LRCLK_BCLK_POL(0); | |
182 | break; | |
183 | case SND_SOC_DAIFMT_NB_IF: | |
184 | regval = ADAU7118_LRCLK_BCLK_POL(2); | |
185 | break; | |
186 | case SND_SOC_DAIFMT_IB_NF: | |
187 | regval = ADAU7118_LRCLK_BCLK_POL(1); | |
188 | break; | |
189 | case SND_SOC_DAIFMT_IB_IF: | |
190 | regval = ADAU7118_LRCLK_BCLK_POL(3); | |
191 | break; | |
192 | default: | |
193 | dev_err(st->dev, "Invalid Inv mask %d", | |
194 | fmt & SND_SOC_DAIFMT_INV_MASK); | |
195 | return -EINVAL; | |
196 | } | |
197 | ||
198 | ret = snd_soc_component_update_bits(dai->component, | |
199 | ADAU7118_REG_SPT_CTRL2, | |
200 | ADAU7118_LRCLK_BCLK_POL_MASK, | |
201 | regval); | |
202 | if (ret < 0) | |
203 | return ret; | |
204 | ||
205 | return 0; | |
206 | } | |
207 | ||
208 | static int adau7118_set_tristate(struct snd_soc_dai *dai, int tristate) | |
209 | { | |
210 | struct adau7118_data *st = | |
211 | snd_soc_component_get_drvdata(dai->component); | |
212 | int ret; | |
213 | ||
214 | dev_dbg(st->dev, "Set tristate, %d\n", tristate); | |
215 | ||
216 | ret = snd_soc_component_update_bits(dai->component, | |
217 | ADAU7118_REG_SPT_CTRL1, | |
218 | ADAU7118_TRISTATE_MASK, | |
219 | ADAU7118_TRISTATE(tristate)); | |
220 | if (ret < 0) | |
221 | return ret; | |
222 | ||
223 | return 0; | |
224 | } | |
225 | ||
226 | static int adau7118_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, | |
227 | unsigned int rx_mask, int slots, | |
228 | int slot_width) | |
229 | { | |
230 | struct adau7118_data *st = | |
231 | snd_soc_component_get_drvdata(dai->component); | |
232 | int ret = 0; | |
233 | u32 regval; | |
234 | ||
235 | dev_dbg(st->dev, "Set tdm, slots:%d width:%d\n", slots, slot_width); | |
236 | ||
237 | switch (slot_width) { | |
238 | case 32: | |
239 | regval = ADAU7118_SLOT_WIDTH(0); | |
240 | break; | |
241 | case 24: | |
242 | regval = ADAU7118_SLOT_WIDTH(2); | |
243 | break; | |
244 | case 16: | |
245 | regval = ADAU7118_SLOT_WIDTH(1); | |
246 | break; | |
247 | default: | |
248 | dev_err(st->dev, "Invalid slot width:%d\n", slot_width); | |
249 | return -EINVAL; | |
250 | } | |
251 | ||
252 | ret = snd_soc_component_update_bits(dai->component, | |
253 | ADAU7118_REG_SPT_CTRL1, | |
254 | ADAU7118_SLOT_WIDTH_MASK, regval); | |
255 | if (ret < 0) | |
256 | return ret; | |
257 | ||
258 | st->slot_width = slot_width; | |
259 | st->slots = slots; | |
260 | ||
261 | return 0; | |
262 | } | |
263 | ||
264 | static int adau7118_hw_params(struct snd_pcm_substream *substream, | |
265 | struct snd_pcm_hw_params *params, | |
266 | struct snd_soc_dai *dai) | |
267 | { | |
268 | struct adau7118_data *st = | |
269 | snd_soc_component_get_drvdata(dai->component); | |
270 | u32 data_width = params_width(params), slots_width; | |
271 | int ret; | |
272 | u32 regval; | |
273 | ||
274 | if (!st->slots) { | |
275 | /* set stereo mode */ | |
276 | ret = snd_soc_component_update_bits(dai->component, | |
277 | ADAU7118_REG_SPT_CTRL1, | |
278 | ADAU7118_SAI_MODE_MASK, | |
279 | ADAU7118_SAI_MODE(0)); | |
280 | if (ret < 0) | |
281 | return ret; | |
282 | ||
283 | slots_width = 32; | |
284 | } else { | |
285 | slots_width = st->slot_width; | |
286 | } | |
287 | ||
288 | if (data_width > slots_width) { | |
289 | dev_err(st->dev, "Invalid data_width:%d, slots_width:%d", | |
290 | data_width, slots_width); | |
291 | return -EINVAL; | |
292 | } | |
293 | ||
294 | if (st->right_j) { | |
295 | switch (slots_width - data_width) { | |
296 | case 8: | |
297 | /* delay bclck by 8 */ | |
298 | regval = ADAU7118_DATA_FMT(2); | |
299 | break; | |
300 | case 12: | |
301 | /* delay bclck by 12 */ | |
302 | regval = ADAU7118_DATA_FMT(3); | |
303 | break; | |
304 | case 16: | |
305 | /* delay bclck by 16 */ | |
306 | regval = ADAU7118_DATA_FMT(4); | |
307 | break; | |
308 | default: | |
309 | dev_err(st->dev, | |
310 | "Cannot set right_j setting, slot_w:%d, data_w:%d\n", | |
311 | slots_width, data_width); | |
312 | return -EINVAL; | |
313 | } | |
314 | ||
315 | ret = snd_soc_component_update_bits(dai->component, | |
316 | ADAU7118_REG_SPT_CTRL1, | |
317 | ADAU7118_DATA_FMT_MASK, | |
318 | regval); | |
319 | if (ret < 0) | |
320 | return ret; | |
321 | } | |
322 | ||
323 | return 0; | |
324 | } | |
325 | ||
326 | static int adau7118_set_bias_level(struct snd_soc_component *component, | |
327 | enum snd_soc_bias_level level) | |
328 | { | |
329 | struct adau7118_data *st = snd_soc_component_get_drvdata(component); | |
330 | int ret = 0; | |
331 | ||
332 | dev_dbg(st->dev, "Set bias level %d\n", level); | |
333 | ||
334 | switch (level) { | |
335 | case SND_SOC_BIAS_ON: | |
336 | case SND_SOC_BIAS_PREPARE: | |
337 | break; | |
338 | ||
339 | case SND_SOC_BIAS_STANDBY: | |
340 | if (snd_soc_component_get_bias_level(component) == | |
341 | SND_SOC_BIAS_OFF) { | |
342 | /* power on */ | |
343 | ret = regulator_enable(st->iovdd); | |
344 | if (ret) | |
345 | return ret; | |
346 | ||
347 | /* there's no timing constraints before enabling dvdd */ | |
348 | ret = regulator_enable(st->dvdd); | |
349 | if (ret) { | |
350 | regulator_disable(st->iovdd); | |
351 | return ret; | |
352 | } | |
353 | ||
354 | if (st->hw_mode) | |
355 | return 0; | |
356 | ||
357 | regcache_cache_only(st->map, false); | |
358 | /* sync cache */ | |
359 | ret = snd_soc_component_cache_sync(component); | |
360 | } | |
361 | break; | |
362 | case SND_SOC_BIAS_OFF: | |
363 | /* power off */ | |
364 | ret = regulator_disable(st->dvdd); | |
365 | if (ret) | |
366 | return ret; | |
367 | ||
368 | ret = regulator_disable(st->iovdd); | |
369 | if (ret) | |
370 | return ret; | |
371 | ||
372 | if (st->hw_mode) | |
373 | return 0; | |
374 | ||
375 | /* cache only */ | |
376 | regcache_mark_dirty(st->map); | |
377 | regcache_cache_only(st->map, true); | |
378 | ||
379 | break; | |
380 | } | |
381 | ||
382 | return ret; | |
383 | } | |
384 | ||
385 | static int adau7118_component_probe(struct snd_soc_component *component) | |
386 | { | |
387 | struct adau7118_data *st = snd_soc_component_get_drvdata(component); | |
388 | struct snd_soc_dapm_context *dapm = | |
389 | snd_soc_component_get_dapm(component); | |
390 | int ret = 0; | |
391 | ||
392 | if (st->hw_mode) { | |
393 | ret = snd_soc_dapm_new_controls(dapm, adau7118_widgets_hw, | |
394 | ARRAY_SIZE(adau7118_widgets_hw)); | |
395 | if (ret) | |
396 | return ret; | |
397 | ||
398 | ret = snd_soc_dapm_add_routes(dapm, adau7118_routes_hw, | |
399 | ARRAY_SIZE(adau7118_routes_hw)); | |
400 | } else { | |
401 | snd_soc_component_init_regmap(component, st->map); | |
402 | ret = snd_soc_dapm_new_controls(dapm, adau7118_widgets_sw, | |
403 | ARRAY_SIZE(adau7118_widgets_sw)); | |
404 | if (ret) | |
405 | return ret; | |
406 | ||
407 | ret = snd_soc_dapm_add_routes(dapm, adau7118_routes_sw, | |
408 | ARRAY_SIZE(adau7118_routes_sw)); | |
409 | } | |
410 | ||
411 | return ret; | |
412 | } | |
413 | ||
414 | static const struct snd_soc_dai_ops adau7118_ops = { | |
415 | .hw_params = adau7118_hw_params, | |
416 | .set_channel_map = adau7118_set_channel_map, | |
417 | .set_fmt = adau7118_set_fmt, | |
418 | .set_tdm_slot = adau7118_set_tdm_slot, | |
419 | .set_tristate = adau7118_set_tristate, | |
420 | }; | |
421 | ||
422 | static struct snd_soc_dai_driver adau7118_dai = { | |
423 | .name = "adau7118-hifi-capture", | |
424 | .capture = { | |
425 | .stream_name = "Capture", | |
426 | .channels_min = 1, | |
427 | .channels_max = 8, | |
428 | .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | | |
429 | SNDRV_PCM_FMTBIT_S20_LE | SNDRV_PCM_FMTBIT_S24_LE | | |
430 | SNDRV_PCM_FMTBIT_S24_3LE, | |
431 | .rates = SNDRV_PCM_RATE_CONTINUOUS, | |
432 | .rate_min = 4000, | |
433 | .rate_max = 192000, | |
434 | .sig_bits = 24, | |
435 | }, | |
436 | }; | |
437 | ||
438 | static const struct snd_soc_component_driver adau7118_component_driver = { | |
439 | .probe = adau7118_component_probe, | |
440 | .set_bias_level = adau7118_set_bias_level, | |
441 | .dapm_widgets = adau7118_widgets, | |
442 | .num_dapm_widgets = ARRAY_SIZE(adau7118_widgets), | |
443 | .use_pmdown_time = 1, | |
444 | .endianness = 1, | |
445 | .non_legacy_dai_naming = 1, | |
446 | }; | |
447 | ||
448 | static void adau7118_regulator_disable(void *data) | |
449 | { | |
450 | struct adau7118_data *st = data; | |
451 | int ret; | |
452 | /* | |
453 | * If we fail to disable DVDD, don't bother in trying IOVDD. We | |
454 | * actually don't want to be left in the situation where DVDD | |
455 | * is enabled and IOVDD is disabled. | |
456 | */ | |
457 | ret = regulator_disable(st->dvdd); | |
458 | if (ret) | |
459 | return; | |
460 | ||
461 | regulator_disable(st->iovdd); | |
462 | } | |
463 | ||
464 | static int adau7118_regulator_setup(struct adau7118_data *st) | |
465 | { | |
b2d6ee75 | 466 | st->iovdd = devm_regulator_get(st->dev, "iovdd"); |
ca514c0f NS |
467 | if (IS_ERR(st->iovdd)) { |
468 | dev_err(st->dev, "Could not get iovdd: %ld\n", | |
469 | PTR_ERR(st->iovdd)); | |
470 | return PTR_ERR(st->iovdd); | |
471 | } | |
472 | ||
b2d6ee75 | 473 | st->dvdd = devm_regulator_get(st->dev, "dvdd"); |
ca514c0f NS |
474 | if (IS_ERR(st->dvdd)) { |
475 | dev_err(st->dev, "Could not get dvdd: %ld\n", | |
476 | PTR_ERR(st->dvdd)); | |
477 | return PTR_ERR(st->dvdd); | |
478 | } | |
479 | /* just assume the device is in reset */ | |
480 | if (!st->hw_mode) { | |
481 | regcache_mark_dirty(st->map); | |
482 | regcache_cache_only(st->map, true); | |
483 | } | |
484 | ||
485 | return devm_add_action_or_reset(st->dev, adau7118_regulator_disable, | |
486 | st); | |
487 | } | |
488 | ||
489 | static int adau7118_parset_dt(const struct adau7118_data *st) | |
490 | { | |
491 | int ret; | |
492 | u32 dec_ratio = 0; | |
493 | /* 4 inputs */ | |
494 | u32 clk_map[4], regval; | |
495 | ||
496 | if (st->hw_mode) | |
497 | return 0; | |
498 | ||
499 | ret = device_property_read_u32(st->dev, "adi,decimation-ratio", | |
500 | &dec_ratio); | |
501 | if (!ret) { | |
502 | switch (dec_ratio) { | |
503 | case 64: | |
504 | regval = ADAU7118_DEC_RATIO(0); | |
505 | break; | |
506 | case 32: | |
507 | regval = ADAU7118_DEC_RATIO(1); | |
508 | break; | |
509 | case 16: | |
510 | regval = ADAU7118_DEC_RATIO(2); | |
511 | break; | |
512 | default: | |
513 | dev_err(st->dev, "Invalid dec ratio: %u", dec_ratio); | |
514 | return -EINVAL; | |
515 | } | |
516 | ||
517 | ret = regmap_update_bits(st->map, | |
518 | ADAU7118_REG_DEC_RATIO_CLK_MAP, | |
519 | ADAU7118_DEC_RATIO_MASK, regval); | |
520 | if (ret) | |
521 | return ret; | |
522 | } | |
523 | ||
524 | ret = device_property_read_u32_array(st->dev, "adi,pdm-clk-map", | |
525 | clk_map, ARRAY_SIZE(clk_map)); | |
526 | if (!ret) { | |
527 | int pdm; | |
528 | u32 _clk_map = 0; | |
529 | ||
530 | for (pdm = 0; pdm < ARRAY_SIZE(clk_map); pdm++) | |
531 | _clk_map |= (clk_map[pdm] << (pdm + 4)); | |
532 | ||
533 | ret = regmap_update_bits(st->map, | |
534 | ADAU7118_REG_DEC_RATIO_CLK_MAP, | |
535 | ADAU7118_CLK_MAP_MASK, _clk_map); | |
536 | if (ret) | |
537 | return ret; | |
538 | } | |
539 | ||
540 | return 0; | |
541 | } | |
542 | ||
543 | int adau7118_probe(struct device *dev, struct regmap *map, bool hw_mode) | |
544 | { | |
545 | struct adau7118_data *st; | |
546 | int ret; | |
547 | ||
548 | st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); | |
549 | if (!st) | |
550 | return -ENOMEM; | |
551 | ||
552 | st->dev = dev; | |
553 | st->hw_mode = hw_mode; | |
554 | dev_set_drvdata(dev, st); | |
555 | ||
556 | if (!hw_mode) { | |
557 | st->map = map; | |
558 | adau7118_dai.ops = &adau7118_ops; | |
559 | /* | |
560 | * Perform a full soft reset. This will set all register's | |
561 | * with their reset values. | |
562 | */ | |
563 | ret = regmap_update_bits(map, ADAU7118_REG_RESET, | |
564 | ADAU7118_FULL_SOFT_R_MASK, | |
565 | ADAU7118_FULL_SOFT_R(1)); | |
566 | if (ret) | |
567 | return ret; | |
568 | } | |
569 | ||
570 | ret = adau7118_parset_dt(st); | |
571 | if (ret) | |
572 | return ret; | |
573 | ||
574 | ret = adau7118_regulator_setup(st); | |
575 | if (ret) | |
576 | return ret; | |
577 | ||
578 | return devm_snd_soc_register_component(dev, | |
579 | &adau7118_component_driver, | |
580 | &adau7118_dai, 1); | |
581 | } | |
582 | EXPORT_SYMBOL_GPL(adau7118_probe); | |
583 | ||
584 | MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>"); | |
585 | MODULE_DESCRIPTION("ADAU7118 8 channel PDM-to-I2S/TDM Converter driver"); | |
586 | MODULE_LICENSE("GPL"); |