Commit | Line | Data |
---|---|---|
be944d42 SW |
1 | /* |
2 | * tegra30_ahub.c - Tegra30 AHUB driver | |
3 | * | |
4 | * Copyright (c) 2011,2012, NVIDIA CORPORATION. All rights reserved. | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms and conditions of the GNU General Public License, | |
8 | * version 2, as published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope it will be useful, but WITHOUT | |
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
13 | * more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
17 | */ | |
18 | ||
19 | #include <linux/clk.h> | |
20 | #include <linux/device.h> | |
21 | #include <linux/io.h> | |
22 | #include <linux/module.h> | |
23 | #include <linux/of_platform.h> | |
24 | #include <linux/platform_device.h> | |
25 | #include <linux/pm_runtime.h> | |
26 | #include <linux/regmap.h> | |
5185e0ac | 27 | #include <linux/reset.h> |
be944d42 | 28 | #include <linux/slab.h> |
be944d42 SW |
29 | #include <sound/soc.h> |
30 | #include "tegra30_ahub.h" | |
31 | ||
32 | #define DRV_NAME "tegra30-ahub" | |
33 | ||
34 | static struct tegra30_ahub *ahub; | |
35 | ||
36 | static inline void tegra30_apbif_write(u32 reg, u32 val) | |
37 | { | |
38 | regmap_write(ahub->regmap_apbif, reg, val); | |
39 | } | |
40 | ||
41 | static inline u32 tegra30_apbif_read(u32 reg) | |
42 | { | |
43 | u32 val; | |
44 | regmap_read(ahub->regmap_apbif, reg, &val); | |
45 | return val; | |
46 | } | |
47 | ||
48 | static inline void tegra30_audio_write(u32 reg, u32 val) | |
49 | { | |
50 | regmap_write(ahub->regmap_ahub, reg, val); | |
51 | } | |
52 | ||
53 | static int tegra30_ahub_runtime_suspend(struct device *dev) | |
54 | { | |
55 | regcache_cache_only(ahub->regmap_apbif, true); | |
56 | regcache_cache_only(ahub->regmap_ahub, true); | |
57 | ||
65d2bdd3 PG |
58 | clk_disable_unprepare(ahub->clk_apbif); |
59 | clk_disable_unprepare(ahub->clk_d_audio); | |
be944d42 SW |
60 | |
61 | return 0; | |
62 | } | |
63 | ||
64 | /* | |
65 | * clk_apbif isn't required for an I2S<->I2S configuration where no PCM data | |
66 | * is read from or sent to memory. However, that's not something the rest of | |
67 | * the driver supports right now, so we'll just treat the two clocks as one | |
68 | * for now. | |
69 | * | |
70 | * These functions should not be a plain ref-count. Instead, each active stream | |
71 | * contributes some requirement to the minimum clock rate, so starting or | |
72 | * stopping streams should dynamically adjust the clock as required. However, | |
73 | * this is not yet implemented. | |
74 | */ | |
75 | static int tegra30_ahub_runtime_resume(struct device *dev) | |
76 | { | |
77 | int ret; | |
78 | ||
65d2bdd3 | 79 | ret = clk_prepare_enable(ahub->clk_d_audio); |
be944d42 SW |
80 | if (ret) { |
81 | dev_err(dev, "clk_enable d_audio failed: %d\n", ret); | |
82 | return ret; | |
83 | } | |
65d2bdd3 | 84 | ret = clk_prepare_enable(ahub->clk_apbif); |
be944d42 SW |
85 | if (ret) { |
86 | dev_err(dev, "clk_enable apbif failed: %d\n", ret); | |
87 | clk_disable(ahub->clk_d_audio); | |
88 | return ret; | |
89 | } | |
90 | ||
91 | regcache_cache_only(ahub->regmap_apbif, false); | |
92 | regcache_cache_only(ahub->regmap_ahub, false); | |
93 | ||
94 | return 0; | |
95 | } | |
96 | ||
97 | int tegra30_ahub_allocate_rx_fifo(enum tegra30_ahub_rxcif *rxcif, | |
5608bd3e SW |
98 | char *dmachan, int dmachan_len, |
99 | dma_addr_t *fiforeg) | |
be944d42 SW |
100 | { |
101 | int channel; | |
102 | u32 reg, val; | |
5e049fce | 103 | struct tegra30_ahub_cif_conf cif_conf; |
be944d42 SW |
104 | |
105 | channel = find_first_zero_bit(ahub->rx_usage, | |
106 | TEGRA30_AHUB_CHANNEL_CTRL_COUNT); | |
107 | if (channel >= TEGRA30_AHUB_CHANNEL_CTRL_COUNT) | |
108 | return -EBUSY; | |
109 | ||
110 | __set_bit(channel, ahub->rx_usage); | |
111 | ||
112 | *rxcif = TEGRA30_AHUB_RXCIF_APBIF_RX0 + channel; | |
5608bd3e | 113 | snprintf(dmachan, dmachan_len, "rx%d", channel); |
be944d42 SW |
114 | *fiforeg = ahub->apbif_addr + TEGRA30_AHUB_CHANNEL_RXFIFO + |
115 | (channel * TEGRA30_AHUB_CHANNEL_RXFIFO_STRIDE); | |
be944d42 | 116 | |
768db0b9 SW |
117 | pm_runtime_get_sync(ahub->dev); |
118 | ||
be944d42 SW |
119 | reg = TEGRA30_AHUB_CHANNEL_CTRL + |
120 | (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); | |
121 | val = tegra30_apbif_read(reg); | |
122 | val &= ~(TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_MASK | | |
123 | TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_MASK); | |
124 | val |= (7 << TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_SHIFT) | | |
125 | TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_EN | | |
126 | TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_16; | |
127 | tegra30_apbif_write(reg, val); | |
128 | ||
5e049fce SW |
129 | cif_conf.threshold = 0; |
130 | cif_conf.audio_channels = 2; | |
131 | cif_conf.client_channels = 2; | |
132 | cif_conf.audio_bits = TEGRA30_AUDIOCIF_BITS_16; | |
133 | cif_conf.client_bits = TEGRA30_AUDIOCIF_BITS_16; | |
134 | cif_conf.expand = 0; | |
135 | cif_conf.stereo_conv = 0; | |
136 | cif_conf.replicate = 0; | |
137 | cif_conf.direction = TEGRA30_AUDIOCIF_DIRECTION_RX; | |
138 | cif_conf.truncate = 0; | |
139 | cif_conf.mono_conv = 0; | |
140 | ||
be944d42 SW |
141 | reg = TEGRA30_AHUB_CIF_RX_CTRL + |
142 | (channel * TEGRA30_AHUB_CIF_RX_CTRL_STRIDE); | |
5e049fce | 143 | ahub->soc_data->set_audio_cif(ahub->regmap_apbif, reg, &cif_conf); |
be944d42 | 144 | |
768db0b9 SW |
145 | pm_runtime_put(ahub->dev); |
146 | ||
be944d42 SW |
147 | return 0; |
148 | } | |
149 | EXPORT_SYMBOL_GPL(tegra30_ahub_allocate_rx_fifo); | |
150 | ||
151 | int tegra30_ahub_enable_rx_fifo(enum tegra30_ahub_rxcif rxcif) | |
152 | { | |
153 | int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0; | |
154 | int reg, val; | |
155 | ||
768db0b9 SW |
156 | pm_runtime_get_sync(ahub->dev); |
157 | ||
be944d42 SW |
158 | reg = TEGRA30_AHUB_CHANNEL_CTRL + |
159 | (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); | |
160 | val = tegra30_apbif_read(reg); | |
161 | val |= TEGRA30_AHUB_CHANNEL_CTRL_RX_EN; | |
162 | tegra30_apbif_write(reg, val); | |
163 | ||
768db0b9 SW |
164 | pm_runtime_put(ahub->dev); |
165 | ||
be944d42 SW |
166 | return 0; |
167 | } | |
168 | EXPORT_SYMBOL_GPL(tegra30_ahub_enable_rx_fifo); | |
169 | ||
170 | int tegra30_ahub_disable_rx_fifo(enum tegra30_ahub_rxcif rxcif) | |
171 | { | |
172 | int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0; | |
173 | int reg, val; | |
174 | ||
768db0b9 SW |
175 | pm_runtime_get_sync(ahub->dev); |
176 | ||
be944d42 SW |
177 | reg = TEGRA30_AHUB_CHANNEL_CTRL + |
178 | (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); | |
179 | val = tegra30_apbif_read(reg); | |
180 | val &= ~TEGRA30_AHUB_CHANNEL_CTRL_RX_EN; | |
181 | tegra30_apbif_write(reg, val); | |
182 | ||
768db0b9 SW |
183 | pm_runtime_put(ahub->dev); |
184 | ||
be944d42 SW |
185 | return 0; |
186 | } | |
187 | EXPORT_SYMBOL_GPL(tegra30_ahub_disable_rx_fifo); | |
188 | ||
189 | int tegra30_ahub_free_rx_fifo(enum tegra30_ahub_rxcif rxcif) | |
190 | { | |
191 | int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0; | |
192 | ||
193 | __clear_bit(channel, ahub->rx_usage); | |
194 | ||
195 | return 0; | |
196 | } | |
197 | EXPORT_SYMBOL_GPL(tegra30_ahub_free_rx_fifo); | |
198 | ||
199 | int tegra30_ahub_allocate_tx_fifo(enum tegra30_ahub_txcif *txcif, | |
5608bd3e SW |
200 | char *dmachan, int dmachan_len, |
201 | dma_addr_t *fiforeg) | |
be944d42 SW |
202 | { |
203 | int channel; | |
204 | u32 reg, val; | |
5e049fce | 205 | struct tegra30_ahub_cif_conf cif_conf; |
be944d42 SW |
206 | |
207 | channel = find_first_zero_bit(ahub->tx_usage, | |
208 | TEGRA30_AHUB_CHANNEL_CTRL_COUNT); | |
209 | if (channel >= TEGRA30_AHUB_CHANNEL_CTRL_COUNT) | |
210 | return -EBUSY; | |
211 | ||
212 | __set_bit(channel, ahub->tx_usage); | |
213 | ||
214 | *txcif = TEGRA30_AHUB_TXCIF_APBIF_TX0 + channel; | |
5608bd3e | 215 | snprintf(dmachan, dmachan_len, "tx%d", channel); |
be944d42 SW |
216 | *fiforeg = ahub->apbif_addr + TEGRA30_AHUB_CHANNEL_TXFIFO + |
217 | (channel * TEGRA30_AHUB_CHANNEL_TXFIFO_STRIDE); | |
be944d42 | 218 | |
768db0b9 SW |
219 | pm_runtime_get_sync(ahub->dev); |
220 | ||
be944d42 SW |
221 | reg = TEGRA30_AHUB_CHANNEL_CTRL + |
222 | (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); | |
223 | val = tegra30_apbif_read(reg); | |
224 | val &= ~(TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_MASK | | |
225 | TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_MASK); | |
226 | val |= (7 << TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_SHIFT) | | |
227 | TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_EN | | |
228 | TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_16; | |
229 | tegra30_apbif_write(reg, val); | |
230 | ||
5e049fce SW |
231 | cif_conf.threshold = 0; |
232 | cif_conf.audio_channels = 2; | |
233 | cif_conf.client_channels = 2; | |
234 | cif_conf.audio_bits = TEGRA30_AUDIOCIF_BITS_16; | |
235 | cif_conf.client_bits = TEGRA30_AUDIOCIF_BITS_16; | |
236 | cif_conf.expand = 0; | |
237 | cif_conf.stereo_conv = 0; | |
238 | cif_conf.replicate = 0; | |
239 | cif_conf.direction = TEGRA30_AUDIOCIF_DIRECTION_TX; | |
240 | cif_conf.truncate = 0; | |
241 | cif_conf.mono_conv = 0; | |
242 | ||
be944d42 SW |
243 | reg = TEGRA30_AHUB_CIF_TX_CTRL + |
244 | (channel * TEGRA30_AHUB_CIF_TX_CTRL_STRIDE); | |
5e049fce | 245 | ahub->soc_data->set_audio_cif(ahub->regmap_apbif, reg, &cif_conf); |
be944d42 | 246 | |
768db0b9 SW |
247 | pm_runtime_put(ahub->dev); |
248 | ||
be944d42 SW |
249 | return 0; |
250 | } | |
251 | EXPORT_SYMBOL_GPL(tegra30_ahub_allocate_tx_fifo); | |
252 | ||
253 | int tegra30_ahub_enable_tx_fifo(enum tegra30_ahub_txcif txcif) | |
254 | { | |
255 | int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0; | |
256 | int reg, val; | |
257 | ||
768db0b9 SW |
258 | pm_runtime_get_sync(ahub->dev); |
259 | ||
be944d42 SW |
260 | reg = TEGRA30_AHUB_CHANNEL_CTRL + |
261 | (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); | |
262 | val = tegra30_apbif_read(reg); | |
263 | val |= TEGRA30_AHUB_CHANNEL_CTRL_TX_EN; | |
264 | tegra30_apbif_write(reg, val); | |
265 | ||
768db0b9 SW |
266 | pm_runtime_put(ahub->dev); |
267 | ||
be944d42 SW |
268 | return 0; |
269 | } | |
270 | EXPORT_SYMBOL_GPL(tegra30_ahub_enable_tx_fifo); | |
271 | ||
272 | int tegra30_ahub_disable_tx_fifo(enum tegra30_ahub_txcif txcif) | |
273 | { | |
274 | int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0; | |
275 | int reg, val; | |
276 | ||
768db0b9 SW |
277 | pm_runtime_get_sync(ahub->dev); |
278 | ||
be944d42 SW |
279 | reg = TEGRA30_AHUB_CHANNEL_CTRL + |
280 | (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); | |
281 | val = tegra30_apbif_read(reg); | |
282 | val &= ~TEGRA30_AHUB_CHANNEL_CTRL_TX_EN; | |
283 | tegra30_apbif_write(reg, val); | |
284 | ||
768db0b9 SW |
285 | pm_runtime_put(ahub->dev); |
286 | ||
be944d42 SW |
287 | return 0; |
288 | } | |
289 | EXPORT_SYMBOL_GPL(tegra30_ahub_disable_tx_fifo); | |
290 | ||
291 | int tegra30_ahub_free_tx_fifo(enum tegra30_ahub_txcif txcif) | |
292 | { | |
293 | int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0; | |
294 | ||
295 | __clear_bit(channel, ahub->tx_usage); | |
296 | ||
297 | return 0; | |
298 | } | |
299 | EXPORT_SYMBOL_GPL(tegra30_ahub_free_tx_fifo); | |
300 | ||
301 | int tegra30_ahub_set_rx_cif_source(enum tegra30_ahub_rxcif rxcif, | |
302 | enum tegra30_ahub_txcif txcif) | |
303 | { | |
304 | int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0; | |
305 | int reg; | |
306 | ||
768db0b9 SW |
307 | pm_runtime_get_sync(ahub->dev); |
308 | ||
be944d42 SW |
309 | reg = TEGRA30_AHUB_AUDIO_RX + |
310 | (channel * TEGRA30_AHUB_AUDIO_RX_STRIDE); | |
311 | tegra30_audio_write(reg, 1 << txcif); | |
312 | ||
768db0b9 SW |
313 | pm_runtime_put(ahub->dev); |
314 | ||
be944d42 SW |
315 | return 0; |
316 | } | |
317 | EXPORT_SYMBOL_GPL(tegra30_ahub_set_rx_cif_source); | |
318 | ||
319 | int tegra30_ahub_unset_rx_cif_source(enum tegra30_ahub_rxcif rxcif) | |
320 | { | |
321 | int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0; | |
322 | int reg; | |
323 | ||
768db0b9 SW |
324 | pm_runtime_get_sync(ahub->dev); |
325 | ||
be944d42 SW |
326 | reg = TEGRA30_AHUB_AUDIO_RX + |
327 | (channel * TEGRA30_AHUB_AUDIO_RX_STRIDE); | |
328 | tegra30_audio_write(reg, 0); | |
329 | ||
768db0b9 SW |
330 | pm_runtime_put(ahub->dev); |
331 | ||
be944d42 SW |
332 | return 0; |
333 | } | |
334 | EXPORT_SYMBOL_GPL(tegra30_ahub_unset_rx_cif_source); | |
335 | ||
5185e0ac SW |
336 | #define MOD_LIST_MASK_TEGRA30 BIT(0) |
337 | #define MOD_LIST_MASK_TEGRA114 BIT(1) | |
f1d6ff79 | 338 | #define MOD_LIST_MASK_TEGRA124 BIT(2) |
95d36075 | 339 | |
5185e0ac | 340 | #define MOD_LIST_MASK_TEGRA30_OR_LATER \ |
f1d6ff79 SW |
341 | (MOD_LIST_MASK_TEGRA30 | MOD_LIST_MASK_TEGRA114 | \ |
342 | MOD_LIST_MASK_TEGRA124) | |
343 | #define MOD_LIST_MASK_TEGRA114_OR_LATER \ | |
344 | (MOD_LIST_MASK_TEGRA114 | MOD_LIST_MASK_TEGRA124) | |
95d36075 SW |
345 | |
346 | static const struct { | |
5185e0ac SW |
347 | const char *rst_name; |
348 | u32 mod_list_mask; | |
349 | } configlink_mods[] = { | |
350 | { "i2s0", MOD_LIST_MASK_TEGRA30_OR_LATER }, | |
351 | { "i2s1", MOD_LIST_MASK_TEGRA30_OR_LATER }, | |
352 | { "i2s2", MOD_LIST_MASK_TEGRA30_OR_LATER }, | |
353 | { "i2s3", MOD_LIST_MASK_TEGRA30_OR_LATER }, | |
354 | { "i2s4", MOD_LIST_MASK_TEGRA30_OR_LATER }, | |
355 | { "dam0", MOD_LIST_MASK_TEGRA30_OR_LATER }, | |
356 | { "dam1", MOD_LIST_MASK_TEGRA30_OR_LATER }, | |
357 | { "dam2", MOD_LIST_MASK_TEGRA30_OR_LATER }, | |
358 | { "spdif", MOD_LIST_MASK_TEGRA30_OR_LATER }, | |
f1d6ff79 SW |
359 | { "amx", MOD_LIST_MASK_TEGRA114_OR_LATER }, |
360 | { "adx", MOD_LIST_MASK_TEGRA114_OR_LATER }, | |
361 | { "amx1", MOD_LIST_MASK_TEGRA124 }, | |
362 | { "adx1", MOD_LIST_MASK_TEGRA124 }, | |
363 | { "afc0", MOD_LIST_MASK_TEGRA124 }, | |
364 | { "afc1", MOD_LIST_MASK_TEGRA124 }, | |
365 | { "afc2", MOD_LIST_MASK_TEGRA124 }, | |
366 | { "afc3", MOD_LIST_MASK_TEGRA124 }, | |
367 | { "afc4", MOD_LIST_MASK_TEGRA124 }, | |
368 | { "afc5", MOD_LIST_MASK_TEGRA124 }, | |
be944d42 SW |
369 | }; |
370 | ||
be944d42 SW |
371 | #define LAST_REG(name) \ |
372 | (TEGRA30_AHUB_##name + \ | |
373 | (TEGRA30_AHUB_##name##_STRIDE * TEGRA30_AHUB_##name##_COUNT) - 4) | |
374 | ||
375 | #define REG_IN_ARRAY(reg, name) \ | |
376 | ((reg >= TEGRA30_AHUB_##name) && \ | |
377 | (reg <= LAST_REG(name) && \ | |
378 | (!((reg - TEGRA30_AHUB_##name) % TEGRA30_AHUB_##name##_STRIDE)))) | |
379 | ||
380 | static bool tegra30_ahub_apbif_wr_rd_reg(struct device *dev, unsigned int reg) | |
381 | { | |
382 | switch (reg) { | |
383 | case TEGRA30_AHUB_CONFIG_LINK_CTRL: | |
384 | case TEGRA30_AHUB_MISC_CTRL: | |
385 | case TEGRA30_AHUB_APBDMA_LIVE_STATUS: | |
386 | case TEGRA30_AHUB_I2S_LIVE_STATUS: | |
387 | case TEGRA30_AHUB_SPDIF_LIVE_STATUS: | |
388 | case TEGRA30_AHUB_I2S_INT_MASK: | |
389 | case TEGRA30_AHUB_DAM_INT_MASK: | |
390 | case TEGRA30_AHUB_SPDIF_INT_MASK: | |
391 | case TEGRA30_AHUB_APBIF_INT_MASK: | |
392 | case TEGRA30_AHUB_I2S_INT_STATUS: | |
393 | case TEGRA30_AHUB_DAM_INT_STATUS: | |
394 | case TEGRA30_AHUB_SPDIF_INT_STATUS: | |
395 | case TEGRA30_AHUB_APBIF_INT_STATUS: | |
396 | case TEGRA30_AHUB_I2S_INT_SOURCE: | |
397 | case TEGRA30_AHUB_DAM_INT_SOURCE: | |
398 | case TEGRA30_AHUB_SPDIF_INT_SOURCE: | |
399 | case TEGRA30_AHUB_APBIF_INT_SOURCE: | |
400 | case TEGRA30_AHUB_I2S_INT_SET: | |
401 | case TEGRA30_AHUB_DAM_INT_SET: | |
402 | case TEGRA30_AHUB_SPDIF_INT_SET: | |
403 | case TEGRA30_AHUB_APBIF_INT_SET: | |
404 | return true; | |
405 | default: | |
406 | break; | |
1d198f26 | 407 | } |
be944d42 SW |
408 | |
409 | if (REG_IN_ARRAY(reg, CHANNEL_CTRL) || | |
410 | REG_IN_ARRAY(reg, CHANNEL_CLEAR) || | |
411 | REG_IN_ARRAY(reg, CHANNEL_STATUS) || | |
412 | REG_IN_ARRAY(reg, CHANNEL_TXFIFO) || | |
413 | REG_IN_ARRAY(reg, CHANNEL_RXFIFO) || | |
414 | REG_IN_ARRAY(reg, CIF_TX_CTRL) || | |
415 | REG_IN_ARRAY(reg, CIF_RX_CTRL) || | |
416 | REG_IN_ARRAY(reg, DAM_LIVE_STATUS)) | |
417 | return true; | |
418 | ||
419 | return false; | |
420 | } | |
421 | ||
422 | static bool tegra30_ahub_apbif_volatile_reg(struct device *dev, | |
423 | unsigned int reg) | |
424 | { | |
425 | switch (reg) { | |
426 | case TEGRA30_AHUB_CONFIG_LINK_CTRL: | |
427 | case TEGRA30_AHUB_MISC_CTRL: | |
428 | case TEGRA30_AHUB_APBDMA_LIVE_STATUS: | |
429 | case TEGRA30_AHUB_I2S_LIVE_STATUS: | |
430 | case TEGRA30_AHUB_SPDIF_LIVE_STATUS: | |
431 | case TEGRA30_AHUB_I2S_INT_STATUS: | |
432 | case TEGRA30_AHUB_DAM_INT_STATUS: | |
433 | case TEGRA30_AHUB_SPDIF_INT_STATUS: | |
434 | case TEGRA30_AHUB_APBIF_INT_STATUS: | |
435 | case TEGRA30_AHUB_I2S_INT_SET: | |
436 | case TEGRA30_AHUB_DAM_INT_SET: | |
437 | case TEGRA30_AHUB_SPDIF_INT_SET: | |
438 | case TEGRA30_AHUB_APBIF_INT_SET: | |
439 | return true; | |
440 | default: | |
441 | break; | |
1d198f26 | 442 | } |
be944d42 SW |
443 | |
444 | if (REG_IN_ARRAY(reg, CHANNEL_CLEAR) || | |
445 | REG_IN_ARRAY(reg, CHANNEL_STATUS) || | |
446 | REG_IN_ARRAY(reg, CHANNEL_TXFIFO) || | |
447 | REG_IN_ARRAY(reg, CHANNEL_RXFIFO) || | |
448 | REG_IN_ARRAY(reg, DAM_LIVE_STATUS)) | |
449 | return true; | |
450 | ||
451 | return false; | |
452 | } | |
453 | ||
454 | static bool tegra30_ahub_apbif_precious_reg(struct device *dev, | |
455 | unsigned int reg) | |
456 | { | |
457 | if (REG_IN_ARRAY(reg, CHANNEL_TXFIFO) || | |
458 | REG_IN_ARRAY(reg, CHANNEL_RXFIFO)) | |
459 | return true; | |
460 | ||
461 | return false; | |
462 | } | |
463 | ||
464 | static const struct regmap_config tegra30_ahub_apbif_regmap_config = { | |
465 | .name = "apbif", | |
466 | .reg_bits = 32, | |
467 | .val_bits = 32, | |
468 | .reg_stride = 4, | |
469 | .max_register = TEGRA30_AHUB_APBIF_INT_SET, | |
470 | .writeable_reg = tegra30_ahub_apbif_wr_rd_reg, | |
471 | .readable_reg = tegra30_ahub_apbif_wr_rd_reg, | |
472 | .volatile_reg = tegra30_ahub_apbif_volatile_reg, | |
473 | .precious_reg = tegra30_ahub_apbif_precious_reg, | |
591d14f0 | 474 | .cache_type = REGCACHE_FLAT, |
be944d42 SW |
475 | }; |
476 | ||
477 | static bool tegra30_ahub_ahub_wr_rd_reg(struct device *dev, unsigned int reg) | |
478 | { | |
479 | if (REG_IN_ARRAY(reg, AUDIO_RX)) | |
480 | return true; | |
481 | ||
482 | return false; | |
483 | } | |
484 | ||
485 | static const struct regmap_config tegra30_ahub_ahub_regmap_config = { | |
486 | .name = "ahub", | |
487 | .reg_bits = 32, | |
488 | .val_bits = 32, | |
489 | .reg_stride = 4, | |
490 | .max_register = LAST_REG(AUDIO_RX), | |
491 | .writeable_reg = tegra30_ahub_ahub_wr_rd_reg, | |
492 | .readable_reg = tegra30_ahub_ahub_wr_rd_reg, | |
591d14f0 | 493 | .cache_type = REGCACHE_FLAT, |
be944d42 SW |
494 | }; |
495 | ||
95d36075 | 496 | static struct tegra30_ahub_soc_data soc_data_tegra30 = { |
5185e0ac | 497 | .mod_list_mask = MOD_LIST_MASK_TEGRA30, |
5e049fce | 498 | .set_audio_cif = tegra30_ahub_set_cif, |
95d36075 SW |
499 | }; |
500 | ||
501 | static struct tegra30_ahub_soc_data soc_data_tegra114 = { | |
5185e0ac | 502 | .mod_list_mask = MOD_LIST_MASK_TEGRA114, |
5e049fce SW |
503 | .set_audio_cif = tegra30_ahub_set_cif, |
504 | }; | |
505 | ||
506 | static struct tegra30_ahub_soc_data soc_data_tegra124 = { | |
f1d6ff79 | 507 | .mod_list_mask = MOD_LIST_MASK_TEGRA124, |
5e049fce | 508 | .set_audio_cif = tegra124_ahub_set_cif, |
95d36075 SW |
509 | }; |
510 | ||
511 | static const struct of_device_id tegra30_ahub_of_match[] = { | |
5e049fce | 512 | { .compatible = "nvidia,tegra124-ahub", .data = &soc_data_tegra124 }, |
95d36075 SW |
513 | { .compatible = "nvidia,tegra114-ahub", .data = &soc_data_tegra114 }, |
514 | { .compatible = "nvidia,tegra30-ahub", .data = &soc_data_tegra30 }, | |
515 | {}, | |
516 | }; | |
517 | ||
4652a0d0 | 518 | static int tegra30_ahub_probe(struct platform_device *pdev) |
be944d42 | 519 | { |
95d36075 SW |
520 | const struct of_device_id *match; |
521 | const struct tegra30_ahub_soc_data *soc_data; | |
5185e0ac | 522 | struct reset_control *rst; |
be944d42 SW |
523 | int i; |
524 | struct resource *res0, *res1, *region; | |
be944d42 SW |
525 | void __iomem *regs_apbif, *regs_ahub; |
526 | int ret = 0; | |
527 | ||
528 | if (ahub) | |
529 | return -ENODEV; | |
530 | ||
95d36075 SW |
531 | match = of_match_device(tegra30_ahub_of_match, &pdev->dev); |
532 | if (!match) | |
533 | return -EINVAL; | |
534 | soc_data = match->data; | |
535 | ||
be944d42 SW |
536 | /* |
537 | * The AHUB hosts a register bus: the "configlink". For this to | |
538 | * operate correctly, all devices on this bus must be out of reset. | |
539 | * Ensure that here. | |
540 | */ | |
5185e0ac SW |
541 | for (i = 0; i < ARRAY_SIZE(configlink_mods); i++) { |
542 | if (!(configlink_mods[i].mod_list_mask & | |
543 | soc_data->mod_list_mask)) | |
95d36075 | 544 | continue; |
5185e0ac SW |
545 | |
546 | rst = reset_control_get(&pdev->dev, | |
547 | configlink_mods[i].rst_name); | |
548 | if (IS_ERR(rst)) { | |
549 | dev_err(&pdev->dev, "Can't get reset %s\n", | |
550 | configlink_mods[i].rst_name); | |
551 | ret = PTR_ERR(rst); | |
be944d42 SW |
552 | goto err; |
553 | } | |
5185e0ac SW |
554 | |
555 | ret = reset_control_deassert(rst); | |
556 | reset_control_put(rst); | |
557 | if (ret) | |
558 | goto err; | |
be944d42 SW |
559 | } |
560 | ||
561 | ahub = devm_kzalloc(&pdev->dev, sizeof(struct tegra30_ahub), | |
562 | GFP_KERNEL); | |
563 | if (!ahub) { | |
564 | dev_err(&pdev->dev, "Can't allocate tegra30_ahub\n"); | |
565 | ret = -ENOMEM; | |
566 | goto err; | |
567 | } | |
568 | dev_set_drvdata(&pdev->dev, ahub); | |
569 | ||
5e049fce | 570 | ahub->soc_data = soc_data; |
be944d42 SW |
571 | ahub->dev = &pdev->dev; |
572 | ||
573 | ahub->clk_d_audio = clk_get(&pdev->dev, "d_audio"); | |
574 | if (IS_ERR(ahub->clk_d_audio)) { | |
575 | dev_err(&pdev->dev, "Can't retrieve ahub d_audio clock\n"); | |
576 | ret = PTR_ERR(ahub->clk_d_audio); | |
577 | goto err; | |
578 | } | |
579 | ||
580 | ahub->clk_apbif = clk_get(&pdev->dev, "apbif"); | |
581 | if (IS_ERR(ahub->clk_apbif)) { | |
582 | dev_err(&pdev->dev, "Can't retrieve ahub apbif clock\n"); | |
583 | ret = PTR_ERR(ahub->clk_apbif); | |
584 | goto err_clk_put_d_audio; | |
585 | } | |
586 | ||
be944d42 SW |
587 | res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
588 | if (!res0) { | |
589 | dev_err(&pdev->dev, "No apbif memory resource\n"); | |
590 | ret = -ENODEV; | |
591 | goto err_clk_put_apbif; | |
592 | } | |
593 | ||
594 | region = devm_request_mem_region(&pdev->dev, res0->start, | |
595 | resource_size(res0), DRV_NAME); | |
596 | if (!region) { | |
597 | dev_err(&pdev->dev, "request region apbif failed\n"); | |
598 | ret = -EBUSY; | |
599 | goto err_clk_put_apbif; | |
600 | } | |
601 | ahub->apbif_addr = res0->start; | |
602 | ||
603 | regs_apbif = devm_ioremap(&pdev->dev, res0->start, | |
604 | resource_size(res0)); | |
605 | if (!regs_apbif) { | |
606 | dev_err(&pdev->dev, "ioremap apbif failed\n"); | |
607 | ret = -ENOMEM; | |
608 | goto err_clk_put_apbif; | |
609 | } | |
610 | ||
611 | ahub->regmap_apbif = devm_regmap_init_mmio(&pdev->dev, regs_apbif, | |
612 | &tegra30_ahub_apbif_regmap_config); | |
613 | if (IS_ERR(ahub->regmap_apbif)) { | |
614 | dev_err(&pdev->dev, "apbif regmap init failed\n"); | |
615 | ret = PTR_ERR(ahub->regmap_apbif); | |
616 | goto err_clk_put_apbif; | |
617 | } | |
618 | regcache_cache_only(ahub->regmap_apbif, true); | |
619 | ||
620 | res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1); | |
621 | if (!res1) { | |
622 | dev_err(&pdev->dev, "No ahub memory resource\n"); | |
623 | ret = -ENODEV; | |
624 | goto err_clk_put_apbif; | |
625 | } | |
626 | ||
627 | region = devm_request_mem_region(&pdev->dev, res1->start, | |
628 | resource_size(res1), DRV_NAME); | |
629 | if (!region) { | |
630 | dev_err(&pdev->dev, "request region ahub failed\n"); | |
631 | ret = -EBUSY; | |
632 | goto err_clk_put_apbif; | |
633 | } | |
634 | ||
635 | regs_ahub = devm_ioremap(&pdev->dev, res1->start, | |
636 | resource_size(res1)); | |
637 | if (!regs_ahub) { | |
638 | dev_err(&pdev->dev, "ioremap ahub failed\n"); | |
639 | ret = -ENOMEM; | |
640 | goto err_clk_put_apbif; | |
641 | } | |
642 | ||
643 | ahub->regmap_ahub = devm_regmap_init_mmio(&pdev->dev, regs_ahub, | |
644 | &tegra30_ahub_ahub_regmap_config); | |
645 | if (IS_ERR(ahub->regmap_ahub)) { | |
646 | dev_err(&pdev->dev, "ahub regmap init failed\n"); | |
647 | ret = PTR_ERR(ahub->regmap_ahub); | |
648 | goto err_clk_put_apbif; | |
649 | } | |
650 | regcache_cache_only(ahub->regmap_ahub, true); | |
651 | ||
652 | pm_runtime_enable(&pdev->dev); | |
653 | if (!pm_runtime_enabled(&pdev->dev)) { | |
654 | ret = tegra30_ahub_runtime_resume(&pdev->dev); | |
655 | if (ret) | |
656 | goto err_pm_disable; | |
657 | } | |
658 | ||
79cf5918 | 659 | of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); |
be944d42 SW |
660 | |
661 | return 0; | |
662 | ||
663 | err_pm_disable: | |
664 | pm_runtime_disable(&pdev->dev); | |
665 | err_clk_put_apbif: | |
666 | clk_put(ahub->clk_apbif); | |
667 | err_clk_put_d_audio: | |
668 | clk_put(ahub->clk_d_audio); | |
ecb2c174 | 669 | ahub = NULL; |
be944d42 SW |
670 | err: |
671 | return ret; | |
672 | } | |
673 | ||
4652a0d0 | 674 | static int tegra30_ahub_remove(struct platform_device *pdev) |
be944d42 SW |
675 | { |
676 | if (!ahub) | |
677 | return -ENODEV; | |
678 | ||
679 | pm_runtime_disable(&pdev->dev); | |
680 | if (!pm_runtime_status_suspended(&pdev->dev)) | |
681 | tegra30_ahub_runtime_suspend(&pdev->dev); | |
682 | ||
683 | clk_put(ahub->clk_apbif); | |
684 | clk_put(ahub->clk_d_audio); | |
685 | ||
ecb2c174 | 686 | ahub = NULL; |
be944d42 SW |
687 | |
688 | return 0; | |
689 | } | |
690 | ||
2f41a3f4 SW |
691 | #ifdef CONFIG_PM_SLEEP |
692 | static int tegra30_ahub_suspend(struct device *dev) | |
693 | { | |
694 | regcache_mark_dirty(ahub->regmap_ahub); | |
695 | regcache_mark_dirty(ahub->regmap_apbif); | |
696 | ||
697 | return 0; | |
698 | } | |
699 | ||
700 | static int tegra30_ahub_resume(struct device *dev) | |
701 | { | |
702 | int ret; | |
703 | ||
249e66c3 SW |
704 | ret = pm_runtime_get_sync(dev); |
705 | if (ret < 0) | |
706 | return ret; | |
2f41a3f4 SW |
707 | ret = regcache_sync(ahub->regmap_ahub); |
708 | ret |= regcache_sync(ahub->regmap_apbif); | |
249e66c3 | 709 | pm_runtime_put(dev); |
2f41a3f4 SW |
710 | |
711 | return ret; | |
712 | } | |
713 | #endif | |
714 | ||
f6e65744 | 715 | static const struct dev_pm_ops tegra30_ahub_pm_ops = { |
be944d42 SW |
716 | SET_RUNTIME_PM_OPS(tegra30_ahub_runtime_suspend, |
717 | tegra30_ahub_runtime_resume, NULL) | |
2f41a3f4 | 718 | SET_SYSTEM_SLEEP_PM_OPS(tegra30_ahub_suspend, tegra30_ahub_resume) |
be944d42 SW |
719 | }; |
720 | ||
721 | static struct platform_driver tegra30_ahub_driver = { | |
722 | .probe = tegra30_ahub_probe, | |
4652a0d0 | 723 | .remove = tegra30_ahub_remove, |
be944d42 SW |
724 | .driver = { |
725 | .name = DRV_NAME, | |
be944d42 SW |
726 | .of_match_table = tegra30_ahub_of_match, |
727 | .pm = &tegra30_ahub_pm_ops, | |
728 | }, | |
729 | }; | |
730 | module_platform_driver(tegra30_ahub_driver); | |
731 | ||
5e049fce SW |
732 | void tegra30_ahub_set_cif(struct regmap *regmap, unsigned int reg, |
733 | struct tegra30_ahub_cif_conf *conf) | |
734 | { | |
735 | unsigned int value; | |
736 | ||
737 | value = (conf->threshold << | |
738 | TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) | | |
739 | ((conf->audio_channels - 1) << | |
740 | TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) | | |
741 | ((conf->client_channels - 1) << | |
742 | TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) | | |
743 | (conf->audio_bits << | |
744 | TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) | | |
745 | (conf->client_bits << | |
746 | TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT) | | |
747 | (conf->expand << | |
748 | TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT) | | |
749 | (conf->stereo_conv << | |
750 | TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT) | | |
751 | (conf->replicate << | |
752 | TEGRA30_AUDIOCIF_CTRL_REPLICATE_SHIFT) | | |
753 | (conf->direction << | |
754 | TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT) | | |
755 | (conf->truncate << | |
756 | TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT) | | |
757 | (conf->mono_conv << | |
758 | TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT); | |
759 | ||
760 | regmap_write(regmap, reg, value); | |
761 | } | |
762 | EXPORT_SYMBOL_GPL(tegra30_ahub_set_cif); | |
763 | ||
764 | void tegra124_ahub_set_cif(struct regmap *regmap, unsigned int reg, | |
765 | struct tegra30_ahub_cif_conf *conf) | |
766 | { | |
767 | unsigned int value; | |
768 | ||
769 | value = (conf->threshold << | |
770 | TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) | | |
771 | ((conf->audio_channels - 1) << | |
772 | TEGRA124_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) | | |
773 | ((conf->client_channels - 1) << | |
774 | TEGRA124_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) | | |
775 | (conf->audio_bits << | |
776 | TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) | | |
777 | (conf->client_bits << | |
778 | TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT) | | |
779 | (conf->expand << | |
780 | TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT) | | |
781 | (conf->stereo_conv << | |
782 | TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT) | | |
783 | (conf->replicate << | |
784 | TEGRA30_AUDIOCIF_CTRL_REPLICATE_SHIFT) | | |
785 | (conf->direction << | |
786 | TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT) | | |
787 | (conf->truncate << | |
788 | TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT) | | |
789 | (conf->mono_conv << | |
790 | TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT); | |
791 | ||
792 | regmap_write(regmap, reg, value); | |
793 | } | |
794 | EXPORT_SYMBOL_GPL(tegra124_ahub_set_cif); | |
795 | ||
be944d42 SW |
796 | MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); |
797 | MODULE_DESCRIPTION("Tegra30 AHUB driver"); | |
798 | MODULE_LICENSE("GPL v2"); | |
799 | MODULE_ALIAS("platform:" DRV_NAME); | |
69c5b753 | 800 | MODULE_DEVICE_TABLE(of, tegra30_ahub_of_match); |