1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | // |
3 | // tegra210_admaif.c - Tegra ADMAIF 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/module.h> |
10 | #include <linux/of_platform.h> |
11 | #include <linux/platform_device.h> |
12 | #include <linux/pm_runtime.h> |
13 | #include <linux/regmap.h> |
14 | #include <sound/pcm_params.h> |
15 | #include <sound/soc.h> |
16 | #include "tegra210_admaif.h" |
17 | #include "tegra_cif.h" |
18 | #include "tegra_pcm.h" |
19 | |
20 | #define CH_REG(offset, reg, id) \ |
21 | ((offset) + (reg) + (TEGRA_ADMAIF_CHANNEL_REG_STRIDE * (id))) |
22 | |
23 | #define CH_TX_REG(reg, id) CH_REG(admaif->soc_data->tx_base, reg, id) |
24 | |
25 | #define CH_RX_REG(reg, id) CH_REG(admaif->soc_data->rx_base, reg, id) |
26 | |
27 | #define REG_DEFAULTS(id, rx_ctrl, tx_ctrl, tx_base, rx_base) \ |
28 | { CH_REG(rx_base, TEGRA_ADMAIF_RX_INT_MASK, id), 0x00000001 }, \ |
29 | { CH_REG(rx_base, TEGRA_ADMAIF_CH_ACIF_RX_CTRL, id), 0x00007700 }, \ |
30 | { CH_REG(rx_base, TEGRA_ADMAIF_RX_FIFO_CTRL, id), rx_ctrl }, \ |
31 | { CH_REG(tx_base, TEGRA_ADMAIF_TX_INT_MASK, id), 0x00000001 }, \ |
32 | { CH_REG(tx_base, TEGRA_ADMAIF_CH_ACIF_TX_CTRL, id), 0x00007700 }, \ |
33 | { CH_REG(tx_base, TEGRA_ADMAIF_TX_FIFO_CTRL, id), tx_ctrl } |
34 | |
35 | #define ADMAIF_REG_DEFAULTS(id, chip) \ |
36 | REG_DEFAULTS((id) - 1, \ |
37 | chip ## _ADMAIF_RX ## id ## _FIFO_CTRL_REG_DEFAULT, \ |
38 | chip ## _ADMAIF_TX ## id ## _FIFO_CTRL_REG_DEFAULT, \ |
39 | chip ## _ADMAIF_TX_BASE, \ |
40 | chip ## _ADMAIF_RX_BASE) |
41 | |
42 | static const struct reg_default tegra186_admaif_reg_defaults[] = { |
43 | {(TEGRA_ADMAIF_GLOBAL_CG_0 + TEGRA186_ADMAIF_GLOBAL_BASE), 0x00000003}, |
44 | ADMAIF_REG_DEFAULTS(1, TEGRA186), |
45 | ADMAIF_REG_DEFAULTS(2, TEGRA186), |
46 | ADMAIF_REG_DEFAULTS(3, TEGRA186), |
47 | ADMAIF_REG_DEFAULTS(4, TEGRA186), |
48 | ADMAIF_REG_DEFAULTS(5, TEGRA186), |
49 | ADMAIF_REG_DEFAULTS(6, TEGRA186), |
50 | ADMAIF_REG_DEFAULTS(7, TEGRA186), |
51 | ADMAIF_REG_DEFAULTS(8, TEGRA186), |
52 | ADMAIF_REG_DEFAULTS(9, TEGRA186), |
53 | ADMAIF_REG_DEFAULTS(10, TEGRA186), |
54 | ADMAIF_REG_DEFAULTS(11, TEGRA186), |
55 | ADMAIF_REG_DEFAULTS(12, TEGRA186), |
56 | ADMAIF_REG_DEFAULTS(13, TEGRA186), |
57 | ADMAIF_REG_DEFAULTS(14, TEGRA186), |
58 | ADMAIF_REG_DEFAULTS(15, TEGRA186), |
59 | ADMAIF_REG_DEFAULTS(16, TEGRA186), |
60 | ADMAIF_REG_DEFAULTS(17, TEGRA186), |
61 | ADMAIF_REG_DEFAULTS(18, TEGRA186), |
62 | ADMAIF_REG_DEFAULTS(19, TEGRA186), |
63 | ADMAIF_REG_DEFAULTS(20, TEGRA186) |
64 | }; |
65 | |
66 | static const struct reg_default tegra210_admaif_reg_defaults[] = { |
67 | {(TEGRA_ADMAIF_GLOBAL_CG_0 + TEGRA210_ADMAIF_GLOBAL_BASE), 0x00000003}, |
68 | ADMAIF_REG_DEFAULTS(1, TEGRA210), |
69 | ADMAIF_REG_DEFAULTS(2, TEGRA210), |
70 | ADMAIF_REG_DEFAULTS(3, TEGRA210), |
71 | ADMAIF_REG_DEFAULTS(4, TEGRA210), |
72 | ADMAIF_REG_DEFAULTS(5, TEGRA210), |
73 | ADMAIF_REG_DEFAULTS(6, TEGRA210), |
74 | ADMAIF_REG_DEFAULTS(7, TEGRA210), |
75 | ADMAIF_REG_DEFAULTS(8, TEGRA210), |
76 | ADMAIF_REG_DEFAULTS(9, TEGRA210), |
77 | ADMAIF_REG_DEFAULTS(10, TEGRA210) |
78 | }; |
79 | |
80 | static bool tegra_admaif_wr_reg(struct device *dev, unsigned int reg) |
81 | { |
82 | struct tegra_admaif *admaif = dev_get_drvdata(dev); |
83 | unsigned int ch_stride = TEGRA_ADMAIF_CHANNEL_REG_STRIDE; |
84 | unsigned int num_ch = admaif->soc_data->num_ch; |
85 | unsigned int rx_base = admaif->soc_data->rx_base; |
86 | unsigned int tx_base = admaif->soc_data->tx_base; |
87 | unsigned int global_base = admaif->soc_data->global_base; |
88 | unsigned int reg_max = admaif->soc_data->regmap_conf->max_register; |
89 | unsigned int rx_max = rx_base + (num_ch * ch_stride); |
90 | unsigned int tx_max = tx_base + (num_ch * ch_stride); |
91 | |
92 | if ((reg >= rx_base) && (reg < rx_max)) { |
93 | reg = (reg - rx_base) % ch_stride; |
94 | if ((reg == TEGRA_ADMAIF_RX_ENABLE) || |
95 | (reg == TEGRA_ADMAIF_RX_FIFO_CTRL) || |
96 | (reg == TEGRA_ADMAIF_RX_SOFT_RESET) || |
97 | (reg == TEGRA_ADMAIF_CH_ACIF_RX_CTRL)) |
98 | return true; |
99 | } else if ((reg >= tx_base) && (reg < tx_max)) { |
100 | reg = (reg - tx_base) % ch_stride; |
101 | if ((reg == TEGRA_ADMAIF_TX_ENABLE) || |
102 | (reg == TEGRA_ADMAIF_TX_FIFO_CTRL) || |
103 | (reg == TEGRA_ADMAIF_TX_SOFT_RESET) || |
104 | (reg == TEGRA_ADMAIF_CH_ACIF_TX_CTRL)) |
105 | return true; |
106 | } else if ((reg >= global_base) && (reg < reg_max)) { |
107 | if (reg == (global_base + TEGRA_ADMAIF_GLOBAL_ENABLE)) |
108 | return true; |
109 | } |
110 | |
111 | return false; |
112 | } |
113 | |
114 | static bool tegra_admaif_rd_reg(struct device *dev, unsigned int reg) |
115 | { |
116 | struct tegra_admaif *admaif = dev_get_drvdata(dev); |
117 | unsigned int ch_stride = TEGRA_ADMAIF_CHANNEL_REG_STRIDE; |
118 | unsigned int num_ch = admaif->soc_data->num_ch; |
119 | unsigned int rx_base = admaif->soc_data->rx_base; |
120 | unsigned int tx_base = admaif->soc_data->tx_base; |
121 | unsigned int global_base = admaif->soc_data->global_base; |
122 | unsigned int reg_max = admaif->soc_data->regmap_conf->max_register; |
123 | unsigned int rx_max = rx_base + (num_ch * ch_stride); |
124 | unsigned int tx_max = tx_base + (num_ch * ch_stride); |
125 | |
126 | if ((reg >= rx_base) && (reg < rx_max)) { |
127 | reg = (reg - rx_base) % ch_stride; |
128 | if ((reg == TEGRA_ADMAIF_RX_ENABLE) || |
129 | (reg == TEGRA_ADMAIF_RX_STATUS) || |
130 | (reg == TEGRA_ADMAIF_RX_INT_STATUS) || |
131 | (reg == TEGRA_ADMAIF_RX_FIFO_CTRL) || |
132 | (reg == TEGRA_ADMAIF_RX_SOFT_RESET) || |
133 | (reg == TEGRA_ADMAIF_CH_ACIF_RX_CTRL)) |
134 | return true; |
135 | } else if ((reg >= tx_base) && (reg < tx_max)) { |
136 | reg = (reg - tx_base) % ch_stride; |
137 | if ((reg == TEGRA_ADMAIF_TX_ENABLE) || |
138 | (reg == TEGRA_ADMAIF_TX_STATUS) || |
139 | (reg == TEGRA_ADMAIF_TX_INT_STATUS) || |
140 | (reg == TEGRA_ADMAIF_TX_FIFO_CTRL) || |
141 | (reg == TEGRA_ADMAIF_TX_SOFT_RESET) || |
142 | (reg == TEGRA_ADMAIF_CH_ACIF_TX_CTRL)) |
143 | return true; |
144 | } else if ((reg >= global_base) && (reg < reg_max)) { |
145 | if ((reg == (global_base + TEGRA_ADMAIF_GLOBAL_ENABLE)) || |
146 | (reg == (global_base + TEGRA_ADMAIF_GLOBAL_CG_0)) || |
147 | (reg == (global_base + TEGRA_ADMAIF_GLOBAL_STATUS)) || |
148 | (reg == (global_base + |
149 | TEGRA_ADMAIF_GLOBAL_RX_ENABLE_STATUS)) || |
150 | (reg == (global_base + |
151 | TEGRA_ADMAIF_GLOBAL_TX_ENABLE_STATUS))) |
152 | return true; |
153 | } |
154 | |
155 | return false; |
156 | } |
157 | |
158 | static bool tegra_admaif_volatile_reg(struct device *dev, unsigned int reg) |
159 | { |
160 | struct tegra_admaif *admaif = dev_get_drvdata(dev); |
161 | unsigned int ch_stride = TEGRA_ADMAIF_CHANNEL_REG_STRIDE; |
162 | unsigned int num_ch = admaif->soc_data->num_ch; |
163 | unsigned int rx_base = admaif->soc_data->rx_base; |
164 | unsigned int tx_base = admaif->soc_data->tx_base; |
165 | unsigned int global_base = admaif->soc_data->global_base; |
166 | unsigned int reg_max = admaif->soc_data->regmap_conf->max_register; |
167 | unsigned int rx_max = rx_base + (num_ch * ch_stride); |
168 | unsigned int tx_max = tx_base + (num_ch * ch_stride); |
169 | |
170 | if ((reg >= rx_base) && (reg < rx_max)) { |
171 | reg = (reg - rx_base) % ch_stride; |
172 | if ((reg == TEGRA_ADMAIF_RX_ENABLE) || |
173 | (reg == TEGRA_ADMAIF_RX_STATUS) || |
174 | (reg == TEGRA_ADMAIF_RX_INT_STATUS) || |
175 | (reg == TEGRA_ADMAIF_RX_SOFT_RESET)) |
176 | return true; |
177 | } else if ((reg >= tx_base) && (reg < tx_max)) { |
178 | reg = (reg - tx_base) % ch_stride; |
179 | if ((reg == TEGRA_ADMAIF_TX_ENABLE) || |
180 | (reg == TEGRA_ADMAIF_TX_STATUS) || |
181 | (reg == TEGRA_ADMAIF_TX_INT_STATUS) || |
182 | (reg == TEGRA_ADMAIF_TX_SOFT_RESET)) |
183 | return true; |
184 | } else if ((reg >= global_base) && (reg < reg_max)) { |
185 | if ((reg == (global_base + TEGRA_ADMAIF_GLOBAL_STATUS)) || |
186 | (reg == (global_base + |
187 | TEGRA_ADMAIF_GLOBAL_RX_ENABLE_STATUS)) || |
188 | (reg == (global_base + |
189 | TEGRA_ADMAIF_GLOBAL_TX_ENABLE_STATUS))) |
190 | return true; |
191 | } |
192 | |
193 | return false; |
194 | } |
195 | |
196 | static const struct regmap_config tegra210_admaif_regmap_config = { |
197 | .reg_bits = 32, |
198 | .reg_stride = 4, |
199 | .val_bits = 32, |
200 | .max_register = TEGRA210_ADMAIF_LAST_REG, |
201 | .writeable_reg = tegra_admaif_wr_reg, |
202 | .readable_reg = tegra_admaif_rd_reg, |
203 | .volatile_reg = tegra_admaif_volatile_reg, |
204 | .reg_defaults = tegra210_admaif_reg_defaults, |
205 | .num_reg_defaults = TEGRA210_ADMAIF_CHANNEL_COUNT * 6 + 1, |
206 | .cache_type = REGCACHE_FLAT, |
207 | }; |
208 | |
209 | static const struct regmap_config tegra186_admaif_regmap_config = { |
210 | .reg_bits = 32, |
211 | .reg_stride = 4, |
212 | .val_bits = 32, |
213 | .max_register = TEGRA186_ADMAIF_LAST_REG, |
214 | .writeable_reg = tegra_admaif_wr_reg, |
215 | .readable_reg = tegra_admaif_rd_reg, |
216 | .volatile_reg = tegra_admaif_volatile_reg, |
217 | .reg_defaults = tegra186_admaif_reg_defaults, |
218 | .num_reg_defaults = TEGRA186_ADMAIF_CHANNEL_COUNT * 6 + 1, |
219 | .cache_type = REGCACHE_FLAT, |
220 | }; |
221 | |
222 | static int __maybe_unused tegra_admaif_runtime_suspend(struct device *dev) |
223 | { |
224 | struct tegra_admaif *admaif = dev_get_drvdata(dev); |
225 | |
226 | regcache_cache_only(map: admaif->regmap, enable: true); |
227 | regcache_mark_dirty(map: admaif->regmap); |
228 | |
229 | return 0; |
230 | } |
231 | |
232 | static int __maybe_unused tegra_admaif_runtime_resume(struct device *dev) |
233 | { |
234 | struct tegra_admaif *admaif = dev_get_drvdata(dev); |
235 | |
236 | regcache_cache_only(map: admaif->regmap, enable: false); |
237 | regcache_sync(map: admaif->regmap); |
238 | |
239 | return 0; |
240 | } |
241 | |
242 | static int tegra_admaif_set_pack_mode(struct regmap *map, unsigned int reg, |
243 | int valid_bit) |
244 | { |
245 | switch (valid_bit) { |
246 | case DATA_8BIT: |
247 | regmap_update_bits(map, reg, PACK8_EN_MASK, PACK8_EN); |
248 | regmap_update_bits(map, reg, PACK16_EN_MASK, val: 0); |
249 | break; |
250 | case DATA_16BIT: |
251 | regmap_update_bits(map, reg, PACK16_EN_MASK, PACK16_EN); |
252 | regmap_update_bits(map, reg, PACK8_EN_MASK, val: 0); |
253 | break; |
254 | case DATA_32BIT: |
255 | regmap_update_bits(map, reg, PACK16_EN_MASK, val: 0); |
256 | regmap_update_bits(map, reg, PACK8_EN_MASK, val: 0); |
257 | break; |
258 | default: |
259 | return -EINVAL; |
260 | } |
261 | |
262 | return 0; |
263 | } |
264 | |
265 | static int tegra_admaif_hw_params(struct snd_pcm_substream *substream, |
266 | struct snd_pcm_hw_params *params, |
267 | struct snd_soc_dai *dai) |
268 | { |
269 | struct device *dev = dai->dev; |
270 | struct tegra_admaif *admaif = snd_soc_dai_get_drvdata(dai); |
271 | struct tegra_cif_conf cif_conf; |
272 | unsigned int reg, path; |
273 | int valid_bit, channels; |
274 | |
275 | memset(&cif_conf, 0, sizeof(struct tegra_cif_conf)); |
276 | |
277 | switch (params_format(p: params)) { |
278 | case SNDRV_PCM_FORMAT_S8: |
279 | cif_conf.audio_bits = TEGRA_ACIF_BITS_8; |
280 | cif_conf.client_bits = TEGRA_ACIF_BITS_8; |
281 | valid_bit = DATA_8BIT; |
282 | break; |
283 | case SNDRV_PCM_FORMAT_S16_LE: |
284 | cif_conf.audio_bits = TEGRA_ACIF_BITS_16; |
285 | cif_conf.client_bits = TEGRA_ACIF_BITS_16; |
286 | valid_bit = DATA_16BIT; |
287 | break; |
288 | case SNDRV_PCM_FORMAT_S32_LE: |
289 | cif_conf.audio_bits = TEGRA_ACIF_BITS_32; |
290 | cif_conf.client_bits = TEGRA_ACIF_BITS_32; |
291 | valid_bit = DATA_32BIT; |
292 | break; |
293 | default: |
294 | dev_err(dev, "unsupported format!\n" ); |
295 | return -EOPNOTSUPP; |
296 | } |
297 | |
298 | channels = params_channels(p: params); |
299 | cif_conf.client_ch = channels; |
300 | cif_conf.audio_ch = channels; |
301 | |
302 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
303 | path = ADMAIF_TX_PATH; |
304 | reg = CH_TX_REG(TEGRA_ADMAIF_CH_ACIF_TX_CTRL, dai->id); |
305 | } else { |
306 | path = ADMAIF_RX_PATH; |
307 | reg = CH_RX_REG(TEGRA_ADMAIF_CH_ACIF_RX_CTRL, dai->id); |
308 | } |
309 | |
310 | cif_conf.mono_conv = admaif->mono_to_stereo[path][dai->id]; |
311 | cif_conf.stereo_conv = admaif->stereo_to_mono[path][dai->id]; |
312 | |
313 | tegra_admaif_set_pack_mode(map: admaif->regmap, reg, valid_bit); |
314 | |
315 | tegra_set_cif(regmap: admaif->regmap, reg, conf: &cif_conf); |
316 | |
317 | return 0; |
318 | } |
319 | |
320 | static int tegra_admaif_start(struct snd_soc_dai *dai, int direction) |
321 | { |
322 | struct tegra_admaif *admaif = snd_soc_dai_get_drvdata(dai); |
323 | unsigned int reg, mask, val; |
324 | |
325 | switch (direction) { |
326 | case SNDRV_PCM_STREAM_PLAYBACK: |
327 | mask = TX_ENABLE_MASK; |
328 | val = TX_ENABLE; |
329 | reg = CH_TX_REG(TEGRA_ADMAIF_TX_ENABLE, dai->id); |
330 | break; |
331 | case SNDRV_PCM_STREAM_CAPTURE: |
332 | mask = RX_ENABLE_MASK; |
333 | val = RX_ENABLE; |
334 | reg = CH_RX_REG(TEGRA_ADMAIF_RX_ENABLE, dai->id); |
335 | break; |
336 | default: |
337 | return -EINVAL; |
338 | } |
339 | |
340 | regmap_update_bits(map: admaif->regmap, reg, mask, val); |
341 | |
342 | return 0; |
343 | } |
344 | |
345 | static int tegra_admaif_stop(struct snd_soc_dai *dai, int direction) |
346 | { |
347 | struct tegra_admaif *admaif = snd_soc_dai_get_drvdata(dai); |
348 | unsigned int enable_reg, status_reg, reset_reg, mask, val; |
349 | char *dir_name; |
350 | int err, enable; |
351 | |
352 | switch (direction) { |
353 | case SNDRV_PCM_STREAM_PLAYBACK: |
354 | mask = TX_ENABLE_MASK; |
355 | enable = TX_ENABLE; |
356 | dir_name = "TX" ; |
357 | enable_reg = CH_TX_REG(TEGRA_ADMAIF_TX_ENABLE, dai->id); |
358 | status_reg = CH_TX_REG(TEGRA_ADMAIF_TX_STATUS, dai->id); |
359 | reset_reg = CH_TX_REG(TEGRA_ADMAIF_TX_SOFT_RESET, dai->id); |
360 | break; |
361 | case SNDRV_PCM_STREAM_CAPTURE: |
362 | mask = RX_ENABLE_MASK; |
363 | enable = RX_ENABLE; |
364 | dir_name = "RX" ; |
365 | enable_reg = CH_RX_REG(TEGRA_ADMAIF_RX_ENABLE, dai->id); |
366 | status_reg = CH_RX_REG(TEGRA_ADMAIF_RX_STATUS, dai->id); |
367 | reset_reg = CH_RX_REG(TEGRA_ADMAIF_RX_SOFT_RESET, dai->id); |
368 | break; |
369 | default: |
370 | return -EINVAL; |
371 | } |
372 | |
373 | /* Disable TX/RX channel */ |
374 | regmap_update_bits(map: admaif->regmap, reg: enable_reg, mask, val: ~enable); |
375 | |
376 | /* Wait until ADMAIF TX/RX status is disabled */ |
377 | err = regmap_read_poll_timeout_atomic(admaif->regmap, status_reg, val, |
378 | !(val & enable), 10, 10000); |
379 | if (err < 0) |
380 | dev_warn(dai->dev, "timeout: failed to disable ADMAIF%d_%s\n" , |
381 | dai->id + 1, dir_name); |
382 | |
383 | /* SW reset */ |
384 | regmap_update_bits(map: admaif->regmap, reg: reset_reg, SW_RESET_MASK, SW_RESET); |
385 | |
386 | /* Wait till SW reset is complete */ |
387 | err = regmap_read_poll_timeout_atomic(admaif->regmap, reset_reg, val, |
388 | !(val & SW_RESET_MASK & SW_RESET), |
389 | 10, 10000); |
390 | if (err) { |
391 | dev_err(dai->dev, "timeout: SW reset failed for ADMAIF%d_%s\n" , |
392 | dai->id + 1, dir_name); |
393 | return err; |
394 | } |
395 | |
396 | return 0; |
397 | } |
398 | |
399 | static int tegra_admaif_trigger(struct snd_pcm_substream *substream, int cmd, |
400 | struct snd_soc_dai *dai) |
401 | { |
402 | int err; |
403 | |
404 | err = snd_dmaengine_pcm_trigger(substream, cmd); |
405 | if (err) |
406 | return err; |
407 | |
408 | switch (cmd) { |
409 | case SNDRV_PCM_TRIGGER_START: |
410 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
411 | case SNDRV_PCM_TRIGGER_RESUME: |
412 | return tegra_admaif_start(dai, direction: substream->stream); |
413 | case SNDRV_PCM_TRIGGER_STOP: |
414 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
415 | case SNDRV_PCM_TRIGGER_SUSPEND: |
416 | return tegra_admaif_stop(dai, direction: substream->stream); |
417 | default: |
418 | return -EINVAL; |
419 | } |
420 | } |
421 | |
422 | static int tegra210_admaif_pget_mono_to_stereo(struct snd_kcontrol *kcontrol, |
423 | struct snd_ctl_elem_value *ucontrol) |
424 | { |
425 | struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); |
426 | struct tegra_admaif *admaif = snd_soc_component_get_drvdata(c: cmpnt); |
427 | struct soc_enum *ec = (struct soc_enum *)kcontrol->private_value; |
428 | |
429 | ucontrol->value.enumerated.item[0] = |
430 | admaif->mono_to_stereo[ADMAIF_TX_PATH][ec->reg]; |
431 | |
432 | return 0; |
433 | } |
434 | |
435 | static int tegra210_admaif_pput_mono_to_stereo(struct snd_kcontrol *kcontrol, |
436 | struct snd_ctl_elem_value *ucontrol) |
437 | { |
438 | struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); |
439 | struct tegra_admaif *admaif = snd_soc_component_get_drvdata(c: cmpnt); |
440 | struct soc_enum *ec = (struct soc_enum *)kcontrol->private_value; |
441 | unsigned int value = ucontrol->value.enumerated.item[0]; |
442 | |
443 | if (value == admaif->mono_to_stereo[ADMAIF_TX_PATH][ec->reg]) |
444 | return 0; |
445 | |
446 | admaif->mono_to_stereo[ADMAIF_TX_PATH][ec->reg] = value; |
447 | |
448 | return 1; |
449 | } |
450 | |
451 | static int tegra210_admaif_cget_mono_to_stereo(struct snd_kcontrol *kcontrol, |
452 | struct snd_ctl_elem_value *ucontrol) |
453 | { |
454 | struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); |
455 | struct tegra_admaif *admaif = snd_soc_component_get_drvdata(c: cmpnt); |
456 | struct soc_enum *ec = (struct soc_enum *)kcontrol->private_value; |
457 | |
458 | ucontrol->value.enumerated.item[0] = |
459 | admaif->mono_to_stereo[ADMAIF_RX_PATH][ec->reg]; |
460 | |
461 | return 0; |
462 | } |
463 | |
464 | static int tegra210_admaif_cput_mono_to_stereo(struct snd_kcontrol *kcontrol, |
465 | struct snd_ctl_elem_value *ucontrol) |
466 | { |
467 | struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); |
468 | struct tegra_admaif *admaif = snd_soc_component_get_drvdata(c: cmpnt); |
469 | struct soc_enum *ec = (struct soc_enum *)kcontrol->private_value; |
470 | unsigned int value = ucontrol->value.enumerated.item[0]; |
471 | |
472 | if (value == admaif->mono_to_stereo[ADMAIF_RX_PATH][ec->reg]) |
473 | return 0; |
474 | |
475 | admaif->mono_to_stereo[ADMAIF_RX_PATH][ec->reg] = value; |
476 | |
477 | return 1; |
478 | } |
479 | |
480 | static int tegra210_admaif_pget_stereo_to_mono(struct snd_kcontrol *kcontrol, |
481 | struct snd_ctl_elem_value *ucontrol) |
482 | { |
483 | struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); |
484 | struct tegra_admaif *admaif = snd_soc_component_get_drvdata(c: cmpnt); |
485 | struct soc_enum *ec = (struct soc_enum *)kcontrol->private_value; |
486 | |
487 | ucontrol->value.enumerated.item[0] = |
488 | admaif->stereo_to_mono[ADMAIF_TX_PATH][ec->reg]; |
489 | |
490 | return 0; |
491 | } |
492 | |
493 | static int tegra210_admaif_pput_stereo_to_mono(struct snd_kcontrol *kcontrol, |
494 | struct snd_ctl_elem_value *ucontrol) |
495 | { |
496 | struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); |
497 | struct tegra_admaif *admaif = snd_soc_component_get_drvdata(c: cmpnt); |
498 | struct soc_enum *ec = (struct soc_enum *)kcontrol->private_value; |
499 | unsigned int value = ucontrol->value.enumerated.item[0]; |
500 | |
501 | if (value == admaif->stereo_to_mono[ADMAIF_TX_PATH][ec->reg]) |
502 | return 0; |
503 | |
504 | admaif->stereo_to_mono[ADMAIF_TX_PATH][ec->reg] = value; |
505 | |
506 | return 1; |
507 | } |
508 | |
509 | static int tegra210_admaif_cget_stereo_to_mono(struct snd_kcontrol *kcontrol, |
510 | struct snd_ctl_elem_value *ucontrol) |
511 | { |
512 | struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); |
513 | struct tegra_admaif *admaif = snd_soc_component_get_drvdata(c: cmpnt); |
514 | struct soc_enum *ec = (struct soc_enum *)kcontrol->private_value; |
515 | |
516 | ucontrol->value.enumerated.item[0] = |
517 | admaif->stereo_to_mono[ADMAIF_RX_PATH][ec->reg]; |
518 | |
519 | return 0; |
520 | } |
521 | |
522 | static int tegra210_admaif_cput_stereo_to_mono(struct snd_kcontrol *kcontrol, |
523 | struct snd_ctl_elem_value *ucontrol) |
524 | { |
525 | struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); |
526 | struct tegra_admaif *admaif = snd_soc_component_get_drvdata(c: cmpnt); |
527 | struct soc_enum *ec = (struct soc_enum *)kcontrol->private_value; |
528 | unsigned int value = ucontrol->value.enumerated.item[0]; |
529 | |
530 | if (value == admaif->stereo_to_mono[ADMAIF_RX_PATH][ec->reg]) |
531 | return 0; |
532 | |
533 | admaif->stereo_to_mono[ADMAIF_RX_PATH][ec->reg] = value; |
534 | |
535 | return 1; |
536 | } |
537 | |
538 | static int tegra_admaif_dai_probe(struct snd_soc_dai *dai) |
539 | { |
540 | struct tegra_admaif *admaif = snd_soc_dai_get_drvdata(dai); |
541 | |
542 | snd_soc_dai_init_dma_data(dai, playback: &admaif->playback_dma_data[dai->id], |
543 | capture: &admaif->capture_dma_data[dai->id]); |
544 | |
545 | return 0; |
546 | } |
547 | |
548 | static const struct snd_soc_dai_ops tegra_admaif_dai_ops = { |
549 | .probe = tegra_admaif_dai_probe, |
550 | .hw_params = tegra_admaif_hw_params, |
551 | .trigger = tegra_admaif_trigger, |
552 | }; |
553 | |
554 | #define DAI(dai_name) \ |
555 | { \ |
556 | .name = dai_name, \ |
557 | .playback = { \ |
558 | .stream_name = dai_name " Playback", \ |
559 | .channels_min = 1, \ |
560 | .channels_max = 16, \ |
561 | .rates = SNDRV_PCM_RATE_8000_192000, \ |
562 | .formats = SNDRV_PCM_FMTBIT_S8 | \ |
563 | SNDRV_PCM_FMTBIT_S16_LE | \ |
564 | SNDRV_PCM_FMTBIT_S32_LE, \ |
565 | }, \ |
566 | .capture = { \ |
567 | .stream_name = dai_name " Capture", \ |
568 | .channels_min = 1, \ |
569 | .channels_max = 16, \ |
570 | .rates = SNDRV_PCM_RATE_8000_192000, \ |
571 | .formats = SNDRV_PCM_FMTBIT_S8 | \ |
572 | SNDRV_PCM_FMTBIT_S16_LE | \ |
573 | SNDRV_PCM_FMTBIT_S32_LE, \ |
574 | }, \ |
575 | .ops = &tegra_admaif_dai_ops, \ |
576 | } |
577 | |
578 | static struct snd_soc_dai_driver tegra210_admaif_cmpnt_dais[] = { |
579 | DAI("ADMAIF1" ), |
580 | DAI("ADMAIF2" ), |
581 | DAI("ADMAIF3" ), |
582 | DAI("ADMAIF4" ), |
583 | DAI("ADMAIF5" ), |
584 | DAI("ADMAIF6" ), |
585 | DAI("ADMAIF7" ), |
586 | DAI("ADMAIF8" ), |
587 | DAI("ADMAIF9" ), |
588 | DAI("ADMAIF10" ), |
589 | }; |
590 | |
591 | static struct snd_soc_dai_driver tegra186_admaif_cmpnt_dais[] = { |
592 | DAI("ADMAIF1" ), |
593 | DAI("ADMAIF2" ), |
594 | DAI("ADMAIF3" ), |
595 | DAI("ADMAIF4" ), |
596 | DAI("ADMAIF5" ), |
597 | DAI("ADMAIF6" ), |
598 | DAI("ADMAIF7" ), |
599 | DAI("ADMAIF8" ), |
600 | DAI("ADMAIF9" ), |
601 | DAI("ADMAIF10" ), |
602 | DAI("ADMAIF11" ), |
603 | DAI("ADMAIF12" ), |
604 | DAI("ADMAIF13" ), |
605 | DAI("ADMAIF14" ), |
606 | DAI("ADMAIF15" ), |
607 | DAI("ADMAIF16" ), |
608 | DAI("ADMAIF17" ), |
609 | DAI("ADMAIF18" ), |
610 | DAI("ADMAIF19" ), |
611 | DAI("ADMAIF20" ), |
612 | }; |
613 | |
614 | static const char * const tegra_admaif_stereo_conv_text[] = { |
615 | "CH0" , "CH1" , "AVG" , |
616 | }; |
617 | |
618 | static const char * const tegra_admaif_mono_conv_text[] = { |
619 | "Zero" , "Copy" , |
620 | }; |
621 | |
622 | /* |
623 | * Below macro is added to avoid looping over all ADMAIFx controls related |
624 | * to mono/stereo conversions in get()/put() callbacks. |
625 | */ |
626 | #define NV_SOC_ENUM_EXT(xname, xreg, xhandler_get, xhandler_put, xenum_text) \ |
627 | { \ |
628 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ |
629 | .info = snd_soc_info_enum_double, \ |
630 | .name = xname, \ |
631 | .get = xhandler_get, \ |
632 | .put = xhandler_put, \ |
633 | .private_value = (unsigned long)&(struct soc_enum) \ |
634 | SOC_ENUM_SINGLE(xreg, 0, ARRAY_SIZE(xenum_text), xenum_text) \ |
635 | } |
636 | |
637 | #define TEGRA_ADMAIF_CIF_CTRL(reg) \ |
638 | NV_SOC_ENUM_EXT("ADMAIF" #reg " Playback Mono To Stereo", reg - 1, \ |
639 | tegra210_admaif_pget_mono_to_stereo, \ |
640 | tegra210_admaif_pput_mono_to_stereo, \ |
641 | tegra_admaif_mono_conv_text), \ |
642 | NV_SOC_ENUM_EXT("ADMAIF" #reg " Playback Stereo To Mono", reg - 1, \ |
643 | tegra210_admaif_pget_stereo_to_mono, \ |
644 | tegra210_admaif_pput_stereo_to_mono, \ |
645 | tegra_admaif_stereo_conv_text), \ |
646 | NV_SOC_ENUM_EXT("ADMAIF" #reg " Capture Mono To Stereo", reg - 1, \ |
647 | tegra210_admaif_cget_mono_to_stereo, \ |
648 | tegra210_admaif_cput_mono_to_stereo, \ |
649 | tegra_admaif_mono_conv_text), \ |
650 | NV_SOC_ENUM_EXT("ADMAIF" #reg " Capture Stereo To Mono", reg - 1, \ |
651 | tegra210_admaif_cget_stereo_to_mono, \ |
652 | tegra210_admaif_cput_stereo_to_mono, \ |
653 | tegra_admaif_stereo_conv_text) |
654 | |
655 | static struct snd_kcontrol_new tegra210_admaif_controls[] = { |
656 | TEGRA_ADMAIF_CIF_CTRL(1), |
657 | TEGRA_ADMAIF_CIF_CTRL(2), |
658 | TEGRA_ADMAIF_CIF_CTRL(3), |
659 | TEGRA_ADMAIF_CIF_CTRL(4), |
660 | TEGRA_ADMAIF_CIF_CTRL(5), |
661 | TEGRA_ADMAIF_CIF_CTRL(6), |
662 | TEGRA_ADMAIF_CIF_CTRL(7), |
663 | TEGRA_ADMAIF_CIF_CTRL(8), |
664 | TEGRA_ADMAIF_CIF_CTRL(9), |
665 | TEGRA_ADMAIF_CIF_CTRL(10), |
666 | }; |
667 | |
668 | static struct snd_kcontrol_new tegra186_admaif_controls[] = { |
669 | TEGRA_ADMAIF_CIF_CTRL(1), |
670 | TEGRA_ADMAIF_CIF_CTRL(2), |
671 | TEGRA_ADMAIF_CIF_CTRL(3), |
672 | TEGRA_ADMAIF_CIF_CTRL(4), |
673 | TEGRA_ADMAIF_CIF_CTRL(5), |
674 | TEGRA_ADMAIF_CIF_CTRL(6), |
675 | TEGRA_ADMAIF_CIF_CTRL(7), |
676 | TEGRA_ADMAIF_CIF_CTRL(8), |
677 | TEGRA_ADMAIF_CIF_CTRL(9), |
678 | TEGRA_ADMAIF_CIF_CTRL(10), |
679 | TEGRA_ADMAIF_CIF_CTRL(11), |
680 | TEGRA_ADMAIF_CIF_CTRL(12), |
681 | TEGRA_ADMAIF_CIF_CTRL(13), |
682 | TEGRA_ADMAIF_CIF_CTRL(14), |
683 | TEGRA_ADMAIF_CIF_CTRL(15), |
684 | TEGRA_ADMAIF_CIF_CTRL(16), |
685 | TEGRA_ADMAIF_CIF_CTRL(17), |
686 | TEGRA_ADMAIF_CIF_CTRL(18), |
687 | TEGRA_ADMAIF_CIF_CTRL(19), |
688 | TEGRA_ADMAIF_CIF_CTRL(20), |
689 | }; |
690 | |
691 | static const struct snd_soc_component_driver tegra210_admaif_cmpnt = { |
692 | .controls = tegra210_admaif_controls, |
693 | .num_controls = ARRAY_SIZE(tegra210_admaif_controls), |
694 | .pcm_construct = tegra_pcm_construct, |
695 | .open = tegra_pcm_open, |
696 | .close = tegra_pcm_close, |
697 | .hw_params = tegra_pcm_hw_params, |
698 | .pointer = tegra_pcm_pointer, |
699 | }; |
700 | |
701 | static const struct snd_soc_component_driver tegra186_admaif_cmpnt = { |
702 | .controls = tegra186_admaif_controls, |
703 | .num_controls = ARRAY_SIZE(tegra186_admaif_controls), |
704 | .pcm_construct = tegra_pcm_construct, |
705 | .open = tegra_pcm_open, |
706 | .close = tegra_pcm_close, |
707 | .hw_params = tegra_pcm_hw_params, |
708 | .pointer = tegra_pcm_pointer, |
709 | }; |
710 | |
711 | static const struct tegra_admaif_soc_data soc_data_tegra210 = { |
712 | .num_ch = TEGRA210_ADMAIF_CHANNEL_COUNT, |
713 | .cmpnt = &tegra210_admaif_cmpnt, |
714 | .dais = tegra210_admaif_cmpnt_dais, |
715 | .regmap_conf = &tegra210_admaif_regmap_config, |
716 | .global_base = TEGRA210_ADMAIF_GLOBAL_BASE, |
717 | .tx_base = TEGRA210_ADMAIF_TX_BASE, |
718 | .rx_base = TEGRA210_ADMAIF_RX_BASE, |
719 | }; |
720 | |
721 | static const struct tegra_admaif_soc_data soc_data_tegra186 = { |
722 | .num_ch = TEGRA186_ADMAIF_CHANNEL_COUNT, |
723 | .cmpnt = &tegra186_admaif_cmpnt, |
724 | .dais = tegra186_admaif_cmpnt_dais, |
725 | .regmap_conf = &tegra186_admaif_regmap_config, |
726 | .global_base = TEGRA186_ADMAIF_GLOBAL_BASE, |
727 | .tx_base = TEGRA186_ADMAIF_TX_BASE, |
728 | .rx_base = TEGRA186_ADMAIF_RX_BASE, |
729 | }; |
730 | |
731 | static const struct of_device_id tegra_admaif_of_match[] = { |
732 | { .compatible = "nvidia,tegra210-admaif" , .data = &soc_data_tegra210 }, |
733 | { .compatible = "nvidia,tegra186-admaif" , .data = &soc_data_tegra186 }, |
734 | {}, |
735 | }; |
736 | MODULE_DEVICE_TABLE(of, tegra_admaif_of_match); |
737 | |
738 | static int tegra_admaif_probe(struct platform_device *pdev) |
739 | { |
740 | struct tegra_admaif *admaif; |
741 | void __iomem *regs; |
742 | struct resource *res; |
743 | int err, i; |
744 | |
745 | admaif = devm_kzalloc(dev: &pdev->dev, size: sizeof(*admaif), GFP_KERNEL); |
746 | if (!admaif) |
747 | return -ENOMEM; |
748 | |
749 | admaif->soc_data = of_device_get_match_data(dev: &pdev->dev); |
750 | |
751 | dev_set_drvdata(dev: &pdev->dev, data: admaif); |
752 | |
753 | admaif->capture_dma_data = |
754 | devm_kcalloc(dev: &pdev->dev, |
755 | n: admaif->soc_data->num_ch, |
756 | size: sizeof(struct snd_dmaengine_dai_dma_data), |
757 | GFP_KERNEL); |
758 | if (!admaif->capture_dma_data) |
759 | return -ENOMEM; |
760 | |
761 | admaif->playback_dma_data = |
762 | devm_kcalloc(dev: &pdev->dev, |
763 | n: admaif->soc_data->num_ch, |
764 | size: sizeof(struct snd_dmaengine_dai_dma_data), |
765 | GFP_KERNEL); |
766 | if (!admaif->playback_dma_data) |
767 | return -ENOMEM; |
768 | |
769 | for (i = 0; i < ADMAIF_PATHS; i++) { |
770 | admaif->mono_to_stereo[i] = |
771 | devm_kcalloc(dev: &pdev->dev, n: admaif->soc_data->num_ch, |
772 | size: sizeof(unsigned int), GFP_KERNEL); |
773 | if (!admaif->mono_to_stereo[i]) |
774 | return -ENOMEM; |
775 | |
776 | admaif->stereo_to_mono[i] = |
777 | devm_kcalloc(dev: &pdev->dev, n: admaif->soc_data->num_ch, |
778 | size: sizeof(unsigned int), GFP_KERNEL); |
779 | if (!admaif->stereo_to_mono[i]) |
780 | return -ENOMEM; |
781 | } |
782 | |
783 | regs = devm_platform_get_and_ioremap_resource(pdev, index: 0, res: &res); |
784 | if (IS_ERR(ptr: regs)) |
785 | return PTR_ERR(ptr: regs); |
786 | |
787 | admaif->regmap = devm_regmap_init_mmio(&pdev->dev, regs, |
788 | admaif->soc_data->regmap_conf); |
789 | if (IS_ERR(ptr: admaif->regmap)) { |
790 | dev_err(&pdev->dev, "regmap init failed\n" ); |
791 | return PTR_ERR(ptr: admaif->regmap); |
792 | } |
793 | |
794 | regcache_cache_only(map: admaif->regmap, enable: true); |
795 | |
796 | regmap_update_bits(map: admaif->regmap, reg: admaif->soc_data->global_base + |
797 | TEGRA_ADMAIF_GLOBAL_ENABLE, mask: 1, val: 1); |
798 | |
799 | for (i = 0; i < admaif->soc_data->num_ch; i++) { |
800 | admaif->playback_dma_data[i].addr = res->start + |
801 | CH_TX_REG(TEGRA_ADMAIF_TX_FIFO_WRITE, i); |
802 | |
803 | admaif->capture_dma_data[i].addr = res->start + |
804 | CH_RX_REG(TEGRA_ADMAIF_RX_FIFO_READ, i); |
805 | |
806 | admaif->playback_dma_data[i].addr_width = 32; |
807 | |
808 | if (of_property_read_string_index(np: pdev->dev.of_node, |
809 | propname: "dma-names" , index: (i * 2) + 1, |
810 | output: &admaif->playback_dma_data[i].chan_name) < 0) { |
811 | dev_err(&pdev->dev, |
812 | "missing property nvidia,dma-names\n" ); |
813 | |
814 | return -ENODEV; |
815 | } |
816 | |
817 | admaif->capture_dma_data[i].addr_width = 32; |
818 | |
819 | if (of_property_read_string_index(np: pdev->dev.of_node, |
820 | propname: "dma-names" , |
821 | index: (i * 2), |
822 | output: &admaif->capture_dma_data[i].chan_name) < 0) { |
823 | dev_err(&pdev->dev, |
824 | "missing property nvidia,dma-names\n" ); |
825 | |
826 | return -ENODEV; |
827 | } |
828 | } |
829 | |
830 | err = devm_snd_soc_register_component(dev: &pdev->dev, |
831 | component_driver: admaif->soc_data->cmpnt, |
832 | dai_drv: admaif->soc_data->dais, |
833 | num_dai: admaif->soc_data->num_ch); |
834 | if (err) { |
835 | dev_err(&pdev->dev, |
836 | "can't register ADMAIF component, err: %d\n" , err); |
837 | return err; |
838 | } |
839 | |
840 | pm_runtime_enable(dev: &pdev->dev); |
841 | |
842 | return 0; |
843 | } |
844 | |
845 | static void tegra_admaif_remove(struct platform_device *pdev) |
846 | { |
847 | pm_runtime_disable(dev: &pdev->dev); |
848 | } |
849 | |
850 | static const struct dev_pm_ops tegra_admaif_pm_ops = { |
851 | SET_RUNTIME_PM_OPS(tegra_admaif_runtime_suspend, |
852 | tegra_admaif_runtime_resume, NULL) |
853 | SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, |
854 | pm_runtime_force_resume) |
855 | }; |
856 | |
857 | static struct platform_driver tegra_admaif_driver = { |
858 | .probe = tegra_admaif_probe, |
859 | .remove_new = tegra_admaif_remove, |
860 | .driver = { |
861 | .name = "tegra210-admaif" , |
862 | .of_match_table = tegra_admaif_of_match, |
863 | .pm = &tegra_admaif_pm_ops, |
864 | }, |
865 | }; |
866 | module_platform_driver(tegra_admaif_driver); |
867 | |
868 | MODULE_AUTHOR("Songhee Baek <sbaek@nvidia.com>" ); |
869 | MODULE_DESCRIPTION("Tegra210 ASoC ADMAIF driver" ); |
870 | MODULE_LICENSE("GPL v2" ); |
871 | |