1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * tegra30_ahub.c - Tegra30 AHUB driver |
4 | * |
5 | * Copyright (c) 2011,2012, NVIDIA CORPORATION. All rights reserved. |
6 | */ |
7 | |
8 | #include <linux/clk.h> |
9 | #include <linux/device.h> |
10 | #include <linux/io.h> |
11 | #include <linux/module.h> |
12 | #include <linux/of_platform.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/pm_runtime.h> |
15 | #include <linux/regmap.h> |
16 | #include <linux/reset.h> |
17 | #include <linux/slab.h> |
18 | #include <sound/soc.h> |
19 | #include "tegra30_ahub.h" |
20 | |
21 | #define DRV_NAME "tegra30-ahub" |
22 | |
23 | static struct tegra30_ahub *ahub; |
24 | |
25 | static inline void tegra30_apbif_write(u32 reg, u32 val) |
26 | { |
27 | regmap_write(map: ahub->regmap_apbif, reg, val); |
28 | } |
29 | |
30 | static inline u32 tegra30_apbif_read(u32 reg) |
31 | { |
32 | u32 val; |
33 | |
34 | regmap_read(map: ahub->regmap_apbif, reg, val: &val); |
35 | return val; |
36 | } |
37 | |
38 | static inline void tegra30_audio_write(u32 reg, u32 val) |
39 | { |
40 | regmap_write(map: ahub->regmap_ahub, reg, val); |
41 | } |
42 | |
43 | static __maybe_unused int tegra30_ahub_runtime_suspend(struct device *dev) |
44 | { |
45 | regcache_cache_only(map: ahub->regmap_apbif, enable: true); |
46 | regcache_cache_only(map: ahub->regmap_ahub, enable: true); |
47 | |
48 | clk_bulk_disable_unprepare(num_clks: ahub->nclocks, clks: ahub->clocks); |
49 | |
50 | return 0; |
51 | } |
52 | |
53 | /* |
54 | * clk_apbif isn't required for an I2S<->I2S configuration where no PCM data |
55 | * is read from or sent to memory. However, that's not something the rest of |
56 | * the driver supports right now, so we'll just treat the two clocks as one |
57 | * for now. |
58 | * |
59 | * These functions should not be a plain ref-count. Instead, each active stream |
60 | * contributes some requirement to the minimum clock rate, so starting or |
61 | * stopping streams should dynamically adjust the clock as required. However, |
62 | * this is not yet implemented. |
63 | */ |
64 | static __maybe_unused int tegra30_ahub_runtime_resume(struct device *dev) |
65 | { |
66 | int ret; |
67 | |
68 | ret = reset_control_bulk_assert(num_rstcs: ahub->nresets, rstcs: ahub->resets); |
69 | if (ret) |
70 | return ret; |
71 | |
72 | ret = clk_bulk_prepare_enable(num_clks: ahub->nclocks, clks: ahub->clocks); |
73 | if (ret) |
74 | return ret; |
75 | |
76 | usleep_range(min: 10, max: 100); |
77 | |
78 | ret = reset_control_bulk_deassert(num_rstcs: ahub->nresets, rstcs: ahub->resets); |
79 | if (ret) |
80 | goto disable_clocks; |
81 | |
82 | regcache_cache_only(map: ahub->regmap_apbif, enable: false); |
83 | regcache_cache_only(map: ahub->regmap_ahub, enable: false); |
84 | regcache_mark_dirty(map: ahub->regmap_apbif); |
85 | regcache_mark_dirty(map: ahub->regmap_ahub); |
86 | |
87 | ret = regcache_sync(map: ahub->regmap_apbif); |
88 | if (ret) |
89 | goto disable_clocks; |
90 | |
91 | ret = regcache_sync(map: ahub->regmap_ahub); |
92 | if (ret) |
93 | goto disable_clocks; |
94 | |
95 | return 0; |
96 | |
97 | disable_clocks: |
98 | clk_bulk_disable_unprepare(num_clks: ahub->nclocks, clks: ahub->clocks); |
99 | |
100 | return ret; |
101 | } |
102 | |
103 | int tegra30_ahub_allocate_rx_fifo(enum tegra30_ahub_rxcif *rxcif, |
104 | char *dmachan, int dmachan_len, |
105 | dma_addr_t *fiforeg) |
106 | { |
107 | int channel; |
108 | u32 reg, val; |
109 | struct tegra30_ahub_cif_conf cif_conf; |
110 | |
111 | channel = find_first_zero_bit(addr: ahub->rx_usage, |
112 | TEGRA30_AHUB_CHANNEL_CTRL_COUNT); |
113 | if (channel >= TEGRA30_AHUB_CHANNEL_CTRL_COUNT) |
114 | return -EBUSY; |
115 | |
116 | __set_bit(channel, ahub->rx_usage); |
117 | |
118 | *rxcif = TEGRA30_AHUB_RXCIF_APBIF_RX0 + channel; |
119 | snprintf(buf: dmachan, size: dmachan_len, fmt: "rx%d" , channel); |
120 | *fiforeg = ahub->apbif_addr + TEGRA30_AHUB_CHANNEL_RXFIFO + |
121 | (channel * TEGRA30_AHUB_CHANNEL_RXFIFO_STRIDE); |
122 | |
123 | pm_runtime_get_sync(dev: ahub->dev); |
124 | |
125 | reg = TEGRA30_AHUB_CHANNEL_CTRL + |
126 | (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); |
127 | val = tegra30_apbif_read(reg); |
128 | val &= ~(TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_MASK | |
129 | TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_MASK); |
130 | val |= (7 << TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_SHIFT) | |
131 | TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_EN | |
132 | TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_16; |
133 | tegra30_apbif_write(reg, val); |
134 | |
135 | cif_conf.threshold = 0; |
136 | cif_conf.audio_channels = 2; |
137 | cif_conf.client_channels = 2; |
138 | cif_conf.audio_bits = TEGRA30_AUDIOCIF_BITS_16; |
139 | cif_conf.client_bits = TEGRA30_AUDIOCIF_BITS_16; |
140 | cif_conf.expand = 0; |
141 | cif_conf.stereo_conv = 0; |
142 | cif_conf.replicate = 0; |
143 | cif_conf.direction = TEGRA30_AUDIOCIF_DIRECTION_RX; |
144 | cif_conf.truncate = 0; |
145 | cif_conf.mono_conv = 0; |
146 | |
147 | reg = TEGRA30_AHUB_CIF_RX_CTRL + |
148 | (channel * TEGRA30_AHUB_CIF_RX_CTRL_STRIDE); |
149 | ahub->soc_data->set_audio_cif(ahub->regmap_apbif, reg, &cif_conf); |
150 | |
151 | pm_runtime_put(dev: ahub->dev); |
152 | |
153 | return 0; |
154 | } |
155 | EXPORT_SYMBOL_GPL(tegra30_ahub_allocate_rx_fifo); |
156 | |
157 | int tegra30_ahub_enable_rx_fifo(enum tegra30_ahub_rxcif rxcif) |
158 | { |
159 | int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0; |
160 | int reg, val; |
161 | |
162 | pm_runtime_get_sync(dev: ahub->dev); |
163 | |
164 | reg = TEGRA30_AHUB_CHANNEL_CTRL + |
165 | (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); |
166 | val = tegra30_apbif_read(reg); |
167 | val |= TEGRA30_AHUB_CHANNEL_CTRL_RX_EN; |
168 | tegra30_apbif_write(reg, val); |
169 | |
170 | pm_runtime_put(dev: ahub->dev); |
171 | |
172 | return 0; |
173 | } |
174 | EXPORT_SYMBOL_GPL(tegra30_ahub_enable_rx_fifo); |
175 | |
176 | int tegra30_ahub_disable_rx_fifo(enum tegra30_ahub_rxcif rxcif) |
177 | { |
178 | int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0; |
179 | int reg, val; |
180 | |
181 | pm_runtime_get_sync(dev: ahub->dev); |
182 | |
183 | reg = TEGRA30_AHUB_CHANNEL_CTRL + |
184 | (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); |
185 | val = tegra30_apbif_read(reg); |
186 | val &= ~TEGRA30_AHUB_CHANNEL_CTRL_RX_EN; |
187 | tegra30_apbif_write(reg, val); |
188 | |
189 | pm_runtime_put(dev: ahub->dev); |
190 | |
191 | return 0; |
192 | } |
193 | EXPORT_SYMBOL_GPL(tegra30_ahub_disable_rx_fifo); |
194 | |
195 | int tegra30_ahub_free_rx_fifo(enum tegra30_ahub_rxcif rxcif) |
196 | { |
197 | int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0; |
198 | |
199 | __clear_bit(channel, ahub->rx_usage); |
200 | |
201 | return 0; |
202 | } |
203 | EXPORT_SYMBOL_GPL(tegra30_ahub_free_rx_fifo); |
204 | |
205 | int tegra30_ahub_allocate_tx_fifo(enum tegra30_ahub_txcif *txcif, |
206 | char *dmachan, int dmachan_len, |
207 | dma_addr_t *fiforeg) |
208 | { |
209 | int channel; |
210 | u32 reg, val; |
211 | struct tegra30_ahub_cif_conf cif_conf; |
212 | |
213 | channel = find_first_zero_bit(addr: ahub->tx_usage, |
214 | TEGRA30_AHUB_CHANNEL_CTRL_COUNT); |
215 | if (channel >= TEGRA30_AHUB_CHANNEL_CTRL_COUNT) |
216 | return -EBUSY; |
217 | |
218 | __set_bit(channel, ahub->tx_usage); |
219 | |
220 | *txcif = TEGRA30_AHUB_TXCIF_APBIF_TX0 + channel; |
221 | snprintf(buf: dmachan, size: dmachan_len, fmt: "tx%d" , channel); |
222 | *fiforeg = ahub->apbif_addr + TEGRA30_AHUB_CHANNEL_TXFIFO + |
223 | (channel * TEGRA30_AHUB_CHANNEL_TXFIFO_STRIDE); |
224 | |
225 | pm_runtime_get_sync(dev: ahub->dev); |
226 | |
227 | reg = TEGRA30_AHUB_CHANNEL_CTRL + |
228 | (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); |
229 | val = tegra30_apbif_read(reg); |
230 | val &= ~(TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_MASK | |
231 | TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_MASK); |
232 | val |= (7 << TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_SHIFT) | |
233 | TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_EN | |
234 | TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_16; |
235 | tegra30_apbif_write(reg, val); |
236 | |
237 | cif_conf.threshold = 0; |
238 | cif_conf.audio_channels = 2; |
239 | cif_conf.client_channels = 2; |
240 | cif_conf.audio_bits = TEGRA30_AUDIOCIF_BITS_16; |
241 | cif_conf.client_bits = TEGRA30_AUDIOCIF_BITS_16; |
242 | cif_conf.expand = 0; |
243 | cif_conf.stereo_conv = 0; |
244 | cif_conf.replicate = 0; |
245 | cif_conf.direction = TEGRA30_AUDIOCIF_DIRECTION_TX; |
246 | cif_conf.truncate = 0; |
247 | cif_conf.mono_conv = 0; |
248 | |
249 | reg = TEGRA30_AHUB_CIF_TX_CTRL + |
250 | (channel * TEGRA30_AHUB_CIF_TX_CTRL_STRIDE); |
251 | ahub->soc_data->set_audio_cif(ahub->regmap_apbif, reg, &cif_conf); |
252 | |
253 | pm_runtime_put(dev: ahub->dev); |
254 | |
255 | return 0; |
256 | } |
257 | EXPORT_SYMBOL_GPL(tegra30_ahub_allocate_tx_fifo); |
258 | |
259 | int tegra30_ahub_enable_tx_fifo(enum tegra30_ahub_txcif txcif) |
260 | { |
261 | int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0; |
262 | int reg, val; |
263 | |
264 | pm_runtime_get_sync(dev: ahub->dev); |
265 | |
266 | reg = TEGRA30_AHUB_CHANNEL_CTRL + |
267 | (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); |
268 | val = tegra30_apbif_read(reg); |
269 | val |= TEGRA30_AHUB_CHANNEL_CTRL_TX_EN; |
270 | tegra30_apbif_write(reg, val); |
271 | |
272 | pm_runtime_put(dev: ahub->dev); |
273 | |
274 | return 0; |
275 | } |
276 | EXPORT_SYMBOL_GPL(tegra30_ahub_enable_tx_fifo); |
277 | |
278 | int tegra30_ahub_disable_tx_fifo(enum tegra30_ahub_txcif txcif) |
279 | { |
280 | int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0; |
281 | int reg, val; |
282 | |
283 | pm_runtime_get_sync(dev: ahub->dev); |
284 | |
285 | reg = TEGRA30_AHUB_CHANNEL_CTRL + |
286 | (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); |
287 | val = tegra30_apbif_read(reg); |
288 | val &= ~TEGRA30_AHUB_CHANNEL_CTRL_TX_EN; |
289 | tegra30_apbif_write(reg, val); |
290 | |
291 | pm_runtime_put(dev: ahub->dev); |
292 | |
293 | return 0; |
294 | } |
295 | EXPORT_SYMBOL_GPL(tegra30_ahub_disable_tx_fifo); |
296 | |
297 | int tegra30_ahub_free_tx_fifo(enum tegra30_ahub_txcif txcif) |
298 | { |
299 | int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0; |
300 | |
301 | __clear_bit(channel, ahub->tx_usage); |
302 | |
303 | return 0; |
304 | } |
305 | EXPORT_SYMBOL_GPL(tegra30_ahub_free_tx_fifo); |
306 | |
307 | int tegra30_ahub_set_rx_cif_source(enum tegra30_ahub_rxcif rxcif, |
308 | enum tegra30_ahub_txcif txcif) |
309 | { |
310 | int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0; |
311 | int reg; |
312 | |
313 | pm_runtime_get_sync(dev: ahub->dev); |
314 | |
315 | reg = TEGRA30_AHUB_AUDIO_RX + |
316 | (channel * TEGRA30_AHUB_AUDIO_RX_STRIDE); |
317 | tegra30_audio_write(reg, val: 1 << txcif); |
318 | |
319 | pm_runtime_put(dev: ahub->dev); |
320 | |
321 | return 0; |
322 | } |
323 | EXPORT_SYMBOL_GPL(tegra30_ahub_set_rx_cif_source); |
324 | |
325 | int tegra30_ahub_unset_rx_cif_source(enum tegra30_ahub_rxcif rxcif) |
326 | { |
327 | int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0; |
328 | int reg; |
329 | |
330 | pm_runtime_get_sync(dev: ahub->dev); |
331 | |
332 | reg = TEGRA30_AHUB_AUDIO_RX + |
333 | (channel * TEGRA30_AHUB_AUDIO_RX_STRIDE); |
334 | tegra30_audio_write(reg, val: 0); |
335 | |
336 | pm_runtime_put(dev: ahub->dev); |
337 | |
338 | return 0; |
339 | } |
340 | EXPORT_SYMBOL_GPL(tegra30_ahub_unset_rx_cif_source); |
341 | |
342 | static const struct reset_control_bulk_data tegra30_ahub_resets_data[] = { |
343 | { "d_audio" }, |
344 | { "apbif" }, |
345 | { "i2s0" }, |
346 | { "i2s1" }, |
347 | { "i2s2" }, |
348 | { "i2s3" }, |
349 | { "i2s4" }, |
350 | { "dam0" }, |
351 | { "dam1" }, |
352 | { "dam2" }, |
353 | { "spdif" }, |
354 | { "amx" }, /* Tegra114+ */ |
355 | { "adx" }, /* Tegra114+ */ |
356 | { "amx1" }, /* Tegra124 */ |
357 | { "adx1" }, /* Tegra124 */ |
358 | { "afc0" }, /* Tegra124 */ |
359 | { "afc1" }, /* Tegra124 */ |
360 | { "afc2" }, /* Tegra124 */ |
361 | { "afc3" }, /* Tegra124 */ |
362 | { "afc4" }, /* Tegra124 */ |
363 | { "afc5" }, /* Tegra124 */ |
364 | }; |
365 | |
366 | #define LAST_REG(name) \ |
367 | (TEGRA30_AHUB_##name + \ |
368 | (TEGRA30_AHUB_##name##_STRIDE * TEGRA30_AHUB_##name##_COUNT) - 4) |
369 | |
370 | #define REG_IN_ARRAY(reg, name) \ |
371 | ((reg >= TEGRA30_AHUB_##name) && \ |
372 | (reg <= LAST_REG(name) && \ |
373 | (!((reg - TEGRA30_AHUB_##name) % TEGRA30_AHUB_##name##_STRIDE)))) |
374 | |
375 | static bool tegra30_ahub_apbif_wr_rd_reg(struct device *dev, unsigned int reg) |
376 | { |
377 | switch (reg) { |
378 | case TEGRA30_AHUB_CONFIG_LINK_CTRL: |
379 | case TEGRA30_AHUB_MISC_CTRL: |
380 | case TEGRA30_AHUB_APBDMA_LIVE_STATUS: |
381 | case TEGRA30_AHUB_I2S_LIVE_STATUS: |
382 | case TEGRA30_AHUB_SPDIF_LIVE_STATUS: |
383 | case TEGRA30_AHUB_I2S_INT_MASK: |
384 | case TEGRA30_AHUB_DAM_INT_MASK: |
385 | case TEGRA30_AHUB_SPDIF_INT_MASK: |
386 | case TEGRA30_AHUB_APBIF_INT_MASK: |
387 | case TEGRA30_AHUB_I2S_INT_STATUS: |
388 | case TEGRA30_AHUB_DAM_INT_STATUS: |
389 | case TEGRA30_AHUB_SPDIF_INT_STATUS: |
390 | case TEGRA30_AHUB_APBIF_INT_STATUS: |
391 | case TEGRA30_AHUB_I2S_INT_SOURCE: |
392 | case TEGRA30_AHUB_DAM_INT_SOURCE: |
393 | case TEGRA30_AHUB_SPDIF_INT_SOURCE: |
394 | case TEGRA30_AHUB_APBIF_INT_SOURCE: |
395 | case TEGRA30_AHUB_I2S_INT_SET: |
396 | case TEGRA30_AHUB_DAM_INT_SET: |
397 | case TEGRA30_AHUB_SPDIF_INT_SET: |
398 | case TEGRA30_AHUB_APBIF_INT_SET: |
399 | return true; |
400 | default: |
401 | break; |
402 | } |
403 | |
404 | if (REG_IN_ARRAY(reg, CHANNEL_CTRL) || |
405 | REG_IN_ARRAY(reg, CHANNEL_CLEAR) || |
406 | REG_IN_ARRAY(reg, CHANNEL_STATUS) || |
407 | REG_IN_ARRAY(reg, CHANNEL_TXFIFO) || |
408 | REG_IN_ARRAY(reg, CHANNEL_RXFIFO) || |
409 | REG_IN_ARRAY(reg, CIF_TX_CTRL) || |
410 | REG_IN_ARRAY(reg, CIF_RX_CTRL) || |
411 | REG_IN_ARRAY(reg, DAM_LIVE_STATUS)) |
412 | return true; |
413 | |
414 | return false; |
415 | } |
416 | |
417 | static bool tegra30_ahub_apbif_volatile_reg(struct device *dev, |
418 | unsigned int reg) |
419 | { |
420 | switch (reg) { |
421 | case TEGRA30_AHUB_CONFIG_LINK_CTRL: |
422 | case TEGRA30_AHUB_MISC_CTRL: |
423 | case TEGRA30_AHUB_APBDMA_LIVE_STATUS: |
424 | case TEGRA30_AHUB_I2S_LIVE_STATUS: |
425 | case TEGRA30_AHUB_SPDIF_LIVE_STATUS: |
426 | case TEGRA30_AHUB_I2S_INT_STATUS: |
427 | case TEGRA30_AHUB_DAM_INT_STATUS: |
428 | case TEGRA30_AHUB_SPDIF_INT_STATUS: |
429 | case TEGRA30_AHUB_APBIF_INT_STATUS: |
430 | case TEGRA30_AHUB_I2S_INT_SET: |
431 | case TEGRA30_AHUB_DAM_INT_SET: |
432 | case TEGRA30_AHUB_SPDIF_INT_SET: |
433 | case TEGRA30_AHUB_APBIF_INT_SET: |
434 | return true; |
435 | default: |
436 | break; |
437 | } |
438 | |
439 | if (REG_IN_ARRAY(reg, CHANNEL_CLEAR) || |
440 | REG_IN_ARRAY(reg, CHANNEL_STATUS) || |
441 | REG_IN_ARRAY(reg, CHANNEL_TXFIFO) || |
442 | REG_IN_ARRAY(reg, CHANNEL_RXFIFO) || |
443 | REG_IN_ARRAY(reg, DAM_LIVE_STATUS)) |
444 | return true; |
445 | |
446 | return false; |
447 | } |
448 | |
449 | static bool tegra30_ahub_apbif_precious_reg(struct device *dev, |
450 | unsigned int reg) |
451 | { |
452 | if (REG_IN_ARRAY(reg, CHANNEL_TXFIFO) || |
453 | REG_IN_ARRAY(reg, CHANNEL_RXFIFO)) |
454 | return true; |
455 | |
456 | return false; |
457 | } |
458 | |
459 | static const struct regmap_config tegra30_ahub_apbif_regmap_config = { |
460 | .name = "apbif" , |
461 | .reg_bits = 32, |
462 | .val_bits = 32, |
463 | .reg_stride = 4, |
464 | .max_register = TEGRA30_AHUB_APBIF_INT_SET, |
465 | .writeable_reg = tegra30_ahub_apbif_wr_rd_reg, |
466 | .readable_reg = tegra30_ahub_apbif_wr_rd_reg, |
467 | .volatile_reg = tegra30_ahub_apbif_volatile_reg, |
468 | .precious_reg = tegra30_ahub_apbif_precious_reg, |
469 | .cache_type = REGCACHE_FLAT, |
470 | }; |
471 | |
472 | static bool tegra30_ahub_ahub_wr_rd_reg(struct device *dev, unsigned int reg) |
473 | { |
474 | if (REG_IN_ARRAY(reg, AUDIO_RX)) |
475 | return true; |
476 | |
477 | return false; |
478 | } |
479 | |
480 | static const struct regmap_config tegra30_ahub_ahub_regmap_config = { |
481 | .name = "ahub" , |
482 | .reg_bits = 32, |
483 | .val_bits = 32, |
484 | .reg_stride = 4, |
485 | .max_register = LAST_REG(AUDIO_RX), |
486 | .writeable_reg = tegra30_ahub_ahub_wr_rd_reg, |
487 | .readable_reg = tegra30_ahub_ahub_wr_rd_reg, |
488 | .cache_type = REGCACHE_FLAT, |
489 | }; |
490 | |
491 | static struct tegra30_ahub_soc_data soc_data_tegra30 = { |
492 | .num_resets = 11, |
493 | .set_audio_cif = tegra30_ahub_set_cif, |
494 | }; |
495 | |
496 | static struct tegra30_ahub_soc_data soc_data_tegra114 = { |
497 | .num_resets = 13, |
498 | .set_audio_cif = tegra30_ahub_set_cif, |
499 | }; |
500 | |
501 | static struct tegra30_ahub_soc_data soc_data_tegra124 = { |
502 | .num_resets = 21, |
503 | .set_audio_cif = tegra124_ahub_set_cif, |
504 | }; |
505 | |
506 | static const struct of_device_id tegra30_ahub_of_match[] = { |
507 | { .compatible = "nvidia,tegra124-ahub" , .data = &soc_data_tegra124 }, |
508 | { .compatible = "nvidia,tegra114-ahub" , .data = &soc_data_tegra114 }, |
509 | { .compatible = "nvidia,tegra30-ahub" , .data = &soc_data_tegra30 }, |
510 | {}, |
511 | }; |
512 | |
513 | static int tegra30_ahub_probe(struct platform_device *pdev) |
514 | { |
515 | const struct tegra30_ahub_soc_data *soc_data; |
516 | struct resource *res0; |
517 | void __iomem *regs_apbif, *regs_ahub; |
518 | int ret = 0; |
519 | |
520 | soc_data = of_device_get_match_data(dev: &pdev->dev); |
521 | if (!soc_data) |
522 | return -EINVAL; |
523 | |
524 | ahub = devm_kzalloc(dev: &pdev->dev, size: sizeof(struct tegra30_ahub), |
525 | GFP_KERNEL); |
526 | if (!ahub) |
527 | return -ENOMEM; |
528 | dev_set_drvdata(dev: &pdev->dev, data: ahub); |
529 | |
530 | BUILD_BUG_ON(sizeof(ahub->resets) != sizeof(tegra30_ahub_resets_data)); |
531 | memcpy(ahub->resets, tegra30_ahub_resets_data, sizeof(ahub->resets)); |
532 | |
533 | ahub->nresets = soc_data->num_resets; |
534 | ahub->soc_data = soc_data; |
535 | ahub->dev = &pdev->dev; |
536 | |
537 | ahub->clocks[ahub->nclocks++].id = "apbif" ; |
538 | ahub->clocks[ahub->nclocks++].id = "d_audio" ; |
539 | |
540 | ret = devm_clk_bulk_get(dev: &pdev->dev, num_clks: ahub->nclocks, clks: ahub->clocks); |
541 | if (ret) |
542 | goto err_unset_ahub; |
543 | |
544 | ret = devm_reset_control_bulk_get_exclusive(dev: &pdev->dev, num_rstcs: ahub->nresets, |
545 | rstcs: ahub->resets); |
546 | if (ret) { |
547 | dev_err(&pdev->dev, "Can't get resets: %d\n" , ret); |
548 | goto err_unset_ahub; |
549 | } |
550 | |
551 | regs_apbif = devm_platform_get_and_ioremap_resource(pdev, index: 0, res: &res0); |
552 | if (IS_ERR(ptr: regs_apbif)) { |
553 | ret = PTR_ERR(ptr: regs_apbif); |
554 | goto err_unset_ahub; |
555 | } |
556 | |
557 | ahub->apbif_addr = res0->start; |
558 | |
559 | ahub->regmap_apbif = devm_regmap_init_mmio(&pdev->dev, regs_apbif, |
560 | &tegra30_ahub_apbif_regmap_config); |
561 | if (IS_ERR(ptr: ahub->regmap_apbif)) { |
562 | dev_err(&pdev->dev, "apbif regmap init failed\n" ); |
563 | ret = PTR_ERR(ptr: ahub->regmap_apbif); |
564 | goto err_unset_ahub; |
565 | } |
566 | regcache_cache_only(map: ahub->regmap_apbif, enable: true); |
567 | |
568 | regs_ahub = devm_platform_ioremap_resource(pdev, index: 1); |
569 | if (IS_ERR(ptr: regs_ahub)) { |
570 | ret = PTR_ERR(ptr: regs_ahub); |
571 | goto err_unset_ahub; |
572 | } |
573 | |
574 | ahub->regmap_ahub = devm_regmap_init_mmio(&pdev->dev, regs_ahub, |
575 | &tegra30_ahub_ahub_regmap_config); |
576 | if (IS_ERR(ptr: ahub->regmap_ahub)) { |
577 | dev_err(&pdev->dev, "ahub regmap init failed\n" ); |
578 | ret = PTR_ERR(ptr: ahub->regmap_ahub); |
579 | goto err_unset_ahub; |
580 | } |
581 | regcache_cache_only(map: ahub->regmap_ahub, enable: true); |
582 | |
583 | pm_runtime_enable(dev: &pdev->dev); |
584 | |
585 | of_platform_populate(root: pdev->dev.of_node, NULL, NULL, parent: &pdev->dev); |
586 | |
587 | return 0; |
588 | |
589 | err_unset_ahub: |
590 | ahub = NULL; |
591 | |
592 | return ret; |
593 | } |
594 | |
595 | static void tegra30_ahub_remove(struct platform_device *pdev) |
596 | { |
597 | pm_runtime_disable(dev: &pdev->dev); |
598 | |
599 | ahub = NULL; |
600 | } |
601 | |
602 | static const struct dev_pm_ops tegra30_ahub_pm_ops = { |
603 | SET_RUNTIME_PM_OPS(tegra30_ahub_runtime_suspend, |
604 | tegra30_ahub_runtime_resume, NULL) |
605 | SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, |
606 | pm_runtime_force_resume) |
607 | }; |
608 | |
609 | static struct platform_driver tegra30_ahub_driver = { |
610 | .probe = tegra30_ahub_probe, |
611 | .remove_new = tegra30_ahub_remove, |
612 | .driver = { |
613 | .name = DRV_NAME, |
614 | .of_match_table = tegra30_ahub_of_match, |
615 | .pm = &tegra30_ahub_pm_ops, |
616 | }, |
617 | }; |
618 | module_platform_driver(tegra30_ahub_driver); |
619 | |
620 | void tegra30_ahub_set_cif(struct regmap *regmap, unsigned int reg, |
621 | struct tegra30_ahub_cif_conf *conf) |
622 | { |
623 | unsigned int value; |
624 | |
625 | value = (conf->threshold << |
626 | TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) | |
627 | ((conf->audio_channels - 1) << |
628 | TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) | |
629 | ((conf->client_channels - 1) << |
630 | TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) | |
631 | (conf->audio_bits << |
632 | TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) | |
633 | (conf->client_bits << |
634 | TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT) | |
635 | (conf->expand << |
636 | TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT) | |
637 | (conf->stereo_conv << |
638 | TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT) | |
639 | (conf->replicate << |
640 | TEGRA30_AUDIOCIF_CTRL_REPLICATE_SHIFT) | |
641 | (conf->direction << |
642 | TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT) | |
643 | (conf->truncate << |
644 | TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT) | |
645 | (conf->mono_conv << |
646 | TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT); |
647 | |
648 | regmap_write(map: regmap, reg, val: value); |
649 | } |
650 | EXPORT_SYMBOL_GPL(tegra30_ahub_set_cif); |
651 | |
652 | void tegra124_ahub_set_cif(struct regmap *regmap, unsigned int reg, |
653 | struct tegra30_ahub_cif_conf *conf) |
654 | { |
655 | unsigned int value; |
656 | |
657 | value = (conf->threshold << |
658 | TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) | |
659 | ((conf->audio_channels - 1) << |
660 | TEGRA124_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) | |
661 | ((conf->client_channels - 1) << |
662 | TEGRA124_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) | |
663 | (conf->audio_bits << |
664 | TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) | |
665 | (conf->client_bits << |
666 | TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT) | |
667 | (conf->expand << |
668 | TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT) | |
669 | (conf->stereo_conv << |
670 | TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT) | |
671 | (conf->replicate << |
672 | TEGRA30_AUDIOCIF_CTRL_REPLICATE_SHIFT) | |
673 | (conf->direction << |
674 | TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT) | |
675 | (conf->truncate << |
676 | TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT) | |
677 | (conf->mono_conv << |
678 | TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT); |
679 | |
680 | regmap_write(map: regmap, reg, val: value); |
681 | } |
682 | EXPORT_SYMBOL_GPL(tegra124_ahub_set_cif); |
683 | |
684 | MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>" ); |
685 | MODULE_DESCRIPTION("Tegra30 AHUB driver" ); |
686 | MODULE_LICENSE("GPL v2" ); |
687 | MODULE_ALIAS("platform:" DRV_NAME); |
688 | MODULE_DEVICE_TABLE(of, tegra30_ahub_of_match); |
689 | |