]>
Commit | Line | Data |
---|---|---|
8c8ff982 SP |
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> | |
f9ec176c | 9 | #include <linux/math64.h> |
8c8ff982 SP |
10 | #include <linux/module.h> |
11 | #include <linux/of_device.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 | ||
7543f16a | 43 | static int __maybe_unused tegra210_dmic_runtime_suspend(struct device *dev) |
8c8ff982 SP |
44 | { |
45 | struct tegra210_dmic *dmic = dev_get_drvdata(dev); | |
46 | ||
47 | regcache_cache_only(dmic->regmap, true); | |
48 | regcache_mark_dirty(dmic->regmap); | |
49 | ||
50 | clk_disable_unprepare(dmic->clk_dmic); | |
51 | ||
52 | return 0; | |
53 | } | |
54 | ||
7543f16a | 55 | static int __maybe_unused tegra210_dmic_runtime_resume(struct device *dev) |
8c8ff982 SP |
56 | { |
57 | struct tegra210_dmic *dmic = dev_get_drvdata(dev); | |
58 | int err; | |
59 | ||
60 | err = clk_prepare_enable(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(dmic->regmap, false); | |
67 | regcache_sync(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(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(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(dmic->clk_dmic, 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(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 | (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) | |
f9ec176c | 133 | gain_q23 = div_u64(gain_q23 * dmic->boost_gain, 100); |
8c8ff982 SP |
134 | |
135 | regmap_write(dmic->regmap, TEGRA210_DMIC_LP_FILTER_GAIN, | |
136 | (unsigned int)gain_q23); | |
137 | ||
138 | switch (params_format(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(dmic->regmap, TEGRA210_DMIC_TX_CIF_CTRL, &cif_conf); | |
155 | ||
156 | return 0; | |
157 | } | |
158 | ||
d8e844be SP |
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(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(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(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(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(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(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(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(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, | |
8c8ff982 SP |
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(comp); | |
268 | ||
d8e844be | 269 | ucontrol->value.enumerated.item[0] = dmic->osr_val; |
8c8ff982 SP |
270 | |
271 | return 0; | |
272 | } | |
273 | ||
d8e844be SP |
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(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, | |
8c8ff982 SP |
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(comp); | |
8c8ff982 | 294 | |
d8e844be | 295 | ucontrol->value.enumerated.item[0] = dmic->lrsel; |
8c8ff982 SP |
296 | |
297 | return 0; | |
298 | } | |
299 | ||
d8e844be SP |
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(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 | ||
8c8ff982 SP |
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, | |
c6d152a8 | 342 | .symmetric_rate = 1, |
8c8ff982 SP |
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, | |
d8e844be SP |
401 | tegra210_dmic_get_boost_gain, |
402 | tegra210_dmic_put_boost_gain), | |
8c8ff982 | 403 | SOC_ENUM_EXT("Channel Select", tegra210_dmic_ch_enum, |
d8e844be | 404 | tegra210_dmic_get_ch_select, tegra210_dmic_put_ch_select), |
8c8ff982 | 405 | SOC_ENUM_EXT("Mono To Stereo", |
d8e844be SP |
406 | tegra210_dmic_mono_conv_enum, |
407 | tegra210_dmic_get_mono_to_stereo, | |
408 | tegra210_dmic_put_mono_to_stereo), | |
8c8ff982 | 409 | SOC_ENUM_EXT("Stereo To Mono", |
d8e844be SP |
410 | tegra210_dmic_stereo_conv_enum, |
411 | tegra210_dmic_get_stereo_to_mono, | |
412 | tegra210_dmic_put_stereo_to_mono), | |
8c8ff982 | 413 | SOC_ENUM_EXT("OSR Value", tegra210_dmic_osr_enum, |
d8e844be | 414 | tegra210_dmic_get_osr_val, tegra210_dmic_put_osr_val), |
8c8ff982 | 415 | SOC_ENUM_EXT("LR Polarity Select", tegra210_dmic_lrsel_enum, |
d8e844be | 416 | tegra210_dmic_get_pol_sel, tegra210_dmic_put_pol_sel), |
8c8ff982 SP |
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; | |
0246c6cb | 439 | } |
8c8ff982 SP |
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; | |
0246c6cb | 455 | } |
8c8ff982 SP |
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; | |
0246c6cb | 470 | } |
8c8ff982 SP |
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, 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, dmic); | |
504 | ||
505 | dmic->clk_dmic = devm_clk_get(dev, "dmic"); | |
506 | if (IS_ERR(dmic->clk_dmic)) { | |
507 | dev_err(dev, "can't retrieve DMIC clock\n"); | |
508 | return PTR_ERR(dmic->clk_dmic); | |
509 | } | |
510 | ||
511 | regs = devm_platform_ioremap_resource(pdev, 0); | |
512 | if (IS_ERR(regs)) | |
513 | return PTR_ERR(regs); | |
514 | ||
515 | dmic->regmap = devm_regmap_init_mmio(dev, regs, | |
516 | &tegra210_dmic_regmap_config); | |
517 | if (IS_ERR(dmic->regmap)) { | |
518 | dev_err(dev, "regmap init failed\n"); | |
519 | return PTR_ERR(dmic->regmap); | |
520 | } | |
521 | ||
522 | regcache_cache_only(dmic->regmap, true); | |
523 | ||
524 | err = devm_snd_soc_register_component(dev, &tegra210_dmic_compnt, | |
525 | 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 int tegra210_dmic_remove(struct platform_device *pdev) | |
538 | { | |
539 | pm_runtime_disable(&pdev->dev); | |
540 | ||
541 | return 0; | |
542 | } | |
543 | ||
544 | static const struct dev_pm_ops tegra210_dmic_pm_ops = { | |
545 | SET_RUNTIME_PM_OPS(tegra210_dmic_runtime_suspend, | |
546 | tegra210_dmic_runtime_resume, NULL) | |
547 | SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, | |
548 | pm_runtime_force_resume) | |
549 | }; | |
550 | ||
551 | static const struct of_device_id tegra210_dmic_of_match[] = { | |
552 | { .compatible = "nvidia,tegra210-dmic" }, | |
553 | {}, | |
554 | }; | |
555 | MODULE_DEVICE_TABLE(of, tegra210_dmic_of_match); | |
556 | ||
557 | static struct platform_driver tegra210_dmic_driver = { | |
558 | .driver = { | |
559 | .name = "tegra210-dmic", | |
560 | .of_match_table = tegra210_dmic_of_match, | |
561 | .pm = &tegra210_dmic_pm_ops, | |
562 | }, | |
563 | .probe = tegra210_dmic_probe, | |
564 | .remove = tegra210_dmic_remove, | |
565 | }; | |
566 | module_platform_driver(tegra210_dmic_driver) | |
567 | ||
568 | MODULE_AUTHOR("Rahul Mittal <rmittal@nvidia.com>"); | |
569 | MODULE_DESCRIPTION("Tegra210 ASoC DMIC driver"); | |
570 | MODULE_LICENSE("GPL v2"); |