1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | // |
3 | // tegra210_dmic.c - Tegra210 DMIC driver |
4 | // |
5 | // Copyright (c) 2020 NVIDIA CORPORATION. All rights reserved. |
6 | |
7 | #include <linux/clk.h> |
8 | #include <linux/device.h> |
9 | #include <linux/math64.h> |
10 | #include <linux/mod_devicetable.h> |
11 | #include <linux/module.h> |
12 | #include <linux/platform_device.h> |
13 | #include <linux/pm_runtime.h> |
14 | #include <linux/regmap.h> |
15 | #include <sound/core.h> |
16 | #include <sound/pcm_params.h> |
17 | #include <sound/soc.h> |
18 | #include "tegra210_dmic.h" |
19 | #include "tegra_cif.h" |
20 | |
21 | static const struct reg_default tegra210_dmic_reg_defaults[] = { |
22 | { TEGRA210_DMIC_TX_INT_MASK, 0x00000001 }, |
23 | { TEGRA210_DMIC_TX_CIF_CTRL, 0x00007700 }, |
24 | { TEGRA210_DMIC_CG, 0x1 }, |
25 | { TEGRA210_DMIC_CTRL, 0x00000301 }, |
26 | /* Below enables all filters - DCR, LP and SC */ |
27 | { TEGRA210_DMIC_DBG_CTRL, 0xe }, |
28 | /* Below as per latest POR value */ |
29 | { TEGRA210_DMIC_DCR_BIQUAD_0_COEF_4, 0x0 }, |
30 | /* LP filter is configured for pass through and used to apply gain */ |
31 | { TEGRA210_DMIC_LP_BIQUAD_0_COEF_0, 0x00800000 }, |
32 | { TEGRA210_DMIC_LP_BIQUAD_0_COEF_1, 0x0 }, |
33 | { TEGRA210_DMIC_LP_BIQUAD_0_COEF_2, 0x0 }, |
34 | { TEGRA210_DMIC_LP_BIQUAD_0_COEF_3, 0x0 }, |
35 | { TEGRA210_DMIC_LP_BIQUAD_0_COEF_4, 0x0 }, |
36 | { TEGRA210_DMIC_LP_BIQUAD_1_COEF_0, 0x00800000 }, |
37 | { TEGRA210_DMIC_LP_BIQUAD_1_COEF_1, 0x0 }, |
38 | { TEGRA210_DMIC_LP_BIQUAD_1_COEF_2, 0x0 }, |
39 | { TEGRA210_DMIC_LP_BIQUAD_1_COEF_3, 0x0 }, |
40 | { TEGRA210_DMIC_LP_BIQUAD_1_COEF_4, 0x0 }, |
41 | }; |
42 | |
43 | static int __maybe_unused tegra210_dmic_runtime_suspend(struct device *dev) |
44 | { |
45 | struct tegra210_dmic *dmic = dev_get_drvdata(dev); |
46 | |
47 | regcache_cache_only(map: dmic->regmap, enable: true); |
48 | regcache_mark_dirty(map: dmic->regmap); |
49 | |
50 | clk_disable_unprepare(clk: dmic->clk_dmic); |
51 | |
52 | return 0; |
53 | } |
54 | |
55 | static int __maybe_unused tegra210_dmic_runtime_resume(struct device *dev) |
56 | { |
57 | struct tegra210_dmic *dmic = dev_get_drvdata(dev); |
58 | int err; |
59 | |
60 | err = clk_prepare_enable(clk: dmic->clk_dmic); |
61 | if (err) { |
62 | dev_err(dev, "failed to enable DMIC clock, err: %d\n" , err); |
63 | return err; |
64 | } |
65 | |
66 | regcache_cache_only(map: dmic->regmap, enable: false); |
67 | regcache_sync(map: dmic->regmap); |
68 | |
69 | return 0; |
70 | } |
71 | |
72 | static int tegra210_dmic_hw_params(struct snd_pcm_substream *substream, |
73 | struct snd_pcm_hw_params *params, |
74 | struct snd_soc_dai *dai) |
75 | { |
76 | struct tegra210_dmic *dmic = snd_soc_dai_get_drvdata(dai); |
77 | unsigned int srate, clk_rate, channels; |
78 | struct tegra_cif_conf cif_conf; |
79 | unsigned long long gain_q23 = DEFAULT_GAIN_Q23; |
80 | int err; |
81 | |
82 | memset(&cif_conf, 0, sizeof(struct tegra_cif_conf)); |
83 | |
84 | channels = params_channels(p: params); |
85 | |
86 | cif_conf.audio_ch = channels; |
87 | |
88 | switch (dmic->ch_select) { |
89 | case DMIC_CH_SELECT_LEFT: |
90 | case DMIC_CH_SELECT_RIGHT: |
91 | cif_conf.client_ch = 1; |
92 | break; |
93 | case DMIC_CH_SELECT_STEREO: |
94 | cif_conf.client_ch = 2; |
95 | break; |
96 | default: |
97 | dev_err(dai->dev, "invalid DMIC client channels\n" ); |
98 | return -EINVAL; |
99 | } |
100 | |
101 | srate = params_rate(p: params); |
102 | |
103 | /* |
104 | * DMIC clock rate is a multiple of 'Over Sampling Ratio' and |
105 | * 'Sample Rate'. The supported OSR values are 64, 128 and 256. |
106 | */ |
107 | clk_rate = (DMIC_OSR_FACTOR << dmic->osr_val) * srate; |
108 | |
109 | err = clk_set_rate(clk: dmic->clk_dmic, rate: clk_rate); |
110 | if (err) { |
111 | dev_err(dai->dev, "can't set DMIC clock rate %u, err: %d\n" , |
112 | clk_rate, err); |
113 | return err; |
114 | } |
115 | |
116 | regmap_update_bits(map: dmic->regmap, |
117 | /* Reg */ |
118 | TEGRA210_DMIC_CTRL, |
119 | /* Mask */ |
120 | TEGRA210_DMIC_CTRL_LRSEL_POLARITY_MASK | |
121 | TEGRA210_DMIC_CTRL_OSR_MASK | |
122 | TEGRA210_DMIC_CTRL_CHANNEL_SELECT_MASK, |
123 | /* Value */ |
124 | val: (dmic->lrsel << LRSEL_POL_SHIFT) | |
125 | (dmic->osr_val << OSR_SHIFT) | |
126 | ((dmic->ch_select + 1) << CH_SEL_SHIFT)); |
127 | |
128 | /* |
129 | * Use LP filter gain register to apply boost. |
130 | * Boost Gain Volume control has 100x factor. |
131 | */ |
132 | if (dmic->boost_gain) |
133 | gain_q23 = div_u64(dividend: gain_q23 * dmic->boost_gain, divisor: 100); |
134 | |
135 | regmap_write(map: dmic->regmap, TEGRA210_DMIC_LP_FILTER_GAIN, |
136 | val: (unsigned int)gain_q23); |
137 | |
138 | switch (params_format(p: params)) { |
139 | case SNDRV_PCM_FORMAT_S16_LE: |
140 | cif_conf.audio_bits = TEGRA_ACIF_BITS_16; |
141 | break; |
142 | case SNDRV_PCM_FORMAT_S32_LE: |
143 | cif_conf.audio_bits = TEGRA_ACIF_BITS_32; |
144 | break; |
145 | default: |
146 | dev_err(dai->dev, "unsupported format!\n" ); |
147 | return -EOPNOTSUPP; |
148 | } |
149 | |
150 | cif_conf.client_bits = TEGRA_ACIF_BITS_24; |
151 | cif_conf.mono_conv = dmic->mono_to_stereo; |
152 | cif_conf.stereo_conv = dmic->stereo_to_mono; |
153 | |
154 | tegra_set_cif(regmap: dmic->regmap, TEGRA210_DMIC_TX_CIF_CTRL, conf: &cif_conf); |
155 | |
156 | return 0; |
157 | } |
158 | |
159 | static int tegra210_dmic_get_boost_gain(struct snd_kcontrol *kcontrol, |
160 | struct snd_ctl_elem_value *ucontrol) |
161 | { |
162 | struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); |
163 | struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(c: comp); |
164 | |
165 | ucontrol->value.integer.value[0] = dmic->boost_gain; |
166 | |
167 | return 0; |
168 | } |
169 | |
170 | static int tegra210_dmic_put_boost_gain(struct snd_kcontrol *kcontrol, |
171 | struct snd_ctl_elem_value *ucontrol) |
172 | { |
173 | struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); |
174 | struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(c: comp); |
175 | int value = ucontrol->value.integer.value[0]; |
176 | |
177 | if (value == dmic->boost_gain) |
178 | return 0; |
179 | |
180 | dmic->boost_gain = value; |
181 | |
182 | return 1; |
183 | } |
184 | |
185 | static int tegra210_dmic_get_ch_select(struct snd_kcontrol *kcontrol, |
186 | struct snd_ctl_elem_value *ucontrol) |
187 | { |
188 | struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); |
189 | struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(c: comp); |
190 | |
191 | ucontrol->value.enumerated.item[0] = dmic->ch_select; |
192 | |
193 | return 0; |
194 | } |
195 | |
196 | static int tegra210_dmic_put_ch_select(struct snd_kcontrol *kcontrol, |
197 | struct snd_ctl_elem_value *ucontrol) |
198 | { |
199 | struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); |
200 | struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(c: comp); |
201 | unsigned int value = ucontrol->value.enumerated.item[0]; |
202 | |
203 | if (value == dmic->ch_select) |
204 | return 0; |
205 | |
206 | dmic->ch_select = value; |
207 | |
208 | return 1; |
209 | } |
210 | |
211 | static int tegra210_dmic_get_mono_to_stereo(struct snd_kcontrol *kcontrol, |
212 | struct snd_ctl_elem_value *ucontrol) |
213 | { |
214 | struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); |
215 | struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(c: comp); |
216 | |
217 | ucontrol->value.enumerated.item[0] = dmic->mono_to_stereo; |
218 | |
219 | return 0; |
220 | } |
221 | |
222 | static int tegra210_dmic_put_mono_to_stereo(struct snd_kcontrol *kcontrol, |
223 | struct snd_ctl_elem_value *ucontrol) |
224 | { |
225 | struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); |
226 | struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(c: comp); |
227 | unsigned int value = ucontrol->value.enumerated.item[0]; |
228 | |
229 | if (value == dmic->mono_to_stereo) |
230 | return 0; |
231 | |
232 | dmic->mono_to_stereo = value; |
233 | |
234 | return 1; |
235 | } |
236 | |
237 | static int tegra210_dmic_get_stereo_to_mono(struct snd_kcontrol *kcontrol, |
238 | struct snd_ctl_elem_value *ucontrol) |
239 | { |
240 | struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); |
241 | struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(c: comp); |
242 | |
243 | ucontrol->value.enumerated.item[0] = dmic->stereo_to_mono; |
244 | |
245 | return 0; |
246 | } |
247 | |
248 | static int tegra210_dmic_put_stereo_to_mono(struct snd_kcontrol *kcontrol, |
249 | struct snd_ctl_elem_value *ucontrol) |
250 | { |
251 | struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); |
252 | struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(c: comp); |
253 | unsigned int value = ucontrol->value.enumerated.item[0]; |
254 | |
255 | if (value == dmic->stereo_to_mono) |
256 | return 0; |
257 | |
258 | dmic->stereo_to_mono = value; |
259 | |
260 | return 1; |
261 | } |
262 | |
263 | static int tegra210_dmic_get_osr_val(struct snd_kcontrol *kcontrol, |
264 | struct snd_ctl_elem_value *ucontrol) |
265 | { |
266 | struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); |
267 | struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(c: comp); |
268 | |
269 | ucontrol->value.enumerated.item[0] = dmic->osr_val; |
270 | |
271 | return 0; |
272 | } |
273 | |
274 | static int tegra210_dmic_put_osr_val(struct snd_kcontrol *kcontrol, |
275 | struct snd_ctl_elem_value *ucontrol) |
276 | { |
277 | struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); |
278 | struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(c: comp); |
279 | unsigned int value = ucontrol->value.enumerated.item[0]; |
280 | |
281 | if (value == dmic->osr_val) |
282 | return 0; |
283 | |
284 | dmic->osr_val = value; |
285 | |
286 | return 1; |
287 | } |
288 | |
289 | static int tegra210_dmic_get_pol_sel(struct snd_kcontrol *kcontrol, |
290 | struct snd_ctl_elem_value *ucontrol) |
291 | { |
292 | struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); |
293 | struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(c: comp); |
294 | |
295 | ucontrol->value.enumerated.item[0] = dmic->lrsel; |
296 | |
297 | return 0; |
298 | } |
299 | |
300 | static int tegra210_dmic_put_pol_sel(struct snd_kcontrol *kcontrol, |
301 | struct snd_ctl_elem_value *ucontrol) |
302 | { |
303 | struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); |
304 | struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(c: comp); |
305 | unsigned int value = ucontrol->value.enumerated.item[0]; |
306 | |
307 | if (value == dmic->lrsel) |
308 | return 0; |
309 | |
310 | dmic->lrsel = value; |
311 | |
312 | return 1; |
313 | } |
314 | |
315 | static const struct snd_soc_dai_ops tegra210_dmic_dai_ops = { |
316 | .hw_params = tegra210_dmic_hw_params, |
317 | }; |
318 | |
319 | static struct snd_soc_dai_driver tegra210_dmic_dais[] = { |
320 | { |
321 | .name = "DMIC-CIF" , |
322 | .capture = { |
323 | .stream_name = "CIF-Capture" , |
324 | .channels_min = 1, |
325 | .channels_max = 2, |
326 | .rates = SNDRV_PCM_RATE_8000_48000, |
327 | .formats = SNDRV_PCM_FMTBIT_S16_LE | |
328 | SNDRV_PCM_FMTBIT_S32_LE, |
329 | }, |
330 | }, |
331 | { |
332 | .name = "DMIC-DAP" , |
333 | .capture = { |
334 | .stream_name = "DAP-Capture" , |
335 | .channels_min = 1, |
336 | .channels_max = 2, |
337 | .rates = SNDRV_PCM_RATE_8000_48000, |
338 | .formats = SNDRV_PCM_FMTBIT_S16_LE | |
339 | SNDRV_PCM_FMTBIT_S32_LE, |
340 | }, |
341 | .ops = &tegra210_dmic_dai_ops, |
342 | .symmetric_rate = 1, |
343 | }, |
344 | }; |
345 | |
346 | static const struct snd_soc_dapm_widget tegra210_dmic_widgets[] = { |
347 | SND_SOC_DAPM_AIF_OUT("TX" , NULL, 0, TEGRA210_DMIC_ENABLE, 0, 0), |
348 | SND_SOC_DAPM_MIC("MIC" , NULL), |
349 | }; |
350 | |
351 | static const struct snd_soc_dapm_route tegra210_dmic_routes[] = { |
352 | { "XBAR-RX" , NULL, "XBAR-Capture" }, |
353 | { "XBAR-Capture" , NULL, "CIF-Capture" }, |
354 | { "CIF-Capture" , NULL, "TX" }, |
355 | { "TX" , NULL, "DAP-Capture" }, |
356 | { "DAP-Capture" , NULL, "MIC" }, |
357 | }; |
358 | |
359 | static const char * const tegra210_dmic_ch_select[] = { |
360 | "Left" , "Right" , "Stereo" , |
361 | }; |
362 | |
363 | static const struct soc_enum tegra210_dmic_ch_enum = |
364 | SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_dmic_ch_select), |
365 | tegra210_dmic_ch_select); |
366 | |
367 | static const char * const tegra210_dmic_mono_conv_text[] = { |
368 | "Zero" , "Copy" , |
369 | }; |
370 | |
371 | static const char * const tegra210_dmic_stereo_conv_text[] = { |
372 | "CH0" , "CH1" , "AVG" , |
373 | }; |
374 | |
375 | static const struct soc_enum tegra210_dmic_mono_conv_enum = |
376 | SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_dmic_mono_conv_text), |
377 | tegra210_dmic_mono_conv_text); |
378 | |
379 | static const struct soc_enum tegra210_dmic_stereo_conv_enum = |
380 | SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_dmic_stereo_conv_text), |
381 | tegra210_dmic_stereo_conv_text); |
382 | |
383 | static const char * const tegra210_dmic_osr_text[] = { |
384 | "OSR_64" , "OSR_128" , "OSR_256" , |
385 | }; |
386 | |
387 | static const struct soc_enum tegra210_dmic_osr_enum = |
388 | SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_dmic_osr_text), |
389 | tegra210_dmic_osr_text); |
390 | |
391 | static const char * const tegra210_dmic_lrsel_text[] = { |
392 | "Left" , "Right" , |
393 | }; |
394 | |
395 | static const struct soc_enum tegra210_dmic_lrsel_enum = |
396 | SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_dmic_lrsel_text), |
397 | tegra210_dmic_lrsel_text); |
398 | |
399 | static const struct snd_kcontrol_new tegra210_dmic_controls[] = { |
400 | SOC_SINGLE_EXT("Boost Gain Volume" , 0, 0, MAX_BOOST_GAIN, 0, |
401 | tegra210_dmic_get_boost_gain, |
402 | tegra210_dmic_put_boost_gain), |
403 | SOC_ENUM_EXT("Channel Select" , tegra210_dmic_ch_enum, |
404 | tegra210_dmic_get_ch_select, tegra210_dmic_put_ch_select), |
405 | SOC_ENUM_EXT("Mono To Stereo" , |
406 | tegra210_dmic_mono_conv_enum, |
407 | tegra210_dmic_get_mono_to_stereo, |
408 | tegra210_dmic_put_mono_to_stereo), |
409 | SOC_ENUM_EXT("Stereo To Mono" , |
410 | tegra210_dmic_stereo_conv_enum, |
411 | tegra210_dmic_get_stereo_to_mono, |
412 | tegra210_dmic_put_stereo_to_mono), |
413 | SOC_ENUM_EXT("OSR Value" , tegra210_dmic_osr_enum, |
414 | tegra210_dmic_get_osr_val, tegra210_dmic_put_osr_val), |
415 | SOC_ENUM_EXT("LR Polarity Select" , tegra210_dmic_lrsel_enum, |
416 | tegra210_dmic_get_pol_sel, tegra210_dmic_put_pol_sel), |
417 | }; |
418 | |
419 | static const struct snd_soc_component_driver tegra210_dmic_compnt = { |
420 | .dapm_widgets = tegra210_dmic_widgets, |
421 | .num_dapm_widgets = ARRAY_SIZE(tegra210_dmic_widgets), |
422 | .dapm_routes = tegra210_dmic_routes, |
423 | .num_dapm_routes = ARRAY_SIZE(tegra210_dmic_routes), |
424 | .controls = tegra210_dmic_controls, |
425 | .num_controls = ARRAY_SIZE(tegra210_dmic_controls), |
426 | }; |
427 | |
428 | static bool tegra210_dmic_wr_reg(struct device *dev, unsigned int reg) |
429 | { |
430 | switch (reg) { |
431 | case TEGRA210_DMIC_TX_INT_MASK ... TEGRA210_DMIC_TX_CIF_CTRL: |
432 | case TEGRA210_DMIC_ENABLE ... TEGRA210_DMIC_CG: |
433 | case TEGRA210_DMIC_CTRL: |
434 | case TEGRA210_DMIC_DBG_CTRL: |
435 | case TEGRA210_DMIC_DCR_BIQUAD_0_COEF_4 ... TEGRA210_DMIC_LP_BIQUAD_1_COEF_4: |
436 | return true; |
437 | default: |
438 | return false; |
439 | } |
440 | } |
441 | |
442 | static bool tegra210_dmic_rd_reg(struct device *dev, unsigned int reg) |
443 | { |
444 | if (tegra210_dmic_wr_reg(dev, reg)) |
445 | return true; |
446 | |
447 | switch (reg) { |
448 | case TEGRA210_DMIC_TX_STATUS: |
449 | case TEGRA210_DMIC_TX_INT_STATUS: |
450 | case TEGRA210_DMIC_STATUS: |
451 | case TEGRA210_DMIC_INT_STATUS: |
452 | return true; |
453 | default: |
454 | return false; |
455 | } |
456 | } |
457 | |
458 | static bool tegra210_dmic_volatile_reg(struct device *dev, unsigned int reg) |
459 | { |
460 | switch (reg) { |
461 | case TEGRA210_DMIC_TX_STATUS: |
462 | case TEGRA210_DMIC_TX_INT_STATUS: |
463 | case TEGRA210_DMIC_TX_INT_SET: |
464 | case TEGRA210_DMIC_SOFT_RESET: |
465 | case TEGRA210_DMIC_STATUS: |
466 | case TEGRA210_DMIC_INT_STATUS: |
467 | return true; |
468 | default: |
469 | return false; |
470 | } |
471 | } |
472 | |
473 | static const struct regmap_config tegra210_dmic_regmap_config = { |
474 | .reg_bits = 32, |
475 | .reg_stride = 4, |
476 | .val_bits = 32, |
477 | .max_register = TEGRA210_DMIC_LP_BIQUAD_1_COEF_4, |
478 | .writeable_reg = tegra210_dmic_wr_reg, |
479 | .readable_reg = tegra210_dmic_rd_reg, |
480 | .volatile_reg = tegra210_dmic_volatile_reg, |
481 | .reg_defaults = tegra210_dmic_reg_defaults, |
482 | .num_reg_defaults = ARRAY_SIZE(tegra210_dmic_reg_defaults), |
483 | .cache_type = REGCACHE_FLAT, |
484 | }; |
485 | |
486 | static int tegra210_dmic_probe(struct platform_device *pdev) |
487 | { |
488 | struct device *dev = &pdev->dev; |
489 | struct tegra210_dmic *dmic; |
490 | void __iomem *regs; |
491 | int err; |
492 | |
493 | dmic = devm_kzalloc(dev, size: sizeof(*dmic), GFP_KERNEL); |
494 | if (!dmic) |
495 | return -ENOMEM; |
496 | |
497 | dmic->osr_val = DMIC_OSR_64; |
498 | dmic->ch_select = DMIC_CH_SELECT_STEREO; |
499 | dmic->lrsel = DMIC_LRSEL_LEFT; |
500 | dmic->boost_gain = 0; |
501 | dmic->stereo_to_mono = 0; /* "CH0" */ |
502 | |
503 | dev_set_drvdata(dev, data: dmic); |
504 | |
505 | dmic->clk_dmic = devm_clk_get(dev, id: "dmic" ); |
506 | if (IS_ERR(ptr: dmic->clk_dmic)) { |
507 | dev_err(dev, "can't retrieve DMIC clock\n" ); |
508 | return PTR_ERR(ptr: dmic->clk_dmic); |
509 | } |
510 | |
511 | regs = devm_platform_ioremap_resource(pdev, index: 0); |
512 | if (IS_ERR(ptr: regs)) |
513 | return PTR_ERR(ptr: regs); |
514 | |
515 | dmic->regmap = devm_regmap_init_mmio(dev, regs, |
516 | &tegra210_dmic_regmap_config); |
517 | if (IS_ERR(ptr: dmic->regmap)) { |
518 | dev_err(dev, "regmap init failed\n" ); |
519 | return PTR_ERR(ptr: dmic->regmap); |
520 | } |
521 | |
522 | regcache_cache_only(map: dmic->regmap, enable: true); |
523 | |
524 | err = devm_snd_soc_register_component(dev, component_driver: &tegra210_dmic_compnt, |
525 | dai_drv: tegra210_dmic_dais, |
526 | ARRAY_SIZE(tegra210_dmic_dais)); |
527 | if (err) { |
528 | dev_err(dev, "can't register DMIC component, err: %d\n" , err); |
529 | return err; |
530 | } |
531 | |
532 | pm_runtime_enable(dev); |
533 | |
534 | return 0; |
535 | } |
536 | |
537 | static void tegra210_dmic_remove(struct platform_device *pdev) |
538 | { |
539 | pm_runtime_disable(dev: &pdev->dev); |
540 | } |
541 | |
542 | static const struct dev_pm_ops tegra210_dmic_pm_ops = { |
543 | SET_RUNTIME_PM_OPS(tegra210_dmic_runtime_suspend, |
544 | tegra210_dmic_runtime_resume, NULL) |
545 | SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, |
546 | pm_runtime_force_resume) |
547 | }; |
548 | |
549 | static const struct of_device_id tegra210_dmic_of_match[] = { |
550 | { .compatible = "nvidia,tegra210-dmic" }, |
551 | {}, |
552 | }; |
553 | MODULE_DEVICE_TABLE(of, tegra210_dmic_of_match); |
554 | |
555 | static struct platform_driver tegra210_dmic_driver = { |
556 | .driver = { |
557 | .name = "tegra210-dmic" , |
558 | .of_match_table = tegra210_dmic_of_match, |
559 | .pm = &tegra210_dmic_pm_ops, |
560 | }, |
561 | .probe = tegra210_dmic_probe, |
562 | .remove_new = tegra210_dmic_remove, |
563 | }; |
564 | module_platform_driver(tegra210_dmic_driver) |
565 | |
566 | MODULE_AUTHOR("Rahul Mittal <rmittal@nvidia.com>" ); |
567 | MODULE_DESCRIPTION("Tegra210 ASoC DMIC driver" ); |
568 | MODULE_LICENSE("GPL v2" ); |
569 | |