19952f691SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2be944d42SStephen Warren /* 3be944d42SStephen Warren * tegra30_ahub.c - Tegra30 AHUB driver 4be944d42SStephen Warren * 5be944d42SStephen Warren * Copyright (c) 2011,2012, NVIDIA CORPORATION. All rights reserved. 6be944d42SStephen Warren */ 7be944d42SStephen Warren 8be944d42SStephen Warren #include <linux/clk.h> 9be944d42SStephen Warren #include <linux/device.h> 10be944d42SStephen Warren #include <linux/io.h> 11be944d42SStephen Warren #include <linux/module.h> 12be944d42SStephen Warren #include <linux/of_platform.h> 13be944d42SStephen Warren #include <linux/platform_device.h> 14be944d42SStephen Warren #include <linux/pm_runtime.h> 15be944d42SStephen Warren #include <linux/regmap.h> 165185e0acSStephen Warren #include <linux/reset.h> 17be944d42SStephen Warren #include <linux/slab.h> 18be944d42SStephen Warren #include <sound/soc.h> 19be944d42SStephen Warren #include "tegra30_ahub.h" 20be944d42SStephen Warren 21be944d42SStephen Warren #define DRV_NAME "tegra30-ahub" 22be944d42SStephen Warren 23be944d42SStephen Warren static struct tegra30_ahub *ahub; 24be944d42SStephen Warren 25be944d42SStephen Warren static inline void tegra30_apbif_write(u32 reg, u32 val) 26be944d42SStephen Warren { 27be944d42SStephen Warren regmap_write(ahub->regmap_apbif, reg, val); 28be944d42SStephen Warren } 29be944d42SStephen Warren 30be944d42SStephen Warren static inline u32 tegra30_apbif_read(u32 reg) 31be944d42SStephen Warren { 32be944d42SStephen Warren u32 val; 33bf3c6ef7SCodrut Grosu 34be944d42SStephen Warren regmap_read(ahub->regmap_apbif, reg, &val); 35be944d42SStephen Warren return val; 36be944d42SStephen Warren } 37be944d42SStephen Warren 38be944d42SStephen Warren static inline void tegra30_audio_write(u32 reg, u32 val) 39be944d42SStephen Warren { 40be944d42SStephen Warren regmap_write(ahub->regmap_ahub, reg, val); 41be944d42SStephen Warren } 42be944d42SStephen Warren 43ccd4cc3eSArnd Bergmann static __maybe_unused int tegra30_ahub_runtime_suspend(struct device *dev) 44be944d42SStephen Warren { 45be944d42SStephen Warren regcache_cache_only(ahub->regmap_apbif, true); 46be944d42SStephen Warren regcache_cache_only(ahub->regmap_ahub, true); 47be944d42SStephen Warren 486d8ac9b1SDmitry Osipenko clk_bulk_disable_unprepare(ahub->nclocks, ahub->clocks); 49be944d42SStephen Warren 50be944d42SStephen Warren return 0; 51be944d42SStephen Warren } 52be944d42SStephen Warren 53be944d42SStephen Warren /* 54be944d42SStephen Warren * clk_apbif isn't required for an I2S<->I2S configuration where no PCM data 55be944d42SStephen Warren * is read from or sent to memory. However, that's not something the rest of 56be944d42SStephen Warren * the driver supports right now, so we'll just treat the two clocks as one 57be944d42SStephen Warren * for now. 58be944d42SStephen Warren * 59be944d42SStephen Warren * These functions should not be a plain ref-count. Instead, each active stream 60be944d42SStephen Warren * contributes some requirement to the minimum clock rate, so starting or 61be944d42SStephen Warren * stopping streams should dynamically adjust the clock as required. However, 62be944d42SStephen Warren * this is not yet implemented. 63be944d42SStephen Warren */ 64ccd4cc3eSArnd Bergmann static __maybe_unused int tegra30_ahub_runtime_resume(struct device *dev) 65be944d42SStephen Warren { 66be944d42SStephen Warren int ret; 67be944d42SStephen Warren 68050086ebSDmitry Osipenko ret = reset_control_bulk_assert(ahub->nresets, ahub->resets); 69ed9ce1edSDmitry Osipenko if (ret) 70ed9ce1edSDmitry Osipenko return ret; 71ed9ce1edSDmitry Osipenko 726d8ac9b1SDmitry Osipenko ret = clk_bulk_prepare_enable(ahub->nclocks, ahub->clocks); 736d8ac9b1SDmitry Osipenko if (ret) 74be944d42SStephen Warren return ret; 75be944d42SStephen Warren 76ed9ce1edSDmitry Osipenko usleep_range(10, 100); 77ed9ce1edSDmitry Osipenko 78050086ebSDmitry Osipenko ret = reset_control_bulk_deassert(ahub->nresets, ahub->resets); 79ed9ce1edSDmitry Osipenko if (ret) 80ed9ce1edSDmitry Osipenko goto disable_clocks; 81ed9ce1edSDmitry Osipenko 82be944d42SStephen Warren regcache_cache_only(ahub->regmap_apbif, false); 83be944d42SStephen Warren regcache_cache_only(ahub->regmap_ahub, false); 84ed9ce1edSDmitry Osipenko regcache_mark_dirty(ahub->regmap_apbif); 85ed9ce1edSDmitry Osipenko regcache_mark_dirty(ahub->regmap_ahub); 86ed9ce1edSDmitry Osipenko 87ed9ce1edSDmitry Osipenko ret = regcache_sync(ahub->regmap_apbif); 88ed9ce1edSDmitry Osipenko if (ret) 89ed9ce1edSDmitry Osipenko goto disable_clocks; 90ed9ce1edSDmitry Osipenko 91ed9ce1edSDmitry Osipenko ret = regcache_sync(ahub->regmap_ahub); 92ed9ce1edSDmitry Osipenko if (ret) 93ed9ce1edSDmitry Osipenko goto disable_clocks; 94be944d42SStephen Warren 95be944d42SStephen Warren return 0; 96ed9ce1edSDmitry Osipenko 97ed9ce1edSDmitry Osipenko disable_clocks: 98ed9ce1edSDmitry Osipenko clk_bulk_disable_unprepare(ahub->nclocks, ahub->clocks); 99ed9ce1edSDmitry Osipenko 100ed9ce1edSDmitry Osipenko return ret; 101be944d42SStephen Warren } 102be944d42SStephen Warren 103be944d42SStephen Warren int tegra30_ahub_allocate_rx_fifo(enum tegra30_ahub_rxcif *rxcif, 1045608bd3eSStephen Warren char *dmachan, int dmachan_len, 1055608bd3eSStephen Warren dma_addr_t *fiforeg) 106be944d42SStephen Warren { 107be944d42SStephen Warren int channel; 108be944d42SStephen Warren u32 reg, val; 1095e049fceSStephen Warren struct tegra30_ahub_cif_conf cif_conf; 110be944d42SStephen Warren 111be944d42SStephen Warren channel = find_first_zero_bit(ahub->rx_usage, 112be944d42SStephen Warren TEGRA30_AHUB_CHANNEL_CTRL_COUNT); 113be944d42SStephen Warren if (channel >= TEGRA30_AHUB_CHANNEL_CTRL_COUNT) 114be944d42SStephen Warren return -EBUSY; 115be944d42SStephen Warren 116be944d42SStephen Warren __set_bit(channel, ahub->rx_usage); 117be944d42SStephen Warren 118be944d42SStephen Warren *rxcif = TEGRA30_AHUB_RXCIF_APBIF_RX0 + channel; 1195608bd3eSStephen Warren snprintf(dmachan, dmachan_len, "rx%d", channel); 120be944d42SStephen Warren *fiforeg = ahub->apbif_addr + TEGRA30_AHUB_CHANNEL_RXFIFO + 121be944d42SStephen Warren (channel * TEGRA30_AHUB_CHANNEL_RXFIFO_STRIDE); 122be944d42SStephen Warren 123768db0b9SStephen Warren pm_runtime_get_sync(ahub->dev); 124768db0b9SStephen Warren 125be944d42SStephen Warren reg = TEGRA30_AHUB_CHANNEL_CTRL + 126be944d42SStephen Warren (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); 127be944d42SStephen Warren val = tegra30_apbif_read(reg); 128be944d42SStephen Warren val &= ~(TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_MASK | 129be944d42SStephen Warren TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_MASK); 130be944d42SStephen Warren val |= (7 << TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_SHIFT) | 131be944d42SStephen Warren TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_EN | 132be944d42SStephen Warren TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_16; 133be944d42SStephen Warren tegra30_apbif_write(reg, val); 134be944d42SStephen Warren 1355e049fceSStephen Warren cif_conf.threshold = 0; 1365e049fceSStephen Warren cif_conf.audio_channels = 2; 1375e049fceSStephen Warren cif_conf.client_channels = 2; 1385e049fceSStephen Warren cif_conf.audio_bits = TEGRA30_AUDIOCIF_BITS_16; 1395e049fceSStephen Warren cif_conf.client_bits = TEGRA30_AUDIOCIF_BITS_16; 1405e049fceSStephen Warren cif_conf.expand = 0; 1415e049fceSStephen Warren cif_conf.stereo_conv = 0; 1425e049fceSStephen Warren cif_conf.replicate = 0; 1435e049fceSStephen Warren cif_conf.direction = TEGRA30_AUDIOCIF_DIRECTION_RX; 1445e049fceSStephen Warren cif_conf.truncate = 0; 1455e049fceSStephen Warren cif_conf.mono_conv = 0; 1465e049fceSStephen Warren 147be944d42SStephen Warren reg = TEGRA30_AHUB_CIF_RX_CTRL + 148be944d42SStephen Warren (channel * TEGRA30_AHUB_CIF_RX_CTRL_STRIDE); 1495e049fceSStephen Warren ahub->soc_data->set_audio_cif(ahub->regmap_apbif, reg, &cif_conf); 150be944d42SStephen Warren 151768db0b9SStephen Warren pm_runtime_put(ahub->dev); 152768db0b9SStephen Warren 153be944d42SStephen Warren return 0; 154be944d42SStephen Warren } 155be944d42SStephen Warren EXPORT_SYMBOL_GPL(tegra30_ahub_allocate_rx_fifo); 156be944d42SStephen Warren 157be944d42SStephen Warren int tegra30_ahub_enable_rx_fifo(enum tegra30_ahub_rxcif rxcif) 158be944d42SStephen Warren { 159be944d42SStephen Warren int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0; 160be944d42SStephen Warren int reg, val; 161be944d42SStephen Warren 162768db0b9SStephen Warren pm_runtime_get_sync(ahub->dev); 163768db0b9SStephen Warren 164be944d42SStephen Warren reg = TEGRA30_AHUB_CHANNEL_CTRL + 165be944d42SStephen Warren (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); 166be944d42SStephen Warren val = tegra30_apbif_read(reg); 167be944d42SStephen Warren val |= TEGRA30_AHUB_CHANNEL_CTRL_RX_EN; 168be944d42SStephen Warren tegra30_apbif_write(reg, val); 169be944d42SStephen Warren 170768db0b9SStephen Warren pm_runtime_put(ahub->dev); 171768db0b9SStephen Warren 172be944d42SStephen Warren return 0; 173be944d42SStephen Warren } 174be944d42SStephen Warren EXPORT_SYMBOL_GPL(tegra30_ahub_enable_rx_fifo); 175be944d42SStephen Warren 176be944d42SStephen Warren int tegra30_ahub_disable_rx_fifo(enum tegra30_ahub_rxcif rxcif) 177be944d42SStephen Warren { 178be944d42SStephen Warren int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0; 179be944d42SStephen Warren int reg, val; 180be944d42SStephen Warren 181768db0b9SStephen Warren pm_runtime_get_sync(ahub->dev); 182768db0b9SStephen Warren 183be944d42SStephen Warren reg = TEGRA30_AHUB_CHANNEL_CTRL + 184be944d42SStephen Warren (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); 185be944d42SStephen Warren val = tegra30_apbif_read(reg); 186be944d42SStephen Warren val &= ~TEGRA30_AHUB_CHANNEL_CTRL_RX_EN; 187be944d42SStephen Warren tegra30_apbif_write(reg, val); 188be944d42SStephen Warren 189768db0b9SStephen Warren pm_runtime_put(ahub->dev); 190768db0b9SStephen Warren 191be944d42SStephen Warren return 0; 192be944d42SStephen Warren } 193be944d42SStephen Warren EXPORT_SYMBOL_GPL(tegra30_ahub_disable_rx_fifo); 194be944d42SStephen Warren 195be944d42SStephen Warren int tegra30_ahub_free_rx_fifo(enum tegra30_ahub_rxcif rxcif) 196be944d42SStephen Warren { 197be944d42SStephen Warren int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0; 198be944d42SStephen Warren 199be944d42SStephen Warren __clear_bit(channel, ahub->rx_usage); 200be944d42SStephen Warren 201be944d42SStephen Warren return 0; 202be944d42SStephen Warren } 203be944d42SStephen Warren EXPORT_SYMBOL_GPL(tegra30_ahub_free_rx_fifo); 204be944d42SStephen Warren 205be944d42SStephen Warren int tegra30_ahub_allocate_tx_fifo(enum tegra30_ahub_txcif *txcif, 2065608bd3eSStephen Warren char *dmachan, int dmachan_len, 2075608bd3eSStephen Warren dma_addr_t *fiforeg) 208be944d42SStephen Warren { 209be944d42SStephen Warren int channel; 210be944d42SStephen Warren u32 reg, val; 2115e049fceSStephen Warren struct tegra30_ahub_cif_conf cif_conf; 212be944d42SStephen Warren 213be944d42SStephen Warren channel = find_first_zero_bit(ahub->tx_usage, 214be944d42SStephen Warren TEGRA30_AHUB_CHANNEL_CTRL_COUNT); 215be944d42SStephen Warren if (channel >= TEGRA30_AHUB_CHANNEL_CTRL_COUNT) 216be944d42SStephen Warren return -EBUSY; 217be944d42SStephen Warren 218be944d42SStephen Warren __set_bit(channel, ahub->tx_usage); 219be944d42SStephen Warren 220be944d42SStephen Warren *txcif = TEGRA30_AHUB_TXCIF_APBIF_TX0 + channel; 2215608bd3eSStephen Warren snprintf(dmachan, dmachan_len, "tx%d", channel); 222be944d42SStephen Warren *fiforeg = ahub->apbif_addr + TEGRA30_AHUB_CHANNEL_TXFIFO + 223be944d42SStephen Warren (channel * TEGRA30_AHUB_CHANNEL_TXFIFO_STRIDE); 224be944d42SStephen Warren 225768db0b9SStephen Warren pm_runtime_get_sync(ahub->dev); 226768db0b9SStephen Warren 227be944d42SStephen Warren reg = TEGRA30_AHUB_CHANNEL_CTRL + 228be944d42SStephen Warren (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); 229be944d42SStephen Warren val = tegra30_apbif_read(reg); 230be944d42SStephen Warren val &= ~(TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_MASK | 231be944d42SStephen Warren TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_MASK); 232be944d42SStephen Warren val |= (7 << TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_SHIFT) | 233be944d42SStephen Warren TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_EN | 234be944d42SStephen Warren TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_16; 235be944d42SStephen Warren tegra30_apbif_write(reg, val); 236be944d42SStephen Warren 2375e049fceSStephen Warren cif_conf.threshold = 0; 2385e049fceSStephen Warren cif_conf.audio_channels = 2; 2395e049fceSStephen Warren cif_conf.client_channels = 2; 2405e049fceSStephen Warren cif_conf.audio_bits = TEGRA30_AUDIOCIF_BITS_16; 2415e049fceSStephen Warren cif_conf.client_bits = TEGRA30_AUDIOCIF_BITS_16; 2425e049fceSStephen Warren cif_conf.expand = 0; 2435e049fceSStephen Warren cif_conf.stereo_conv = 0; 2445e049fceSStephen Warren cif_conf.replicate = 0; 2455e049fceSStephen Warren cif_conf.direction = TEGRA30_AUDIOCIF_DIRECTION_TX; 2465e049fceSStephen Warren cif_conf.truncate = 0; 2475e049fceSStephen Warren cif_conf.mono_conv = 0; 2485e049fceSStephen Warren 249be944d42SStephen Warren reg = TEGRA30_AHUB_CIF_TX_CTRL + 250be944d42SStephen Warren (channel * TEGRA30_AHUB_CIF_TX_CTRL_STRIDE); 2515e049fceSStephen Warren ahub->soc_data->set_audio_cif(ahub->regmap_apbif, reg, &cif_conf); 252be944d42SStephen Warren 253768db0b9SStephen Warren pm_runtime_put(ahub->dev); 254768db0b9SStephen Warren 255be944d42SStephen Warren return 0; 256be944d42SStephen Warren } 257be944d42SStephen Warren EXPORT_SYMBOL_GPL(tegra30_ahub_allocate_tx_fifo); 258be944d42SStephen Warren 259be944d42SStephen Warren int tegra30_ahub_enable_tx_fifo(enum tegra30_ahub_txcif txcif) 260be944d42SStephen Warren { 261be944d42SStephen Warren int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0; 262be944d42SStephen Warren int reg, val; 263be944d42SStephen Warren 264768db0b9SStephen Warren pm_runtime_get_sync(ahub->dev); 265768db0b9SStephen Warren 266be944d42SStephen Warren reg = TEGRA30_AHUB_CHANNEL_CTRL + 267be944d42SStephen Warren (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); 268be944d42SStephen Warren val = tegra30_apbif_read(reg); 269be944d42SStephen Warren val |= TEGRA30_AHUB_CHANNEL_CTRL_TX_EN; 270be944d42SStephen Warren tegra30_apbif_write(reg, val); 271be944d42SStephen Warren 272768db0b9SStephen Warren pm_runtime_put(ahub->dev); 273768db0b9SStephen Warren 274be944d42SStephen Warren return 0; 275be944d42SStephen Warren } 276be944d42SStephen Warren EXPORT_SYMBOL_GPL(tegra30_ahub_enable_tx_fifo); 277be944d42SStephen Warren 278be944d42SStephen Warren int tegra30_ahub_disable_tx_fifo(enum tegra30_ahub_txcif txcif) 279be944d42SStephen Warren { 280be944d42SStephen Warren int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0; 281be944d42SStephen Warren int reg, val; 282be944d42SStephen Warren 283768db0b9SStephen Warren pm_runtime_get_sync(ahub->dev); 284768db0b9SStephen Warren 285be944d42SStephen Warren reg = TEGRA30_AHUB_CHANNEL_CTRL + 286be944d42SStephen Warren (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); 287be944d42SStephen Warren val = tegra30_apbif_read(reg); 288be944d42SStephen Warren val &= ~TEGRA30_AHUB_CHANNEL_CTRL_TX_EN; 289be944d42SStephen Warren tegra30_apbif_write(reg, val); 290be944d42SStephen Warren 291768db0b9SStephen Warren pm_runtime_put(ahub->dev); 292768db0b9SStephen Warren 293be944d42SStephen Warren return 0; 294be944d42SStephen Warren } 295be944d42SStephen Warren EXPORT_SYMBOL_GPL(tegra30_ahub_disable_tx_fifo); 296be944d42SStephen Warren 297be944d42SStephen Warren int tegra30_ahub_free_tx_fifo(enum tegra30_ahub_txcif txcif) 298be944d42SStephen Warren { 299be944d42SStephen Warren int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0; 300be944d42SStephen Warren 301be944d42SStephen Warren __clear_bit(channel, ahub->tx_usage); 302be944d42SStephen Warren 303be944d42SStephen Warren return 0; 304be944d42SStephen Warren } 305be944d42SStephen Warren EXPORT_SYMBOL_GPL(tegra30_ahub_free_tx_fifo); 306be944d42SStephen Warren 307be944d42SStephen Warren int tegra30_ahub_set_rx_cif_source(enum tegra30_ahub_rxcif rxcif, 308be944d42SStephen Warren enum tegra30_ahub_txcif txcif) 309be944d42SStephen Warren { 310be944d42SStephen Warren int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0; 311be944d42SStephen Warren int reg; 312be944d42SStephen Warren 313768db0b9SStephen Warren pm_runtime_get_sync(ahub->dev); 314768db0b9SStephen Warren 315be944d42SStephen Warren reg = TEGRA30_AHUB_AUDIO_RX + 316be944d42SStephen Warren (channel * TEGRA30_AHUB_AUDIO_RX_STRIDE); 317be944d42SStephen Warren tegra30_audio_write(reg, 1 << txcif); 318be944d42SStephen Warren 319768db0b9SStephen Warren pm_runtime_put(ahub->dev); 320768db0b9SStephen Warren 321be944d42SStephen Warren return 0; 322be944d42SStephen Warren } 323be944d42SStephen Warren EXPORT_SYMBOL_GPL(tegra30_ahub_set_rx_cif_source); 324be944d42SStephen Warren 325be944d42SStephen Warren int tegra30_ahub_unset_rx_cif_source(enum tegra30_ahub_rxcif rxcif) 326be944d42SStephen Warren { 327be944d42SStephen Warren int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0; 328be944d42SStephen Warren int reg; 329be944d42SStephen Warren 330768db0b9SStephen Warren pm_runtime_get_sync(ahub->dev); 331768db0b9SStephen Warren 332be944d42SStephen Warren reg = TEGRA30_AHUB_AUDIO_RX + 333be944d42SStephen Warren (channel * TEGRA30_AHUB_AUDIO_RX_STRIDE); 334be944d42SStephen Warren tegra30_audio_write(reg, 0); 335be944d42SStephen Warren 336768db0b9SStephen Warren pm_runtime_put(ahub->dev); 337768db0b9SStephen Warren 338be944d42SStephen Warren return 0; 339be944d42SStephen Warren } 340be944d42SStephen Warren EXPORT_SYMBOL_GPL(tegra30_ahub_unset_rx_cif_source); 341be944d42SStephen Warren 342050086ebSDmitry Osipenko static const struct reset_control_bulk_data tegra30_ahub_resets_data[] = { 343050086ebSDmitry Osipenko { "d_audio" }, 344050086ebSDmitry Osipenko { "apbif" }, 345050086ebSDmitry Osipenko { "i2s0" }, 346050086ebSDmitry Osipenko { "i2s1" }, 347050086ebSDmitry Osipenko { "i2s2" }, 348050086ebSDmitry Osipenko { "i2s3" }, 349050086ebSDmitry Osipenko { "i2s4" }, 350050086ebSDmitry Osipenko { "dam0" }, 351050086ebSDmitry Osipenko { "dam1" }, 352050086ebSDmitry Osipenko { "dam2" }, 353050086ebSDmitry Osipenko { "spdif" }, 354050086ebSDmitry Osipenko { "amx" }, /* Tegra114+ */ 355050086ebSDmitry Osipenko { "adx" }, /* Tegra114+ */ 356050086ebSDmitry Osipenko { "amx1" }, /* Tegra124 */ 357050086ebSDmitry Osipenko { "adx1" }, /* Tegra124 */ 358050086ebSDmitry Osipenko { "afc0" }, /* Tegra124 */ 359050086ebSDmitry Osipenko { "afc1" }, /* Tegra124 */ 360050086ebSDmitry Osipenko { "afc2" }, /* Tegra124 */ 361050086ebSDmitry Osipenko { "afc3" }, /* Tegra124 */ 362050086ebSDmitry Osipenko { "afc4" }, /* Tegra124 */ 363050086ebSDmitry Osipenko { "afc5" }, /* Tegra124 */ 364be944d42SStephen Warren }; 365be944d42SStephen Warren 366be944d42SStephen Warren #define LAST_REG(name) \ 367be944d42SStephen Warren (TEGRA30_AHUB_##name + \ 368be944d42SStephen Warren (TEGRA30_AHUB_##name##_STRIDE * TEGRA30_AHUB_##name##_COUNT) - 4) 369be944d42SStephen Warren 370be944d42SStephen Warren #define REG_IN_ARRAY(reg, name) \ 371be944d42SStephen Warren ((reg >= TEGRA30_AHUB_##name) && \ 372be944d42SStephen Warren (reg <= LAST_REG(name) && \ 373be944d42SStephen Warren (!((reg - TEGRA30_AHUB_##name) % TEGRA30_AHUB_##name##_STRIDE)))) 374be944d42SStephen Warren 375be944d42SStephen Warren static bool tegra30_ahub_apbif_wr_rd_reg(struct device *dev, unsigned int reg) 376be944d42SStephen Warren { 377be944d42SStephen Warren switch (reg) { 378be944d42SStephen Warren case TEGRA30_AHUB_CONFIG_LINK_CTRL: 379be944d42SStephen Warren case TEGRA30_AHUB_MISC_CTRL: 380be944d42SStephen Warren case TEGRA30_AHUB_APBDMA_LIVE_STATUS: 381be944d42SStephen Warren case TEGRA30_AHUB_I2S_LIVE_STATUS: 382be944d42SStephen Warren case TEGRA30_AHUB_SPDIF_LIVE_STATUS: 383be944d42SStephen Warren case TEGRA30_AHUB_I2S_INT_MASK: 384be944d42SStephen Warren case TEGRA30_AHUB_DAM_INT_MASK: 385be944d42SStephen Warren case TEGRA30_AHUB_SPDIF_INT_MASK: 386be944d42SStephen Warren case TEGRA30_AHUB_APBIF_INT_MASK: 387be944d42SStephen Warren case TEGRA30_AHUB_I2S_INT_STATUS: 388be944d42SStephen Warren case TEGRA30_AHUB_DAM_INT_STATUS: 389be944d42SStephen Warren case TEGRA30_AHUB_SPDIF_INT_STATUS: 390be944d42SStephen Warren case TEGRA30_AHUB_APBIF_INT_STATUS: 391be944d42SStephen Warren case TEGRA30_AHUB_I2S_INT_SOURCE: 392be944d42SStephen Warren case TEGRA30_AHUB_DAM_INT_SOURCE: 393be944d42SStephen Warren case TEGRA30_AHUB_SPDIF_INT_SOURCE: 394be944d42SStephen Warren case TEGRA30_AHUB_APBIF_INT_SOURCE: 395be944d42SStephen Warren case TEGRA30_AHUB_I2S_INT_SET: 396be944d42SStephen Warren case TEGRA30_AHUB_DAM_INT_SET: 397be944d42SStephen Warren case TEGRA30_AHUB_SPDIF_INT_SET: 398be944d42SStephen Warren case TEGRA30_AHUB_APBIF_INT_SET: 399be944d42SStephen Warren return true; 400be944d42SStephen Warren default: 401be944d42SStephen Warren break; 4021d198f26SJoe Perches } 403be944d42SStephen Warren 404be944d42SStephen Warren if (REG_IN_ARRAY(reg, CHANNEL_CTRL) || 405be944d42SStephen Warren REG_IN_ARRAY(reg, CHANNEL_CLEAR) || 406be944d42SStephen Warren REG_IN_ARRAY(reg, CHANNEL_STATUS) || 407be944d42SStephen Warren REG_IN_ARRAY(reg, CHANNEL_TXFIFO) || 408be944d42SStephen Warren REG_IN_ARRAY(reg, CHANNEL_RXFIFO) || 409be944d42SStephen Warren REG_IN_ARRAY(reg, CIF_TX_CTRL) || 410be944d42SStephen Warren REG_IN_ARRAY(reg, CIF_RX_CTRL) || 411be944d42SStephen Warren REG_IN_ARRAY(reg, DAM_LIVE_STATUS)) 412be944d42SStephen Warren return true; 413be944d42SStephen Warren 414be944d42SStephen Warren return false; 415be944d42SStephen Warren } 416be944d42SStephen Warren 417be944d42SStephen Warren static bool tegra30_ahub_apbif_volatile_reg(struct device *dev, 418be944d42SStephen Warren unsigned int reg) 419be944d42SStephen Warren { 420be944d42SStephen Warren switch (reg) { 421be944d42SStephen Warren case TEGRA30_AHUB_CONFIG_LINK_CTRL: 422be944d42SStephen Warren case TEGRA30_AHUB_MISC_CTRL: 423be944d42SStephen Warren case TEGRA30_AHUB_APBDMA_LIVE_STATUS: 424be944d42SStephen Warren case TEGRA30_AHUB_I2S_LIVE_STATUS: 425be944d42SStephen Warren case TEGRA30_AHUB_SPDIF_LIVE_STATUS: 426be944d42SStephen Warren case TEGRA30_AHUB_I2S_INT_STATUS: 427be944d42SStephen Warren case TEGRA30_AHUB_DAM_INT_STATUS: 428be944d42SStephen Warren case TEGRA30_AHUB_SPDIF_INT_STATUS: 429be944d42SStephen Warren case TEGRA30_AHUB_APBIF_INT_STATUS: 430be944d42SStephen Warren case TEGRA30_AHUB_I2S_INT_SET: 431be944d42SStephen Warren case TEGRA30_AHUB_DAM_INT_SET: 432be944d42SStephen Warren case TEGRA30_AHUB_SPDIF_INT_SET: 433be944d42SStephen Warren case TEGRA30_AHUB_APBIF_INT_SET: 434be944d42SStephen Warren return true; 435be944d42SStephen Warren default: 436be944d42SStephen Warren break; 4371d198f26SJoe Perches } 438be944d42SStephen Warren 439be944d42SStephen Warren if (REG_IN_ARRAY(reg, CHANNEL_CLEAR) || 440be944d42SStephen Warren REG_IN_ARRAY(reg, CHANNEL_STATUS) || 441be944d42SStephen Warren REG_IN_ARRAY(reg, CHANNEL_TXFIFO) || 442be944d42SStephen Warren REG_IN_ARRAY(reg, CHANNEL_RXFIFO) || 443be944d42SStephen Warren REG_IN_ARRAY(reg, DAM_LIVE_STATUS)) 444be944d42SStephen Warren return true; 445be944d42SStephen Warren 446be944d42SStephen Warren return false; 447be944d42SStephen Warren } 448be944d42SStephen Warren 449be944d42SStephen Warren static bool tegra30_ahub_apbif_precious_reg(struct device *dev, 450be944d42SStephen Warren unsigned int reg) 451be944d42SStephen Warren { 452be944d42SStephen Warren if (REG_IN_ARRAY(reg, CHANNEL_TXFIFO) || 453be944d42SStephen Warren REG_IN_ARRAY(reg, CHANNEL_RXFIFO)) 454be944d42SStephen Warren return true; 455be944d42SStephen Warren 456be944d42SStephen Warren return false; 457be944d42SStephen Warren } 458be944d42SStephen Warren 459be944d42SStephen Warren static const struct regmap_config tegra30_ahub_apbif_regmap_config = { 460be944d42SStephen Warren .name = "apbif", 461be944d42SStephen Warren .reg_bits = 32, 462be944d42SStephen Warren .val_bits = 32, 463be944d42SStephen Warren .reg_stride = 4, 464be944d42SStephen Warren .max_register = TEGRA30_AHUB_APBIF_INT_SET, 465be944d42SStephen Warren .writeable_reg = tegra30_ahub_apbif_wr_rd_reg, 466be944d42SStephen Warren .readable_reg = tegra30_ahub_apbif_wr_rd_reg, 467be944d42SStephen Warren .volatile_reg = tegra30_ahub_apbif_volatile_reg, 468be944d42SStephen Warren .precious_reg = tegra30_ahub_apbif_precious_reg, 469591d14f0SDylan Reid .cache_type = REGCACHE_FLAT, 470be944d42SStephen Warren }; 471be944d42SStephen Warren 472be944d42SStephen Warren static bool tegra30_ahub_ahub_wr_rd_reg(struct device *dev, unsigned int reg) 473be944d42SStephen Warren { 474be944d42SStephen Warren if (REG_IN_ARRAY(reg, AUDIO_RX)) 475be944d42SStephen Warren return true; 476be944d42SStephen Warren 477be944d42SStephen Warren return false; 478be944d42SStephen Warren } 479be944d42SStephen Warren 480be944d42SStephen Warren static const struct regmap_config tegra30_ahub_ahub_regmap_config = { 481be944d42SStephen Warren .name = "ahub", 482be944d42SStephen Warren .reg_bits = 32, 483be944d42SStephen Warren .val_bits = 32, 484be944d42SStephen Warren .reg_stride = 4, 485be944d42SStephen Warren .max_register = LAST_REG(AUDIO_RX), 486be944d42SStephen Warren .writeable_reg = tegra30_ahub_ahub_wr_rd_reg, 487be944d42SStephen Warren .readable_reg = tegra30_ahub_ahub_wr_rd_reg, 488591d14f0SDylan Reid .cache_type = REGCACHE_FLAT, 489be944d42SStephen Warren }; 490be944d42SStephen Warren 49195d36075SStephen Warren static struct tegra30_ahub_soc_data soc_data_tegra30 = { 492050086ebSDmitry Osipenko .num_resets = 11, 4935e049fceSStephen Warren .set_audio_cif = tegra30_ahub_set_cif, 49495d36075SStephen Warren }; 49595d36075SStephen Warren 49695d36075SStephen Warren static struct tegra30_ahub_soc_data soc_data_tegra114 = { 497050086ebSDmitry Osipenko .num_resets = 13, 4985e049fceSStephen Warren .set_audio_cif = tegra30_ahub_set_cif, 4995e049fceSStephen Warren }; 5005e049fceSStephen Warren 5015e049fceSStephen Warren static struct tegra30_ahub_soc_data soc_data_tegra124 = { 502050086ebSDmitry Osipenko .num_resets = 21, 5035e049fceSStephen Warren .set_audio_cif = tegra124_ahub_set_cif, 50495d36075SStephen Warren }; 50595d36075SStephen Warren 50695d36075SStephen Warren static const struct of_device_id tegra30_ahub_of_match[] = { 5075e049fceSStephen Warren { .compatible = "nvidia,tegra124-ahub", .data = &soc_data_tegra124 }, 50895d36075SStephen Warren { .compatible = "nvidia,tegra114-ahub", .data = &soc_data_tegra114 }, 50995d36075SStephen Warren { .compatible = "nvidia,tegra30-ahub", .data = &soc_data_tegra30 }, 51095d36075SStephen Warren {}, 51195d36075SStephen Warren }; 51295d36075SStephen Warren 5134652a0d0SBill Pemberton static int tegra30_ahub_probe(struct platform_device *pdev) 514be944d42SStephen Warren { 51595d36075SStephen Warren const struct of_device_id *match; 51695d36075SStephen Warren const struct tegra30_ahub_soc_data *soc_data; 517a813d0e8SYueHaibing struct resource *res0; 518be944d42SStephen Warren void __iomem *regs_apbif, *regs_ahub; 519be944d42SStephen Warren int ret = 0; 520be944d42SStephen Warren 52195d36075SStephen Warren match = of_match_device(tegra30_ahub_of_match, &pdev->dev); 52295d36075SStephen Warren if (!match) 52395d36075SStephen Warren return -EINVAL; 52495d36075SStephen Warren soc_data = match->data; 52595d36075SStephen Warren 526be944d42SStephen Warren ahub = devm_kzalloc(&pdev->dev, sizeof(struct tegra30_ahub), 527be944d42SStephen Warren GFP_KERNEL); 528e2c187a6SCodrut Grosu if (!ahub) 5298833c01aSVaishali Thakkar return -ENOMEM; 530be944d42SStephen Warren dev_set_drvdata(&pdev->dev, ahub); 531be944d42SStephen Warren 532050086ebSDmitry Osipenko BUILD_BUG_ON(sizeof(ahub->resets) != sizeof(tegra30_ahub_resets_data)); 533050086ebSDmitry Osipenko memcpy(ahub->resets, tegra30_ahub_resets_data, sizeof(ahub->resets)); 534050086ebSDmitry Osipenko 535050086ebSDmitry Osipenko ahub->nresets = soc_data->num_resets; 5365e049fceSStephen Warren ahub->soc_data = soc_data; 537be944d42SStephen Warren ahub->dev = &pdev->dev; 538be944d42SStephen Warren 5396d8ac9b1SDmitry Osipenko ahub->clocks[ahub->nclocks++].id = "apbif"; 5406d8ac9b1SDmitry Osipenko ahub->clocks[ahub->nclocks++].id = "d_audio"; 541be944d42SStephen Warren 5426d8ac9b1SDmitry Osipenko ret = devm_clk_bulk_get(&pdev->dev, ahub->nclocks, ahub->clocks); 5436d8ac9b1SDmitry Osipenko if (ret) 5445d956e3cSDmitry Osipenko goto err_unset_ahub; 545be944d42SStephen Warren 546050086ebSDmitry Osipenko ret = devm_reset_control_bulk_get_exclusive(&pdev->dev, ahub->nresets, 547050086ebSDmitry Osipenko ahub->resets); 548050086ebSDmitry Osipenko if (ret) { 549050086ebSDmitry Osipenko dev_err(&pdev->dev, "Can't get resets: %d\n", ret); 5505d956e3cSDmitry Osipenko goto err_unset_ahub; 551ed9ce1edSDmitry Osipenko } 552ed9ce1edSDmitry Osipenko 553*fc8344e6SYang Yingliang regs_apbif = devm_platform_get_and_ioremap_resource(pdev, 0, &res0); 5545d956e3cSDmitry Osipenko if (IS_ERR(regs_apbif)) { 5555d956e3cSDmitry Osipenko ret = PTR_ERR(regs_apbif); 5565d956e3cSDmitry Osipenko goto err_unset_ahub; 5575d956e3cSDmitry Osipenko } 558be944d42SStephen Warren 559be944d42SStephen Warren ahub->apbif_addr = res0->start; 560be944d42SStephen Warren 561be944d42SStephen Warren ahub->regmap_apbif = devm_regmap_init_mmio(&pdev->dev, regs_apbif, 562be944d42SStephen Warren &tegra30_ahub_apbif_regmap_config); 563be944d42SStephen Warren if (IS_ERR(ahub->regmap_apbif)) { 564be944d42SStephen Warren dev_err(&pdev->dev, "apbif regmap init failed\n"); 565be944d42SStephen Warren ret = PTR_ERR(ahub->regmap_apbif); 5665d956e3cSDmitry Osipenko goto err_unset_ahub; 567be944d42SStephen Warren } 568be944d42SStephen Warren regcache_cache_only(ahub->regmap_apbif, true); 569be944d42SStephen Warren 570a813d0e8SYueHaibing regs_ahub = devm_platform_ioremap_resource(pdev, 1); 5715d956e3cSDmitry Osipenko if (IS_ERR(regs_ahub)) { 5725d956e3cSDmitry Osipenko ret = PTR_ERR(regs_ahub); 5735d956e3cSDmitry Osipenko goto err_unset_ahub; 5745d956e3cSDmitry Osipenko } 575be944d42SStephen Warren 576be944d42SStephen Warren ahub->regmap_ahub = devm_regmap_init_mmio(&pdev->dev, regs_ahub, 577be944d42SStephen Warren &tegra30_ahub_ahub_regmap_config); 578be944d42SStephen Warren if (IS_ERR(ahub->regmap_ahub)) { 579be944d42SStephen Warren dev_err(&pdev->dev, "ahub regmap init failed\n"); 580be944d42SStephen Warren ret = PTR_ERR(ahub->regmap_ahub); 5815d956e3cSDmitry Osipenko goto err_unset_ahub; 582be944d42SStephen Warren } 583be944d42SStephen Warren regcache_cache_only(ahub->regmap_ahub, true); 584be944d42SStephen Warren 585be944d42SStephen Warren pm_runtime_enable(&pdev->dev); 586be944d42SStephen Warren 58779cf5918SPrashant Gaikwad of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); 588be944d42SStephen Warren 589be944d42SStephen Warren return 0; 590be944d42SStephen Warren 5915d956e3cSDmitry Osipenko err_unset_ahub: 5925d956e3cSDmitry Osipenko ahub = NULL; 5938833c01aSVaishali Thakkar 594be944d42SStephen Warren return ret; 595be944d42SStephen Warren } 596be944d42SStephen Warren 5974652a0d0SBill Pemberton static int tegra30_ahub_remove(struct platform_device *pdev) 598be944d42SStephen Warren { 599be944d42SStephen Warren pm_runtime_disable(&pdev->dev); 600be944d42SStephen Warren 6015d956e3cSDmitry Osipenko ahub = NULL; 6025d956e3cSDmitry Osipenko 603be944d42SStephen Warren return 0; 604be944d42SStephen Warren } 605be944d42SStephen Warren 606f6e65744SBill Pemberton static const struct dev_pm_ops tegra30_ahub_pm_ops = { 607be944d42SStephen Warren SET_RUNTIME_PM_OPS(tegra30_ahub_runtime_suspend, 608be944d42SStephen Warren tegra30_ahub_runtime_resume, NULL) 609e2965c2cSDmitry Osipenko SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 610e2965c2cSDmitry Osipenko pm_runtime_force_resume) 611be944d42SStephen Warren }; 612be944d42SStephen Warren 613be944d42SStephen Warren static struct platform_driver tegra30_ahub_driver = { 614be944d42SStephen Warren .probe = tegra30_ahub_probe, 6154652a0d0SBill Pemberton .remove = tegra30_ahub_remove, 616be944d42SStephen Warren .driver = { 617be944d42SStephen Warren .name = DRV_NAME, 618be944d42SStephen Warren .of_match_table = tegra30_ahub_of_match, 619be944d42SStephen Warren .pm = &tegra30_ahub_pm_ops, 620be944d42SStephen Warren }, 621be944d42SStephen Warren }; 622be944d42SStephen Warren module_platform_driver(tegra30_ahub_driver); 623be944d42SStephen Warren 6245e049fceSStephen Warren void tegra30_ahub_set_cif(struct regmap *regmap, unsigned int reg, 6255e049fceSStephen Warren struct tegra30_ahub_cif_conf *conf) 6265e049fceSStephen Warren { 6275e049fceSStephen Warren unsigned int value; 6285e049fceSStephen Warren 6295e049fceSStephen Warren value = (conf->threshold << 6305e049fceSStephen Warren TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) | 6315e049fceSStephen Warren ((conf->audio_channels - 1) << 6325e049fceSStephen Warren TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) | 6335e049fceSStephen Warren ((conf->client_channels - 1) << 6345e049fceSStephen Warren TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) | 6355e049fceSStephen Warren (conf->audio_bits << 6365e049fceSStephen Warren TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) | 6375e049fceSStephen Warren (conf->client_bits << 6385e049fceSStephen Warren TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT) | 6395e049fceSStephen Warren (conf->expand << 6405e049fceSStephen Warren TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT) | 6415e049fceSStephen Warren (conf->stereo_conv << 6425e049fceSStephen Warren TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT) | 6435e049fceSStephen Warren (conf->replicate << 6445e049fceSStephen Warren TEGRA30_AUDIOCIF_CTRL_REPLICATE_SHIFT) | 6455e049fceSStephen Warren (conf->direction << 6465e049fceSStephen Warren TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT) | 6475e049fceSStephen Warren (conf->truncate << 6485e049fceSStephen Warren TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT) | 6495e049fceSStephen Warren (conf->mono_conv << 6505e049fceSStephen Warren TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT); 6515e049fceSStephen Warren 6525e049fceSStephen Warren regmap_write(regmap, reg, value); 6535e049fceSStephen Warren } 6545e049fceSStephen Warren EXPORT_SYMBOL_GPL(tegra30_ahub_set_cif); 6555e049fceSStephen Warren 6565e049fceSStephen Warren void tegra124_ahub_set_cif(struct regmap *regmap, unsigned int reg, 6575e049fceSStephen Warren struct tegra30_ahub_cif_conf *conf) 6585e049fceSStephen Warren { 6595e049fceSStephen Warren unsigned int value; 6605e049fceSStephen Warren 6615e049fceSStephen Warren value = (conf->threshold << 6625e049fceSStephen Warren TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) | 6635e049fceSStephen Warren ((conf->audio_channels - 1) << 6645e049fceSStephen Warren TEGRA124_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) | 6655e049fceSStephen Warren ((conf->client_channels - 1) << 6665e049fceSStephen Warren TEGRA124_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) | 6675e049fceSStephen Warren (conf->audio_bits << 6685e049fceSStephen Warren TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) | 6695e049fceSStephen Warren (conf->client_bits << 6705e049fceSStephen Warren TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT) | 6715e049fceSStephen Warren (conf->expand << 6725e049fceSStephen Warren TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT) | 6735e049fceSStephen Warren (conf->stereo_conv << 6745e049fceSStephen Warren TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT) | 6755e049fceSStephen Warren (conf->replicate << 6765e049fceSStephen Warren TEGRA30_AUDIOCIF_CTRL_REPLICATE_SHIFT) | 6775e049fceSStephen Warren (conf->direction << 6785e049fceSStephen Warren TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT) | 6795e049fceSStephen Warren (conf->truncate << 6805e049fceSStephen Warren TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT) | 6815e049fceSStephen Warren (conf->mono_conv << 6825e049fceSStephen Warren TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT); 6835e049fceSStephen Warren 6845e049fceSStephen Warren regmap_write(regmap, reg, value); 6855e049fceSStephen Warren } 6865e049fceSStephen Warren EXPORT_SYMBOL_GPL(tegra124_ahub_set_cif); 6875e049fceSStephen Warren 688be944d42SStephen Warren MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); 689be944d42SStephen Warren MODULE_DESCRIPTION("Tegra30 AHUB driver"); 690be944d42SStephen Warren MODULE_LICENSE("GPL v2"); 691be944d42SStephen Warren MODULE_ALIAS("platform:" DRV_NAME); 69269c5b753SStephen Warren MODULE_DEVICE_TABLE(of, tegra30_ahub_of_match); 693