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