Commit | Line | Data |
---|---|---|
a99ab6f3 SP |
1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | // | |
3 | // tegra210_adx.c - Tegra210 ADX driver | |
4 | // | |
6dfe70be | 5 | // Copyright (c) 2021-2023 NVIDIA CORPORATION. All rights reserved. |
a99ab6f3 SP |
6 | |
7 | #include <linux/clk.h> | |
8 | #include <linux/device.h> | |
9 | #include <linux/io.h> | |
10 | #include <linux/module.h> | |
11 | #include <linux/of.h> | |
12 | #include <linux/of_device.h> | |
13 | #include <linux/platform_device.h> | |
14 | #include <linux/pm_runtime.h> | |
15 | #include <linux/regmap.h> | |
16 | #include <sound/core.h> | |
17 | #include <sound/pcm.h> | |
18 | #include <sound/pcm_params.h> | |
19 | #include <sound/soc.h> | |
20 | ||
21 | #include "tegra210_adx.h" | |
22 | #include "tegra_cif.h" | |
23 | ||
24 | static const struct reg_default tegra210_adx_reg_defaults[] = { | |
25 | { TEGRA210_ADX_RX_INT_MASK, 0x00000001}, | |
26 | { TEGRA210_ADX_RX_CIF_CTRL, 0x00007000}, | |
27 | { TEGRA210_ADX_TX_INT_MASK, 0x0000000f }, | |
28 | { TEGRA210_ADX_TX1_CIF_CTRL, 0x00007000}, | |
29 | { TEGRA210_ADX_TX2_CIF_CTRL, 0x00007000}, | |
30 | { TEGRA210_ADX_TX3_CIF_CTRL, 0x00007000}, | |
31 | { TEGRA210_ADX_TX4_CIF_CTRL, 0x00007000}, | |
32 | { TEGRA210_ADX_CG, 0x1}, | |
33 | { TEGRA210_ADX_CFG_RAM_CTRL, 0x00004000}, | |
34 | }; | |
35 | ||
36 | static void tegra210_adx_write_map_ram(struct tegra210_adx *adx) | |
37 | { | |
38 | int i; | |
39 | ||
40 | regmap_write(adx->regmap, TEGRA210_ADX_CFG_RAM_CTRL, | |
41 | TEGRA210_ADX_CFG_RAM_CTRL_SEQ_ACCESS_EN | | |
42 | TEGRA210_ADX_CFG_RAM_CTRL_ADDR_INIT_EN | | |
43 | TEGRA210_ADX_CFG_RAM_CTRL_RW_WRITE); | |
44 | ||
45 | for (i = 0; i < TEGRA210_ADX_RAM_DEPTH; i++) | |
46 | regmap_write(adx->regmap, TEGRA210_ADX_CFG_RAM_DATA, | |
47 | adx->map[i]); | |
48 | ||
49 | regmap_write(adx->regmap, TEGRA210_ADX_IN_BYTE_EN0, adx->byte_mask[0]); | |
50 | regmap_write(adx->regmap, TEGRA210_ADX_IN_BYTE_EN1, adx->byte_mask[1]); | |
51 | } | |
52 | ||
53 | static int tegra210_adx_startup(struct snd_pcm_substream *substream, | |
54 | struct snd_soc_dai *dai) | |
55 | { | |
56 | struct tegra210_adx *adx = snd_soc_dai_get_drvdata(dai); | |
57 | unsigned int val; | |
58 | int err; | |
59 | ||
60 | /* Ensure if ADX status is disabled */ | |
61 | err = regmap_read_poll_timeout_atomic(adx->regmap, TEGRA210_ADX_STATUS, | |
62 | val, !(val & 0x1), 10, 10000); | |
63 | if (err < 0) { | |
64 | dev_err(dai->dev, "failed to stop ADX, err = %d\n", err); | |
65 | return err; | |
66 | } | |
67 | ||
68 | /* | |
69 | * Soft Reset: Below performs module soft reset which clears | |
70 | * all FSM logic, flushes flow control of FIFO and resets the | |
71 | * state register. It also brings module back to disabled | |
72 | * state (without flushing the data in the pipe). | |
73 | */ | |
74 | regmap_update_bits(adx->regmap, TEGRA210_ADX_SOFT_RESET, | |
75 | TEGRA210_ADX_SOFT_RESET_SOFT_RESET_MASK, | |
76 | TEGRA210_ADX_SOFT_RESET_SOFT_EN); | |
77 | ||
78 | err = regmap_read_poll_timeout(adx->regmap, TEGRA210_ADX_SOFT_RESET, | |
79 | val, !(val & 0x1), 10, 10000); | |
80 | if (err < 0) { | |
81 | dev_err(dai->dev, "failed to reset ADX, err = %d\n", err); | |
82 | return err; | |
83 | } | |
84 | ||
85 | return 0; | |
86 | } | |
87 | ||
88 | static int __maybe_unused tegra210_adx_runtime_suspend(struct device *dev) | |
89 | { | |
90 | struct tegra210_adx *adx = dev_get_drvdata(dev); | |
91 | ||
92 | regcache_cache_only(adx->regmap, true); | |
93 | regcache_mark_dirty(adx->regmap); | |
94 | ||
95 | return 0; | |
96 | } | |
97 | ||
98 | static int __maybe_unused tegra210_adx_runtime_resume(struct device *dev) | |
99 | { | |
100 | struct tegra210_adx *adx = dev_get_drvdata(dev); | |
101 | ||
102 | regcache_cache_only(adx->regmap, false); | |
103 | regcache_sync(adx->regmap); | |
104 | ||
105 | tegra210_adx_write_map_ram(adx); | |
106 | ||
107 | return 0; | |
108 | } | |
109 | ||
110 | static int tegra210_adx_set_audio_cif(struct snd_soc_dai *dai, | |
111 | unsigned int channels, | |
35f8a9d8 | 112 | snd_pcm_format_t format, |
a99ab6f3 SP |
113 | unsigned int reg) |
114 | { | |
115 | struct tegra210_adx *adx = snd_soc_dai_get_drvdata(dai); | |
116 | struct tegra_cif_conf cif_conf; | |
117 | int audio_bits; | |
118 | ||
119 | memset(&cif_conf, 0, sizeof(struct tegra_cif_conf)); | |
120 | ||
121 | if (channels < 1 || channels > 16) | |
122 | return -EINVAL; | |
123 | ||
124 | switch (format) { | |
125 | case SNDRV_PCM_FORMAT_S8: | |
126 | audio_bits = TEGRA_ACIF_BITS_8; | |
127 | break; | |
128 | case SNDRV_PCM_FORMAT_S16_LE: | |
129 | audio_bits = TEGRA_ACIF_BITS_16; | |
130 | break; | |
131 | case SNDRV_PCM_FORMAT_S32_LE: | |
132 | audio_bits = TEGRA_ACIF_BITS_32; | |
133 | break; | |
134 | default: | |
135 | return -EINVAL; | |
136 | } | |
137 | ||
138 | cif_conf.audio_ch = channels; | |
139 | cif_conf.client_ch = channels; | |
140 | cif_conf.audio_bits = audio_bits; | |
141 | cif_conf.client_bits = audio_bits; | |
142 | ||
143 | tegra_set_cif(adx->regmap, reg, &cif_conf); | |
144 | ||
145 | return 0; | |
146 | } | |
147 | ||
148 | static int tegra210_adx_out_hw_params(struct snd_pcm_substream *substream, | |
149 | struct snd_pcm_hw_params *params, | |
150 | struct snd_soc_dai *dai) | |
151 | { | |
152 | return tegra210_adx_set_audio_cif(dai, params_channels(params), | |
153 | params_format(params), | |
154 | TEGRA210_ADX_TX1_CIF_CTRL + ((dai->id - 1) * TEGRA210_ADX_AUDIOCIF_CH_STRIDE)); | |
155 | } | |
156 | ||
157 | static int tegra210_adx_in_hw_params(struct snd_pcm_substream *substream, | |
158 | struct snd_pcm_hw_params *params, | |
159 | struct snd_soc_dai *dai) | |
160 | { | |
161 | return tegra210_adx_set_audio_cif(dai, params_channels(params), | |
162 | params_format(params), | |
163 | TEGRA210_ADX_RX_CIF_CTRL); | |
164 | } | |
165 | ||
166 | static int tegra210_adx_get_byte_map(struct snd_kcontrol *kcontrol, | |
167 | struct snd_ctl_elem_value *ucontrol) | |
168 | { | |
169 | struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); | |
170 | struct tegra210_adx *adx = snd_soc_component_get_drvdata(cmpnt); | |
171 | struct soc_mixer_control *mc; | |
172 | unsigned char *bytes_map = (unsigned char *)&adx->map; | |
173 | int enabled; | |
174 | ||
175 | mc = (struct soc_mixer_control *)kcontrol->private_value; | |
176 | enabled = adx->byte_mask[mc->reg / 32] & (1 << (mc->reg % 32)); | |
177 | ||
6dfe70be S |
178 | /* |
179 | * TODO: Simplify this logic to just return from bytes_map[] | |
180 | * | |
181 | * Presently below is required since bytes_map[] is | |
182 | * tightly packed and cannot store the control value of 256. | |
183 | * Byte mask state is used to know if 256 needs to be returned. | |
184 | * Note that for control value of 256, the put() call stores 0 | |
185 | * in the bytes_map[] and disables the corresponding bit in | |
186 | * byte_mask[]. | |
187 | */ | |
a99ab6f3 SP |
188 | if (enabled) |
189 | ucontrol->value.integer.value[0] = bytes_map[mc->reg]; | |
190 | else | |
6dfe70be | 191 | ucontrol->value.integer.value[0] = 256; |
a99ab6f3 SP |
192 | |
193 | return 0; | |
194 | } | |
195 | ||
196 | static int tegra210_adx_put_byte_map(struct snd_kcontrol *kcontrol, | |
197 | struct snd_ctl_elem_value *ucontrol) | |
198 | { | |
199 | struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); | |
200 | struct tegra210_adx *adx = snd_soc_component_get_drvdata(cmpnt); | |
201 | unsigned char *bytes_map = (unsigned char *)&adx->map; | |
202 | int value = ucontrol->value.integer.value[0]; | |
203 | struct soc_mixer_control *mc = | |
d8d6253b | 204 | (struct soc_mixer_control *)kcontrol->private_value; |
6dfe70be | 205 | unsigned int mask_val = adx->byte_mask[mc->reg / 32]; |
a99ab6f3 | 206 | |
6dfe70be S |
207 | if (value >= 0 && value <= 255) |
208 | mask_val |= (1 << (mc->reg % 32)); | |
209 | else | |
210 | mask_val &= ~(1 << (mc->reg % 32)); | |
211 | ||
212 | if (mask_val == adx->byte_mask[mc->reg / 32]) | |
3c97881b SP |
213 | return 0; |
214 | ||
6dfe70be S |
215 | /* Update byte map and slot */ |
216 | bytes_map[mc->reg] = value % 256; | |
217 | adx->byte_mask[mc->reg / 32] = mask_val; | |
a99ab6f3 SP |
218 | |
219 | return 1; | |
220 | } | |
221 | ||
313fab48 | 222 | static const struct snd_soc_dai_ops tegra210_adx_in_dai_ops = { |
a99ab6f3 SP |
223 | .hw_params = tegra210_adx_in_hw_params, |
224 | .startup = tegra210_adx_startup, | |
225 | }; | |
226 | ||
313fab48 | 227 | static const struct snd_soc_dai_ops tegra210_adx_out_dai_ops = { |
a99ab6f3 SP |
228 | .hw_params = tegra210_adx_out_hw_params, |
229 | }; | |
230 | ||
231 | #define IN_DAI \ | |
232 | { \ | |
233 | .name = "ADX-RX-CIF", \ | |
234 | .playback = { \ | |
235 | .stream_name = "RX-CIF-Playback", \ | |
236 | .channels_min = 1, \ | |
237 | .channels_max = 16, \ | |
238 | .rates = SNDRV_PCM_RATE_8000_192000, \ | |
239 | .formats = SNDRV_PCM_FMTBIT_S8 | \ | |
240 | SNDRV_PCM_FMTBIT_S16_LE | \ | |
241 | SNDRV_PCM_FMTBIT_S32_LE, \ | |
242 | }, \ | |
243 | .capture = { \ | |
244 | .stream_name = "RX-CIF-Capture", \ | |
245 | .channels_min = 1, \ | |
246 | .channels_max = 16, \ | |
247 | .rates = SNDRV_PCM_RATE_8000_192000, \ | |
248 | .formats = SNDRV_PCM_FMTBIT_S8 | \ | |
249 | SNDRV_PCM_FMTBIT_S16_LE | \ | |
250 | SNDRV_PCM_FMTBIT_S32_LE, \ | |
251 | }, \ | |
252 | .ops = &tegra210_adx_in_dai_ops, \ | |
253 | } | |
254 | ||
255 | #define OUT_DAI(id) \ | |
256 | { \ | |
257 | .name = "ADX-TX" #id "-CIF", \ | |
258 | .playback = { \ | |
259 | .stream_name = "TX" #id "-CIF-Playback",\ | |
260 | .channels_min = 1, \ | |
261 | .channels_max = 16, \ | |
262 | .rates = SNDRV_PCM_RATE_8000_192000, \ | |
263 | .formats = SNDRV_PCM_FMTBIT_S8 | \ | |
264 | SNDRV_PCM_FMTBIT_S16_LE | \ | |
265 | SNDRV_PCM_FMTBIT_S32_LE, \ | |
266 | }, \ | |
267 | .capture = { \ | |
268 | .stream_name = "TX" #id "-CIF-Capture", \ | |
269 | .channels_min = 1, \ | |
270 | .channels_max = 16, \ | |
271 | .rates = SNDRV_PCM_RATE_8000_192000, \ | |
272 | .formats = SNDRV_PCM_FMTBIT_S8 | \ | |
273 | SNDRV_PCM_FMTBIT_S16_LE | \ | |
274 | SNDRV_PCM_FMTBIT_S32_LE, \ | |
275 | }, \ | |
276 | .ops = &tegra210_adx_out_dai_ops, \ | |
277 | } | |
278 | ||
279 | static struct snd_soc_dai_driver tegra210_adx_dais[] = { | |
280 | IN_DAI, | |
281 | OUT_DAI(1), | |
282 | OUT_DAI(2), | |
283 | OUT_DAI(3), | |
284 | OUT_DAI(4), | |
285 | }; | |
286 | ||
287 | static const struct snd_soc_dapm_widget tegra210_adx_widgets[] = { | |
288 | SND_SOC_DAPM_AIF_IN("RX", NULL, 0, TEGRA210_ADX_ENABLE, | |
289 | TEGRA210_ADX_ENABLE_SHIFT, 0), | |
290 | SND_SOC_DAPM_AIF_OUT("TX1", NULL, 0, TEGRA210_ADX_CTRL, 0, 0), | |
291 | SND_SOC_DAPM_AIF_OUT("TX2", NULL, 0, TEGRA210_ADX_CTRL, 1, 0), | |
292 | SND_SOC_DAPM_AIF_OUT("TX3", NULL, 0, TEGRA210_ADX_CTRL, 2, 0), | |
293 | SND_SOC_DAPM_AIF_OUT("TX4", NULL, 0, TEGRA210_ADX_CTRL, 3, 0), | |
294 | }; | |
295 | ||
296 | #define STREAM_ROUTES(id, sname) \ | |
297 | { "XBAR-" sname, NULL, "XBAR-TX" }, \ | |
298 | { "RX-CIF-" sname, NULL, "XBAR-" sname }, \ | |
299 | { "RX", NULL, "RX-CIF-" sname }, \ | |
300 | { "TX" #id, NULL, "RX" }, \ | |
301 | { "TX" #id "-CIF-" sname, NULL, "TX" #id }, \ | |
302 | { "TX" #id " XBAR-" sname, NULL, "TX" #id "-CIF-" sname }, \ | |
303 | { "TX" #id " XBAR-RX", NULL, "TX" #id " XBAR-" sname } | |
304 | ||
305 | #define ADX_ROUTES(id) \ | |
306 | STREAM_ROUTES(id, "Playback"), \ | |
307 | STREAM_ROUTES(id, "Capture") | |
308 | ||
309 | #define STREAM_ROUTES(id, sname) \ | |
310 | { "XBAR-" sname, NULL, "XBAR-TX" }, \ | |
311 | { "RX-CIF-" sname, NULL, "XBAR-" sname }, \ | |
312 | { "RX", NULL, "RX-CIF-" sname }, \ | |
313 | { "TX" #id, NULL, "RX" }, \ | |
314 | { "TX" #id "-CIF-" sname, NULL, "TX" #id }, \ | |
315 | { "TX" #id " XBAR-" sname, NULL, "TX" #id "-CIF-" sname }, \ | |
316 | { "TX" #id " XBAR-RX", NULL, "TX" #id " XBAR-" sname } | |
317 | ||
318 | #define ADX_ROUTES(id) \ | |
319 | STREAM_ROUTES(id, "Playback"), \ | |
320 | STREAM_ROUTES(id, "Capture") | |
321 | ||
322 | static const struct snd_soc_dapm_route tegra210_adx_routes[] = { | |
323 | ADX_ROUTES(1), | |
324 | ADX_ROUTES(2), | |
325 | ADX_ROUTES(3), | |
326 | ADX_ROUTES(4), | |
327 | }; | |
328 | ||
329 | #define TEGRA210_ADX_BYTE_MAP_CTRL(reg) \ | |
330 | SOC_SINGLE_EXT("Byte Map " #reg, reg, 0, 256, 0, \ | |
331 | tegra210_adx_get_byte_map, \ | |
332 | tegra210_adx_put_byte_map) | |
333 | ||
334 | static struct snd_kcontrol_new tegra210_adx_controls[] = { | |
335 | TEGRA210_ADX_BYTE_MAP_CTRL(0), | |
336 | TEGRA210_ADX_BYTE_MAP_CTRL(1), | |
337 | TEGRA210_ADX_BYTE_MAP_CTRL(2), | |
338 | TEGRA210_ADX_BYTE_MAP_CTRL(3), | |
339 | TEGRA210_ADX_BYTE_MAP_CTRL(4), | |
340 | TEGRA210_ADX_BYTE_MAP_CTRL(5), | |
341 | TEGRA210_ADX_BYTE_MAP_CTRL(6), | |
342 | TEGRA210_ADX_BYTE_MAP_CTRL(7), | |
343 | TEGRA210_ADX_BYTE_MAP_CTRL(8), | |
344 | TEGRA210_ADX_BYTE_MAP_CTRL(9), | |
345 | TEGRA210_ADX_BYTE_MAP_CTRL(10), | |
346 | TEGRA210_ADX_BYTE_MAP_CTRL(11), | |
347 | TEGRA210_ADX_BYTE_MAP_CTRL(12), | |
348 | TEGRA210_ADX_BYTE_MAP_CTRL(13), | |
349 | TEGRA210_ADX_BYTE_MAP_CTRL(14), | |
350 | TEGRA210_ADX_BYTE_MAP_CTRL(15), | |
351 | TEGRA210_ADX_BYTE_MAP_CTRL(16), | |
352 | TEGRA210_ADX_BYTE_MAP_CTRL(17), | |
353 | TEGRA210_ADX_BYTE_MAP_CTRL(18), | |
354 | TEGRA210_ADX_BYTE_MAP_CTRL(19), | |
355 | TEGRA210_ADX_BYTE_MAP_CTRL(20), | |
356 | TEGRA210_ADX_BYTE_MAP_CTRL(21), | |
357 | TEGRA210_ADX_BYTE_MAP_CTRL(22), | |
358 | TEGRA210_ADX_BYTE_MAP_CTRL(23), | |
359 | TEGRA210_ADX_BYTE_MAP_CTRL(24), | |
360 | TEGRA210_ADX_BYTE_MAP_CTRL(25), | |
361 | TEGRA210_ADX_BYTE_MAP_CTRL(26), | |
362 | TEGRA210_ADX_BYTE_MAP_CTRL(27), | |
363 | TEGRA210_ADX_BYTE_MAP_CTRL(28), | |
364 | TEGRA210_ADX_BYTE_MAP_CTRL(29), | |
365 | TEGRA210_ADX_BYTE_MAP_CTRL(30), | |
366 | TEGRA210_ADX_BYTE_MAP_CTRL(31), | |
367 | TEGRA210_ADX_BYTE_MAP_CTRL(32), | |
368 | TEGRA210_ADX_BYTE_MAP_CTRL(33), | |
369 | TEGRA210_ADX_BYTE_MAP_CTRL(34), | |
370 | TEGRA210_ADX_BYTE_MAP_CTRL(35), | |
371 | TEGRA210_ADX_BYTE_MAP_CTRL(36), | |
372 | TEGRA210_ADX_BYTE_MAP_CTRL(37), | |
373 | TEGRA210_ADX_BYTE_MAP_CTRL(38), | |
374 | TEGRA210_ADX_BYTE_MAP_CTRL(39), | |
375 | TEGRA210_ADX_BYTE_MAP_CTRL(40), | |
376 | TEGRA210_ADX_BYTE_MAP_CTRL(41), | |
377 | TEGRA210_ADX_BYTE_MAP_CTRL(42), | |
378 | TEGRA210_ADX_BYTE_MAP_CTRL(43), | |
379 | TEGRA210_ADX_BYTE_MAP_CTRL(44), | |
380 | TEGRA210_ADX_BYTE_MAP_CTRL(45), | |
381 | TEGRA210_ADX_BYTE_MAP_CTRL(46), | |
382 | TEGRA210_ADX_BYTE_MAP_CTRL(47), | |
383 | TEGRA210_ADX_BYTE_MAP_CTRL(48), | |
384 | TEGRA210_ADX_BYTE_MAP_CTRL(49), | |
385 | TEGRA210_ADX_BYTE_MAP_CTRL(50), | |
386 | TEGRA210_ADX_BYTE_MAP_CTRL(51), | |
387 | TEGRA210_ADX_BYTE_MAP_CTRL(52), | |
388 | TEGRA210_ADX_BYTE_MAP_CTRL(53), | |
389 | TEGRA210_ADX_BYTE_MAP_CTRL(54), | |
390 | TEGRA210_ADX_BYTE_MAP_CTRL(55), | |
391 | TEGRA210_ADX_BYTE_MAP_CTRL(56), | |
392 | TEGRA210_ADX_BYTE_MAP_CTRL(57), | |
393 | TEGRA210_ADX_BYTE_MAP_CTRL(58), | |
394 | TEGRA210_ADX_BYTE_MAP_CTRL(59), | |
395 | TEGRA210_ADX_BYTE_MAP_CTRL(60), | |
396 | TEGRA210_ADX_BYTE_MAP_CTRL(61), | |
397 | TEGRA210_ADX_BYTE_MAP_CTRL(62), | |
398 | TEGRA210_ADX_BYTE_MAP_CTRL(63), | |
399 | }; | |
400 | ||
401 | static const struct snd_soc_component_driver tegra210_adx_cmpnt = { | |
402 | .dapm_widgets = tegra210_adx_widgets, | |
403 | .num_dapm_widgets = ARRAY_SIZE(tegra210_adx_widgets), | |
404 | .dapm_routes = tegra210_adx_routes, | |
405 | .num_dapm_routes = ARRAY_SIZE(tegra210_adx_routes), | |
406 | .controls = tegra210_adx_controls, | |
407 | .num_controls = ARRAY_SIZE(tegra210_adx_controls), | |
408 | }; | |
409 | ||
410 | static bool tegra210_adx_wr_reg(struct device *dev, | |
411 | unsigned int reg) | |
412 | { | |
413 | switch (reg) { | |
414 | case TEGRA210_ADX_TX_INT_MASK ... TEGRA210_ADX_TX4_CIF_CTRL: | |
415 | case TEGRA210_ADX_RX_INT_MASK ... TEGRA210_ADX_RX_CIF_CTRL: | |
416 | case TEGRA210_ADX_ENABLE ... TEGRA210_ADX_CG: | |
417 | case TEGRA210_ADX_CTRL ... TEGRA210_ADX_IN_BYTE_EN1: | |
418 | case TEGRA210_ADX_CFG_RAM_CTRL ... TEGRA210_ADX_CFG_RAM_DATA: | |
419 | return true; | |
420 | default: | |
421 | return false; | |
422 | } | |
423 | } | |
424 | ||
425 | static bool tegra210_adx_rd_reg(struct device *dev, | |
426 | unsigned int reg) | |
427 | { | |
428 | switch (reg) { | |
429 | case TEGRA210_ADX_RX_STATUS ... TEGRA210_ADX_CFG_RAM_DATA: | |
430 | return true; | |
431 | default: | |
432 | return false; | |
433 | } | |
434 | } | |
435 | ||
436 | static bool tegra210_adx_volatile_reg(struct device *dev, | |
437 | unsigned int reg) | |
438 | { | |
439 | switch (reg) { | |
440 | case TEGRA210_ADX_RX_STATUS: | |
441 | case TEGRA210_ADX_RX_INT_STATUS: | |
442 | case TEGRA210_ADX_RX_INT_SET: | |
443 | case TEGRA210_ADX_TX_STATUS: | |
444 | case TEGRA210_ADX_TX_INT_STATUS: | |
445 | case TEGRA210_ADX_TX_INT_SET: | |
446 | case TEGRA210_ADX_SOFT_RESET: | |
447 | case TEGRA210_ADX_STATUS: | |
448 | case TEGRA210_ADX_INT_STATUS: | |
449 | case TEGRA210_ADX_CFG_RAM_CTRL: | |
450 | case TEGRA210_ADX_CFG_RAM_DATA: | |
451 | return true; | |
452 | default: | |
453 | break; | |
454 | } | |
455 | ||
456 | return false; | |
457 | } | |
458 | ||
459 | static const struct regmap_config tegra210_adx_regmap_config = { | |
460 | .reg_bits = 32, | |
461 | .reg_stride = 4, | |
462 | .val_bits = 32, | |
463 | .max_register = TEGRA210_ADX_CFG_RAM_DATA, | |
464 | .writeable_reg = tegra210_adx_wr_reg, | |
465 | .readable_reg = tegra210_adx_rd_reg, | |
466 | .volatile_reg = tegra210_adx_volatile_reg, | |
467 | .reg_defaults = tegra210_adx_reg_defaults, | |
468 | .num_reg_defaults = ARRAY_SIZE(tegra210_adx_reg_defaults), | |
469 | .cache_type = REGCACHE_FLAT, | |
470 | }; | |
471 | ||
472 | static const struct of_device_id tegra210_adx_of_match[] = { | |
473 | { .compatible = "nvidia,tegra210-adx" }, | |
474 | {}, | |
475 | }; | |
476 | MODULE_DEVICE_TABLE(of, tegra210_adx_of_match); | |
477 | ||
478 | static int tegra210_adx_platform_probe(struct platform_device *pdev) | |
479 | { | |
480 | struct device *dev = &pdev->dev; | |
481 | struct tegra210_adx *adx; | |
482 | void __iomem *regs; | |
483 | int err; | |
484 | ||
485 | adx = devm_kzalloc(dev, sizeof(*adx), GFP_KERNEL); | |
486 | if (!adx) | |
487 | return -ENOMEM; | |
488 | ||
489 | dev_set_drvdata(dev, adx); | |
490 | ||
491 | regs = devm_platform_ioremap_resource(pdev, 0); | |
492 | if (IS_ERR(regs)) | |
493 | return PTR_ERR(regs); | |
494 | ||
495 | adx->regmap = devm_regmap_init_mmio(dev, regs, | |
496 | &tegra210_adx_regmap_config); | |
497 | if (IS_ERR(adx->regmap)) { | |
498 | dev_err(dev, "regmap init failed\n"); | |
499 | return PTR_ERR(adx->regmap); | |
500 | } | |
501 | ||
502 | regcache_cache_only(adx->regmap, true); | |
503 | ||
504 | err = devm_snd_soc_register_component(dev, &tegra210_adx_cmpnt, | |
505 | tegra210_adx_dais, | |
506 | ARRAY_SIZE(tegra210_adx_dais)); | |
507 | if (err) { | |
508 | dev_err(dev, "can't register ADX component, err: %d\n", err); | |
509 | return err; | |
510 | } | |
511 | ||
512 | pm_runtime_enable(dev); | |
513 | ||
514 | return 0; | |
515 | } | |
516 | ||
f129152c | 517 | static void tegra210_adx_platform_remove(struct platform_device *pdev) |
a99ab6f3 SP |
518 | { |
519 | pm_runtime_disable(&pdev->dev); | |
a99ab6f3 SP |
520 | } |
521 | ||
522 | static const struct dev_pm_ops tegra210_adx_pm_ops = { | |
523 | SET_RUNTIME_PM_OPS(tegra210_adx_runtime_suspend, | |
524 | tegra210_adx_runtime_resume, NULL) | |
cf36de4f SP |
525 | SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, |
526 | pm_runtime_force_resume) | |
a99ab6f3 SP |
527 | }; |
528 | ||
529 | static struct platform_driver tegra210_adx_driver = { | |
530 | .driver = { | |
531 | .name = "tegra210-adx", | |
532 | .of_match_table = tegra210_adx_of_match, | |
533 | .pm = &tegra210_adx_pm_ops, | |
534 | }, | |
535 | .probe = tegra210_adx_platform_probe, | |
f129152c | 536 | .remove_new = tegra210_adx_platform_remove, |
a99ab6f3 SP |
537 | }; |
538 | module_platform_driver(tegra210_adx_driver); | |
539 | ||
540 | MODULE_AUTHOR("Arun Shamanna Lakshmi <aruns@nvidia.com>"); | |
541 | MODULE_DESCRIPTION("Tegra210 ADX ASoC driver"); | |
542 | MODULE_LICENSE("GPL v2"); |