1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | // |
3 | // tegra210_mixer.c - Tegra210 MIXER driver |
4 | // |
5 | // Copyright (c) 2021 NVIDIA CORPORATION. All rights reserved. |
6 | |
7 | #include <linux/clk.h> |
8 | #include <linux/device.h> |
9 | #include <linux/io.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.h> |
17 | #include <sound/pcm_params.h> |
18 | #include <sound/soc.h> |
19 | |
20 | #include "tegra210_mixer.h" |
21 | #include "tegra_cif.h" |
22 | |
23 | #define MIXER_REG(reg, id) ((reg) + ((id) * TEGRA210_MIXER_REG_STRIDE)) |
24 | #define MIXER_REG_BASE(reg) ((reg) % TEGRA210_MIXER_REG_STRIDE) |
25 | |
26 | #define MIXER_GAIN_CFG_RAM_ADDR(id) \ |
27 | (TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_0 + \ |
28 | ((id) * TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_STRIDE)) |
29 | |
30 | #define MIXER_RX_REG_DEFAULTS(id) \ |
31 | { MIXER_REG(TEGRA210_MIXER_RX1_CIF_CTRL, id), 0x00007700}, \ |
32 | { MIXER_REG(TEGRA210_MIXER_RX1_CTRL, id), 0x00010823}, \ |
33 | { MIXER_REG(TEGRA210_MIXER_RX1_PEAK_CTRL, id), 0x000012c0} |
34 | |
35 | #define MIXER_TX_REG_DEFAULTS(id) \ |
36 | { MIXER_REG(TEGRA210_MIXER_TX1_INT_MASK, (id)), 0x00000001}, \ |
37 | { MIXER_REG(TEGRA210_MIXER_TX1_CIF_CTRL, (id)), 0x00007700} |
38 | |
39 | #define REG_DURATION_PARAM(reg, i) ((reg) + NUM_GAIN_POLY_COEFFS + 1 + (i)) |
40 | |
41 | static const struct reg_default tegra210_mixer_reg_defaults[] = { |
42 | /* Inputs */ |
43 | MIXER_RX_REG_DEFAULTS(0), |
44 | MIXER_RX_REG_DEFAULTS(1), |
45 | MIXER_RX_REG_DEFAULTS(2), |
46 | MIXER_RX_REG_DEFAULTS(3), |
47 | MIXER_RX_REG_DEFAULTS(4), |
48 | MIXER_RX_REG_DEFAULTS(5), |
49 | MIXER_RX_REG_DEFAULTS(6), |
50 | MIXER_RX_REG_DEFAULTS(7), |
51 | MIXER_RX_REG_DEFAULTS(8), |
52 | MIXER_RX_REG_DEFAULTS(9), |
53 | /* Outputs */ |
54 | MIXER_TX_REG_DEFAULTS(0), |
55 | MIXER_TX_REG_DEFAULTS(1), |
56 | MIXER_TX_REG_DEFAULTS(2), |
57 | MIXER_TX_REG_DEFAULTS(3), |
58 | MIXER_TX_REG_DEFAULTS(4), |
59 | |
60 | { TEGRA210_MIXER_CG, 0x00000001}, |
61 | { TEGRA210_MIXER_GAIN_CFG_RAM_CTRL, 0x00004000}, |
62 | { TEGRA210_MIXER_PEAKM_RAM_CTRL, 0x00004000}, |
63 | { TEGRA210_MIXER_ENABLE, 0x1 }, |
64 | }; |
65 | |
66 | /* Default gain parameters */ |
67 | static const struct tegra210_mixer_gain_params gain_params = { |
68 | /* Polynomial coefficients */ |
69 | { 0, 0, 0, 0, 0, 0, 0, 0x1000000, 0 }, |
70 | /* Gain value */ |
71 | 0x10000, |
72 | /* Duration Parameters */ |
73 | { 0, 0, 0x400, 0x8000000 }, |
74 | }; |
75 | |
76 | static int __maybe_unused tegra210_mixer_runtime_suspend(struct device *dev) |
77 | { |
78 | struct tegra210_mixer *mixer = dev_get_drvdata(dev); |
79 | |
80 | regcache_cache_only(map: mixer->regmap, enable: true); |
81 | regcache_mark_dirty(map: mixer->regmap); |
82 | |
83 | return 0; |
84 | } |
85 | |
86 | static int __maybe_unused tegra210_mixer_runtime_resume(struct device *dev) |
87 | { |
88 | struct tegra210_mixer *mixer = dev_get_drvdata(dev); |
89 | |
90 | regcache_cache_only(map: mixer->regmap, enable: false); |
91 | regcache_sync(map: mixer->regmap); |
92 | |
93 | return 0; |
94 | } |
95 | |
96 | static int tegra210_mixer_write_ram(struct tegra210_mixer *mixer, |
97 | unsigned int addr, |
98 | unsigned int coef) |
99 | { |
100 | unsigned int reg, val; |
101 | int err; |
102 | |
103 | /* Check if busy */ |
104 | err = regmap_read_poll_timeout(mixer->regmap, |
105 | TEGRA210_MIXER_GAIN_CFG_RAM_CTRL, |
106 | val, !(val & 0x80000000), 10, 10000); |
107 | if (err < 0) |
108 | return err; |
109 | |
110 | reg = (addr << TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_SHIFT) & |
111 | TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_MASK; |
112 | reg |= TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_INIT_EN; |
113 | reg |= TEGRA210_MIXER_GAIN_CFG_RAM_RW_WRITE; |
114 | reg |= TEGRA210_MIXER_GAIN_CFG_RAM_SEQ_ACCESS_EN; |
115 | |
116 | regmap_write(map: mixer->regmap, |
117 | TEGRA210_MIXER_GAIN_CFG_RAM_CTRL, |
118 | val: reg); |
119 | regmap_write(map: mixer->regmap, |
120 | TEGRA210_MIXER_GAIN_CFG_RAM_DATA, |
121 | val: coef); |
122 | |
123 | return 0; |
124 | } |
125 | |
126 | static int tegra210_mixer_configure_gain(struct snd_soc_component *cmpnt, |
127 | unsigned int id, bool instant_gain) |
128 | { |
129 | struct tegra210_mixer *mixer = snd_soc_component_get_drvdata(c: cmpnt); |
130 | unsigned int reg = MIXER_GAIN_CFG_RAM_ADDR(id); |
131 | int err, i; |
132 | |
133 | pm_runtime_get_sync(dev: cmpnt->dev); |
134 | |
135 | /* Write default gain poly coefficients */ |
136 | for (i = 0; i < NUM_GAIN_POLY_COEFFS; i++) { |
137 | err = tegra210_mixer_write_ram(mixer, addr: reg + i, |
138 | coef: gain_params.poly_coeff[i]); |
139 | |
140 | if (err < 0) |
141 | goto rpm_put; |
142 | } |
143 | |
144 | /* Write stored gain value */ |
145 | err = tegra210_mixer_write_ram(mixer, addr: reg + NUM_GAIN_POLY_COEFFS, |
146 | coef: mixer->gain_value[id]); |
147 | if (err < 0) |
148 | goto rpm_put; |
149 | |
150 | /* Write duration parameters */ |
151 | for (i = 0; i < NUM_DURATION_PARMS; i++) { |
152 | int val; |
153 | |
154 | if (instant_gain) |
155 | val = 1; |
156 | else |
157 | val = gain_params.duration[i]; |
158 | |
159 | err = tegra210_mixer_write_ram(mixer, |
160 | REG_DURATION_PARAM(reg, i), |
161 | coef: val); |
162 | if (err < 0) |
163 | goto rpm_put; |
164 | } |
165 | |
166 | /* Trigger to apply gain configurations */ |
167 | err = tegra210_mixer_write_ram(mixer, addr: reg + REG_CFG_DONE_TRIGGER, |
168 | VAL_CFG_DONE_TRIGGER); |
169 | |
170 | rpm_put: |
171 | pm_runtime_put(dev: cmpnt->dev); |
172 | |
173 | return err; |
174 | } |
175 | |
176 | static int tegra210_mixer_get_gain(struct snd_kcontrol *kcontrol, |
177 | struct snd_ctl_elem_value *ucontrol) |
178 | { |
179 | struct soc_mixer_control *mc = |
180 | (struct soc_mixer_control *)kcontrol->private_value; |
181 | struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); |
182 | struct tegra210_mixer *mixer = snd_soc_component_get_drvdata(c: cmpnt); |
183 | unsigned int reg = mc->reg; |
184 | unsigned int i; |
185 | |
186 | i = (reg - TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_0) / |
187 | TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_STRIDE; |
188 | |
189 | ucontrol->value.integer.value[0] = mixer->gain_value[i]; |
190 | |
191 | return 0; |
192 | } |
193 | |
194 | static int tegra210_mixer_apply_gain(struct snd_kcontrol *kcontrol, |
195 | struct snd_ctl_elem_value *ucontrol, |
196 | bool instant_gain) |
197 | { |
198 | struct soc_mixer_control *mc = |
199 | (struct soc_mixer_control *)kcontrol->private_value; |
200 | struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); |
201 | struct tegra210_mixer *mixer = snd_soc_component_get_drvdata(c: cmpnt); |
202 | unsigned int reg = mc->reg, id; |
203 | int err; |
204 | |
205 | /* Save gain value for specific MIXER input */ |
206 | id = (reg - TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_0) / |
207 | TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_STRIDE; |
208 | |
209 | if (mixer->gain_value[id] == ucontrol->value.integer.value[0]) |
210 | return 0; |
211 | |
212 | mixer->gain_value[id] = ucontrol->value.integer.value[0]; |
213 | |
214 | err = tegra210_mixer_configure_gain(cmpnt, id, instant_gain); |
215 | if (err) { |
216 | dev_err(cmpnt->dev, "Failed to apply gain\n" ); |
217 | return err; |
218 | } |
219 | |
220 | return 1; |
221 | } |
222 | |
223 | static int tegra210_mixer_put_gain(struct snd_kcontrol *kcontrol, |
224 | struct snd_ctl_elem_value *ucontrol) |
225 | { |
226 | return tegra210_mixer_apply_gain(kcontrol, ucontrol, instant_gain: false); |
227 | } |
228 | |
229 | static int tegra210_mixer_put_instant_gain(struct snd_kcontrol *kcontrol, |
230 | struct snd_ctl_elem_value *ucontrol) |
231 | { |
232 | return tegra210_mixer_apply_gain(kcontrol, ucontrol, instant_gain: true); |
233 | } |
234 | |
235 | static int tegra210_mixer_set_audio_cif(struct tegra210_mixer *mixer, |
236 | struct snd_pcm_hw_params *params, |
237 | unsigned int reg, |
238 | unsigned int id) |
239 | { |
240 | unsigned int channels, audio_bits; |
241 | struct tegra_cif_conf cif_conf; |
242 | |
243 | memset(&cif_conf, 0, sizeof(struct tegra_cif_conf)); |
244 | |
245 | channels = params_channels(p: params); |
246 | |
247 | switch (params_format(p: params)) { |
248 | case SNDRV_PCM_FORMAT_S16_LE: |
249 | audio_bits = TEGRA_ACIF_BITS_16; |
250 | break; |
251 | case SNDRV_PCM_FORMAT_S32_LE: |
252 | audio_bits = TEGRA_ACIF_BITS_32; |
253 | break; |
254 | default: |
255 | return -EINVAL; |
256 | } |
257 | |
258 | cif_conf.audio_ch = channels; |
259 | cif_conf.client_ch = channels; |
260 | cif_conf.audio_bits = audio_bits; |
261 | cif_conf.client_bits = audio_bits; |
262 | |
263 | tegra_set_cif(regmap: mixer->regmap, |
264 | reg: reg + (id * TEGRA210_MIXER_REG_STRIDE), |
265 | conf: &cif_conf); |
266 | |
267 | return 0; |
268 | } |
269 | |
270 | static int tegra210_mixer_in_hw_params(struct snd_pcm_substream *substream, |
271 | struct snd_pcm_hw_params *params, |
272 | struct snd_soc_dai *dai) |
273 | { |
274 | struct tegra210_mixer *mixer = snd_soc_dai_get_drvdata(dai); |
275 | int err; |
276 | |
277 | err = tegra210_mixer_set_audio_cif(mixer, params, |
278 | TEGRA210_MIXER_RX1_CIF_CTRL, |
279 | id: dai->id); |
280 | if (err < 0) |
281 | return err; |
282 | |
283 | return tegra210_mixer_configure_gain(cmpnt: dai->component, id: dai->id, instant_gain: false); |
284 | } |
285 | |
286 | static int tegra210_mixer_out_hw_params(struct snd_pcm_substream *substream, |
287 | struct snd_pcm_hw_params *params, |
288 | struct snd_soc_dai *dai) |
289 | { |
290 | struct tegra210_mixer *mixer = snd_soc_dai_get_drvdata(dai); |
291 | |
292 | return tegra210_mixer_set_audio_cif(mixer, params, |
293 | TEGRA210_MIXER_TX1_CIF_CTRL, |
294 | id: dai->id - TEGRA210_MIXER_RX_MAX); |
295 | } |
296 | |
297 | static const struct snd_soc_dai_ops tegra210_mixer_out_dai_ops = { |
298 | .hw_params = tegra210_mixer_out_hw_params, |
299 | }; |
300 | |
301 | static const struct snd_soc_dai_ops tegra210_mixer_in_dai_ops = { |
302 | .hw_params = tegra210_mixer_in_hw_params, |
303 | }; |
304 | |
305 | #define IN_DAI(id) \ |
306 | { \ |
307 | .name = "MIXER-RX-CIF"#id, \ |
308 | .playback = { \ |
309 | .stream_name = "RX" #id "-CIF-Playback",\ |
310 | .channels_min = 1, \ |
311 | .channels_max = 8, \ |
312 | .rates = SNDRV_PCM_RATE_8000_192000, \ |
313 | .formats = SNDRV_PCM_FMTBIT_S8 | \ |
314 | SNDRV_PCM_FMTBIT_S16_LE | \ |
315 | SNDRV_PCM_FMTBIT_S32_LE, \ |
316 | }, \ |
317 | .capture = { \ |
318 | .stream_name = "RX" #id "-CIF-Capture", \ |
319 | .channels_min = 1, \ |
320 | .channels_max = 8, \ |
321 | .rates = SNDRV_PCM_RATE_8000_192000, \ |
322 | .formats = SNDRV_PCM_FMTBIT_S8 | \ |
323 | SNDRV_PCM_FMTBIT_S16_LE | \ |
324 | SNDRV_PCM_FMTBIT_S32_LE, \ |
325 | }, \ |
326 | .ops = &tegra210_mixer_in_dai_ops, \ |
327 | } |
328 | |
329 | #define OUT_DAI(id) \ |
330 | { \ |
331 | .name = "MIXER-TX-CIF" #id, \ |
332 | .playback = { \ |
333 | .stream_name = "TX" #id "-CIF-Playback",\ |
334 | .channels_min = 1, \ |
335 | .channels_max = 8, \ |
336 | .rates = SNDRV_PCM_RATE_8000_192000, \ |
337 | .formats = SNDRV_PCM_FMTBIT_S8 | \ |
338 | SNDRV_PCM_FMTBIT_S16_LE | \ |
339 | SNDRV_PCM_FMTBIT_S32_LE, \ |
340 | }, \ |
341 | .capture = { \ |
342 | .stream_name = "TX" #id "-CIF-Capture", \ |
343 | .channels_min = 1, \ |
344 | .channels_max = 8, \ |
345 | .rates = SNDRV_PCM_RATE_8000_192000, \ |
346 | .formats = SNDRV_PCM_FMTBIT_S8 | \ |
347 | SNDRV_PCM_FMTBIT_S16_LE | \ |
348 | SNDRV_PCM_FMTBIT_S32_LE, \ |
349 | }, \ |
350 | .ops = &tegra210_mixer_out_dai_ops, \ |
351 | } |
352 | |
353 | static struct snd_soc_dai_driver tegra210_mixer_dais[] = { |
354 | /* Mixer Input */ |
355 | IN_DAI(1), |
356 | IN_DAI(2), |
357 | IN_DAI(3), |
358 | IN_DAI(4), |
359 | IN_DAI(5), |
360 | IN_DAI(6), |
361 | IN_DAI(7), |
362 | IN_DAI(8), |
363 | IN_DAI(9), |
364 | IN_DAI(10), |
365 | |
366 | /* Mixer Output */ |
367 | OUT_DAI(1), |
368 | OUT_DAI(2), |
369 | OUT_DAI(3), |
370 | OUT_DAI(4), |
371 | OUT_DAI(5), |
372 | }; |
373 | |
374 | #define ADDER_CTRL_DECL(name, reg) \ |
375 | static const struct snd_kcontrol_new name[] = { \ |
376 | SOC_DAPM_SINGLE("RX1", reg, 0, 1, 0), \ |
377 | SOC_DAPM_SINGLE("RX2", reg, 1, 1, 0), \ |
378 | SOC_DAPM_SINGLE("RX3", reg, 2, 1, 0), \ |
379 | SOC_DAPM_SINGLE("RX4", reg, 3, 1, 0), \ |
380 | SOC_DAPM_SINGLE("RX5", reg, 4, 1, 0), \ |
381 | SOC_DAPM_SINGLE("RX6", reg, 5, 1, 0), \ |
382 | SOC_DAPM_SINGLE("RX7", reg, 6, 1, 0), \ |
383 | SOC_DAPM_SINGLE("RX8", reg, 7, 1, 0), \ |
384 | SOC_DAPM_SINGLE("RX9", reg, 8, 1, 0), \ |
385 | SOC_DAPM_SINGLE("RX10", reg, 9, 1, 0), \ |
386 | } |
387 | |
388 | ADDER_CTRL_DECL(adder1, TEGRA210_MIXER_TX1_ADDER_CONFIG); |
389 | ADDER_CTRL_DECL(adder2, TEGRA210_MIXER_TX2_ADDER_CONFIG); |
390 | ADDER_CTRL_DECL(adder3, TEGRA210_MIXER_TX3_ADDER_CONFIG); |
391 | ADDER_CTRL_DECL(adder4, TEGRA210_MIXER_TX4_ADDER_CONFIG); |
392 | ADDER_CTRL_DECL(adder5, TEGRA210_MIXER_TX5_ADDER_CONFIG); |
393 | |
394 | #define GAIN_CTRL(id) \ |
395 | SOC_SINGLE_EXT("RX" #id " Gain Volume", \ |
396 | MIXER_GAIN_CFG_RAM_ADDR((id) - 1), 0, \ |
397 | 0x20000, 0, tegra210_mixer_get_gain, \ |
398 | tegra210_mixer_put_gain), \ |
399 | SOC_SINGLE_EXT("RX" #id " Instant Gain Volume", \ |
400 | MIXER_GAIN_CFG_RAM_ADDR((id) - 1), 0, \ |
401 | 0x20000, 0, tegra210_mixer_get_gain, \ |
402 | tegra210_mixer_put_instant_gain), |
403 | |
404 | /* Volume controls for all MIXER inputs */ |
405 | static const struct snd_kcontrol_new tegra210_mixer_gain_ctls[] = { |
406 | GAIN_CTRL(1) |
407 | GAIN_CTRL(2) |
408 | GAIN_CTRL(3) |
409 | GAIN_CTRL(4) |
410 | GAIN_CTRL(5) |
411 | GAIN_CTRL(6) |
412 | GAIN_CTRL(7) |
413 | GAIN_CTRL(8) |
414 | GAIN_CTRL(9) |
415 | GAIN_CTRL(10) |
416 | }; |
417 | |
418 | static const struct snd_soc_dapm_widget tegra210_mixer_widgets[] = { |
419 | SND_SOC_DAPM_AIF_IN("RX1" , NULL, 0, SND_SOC_NOPM, 0, 0), |
420 | SND_SOC_DAPM_AIF_IN("RX2" , NULL, 0, SND_SOC_NOPM, 0, 0), |
421 | SND_SOC_DAPM_AIF_IN("RX3" , NULL, 0, SND_SOC_NOPM, 0, 0), |
422 | SND_SOC_DAPM_AIF_IN("RX4" , NULL, 0, SND_SOC_NOPM, 0, 0), |
423 | SND_SOC_DAPM_AIF_IN("RX5" , NULL, 0, SND_SOC_NOPM, 0, 0), |
424 | SND_SOC_DAPM_AIF_IN("RX6" , NULL, 0, SND_SOC_NOPM, 0, 0), |
425 | SND_SOC_DAPM_AIF_IN("RX7" , NULL, 0, SND_SOC_NOPM, 0, 0), |
426 | SND_SOC_DAPM_AIF_IN("RX8" , NULL, 0, SND_SOC_NOPM, 0, 0), |
427 | SND_SOC_DAPM_AIF_IN("RX9" , NULL, 0, SND_SOC_NOPM, 0, 0), |
428 | SND_SOC_DAPM_AIF_IN("RX10" , NULL, 0, SND_SOC_NOPM, 0, 0), |
429 | SND_SOC_DAPM_AIF_OUT("TX1" , NULL, 0, TEGRA210_MIXER_TX1_ENABLE, 0, 0), |
430 | SND_SOC_DAPM_AIF_OUT("TX2" , NULL, 0, TEGRA210_MIXER_TX2_ENABLE, 0, 0), |
431 | SND_SOC_DAPM_AIF_OUT("TX3" , NULL, 0, TEGRA210_MIXER_TX3_ENABLE, 0, 0), |
432 | SND_SOC_DAPM_AIF_OUT("TX4" , NULL, 0, TEGRA210_MIXER_TX4_ENABLE, 0, 0), |
433 | SND_SOC_DAPM_AIF_OUT("TX5" , NULL, 0, TEGRA210_MIXER_TX5_ENABLE, 0, 0), |
434 | SND_SOC_DAPM_MIXER("Adder1" , SND_SOC_NOPM, 1, 0, adder1, |
435 | ARRAY_SIZE(adder1)), |
436 | SND_SOC_DAPM_MIXER("Adder2" , SND_SOC_NOPM, 1, 0, adder2, |
437 | ARRAY_SIZE(adder2)), |
438 | SND_SOC_DAPM_MIXER("Adder3" , SND_SOC_NOPM, 1, 0, adder3, |
439 | ARRAY_SIZE(adder3)), |
440 | SND_SOC_DAPM_MIXER("Adder4" , SND_SOC_NOPM, 1, 0, adder4, |
441 | ARRAY_SIZE(adder4)), |
442 | SND_SOC_DAPM_MIXER("Adder5" , SND_SOC_NOPM, 1, 0, adder5, |
443 | ARRAY_SIZE(adder5)), |
444 | }; |
445 | |
446 | #define RX_ROUTES(id, sname) \ |
447 | { "RX" #id " XBAR-" sname, NULL, "RX" #id " XBAR-TX" }, \ |
448 | { "RX" #id "-CIF-" sname, NULL, "RX" #id " XBAR-" sname }, \ |
449 | { "RX" #id, NULL, "RX" #id "-CIF-" sname } |
450 | |
451 | #define MIXER_RX_ROUTES(id) \ |
452 | RX_ROUTES(id, "Playback"), \ |
453 | RX_ROUTES(id, "Capture") |
454 | |
455 | #define ADDER_ROUTES(id, sname) \ |
456 | { "Adder" #id, "RX1", "RX1" }, \ |
457 | { "Adder" #id, "RX2", "RX2" }, \ |
458 | { "Adder" #id, "RX3", "RX3" }, \ |
459 | { "Adder" #id, "RX4", "RX4" }, \ |
460 | { "Adder" #id, "RX5", "RX5" }, \ |
461 | { "Adder" #id, "RX6", "RX6" }, \ |
462 | { "Adder" #id, "RX7", "RX7" }, \ |
463 | { "Adder" #id, "RX8", "RX8" }, \ |
464 | { "Adder" #id, "RX9", "RX9" }, \ |
465 | { "Adder" #id, "RX10", "RX10" }, \ |
466 | { "TX" #id, NULL, "Adder" #id }, \ |
467 | { "TX" #id "-CIF-" sname, NULL, "TX" #id }, \ |
468 | { "TX" #id " XBAR-" sname, NULL, "TX" #id "-CIF-" sname }, \ |
469 | { "TX" #id " XBAR-RX", NULL, "TX" #id " XBAR-" sname } \ |
470 | |
471 | #define TX_ROUTES(id, sname) \ |
472 | ADDER_ROUTES(1, sname), \ |
473 | ADDER_ROUTES(2, sname), \ |
474 | ADDER_ROUTES(3, sname), \ |
475 | ADDER_ROUTES(4, sname), \ |
476 | ADDER_ROUTES(5, sname) |
477 | |
478 | #define MIXER_TX_ROUTES(id) \ |
479 | TX_ROUTES(id, "Playback"), \ |
480 | TX_ROUTES(id, "Capture") |
481 | |
482 | static const struct snd_soc_dapm_route tegra210_mixer_routes[] = { |
483 | /* Input */ |
484 | MIXER_RX_ROUTES(1), |
485 | MIXER_RX_ROUTES(2), |
486 | MIXER_RX_ROUTES(3), |
487 | MIXER_RX_ROUTES(4), |
488 | MIXER_RX_ROUTES(5), |
489 | MIXER_RX_ROUTES(6), |
490 | MIXER_RX_ROUTES(7), |
491 | MIXER_RX_ROUTES(8), |
492 | MIXER_RX_ROUTES(9), |
493 | MIXER_RX_ROUTES(10), |
494 | /* Output */ |
495 | MIXER_TX_ROUTES(1), |
496 | MIXER_TX_ROUTES(2), |
497 | MIXER_TX_ROUTES(3), |
498 | MIXER_TX_ROUTES(4), |
499 | MIXER_TX_ROUTES(5), |
500 | }; |
501 | |
502 | static const struct snd_soc_component_driver tegra210_mixer_cmpnt = { |
503 | .dapm_widgets = tegra210_mixer_widgets, |
504 | .num_dapm_widgets = ARRAY_SIZE(tegra210_mixer_widgets), |
505 | .dapm_routes = tegra210_mixer_routes, |
506 | .num_dapm_routes = ARRAY_SIZE(tegra210_mixer_routes), |
507 | .controls = tegra210_mixer_gain_ctls, |
508 | .num_controls = ARRAY_SIZE(tegra210_mixer_gain_ctls), |
509 | }; |
510 | |
511 | static bool tegra210_mixer_wr_reg(struct device *dev, |
512 | unsigned int reg) |
513 | { |
514 | if (reg < TEGRA210_MIXER_RX_LIMIT) |
515 | reg = MIXER_REG_BASE(reg); |
516 | else if (reg < TEGRA210_MIXER_TX_LIMIT) |
517 | reg = MIXER_REG_BASE(reg) + TEGRA210_MIXER_TX1_ENABLE; |
518 | |
519 | switch (reg) { |
520 | case TEGRA210_MIXER_RX1_SOFT_RESET: |
521 | case TEGRA210_MIXER_RX1_CIF_CTRL ... TEGRA210_MIXER_RX1_PEAK_CTRL: |
522 | |
523 | case TEGRA210_MIXER_TX1_ENABLE: |
524 | case TEGRA210_MIXER_TX1_SOFT_RESET: |
525 | case TEGRA210_MIXER_TX1_INT_MASK ... TEGRA210_MIXER_TX1_ADDER_CONFIG: |
526 | |
527 | case TEGRA210_MIXER_ENABLE ... TEGRA210_MIXER_CG: |
528 | case TEGRA210_MIXER_GAIN_CFG_RAM_CTRL ... TEGRA210_MIXER_CTRL: |
529 | return true; |
530 | default: |
531 | return false; |
532 | } |
533 | } |
534 | |
535 | static bool tegra210_mixer_rd_reg(struct device *dev, |
536 | unsigned int reg) |
537 | { |
538 | if (reg < TEGRA210_MIXER_RX_LIMIT) |
539 | reg = MIXER_REG_BASE(reg); |
540 | else if (reg < TEGRA210_MIXER_TX_LIMIT) |
541 | reg = MIXER_REG_BASE(reg) + TEGRA210_MIXER_TX1_ENABLE; |
542 | |
543 | switch (reg) { |
544 | case TEGRA210_MIXER_RX1_SOFT_RESET ... TEGRA210_MIXER_RX1_SAMPLE_COUNT: |
545 | case TEGRA210_MIXER_TX1_ENABLE ... TEGRA210_MIXER_TX1_ADDER_CONFIG: |
546 | case TEGRA210_MIXER_ENABLE ... TEGRA210_MIXER_CTRL: |
547 | return true; |
548 | default: |
549 | return false; |
550 | } |
551 | } |
552 | |
553 | static bool tegra210_mixer_volatile_reg(struct device *dev, |
554 | unsigned int reg) |
555 | { |
556 | if (reg < TEGRA210_MIXER_RX_LIMIT) |
557 | reg = MIXER_REG_BASE(reg); |
558 | else if (reg < TEGRA210_MIXER_TX_LIMIT) |
559 | reg = MIXER_REG_BASE(reg) + TEGRA210_MIXER_TX1_ENABLE; |
560 | |
561 | switch (reg) { |
562 | case TEGRA210_MIXER_RX1_SOFT_RESET: |
563 | case TEGRA210_MIXER_RX1_STATUS: |
564 | |
565 | case TEGRA210_MIXER_TX1_SOFT_RESET: |
566 | case TEGRA210_MIXER_TX1_STATUS: |
567 | case TEGRA210_MIXER_TX1_INT_STATUS: |
568 | case TEGRA210_MIXER_TX1_INT_SET: |
569 | |
570 | case TEGRA210_MIXER_SOFT_RESET: |
571 | case TEGRA210_MIXER_STATUS: |
572 | case TEGRA210_MIXER_INT_STATUS: |
573 | case TEGRA210_MIXER_GAIN_CFG_RAM_CTRL: |
574 | case TEGRA210_MIXER_GAIN_CFG_RAM_DATA: |
575 | case TEGRA210_MIXER_PEAKM_RAM_CTRL: |
576 | case TEGRA210_MIXER_PEAKM_RAM_DATA: |
577 | return true; |
578 | default: |
579 | return false; |
580 | } |
581 | } |
582 | |
583 | static bool tegra210_mixer_precious_reg(struct device *dev, |
584 | unsigned int reg) |
585 | { |
586 | switch (reg) { |
587 | case TEGRA210_MIXER_GAIN_CFG_RAM_DATA: |
588 | case TEGRA210_MIXER_PEAKM_RAM_DATA: |
589 | return true; |
590 | default: |
591 | return false; |
592 | } |
593 | } |
594 | |
595 | static const struct regmap_config tegra210_mixer_regmap_config = { |
596 | .reg_bits = 32, |
597 | .reg_stride = 4, |
598 | .val_bits = 32, |
599 | .max_register = TEGRA210_MIXER_CTRL, |
600 | .writeable_reg = tegra210_mixer_wr_reg, |
601 | .readable_reg = tegra210_mixer_rd_reg, |
602 | .volatile_reg = tegra210_mixer_volatile_reg, |
603 | .precious_reg = tegra210_mixer_precious_reg, |
604 | .reg_defaults = tegra210_mixer_reg_defaults, |
605 | .num_reg_defaults = ARRAY_SIZE(tegra210_mixer_reg_defaults), |
606 | .cache_type = REGCACHE_FLAT, |
607 | }; |
608 | |
609 | static const struct of_device_id tegra210_mixer_of_match[] = { |
610 | { .compatible = "nvidia,tegra210-amixer" }, |
611 | {}, |
612 | }; |
613 | MODULE_DEVICE_TABLE(of, tegra210_mixer_of_match); |
614 | |
615 | static int tegra210_mixer_platform_probe(struct platform_device *pdev) |
616 | { |
617 | struct device *dev = &pdev->dev; |
618 | struct tegra210_mixer *mixer; |
619 | void __iomem *regs; |
620 | int err, i; |
621 | |
622 | mixer = devm_kzalloc(dev, size: sizeof(*mixer), GFP_KERNEL); |
623 | if (!mixer) |
624 | return -ENOMEM; |
625 | |
626 | dev_set_drvdata(dev, data: mixer); |
627 | |
628 | /* Use default gain value for all MIXER inputs */ |
629 | for (i = 0; i < TEGRA210_MIXER_RX_MAX; i++) |
630 | mixer->gain_value[i] = gain_params.gain_value; |
631 | |
632 | regs = devm_platform_ioremap_resource(pdev, index: 0); |
633 | if (IS_ERR(ptr: regs)) |
634 | return PTR_ERR(ptr: regs); |
635 | |
636 | mixer->regmap = devm_regmap_init_mmio(dev, regs, |
637 | &tegra210_mixer_regmap_config); |
638 | if (IS_ERR(ptr: mixer->regmap)) { |
639 | dev_err(dev, "regmap init failed\n" ); |
640 | return PTR_ERR(ptr: mixer->regmap); |
641 | } |
642 | |
643 | regcache_cache_only(map: mixer->regmap, enable: true); |
644 | |
645 | err = devm_snd_soc_register_component(dev, component_driver: &tegra210_mixer_cmpnt, |
646 | dai_drv: tegra210_mixer_dais, |
647 | ARRAY_SIZE(tegra210_mixer_dais)); |
648 | if (err) { |
649 | dev_err(dev, "can't register MIXER component, err: %d\n" , err); |
650 | return err; |
651 | } |
652 | |
653 | pm_runtime_enable(dev); |
654 | |
655 | return 0; |
656 | } |
657 | |
658 | static void tegra210_mixer_platform_remove(struct platform_device *pdev) |
659 | { |
660 | pm_runtime_disable(dev: &pdev->dev); |
661 | } |
662 | |
663 | static const struct dev_pm_ops tegra210_mixer_pm_ops = { |
664 | SET_RUNTIME_PM_OPS(tegra210_mixer_runtime_suspend, |
665 | tegra210_mixer_runtime_resume, NULL) |
666 | SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, |
667 | pm_runtime_force_resume) |
668 | }; |
669 | |
670 | static struct platform_driver tegra210_mixer_driver = { |
671 | .driver = { |
672 | .name = "tegra210_mixer" , |
673 | .of_match_table = tegra210_mixer_of_match, |
674 | .pm = &tegra210_mixer_pm_ops, |
675 | }, |
676 | .probe = tegra210_mixer_platform_probe, |
677 | .remove_new = tegra210_mixer_platform_remove, |
678 | }; |
679 | module_platform_driver(tegra210_mixer_driver); |
680 | |
681 | MODULE_AUTHOR("Arun Shamanna Lakshmi <aruns@nvidia.com>" ); |
682 | MODULE_DESCRIPTION("Tegra210 MIXER ASoC driver" ); |
683 | MODULE_LICENSE("GPL v2" ); |
684 | |