18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * tegra30_ahub.c - Tegra30 AHUB driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2011,2012, NVIDIA CORPORATION. All rights reserved. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/clk.h> 98c2ecf20Sopenharmony_ci#include <linux/device.h> 108c2ecf20Sopenharmony_ci#include <linux/io.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 138c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 148c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 158c2ecf20Sopenharmony_ci#include <linux/regmap.h> 168c2ecf20Sopenharmony_ci#include <linux/reset.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <sound/soc.h> 198c2ecf20Sopenharmony_ci#include "tegra30_ahub.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define DRV_NAME "tegra30-ahub" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic struct tegra30_ahub *ahub; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic inline void tegra30_apbif_write(u32 reg, u32 val) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci regmap_write(ahub->regmap_apbif, reg, val); 288c2ecf20Sopenharmony_ci} 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic inline u32 tegra30_apbif_read(u32 reg) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci u32 val; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci regmap_read(ahub->regmap_apbif, reg, &val); 358c2ecf20Sopenharmony_ci return val; 368c2ecf20Sopenharmony_ci} 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic inline void tegra30_audio_write(u32 reg, u32 val) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci regmap_write(ahub->regmap_ahub, reg, val); 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic int tegra30_ahub_runtime_suspend(struct device *dev) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci regcache_cache_only(ahub->regmap_apbif, true); 468c2ecf20Sopenharmony_ci regcache_cache_only(ahub->regmap_ahub, true); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci clk_disable_unprepare(ahub->clk_apbif); 498c2ecf20Sopenharmony_ci clk_disable_unprepare(ahub->clk_d_audio); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci return 0; 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* 558c2ecf20Sopenharmony_ci * clk_apbif isn't required for an I2S<->I2S configuration where no PCM data 568c2ecf20Sopenharmony_ci * is read from or sent to memory. However, that's not something the rest of 578c2ecf20Sopenharmony_ci * the driver supports right now, so we'll just treat the two clocks as one 588c2ecf20Sopenharmony_ci * for now. 598c2ecf20Sopenharmony_ci * 608c2ecf20Sopenharmony_ci * These functions should not be a plain ref-count. Instead, each active stream 618c2ecf20Sopenharmony_ci * contributes some requirement to the minimum clock rate, so starting or 628c2ecf20Sopenharmony_ci * stopping streams should dynamically adjust the clock as required. However, 638c2ecf20Sopenharmony_ci * this is not yet implemented. 648c2ecf20Sopenharmony_ci */ 658c2ecf20Sopenharmony_cistatic int tegra30_ahub_runtime_resume(struct device *dev) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci int ret; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci ret = clk_prepare_enable(ahub->clk_d_audio); 708c2ecf20Sopenharmony_ci if (ret) { 718c2ecf20Sopenharmony_ci dev_err(dev, "clk_enable d_audio failed: %d\n", ret); 728c2ecf20Sopenharmony_ci return ret; 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci ret = clk_prepare_enable(ahub->clk_apbif); 758c2ecf20Sopenharmony_ci if (ret) { 768c2ecf20Sopenharmony_ci dev_err(dev, "clk_enable apbif failed: %d\n", ret); 778c2ecf20Sopenharmony_ci clk_disable(ahub->clk_d_audio); 788c2ecf20Sopenharmony_ci return ret; 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci regcache_cache_only(ahub->regmap_apbif, false); 828c2ecf20Sopenharmony_ci regcache_cache_only(ahub->regmap_ahub, false); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci return 0; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ciint tegra30_ahub_allocate_rx_fifo(enum tegra30_ahub_rxcif *rxcif, 888c2ecf20Sopenharmony_ci char *dmachan, int dmachan_len, 898c2ecf20Sopenharmony_ci dma_addr_t *fiforeg) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci int channel; 928c2ecf20Sopenharmony_ci u32 reg, val; 938c2ecf20Sopenharmony_ci struct tegra30_ahub_cif_conf cif_conf; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci channel = find_first_zero_bit(ahub->rx_usage, 968c2ecf20Sopenharmony_ci TEGRA30_AHUB_CHANNEL_CTRL_COUNT); 978c2ecf20Sopenharmony_ci if (channel >= TEGRA30_AHUB_CHANNEL_CTRL_COUNT) 988c2ecf20Sopenharmony_ci return -EBUSY; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci __set_bit(channel, ahub->rx_usage); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci *rxcif = TEGRA30_AHUB_RXCIF_APBIF_RX0 + channel; 1038c2ecf20Sopenharmony_ci snprintf(dmachan, dmachan_len, "rx%d", channel); 1048c2ecf20Sopenharmony_ci *fiforeg = ahub->apbif_addr + TEGRA30_AHUB_CHANNEL_RXFIFO + 1058c2ecf20Sopenharmony_ci (channel * TEGRA30_AHUB_CHANNEL_RXFIFO_STRIDE); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci pm_runtime_get_sync(ahub->dev); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci reg = TEGRA30_AHUB_CHANNEL_CTRL + 1108c2ecf20Sopenharmony_ci (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); 1118c2ecf20Sopenharmony_ci val = tegra30_apbif_read(reg); 1128c2ecf20Sopenharmony_ci val &= ~(TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_MASK | 1138c2ecf20Sopenharmony_ci TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_MASK); 1148c2ecf20Sopenharmony_ci val |= (7 << TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_SHIFT) | 1158c2ecf20Sopenharmony_ci TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_EN | 1168c2ecf20Sopenharmony_ci TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_16; 1178c2ecf20Sopenharmony_ci tegra30_apbif_write(reg, val); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci cif_conf.threshold = 0; 1208c2ecf20Sopenharmony_ci cif_conf.audio_channels = 2; 1218c2ecf20Sopenharmony_ci cif_conf.client_channels = 2; 1228c2ecf20Sopenharmony_ci cif_conf.audio_bits = TEGRA30_AUDIOCIF_BITS_16; 1238c2ecf20Sopenharmony_ci cif_conf.client_bits = TEGRA30_AUDIOCIF_BITS_16; 1248c2ecf20Sopenharmony_ci cif_conf.expand = 0; 1258c2ecf20Sopenharmony_ci cif_conf.stereo_conv = 0; 1268c2ecf20Sopenharmony_ci cif_conf.replicate = 0; 1278c2ecf20Sopenharmony_ci cif_conf.direction = TEGRA30_AUDIOCIF_DIRECTION_RX; 1288c2ecf20Sopenharmony_ci cif_conf.truncate = 0; 1298c2ecf20Sopenharmony_ci cif_conf.mono_conv = 0; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci reg = TEGRA30_AHUB_CIF_RX_CTRL + 1328c2ecf20Sopenharmony_ci (channel * TEGRA30_AHUB_CIF_RX_CTRL_STRIDE); 1338c2ecf20Sopenharmony_ci ahub->soc_data->set_audio_cif(ahub->regmap_apbif, reg, &cif_conf); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci pm_runtime_put(ahub->dev); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci return 0; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tegra30_ahub_allocate_rx_fifo); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ciint tegra30_ahub_enable_rx_fifo(enum tegra30_ahub_rxcif rxcif) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0; 1448c2ecf20Sopenharmony_ci int reg, val; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci pm_runtime_get_sync(ahub->dev); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci reg = TEGRA30_AHUB_CHANNEL_CTRL + 1498c2ecf20Sopenharmony_ci (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); 1508c2ecf20Sopenharmony_ci val = tegra30_apbif_read(reg); 1518c2ecf20Sopenharmony_ci val |= TEGRA30_AHUB_CHANNEL_CTRL_RX_EN; 1528c2ecf20Sopenharmony_ci tegra30_apbif_write(reg, val); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci pm_runtime_put(ahub->dev); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci return 0; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tegra30_ahub_enable_rx_fifo); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ciint tegra30_ahub_disable_rx_fifo(enum tegra30_ahub_rxcif rxcif) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0; 1638c2ecf20Sopenharmony_ci int reg, val; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci pm_runtime_get_sync(ahub->dev); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci reg = TEGRA30_AHUB_CHANNEL_CTRL + 1688c2ecf20Sopenharmony_ci (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); 1698c2ecf20Sopenharmony_ci val = tegra30_apbif_read(reg); 1708c2ecf20Sopenharmony_ci val &= ~TEGRA30_AHUB_CHANNEL_CTRL_RX_EN; 1718c2ecf20Sopenharmony_ci tegra30_apbif_write(reg, val); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci pm_runtime_put(ahub->dev); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci return 0; 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tegra30_ahub_disable_rx_fifo); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ciint tegra30_ahub_free_rx_fifo(enum tegra30_ahub_rxcif rxcif) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci __clear_bit(channel, ahub->rx_usage); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci return 0; 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tegra30_ahub_free_rx_fifo); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ciint tegra30_ahub_allocate_tx_fifo(enum tegra30_ahub_txcif *txcif, 1908c2ecf20Sopenharmony_ci char *dmachan, int dmachan_len, 1918c2ecf20Sopenharmony_ci dma_addr_t *fiforeg) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci int channel; 1948c2ecf20Sopenharmony_ci u32 reg, val; 1958c2ecf20Sopenharmony_ci struct tegra30_ahub_cif_conf cif_conf; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci channel = find_first_zero_bit(ahub->tx_usage, 1988c2ecf20Sopenharmony_ci TEGRA30_AHUB_CHANNEL_CTRL_COUNT); 1998c2ecf20Sopenharmony_ci if (channel >= TEGRA30_AHUB_CHANNEL_CTRL_COUNT) 2008c2ecf20Sopenharmony_ci return -EBUSY; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci __set_bit(channel, ahub->tx_usage); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci *txcif = TEGRA30_AHUB_TXCIF_APBIF_TX0 + channel; 2058c2ecf20Sopenharmony_ci snprintf(dmachan, dmachan_len, "tx%d", channel); 2068c2ecf20Sopenharmony_ci *fiforeg = ahub->apbif_addr + TEGRA30_AHUB_CHANNEL_TXFIFO + 2078c2ecf20Sopenharmony_ci (channel * TEGRA30_AHUB_CHANNEL_TXFIFO_STRIDE); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci pm_runtime_get_sync(ahub->dev); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci reg = TEGRA30_AHUB_CHANNEL_CTRL + 2128c2ecf20Sopenharmony_ci (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); 2138c2ecf20Sopenharmony_ci val = tegra30_apbif_read(reg); 2148c2ecf20Sopenharmony_ci val &= ~(TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_MASK | 2158c2ecf20Sopenharmony_ci TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_MASK); 2168c2ecf20Sopenharmony_ci val |= (7 << TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_SHIFT) | 2178c2ecf20Sopenharmony_ci TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_EN | 2188c2ecf20Sopenharmony_ci TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_16; 2198c2ecf20Sopenharmony_ci tegra30_apbif_write(reg, val); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci cif_conf.threshold = 0; 2228c2ecf20Sopenharmony_ci cif_conf.audio_channels = 2; 2238c2ecf20Sopenharmony_ci cif_conf.client_channels = 2; 2248c2ecf20Sopenharmony_ci cif_conf.audio_bits = TEGRA30_AUDIOCIF_BITS_16; 2258c2ecf20Sopenharmony_ci cif_conf.client_bits = TEGRA30_AUDIOCIF_BITS_16; 2268c2ecf20Sopenharmony_ci cif_conf.expand = 0; 2278c2ecf20Sopenharmony_ci cif_conf.stereo_conv = 0; 2288c2ecf20Sopenharmony_ci cif_conf.replicate = 0; 2298c2ecf20Sopenharmony_ci cif_conf.direction = TEGRA30_AUDIOCIF_DIRECTION_TX; 2308c2ecf20Sopenharmony_ci cif_conf.truncate = 0; 2318c2ecf20Sopenharmony_ci cif_conf.mono_conv = 0; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci reg = TEGRA30_AHUB_CIF_TX_CTRL + 2348c2ecf20Sopenharmony_ci (channel * TEGRA30_AHUB_CIF_TX_CTRL_STRIDE); 2358c2ecf20Sopenharmony_ci ahub->soc_data->set_audio_cif(ahub->regmap_apbif, reg, &cif_conf); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci pm_runtime_put(ahub->dev); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci return 0; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tegra30_ahub_allocate_tx_fifo); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ciint tegra30_ahub_enable_tx_fifo(enum tegra30_ahub_txcif txcif) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0; 2468c2ecf20Sopenharmony_ci int reg, val; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci pm_runtime_get_sync(ahub->dev); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci reg = TEGRA30_AHUB_CHANNEL_CTRL + 2518c2ecf20Sopenharmony_ci (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); 2528c2ecf20Sopenharmony_ci val = tegra30_apbif_read(reg); 2538c2ecf20Sopenharmony_ci val |= TEGRA30_AHUB_CHANNEL_CTRL_TX_EN; 2548c2ecf20Sopenharmony_ci tegra30_apbif_write(reg, val); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci pm_runtime_put(ahub->dev); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci return 0; 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tegra30_ahub_enable_tx_fifo); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ciint tegra30_ahub_disable_tx_fifo(enum tegra30_ahub_txcif txcif) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0; 2658c2ecf20Sopenharmony_ci int reg, val; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci pm_runtime_get_sync(ahub->dev); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci reg = TEGRA30_AHUB_CHANNEL_CTRL + 2708c2ecf20Sopenharmony_ci (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); 2718c2ecf20Sopenharmony_ci val = tegra30_apbif_read(reg); 2728c2ecf20Sopenharmony_ci val &= ~TEGRA30_AHUB_CHANNEL_CTRL_TX_EN; 2738c2ecf20Sopenharmony_ci tegra30_apbif_write(reg, val); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci pm_runtime_put(ahub->dev); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci return 0; 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tegra30_ahub_disable_tx_fifo); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ciint tegra30_ahub_free_tx_fifo(enum tegra30_ahub_txcif txcif) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci __clear_bit(channel, ahub->tx_usage); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci return 0; 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tegra30_ahub_free_tx_fifo); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ciint tegra30_ahub_set_rx_cif_source(enum tegra30_ahub_rxcif rxcif, 2928c2ecf20Sopenharmony_ci enum tegra30_ahub_txcif txcif) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0; 2958c2ecf20Sopenharmony_ci int reg; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci pm_runtime_get_sync(ahub->dev); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci reg = TEGRA30_AHUB_AUDIO_RX + 3008c2ecf20Sopenharmony_ci (channel * TEGRA30_AHUB_AUDIO_RX_STRIDE); 3018c2ecf20Sopenharmony_ci tegra30_audio_write(reg, 1 << txcif); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci pm_runtime_put(ahub->dev); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci return 0; 3068c2ecf20Sopenharmony_ci} 3078c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tegra30_ahub_set_rx_cif_source); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ciint tegra30_ahub_unset_rx_cif_source(enum tegra30_ahub_rxcif rxcif) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0; 3128c2ecf20Sopenharmony_ci int reg; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci pm_runtime_get_sync(ahub->dev); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci reg = TEGRA30_AHUB_AUDIO_RX + 3178c2ecf20Sopenharmony_ci (channel * TEGRA30_AHUB_AUDIO_RX_STRIDE); 3188c2ecf20Sopenharmony_ci tegra30_audio_write(reg, 0); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci pm_runtime_put(ahub->dev); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci return 0; 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tegra30_ahub_unset_rx_cif_source); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci#define MOD_LIST_MASK_TEGRA30 BIT(0) 3278c2ecf20Sopenharmony_ci#define MOD_LIST_MASK_TEGRA114 BIT(1) 3288c2ecf20Sopenharmony_ci#define MOD_LIST_MASK_TEGRA124 BIT(2) 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci#define MOD_LIST_MASK_TEGRA30_OR_LATER \ 3318c2ecf20Sopenharmony_ci (MOD_LIST_MASK_TEGRA30 | MOD_LIST_MASK_TEGRA114 | \ 3328c2ecf20Sopenharmony_ci MOD_LIST_MASK_TEGRA124) 3338c2ecf20Sopenharmony_ci#define MOD_LIST_MASK_TEGRA114_OR_LATER \ 3348c2ecf20Sopenharmony_ci (MOD_LIST_MASK_TEGRA114 | MOD_LIST_MASK_TEGRA124) 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic const struct { 3378c2ecf20Sopenharmony_ci const char *rst_name; 3388c2ecf20Sopenharmony_ci u32 mod_list_mask; 3398c2ecf20Sopenharmony_ci} configlink_mods[] = { 3408c2ecf20Sopenharmony_ci { "i2s0", MOD_LIST_MASK_TEGRA30_OR_LATER }, 3418c2ecf20Sopenharmony_ci { "i2s1", MOD_LIST_MASK_TEGRA30_OR_LATER }, 3428c2ecf20Sopenharmony_ci { "i2s2", MOD_LIST_MASK_TEGRA30_OR_LATER }, 3438c2ecf20Sopenharmony_ci { "i2s3", MOD_LIST_MASK_TEGRA30_OR_LATER }, 3448c2ecf20Sopenharmony_ci { "i2s4", MOD_LIST_MASK_TEGRA30_OR_LATER }, 3458c2ecf20Sopenharmony_ci { "dam0", MOD_LIST_MASK_TEGRA30_OR_LATER }, 3468c2ecf20Sopenharmony_ci { "dam1", MOD_LIST_MASK_TEGRA30_OR_LATER }, 3478c2ecf20Sopenharmony_ci { "dam2", MOD_LIST_MASK_TEGRA30_OR_LATER }, 3488c2ecf20Sopenharmony_ci { "spdif", MOD_LIST_MASK_TEGRA30_OR_LATER }, 3498c2ecf20Sopenharmony_ci { "amx", MOD_LIST_MASK_TEGRA114_OR_LATER }, 3508c2ecf20Sopenharmony_ci { "adx", MOD_LIST_MASK_TEGRA114_OR_LATER }, 3518c2ecf20Sopenharmony_ci { "amx1", MOD_LIST_MASK_TEGRA124 }, 3528c2ecf20Sopenharmony_ci { "adx1", MOD_LIST_MASK_TEGRA124 }, 3538c2ecf20Sopenharmony_ci { "afc0", MOD_LIST_MASK_TEGRA124 }, 3548c2ecf20Sopenharmony_ci { "afc1", MOD_LIST_MASK_TEGRA124 }, 3558c2ecf20Sopenharmony_ci { "afc2", MOD_LIST_MASK_TEGRA124 }, 3568c2ecf20Sopenharmony_ci { "afc3", MOD_LIST_MASK_TEGRA124 }, 3578c2ecf20Sopenharmony_ci { "afc4", MOD_LIST_MASK_TEGRA124 }, 3588c2ecf20Sopenharmony_ci { "afc5", MOD_LIST_MASK_TEGRA124 }, 3598c2ecf20Sopenharmony_ci}; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci#define LAST_REG(name) \ 3628c2ecf20Sopenharmony_ci (TEGRA30_AHUB_##name + \ 3638c2ecf20Sopenharmony_ci (TEGRA30_AHUB_##name##_STRIDE * TEGRA30_AHUB_##name##_COUNT) - 4) 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci#define REG_IN_ARRAY(reg, name) \ 3668c2ecf20Sopenharmony_ci ((reg >= TEGRA30_AHUB_##name) && \ 3678c2ecf20Sopenharmony_ci (reg <= LAST_REG(name) && \ 3688c2ecf20Sopenharmony_ci (!((reg - TEGRA30_AHUB_##name) % TEGRA30_AHUB_##name##_STRIDE)))) 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic bool tegra30_ahub_apbif_wr_rd_reg(struct device *dev, unsigned int reg) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci switch (reg) { 3738c2ecf20Sopenharmony_ci case TEGRA30_AHUB_CONFIG_LINK_CTRL: 3748c2ecf20Sopenharmony_ci case TEGRA30_AHUB_MISC_CTRL: 3758c2ecf20Sopenharmony_ci case TEGRA30_AHUB_APBDMA_LIVE_STATUS: 3768c2ecf20Sopenharmony_ci case TEGRA30_AHUB_I2S_LIVE_STATUS: 3778c2ecf20Sopenharmony_ci case TEGRA30_AHUB_SPDIF_LIVE_STATUS: 3788c2ecf20Sopenharmony_ci case TEGRA30_AHUB_I2S_INT_MASK: 3798c2ecf20Sopenharmony_ci case TEGRA30_AHUB_DAM_INT_MASK: 3808c2ecf20Sopenharmony_ci case TEGRA30_AHUB_SPDIF_INT_MASK: 3818c2ecf20Sopenharmony_ci case TEGRA30_AHUB_APBIF_INT_MASK: 3828c2ecf20Sopenharmony_ci case TEGRA30_AHUB_I2S_INT_STATUS: 3838c2ecf20Sopenharmony_ci case TEGRA30_AHUB_DAM_INT_STATUS: 3848c2ecf20Sopenharmony_ci case TEGRA30_AHUB_SPDIF_INT_STATUS: 3858c2ecf20Sopenharmony_ci case TEGRA30_AHUB_APBIF_INT_STATUS: 3868c2ecf20Sopenharmony_ci case TEGRA30_AHUB_I2S_INT_SOURCE: 3878c2ecf20Sopenharmony_ci case TEGRA30_AHUB_DAM_INT_SOURCE: 3888c2ecf20Sopenharmony_ci case TEGRA30_AHUB_SPDIF_INT_SOURCE: 3898c2ecf20Sopenharmony_ci case TEGRA30_AHUB_APBIF_INT_SOURCE: 3908c2ecf20Sopenharmony_ci case TEGRA30_AHUB_I2S_INT_SET: 3918c2ecf20Sopenharmony_ci case TEGRA30_AHUB_DAM_INT_SET: 3928c2ecf20Sopenharmony_ci case TEGRA30_AHUB_SPDIF_INT_SET: 3938c2ecf20Sopenharmony_ci case TEGRA30_AHUB_APBIF_INT_SET: 3948c2ecf20Sopenharmony_ci return true; 3958c2ecf20Sopenharmony_ci default: 3968c2ecf20Sopenharmony_ci break; 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci if (REG_IN_ARRAY(reg, CHANNEL_CTRL) || 4008c2ecf20Sopenharmony_ci REG_IN_ARRAY(reg, CHANNEL_CLEAR) || 4018c2ecf20Sopenharmony_ci REG_IN_ARRAY(reg, CHANNEL_STATUS) || 4028c2ecf20Sopenharmony_ci REG_IN_ARRAY(reg, CHANNEL_TXFIFO) || 4038c2ecf20Sopenharmony_ci REG_IN_ARRAY(reg, CHANNEL_RXFIFO) || 4048c2ecf20Sopenharmony_ci REG_IN_ARRAY(reg, CIF_TX_CTRL) || 4058c2ecf20Sopenharmony_ci REG_IN_ARRAY(reg, CIF_RX_CTRL) || 4068c2ecf20Sopenharmony_ci REG_IN_ARRAY(reg, DAM_LIVE_STATUS)) 4078c2ecf20Sopenharmony_ci return true; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci return false; 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic bool tegra30_ahub_apbif_volatile_reg(struct device *dev, 4138c2ecf20Sopenharmony_ci unsigned int reg) 4148c2ecf20Sopenharmony_ci{ 4158c2ecf20Sopenharmony_ci switch (reg) { 4168c2ecf20Sopenharmony_ci case TEGRA30_AHUB_CONFIG_LINK_CTRL: 4178c2ecf20Sopenharmony_ci case TEGRA30_AHUB_MISC_CTRL: 4188c2ecf20Sopenharmony_ci case TEGRA30_AHUB_APBDMA_LIVE_STATUS: 4198c2ecf20Sopenharmony_ci case TEGRA30_AHUB_I2S_LIVE_STATUS: 4208c2ecf20Sopenharmony_ci case TEGRA30_AHUB_SPDIF_LIVE_STATUS: 4218c2ecf20Sopenharmony_ci case TEGRA30_AHUB_I2S_INT_STATUS: 4228c2ecf20Sopenharmony_ci case TEGRA30_AHUB_DAM_INT_STATUS: 4238c2ecf20Sopenharmony_ci case TEGRA30_AHUB_SPDIF_INT_STATUS: 4248c2ecf20Sopenharmony_ci case TEGRA30_AHUB_APBIF_INT_STATUS: 4258c2ecf20Sopenharmony_ci case TEGRA30_AHUB_I2S_INT_SET: 4268c2ecf20Sopenharmony_ci case TEGRA30_AHUB_DAM_INT_SET: 4278c2ecf20Sopenharmony_ci case TEGRA30_AHUB_SPDIF_INT_SET: 4288c2ecf20Sopenharmony_ci case TEGRA30_AHUB_APBIF_INT_SET: 4298c2ecf20Sopenharmony_ci return true; 4308c2ecf20Sopenharmony_ci default: 4318c2ecf20Sopenharmony_ci break; 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci if (REG_IN_ARRAY(reg, CHANNEL_CLEAR) || 4358c2ecf20Sopenharmony_ci REG_IN_ARRAY(reg, CHANNEL_STATUS) || 4368c2ecf20Sopenharmony_ci REG_IN_ARRAY(reg, CHANNEL_TXFIFO) || 4378c2ecf20Sopenharmony_ci REG_IN_ARRAY(reg, CHANNEL_RXFIFO) || 4388c2ecf20Sopenharmony_ci REG_IN_ARRAY(reg, DAM_LIVE_STATUS)) 4398c2ecf20Sopenharmony_ci return true; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci return false; 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cistatic bool tegra30_ahub_apbif_precious_reg(struct device *dev, 4458c2ecf20Sopenharmony_ci unsigned int reg) 4468c2ecf20Sopenharmony_ci{ 4478c2ecf20Sopenharmony_ci if (REG_IN_ARRAY(reg, CHANNEL_TXFIFO) || 4488c2ecf20Sopenharmony_ci REG_IN_ARRAY(reg, CHANNEL_RXFIFO)) 4498c2ecf20Sopenharmony_ci return true; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci return false; 4528c2ecf20Sopenharmony_ci} 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_cistatic const struct regmap_config tegra30_ahub_apbif_regmap_config = { 4558c2ecf20Sopenharmony_ci .name = "apbif", 4568c2ecf20Sopenharmony_ci .reg_bits = 32, 4578c2ecf20Sopenharmony_ci .val_bits = 32, 4588c2ecf20Sopenharmony_ci .reg_stride = 4, 4598c2ecf20Sopenharmony_ci .max_register = TEGRA30_AHUB_APBIF_INT_SET, 4608c2ecf20Sopenharmony_ci .writeable_reg = tegra30_ahub_apbif_wr_rd_reg, 4618c2ecf20Sopenharmony_ci .readable_reg = tegra30_ahub_apbif_wr_rd_reg, 4628c2ecf20Sopenharmony_ci .volatile_reg = tegra30_ahub_apbif_volatile_reg, 4638c2ecf20Sopenharmony_ci .precious_reg = tegra30_ahub_apbif_precious_reg, 4648c2ecf20Sopenharmony_ci .cache_type = REGCACHE_FLAT, 4658c2ecf20Sopenharmony_ci}; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_cistatic bool tegra30_ahub_ahub_wr_rd_reg(struct device *dev, unsigned int reg) 4688c2ecf20Sopenharmony_ci{ 4698c2ecf20Sopenharmony_ci if (REG_IN_ARRAY(reg, AUDIO_RX)) 4708c2ecf20Sopenharmony_ci return true; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci return false; 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_cistatic const struct regmap_config tegra30_ahub_ahub_regmap_config = { 4768c2ecf20Sopenharmony_ci .name = "ahub", 4778c2ecf20Sopenharmony_ci .reg_bits = 32, 4788c2ecf20Sopenharmony_ci .val_bits = 32, 4798c2ecf20Sopenharmony_ci .reg_stride = 4, 4808c2ecf20Sopenharmony_ci .max_register = LAST_REG(AUDIO_RX), 4818c2ecf20Sopenharmony_ci .writeable_reg = tegra30_ahub_ahub_wr_rd_reg, 4828c2ecf20Sopenharmony_ci .readable_reg = tegra30_ahub_ahub_wr_rd_reg, 4838c2ecf20Sopenharmony_ci .cache_type = REGCACHE_FLAT, 4848c2ecf20Sopenharmony_ci}; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_cistatic struct tegra30_ahub_soc_data soc_data_tegra30 = { 4878c2ecf20Sopenharmony_ci .mod_list_mask = MOD_LIST_MASK_TEGRA30, 4888c2ecf20Sopenharmony_ci .set_audio_cif = tegra30_ahub_set_cif, 4898c2ecf20Sopenharmony_ci}; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_cistatic struct tegra30_ahub_soc_data soc_data_tegra114 = { 4928c2ecf20Sopenharmony_ci .mod_list_mask = MOD_LIST_MASK_TEGRA114, 4938c2ecf20Sopenharmony_ci .set_audio_cif = tegra30_ahub_set_cif, 4948c2ecf20Sopenharmony_ci}; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_cistatic struct tegra30_ahub_soc_data soc_data_tegra124 = { 4978c2ecf20Sopenharmony_ci .mod_list_mask = MOD_LIST_MASK_TEGRA124, 4988c2ecf20Sopenharmony_ci .set_audio_cif = tegra124_ahub_set_cif, 4998c2ecf20Sopenharmony_ci}; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_cistatic const struct of_device_id tegra30_ahub_of_match[] = { 5028c2ecf20Sopenharmony_ci { .compatible = "nvidia,tegra124-ahub", .data = &soc_data_tegra124 }, 5038c2ecf20Sopenharmony_ci { .compatible = "nvidia,tegra114-ahub", .data = &soc_data_tegra114 }, 5048c2ecf20Sopenharmony_ci { .compatible = "nvidia,tegra30-ahub", .data = &soc_data_tegra30 }, 5058c2ecf20Sopenharmony_ci {}, 5068c2ecf20Sopenharmony_ci}; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_cistatic int tegra30_ahub_probe(struct platform_device *pdev) 5098c2ecf20Sopenharmony_ci{ 5108c2ecf20Sopenharmony_ci const struct of_device_id *match; 5118c2ecf20Sopenharmony_ci const struct tegra30_ahub_soc_data *soc_data; 5128c2ecf20Sopenharmony_ci struct reset_control *rst; 5138c2ecf20Sopenharmony_ci int i; 5148c2ecf20Sopenharmony_ci struct resource *res0; 5158c2ecf20Sopenharmony_ci void __iomem *regs_apbif, *regs_ahub; 5168c2ecf20Sopenharmony_ci int ret = 0; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci if (ahub) 5198c2ecf20Sopenharmony_ci return -ENODEV; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci match = of_match_device(tegra30_ahub_of_match, &pdev->dev); 5228c2ecf20Sopenharmony_ci if (!match) 5238c2ecf20Sopenharmony_ci return -EINVAL; 5248c2ecf20Sopenharmony_ci soc_data = match->data; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci /* 5278c2ecf20Sopenharmony_ci * The AHUB hosts a register bus: the "configlink". For this to 5288c2ecf20Sopenharmony_ci * operate correctly, all devices on this bus must be out of reset. 5298c2ecf20Sopenharmony_ci * Ensure that here. 5308c2ecf20Sopenharmony_ci */ 5318c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(configlink_mods); i++) { 5328c2ecf20Sopenharmony_ci if (!(configlink_mods[i].mod_list_mask & 5338c2ecf20Sopenharmony_ci soc_data->mod_list_mask)) 5348c2ecf20Sopenharmony_ci continue; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci rst = reset_control_get_exclusive(&pdev->dev, 5378c2ecf20Sopenharmony_ci configlink_mods[i].rst_name); 5388c2ecf20Sopenharmony_ci if (IS_ERR(rst)) { 5398c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Can't get reset %s\n", 5408c2ecf20Sopenharmony_ci configlink_mods[i].rst_name); 5418c2ecf20Sopenharmony_ci ret = PTR_ERR(rst); 5428c2ecf20Sopenharmony_ci return ret; 5438c2ecf20Sopenharmony_ci } 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci ret = reset_control_deassert(rst); 5468c2ecf20Sopenharmony_ci reset_control_put(rst); 5478c2ecf20Sopenharmony_ci if (ret) 5488c2ecf20Sopenharmony_ci return ret; 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci ahub = devm_kzalloc(&pdev->dev, sizeof(struct tegra30_ahub), 5528c2ecf20Sopenharmony_ci GFP_KERNEL); 5538c2ecf20Sopenharmony_ci if (!ahub) 5548c2ecf20Sopenharmony_ci return -ENOMEM; 5558c2ecf20Sopenharmony_ci dev_set_drvdata(&pdev->dev, ahub); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci ahub->soc_data = soc_data; 5588c2ecf20Sopenharmony_ci ahub->dev = &pdev->dev; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci ahub->clk_d_audio = devm_clk_get(&pdev->dev, "d_audio"); 5618c2ecf20Sopenharmony_ci if (IS_ERR(ahub->clk_d_audio)) { 5628c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Can't retrieve ahub d_audio clock\n"); 5638c2ecf20Sopenharmony_ci ret = PTR_ERR(ahub->clk_d_audio); 5648c2ecf20Sopenharmony_ci return ret; 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci ahub->clk_apbif = devm_clk_get(&pdev->dev, "apbif"); 5688c2ecf20Sopenharmony_ci if (IS_ERR(ahub->clk_apbif)) { 5698c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Can't retrieve ahub apbif clock\n"); 5708c2ecf20Sopenharmony_ci ret = PTR_ERR(ahub->clk_apbif); 5718c2ecf20Sopenharmony_ci return ret; 5728c2ecf20Sopenharmony_ci } 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); 5758c2ecf20Sopenharmony_ci regs_apbif = devm_ioremap_resource(&pdev->dev, res0); 5768c2ecf20Sopenharmony_ci if (IS_ERR(regs_apbif)) 5778c2ecf20Sopenharmony_ci return PTR_ERR(regs_apbif); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci ahub->apbif_addr = res0->start; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci ahub->regmap_apbif = devm_regmap_init_mmio(&pdev->dev, regs_apbif, 5828c2ecf20Sopenharmony_ci &tegra30_ahub_apbif_regmap_config); 5838c2ecf20Sopenharmony_ci if (IS_ERR(ahub->regmap_apbif)) { 5848c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "apbif regmap init failed\n"); 5858c2ecf20Sopenharmony_ci ret = PTR_ERR(ahub->regmap_apbif); 5868c2ecf20Sopenharmony_ci return ret; 5878c2ecf20Sopenharmony_ci } 5888c2ecf20Sopenharmony_ci regcache_cache_only(ahub->regmap_apbif, true); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci regs_ahub = devm_platform_ioremap_resource(pdev, 1); 5918c2ecf20Sopenharmony_ci if (IS_ERR(regs_ahub)) 5928c2ecf20Sopenharmony_ci return PTR_ERR(regs_ahub); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci ahub->regmap_ahub = devm_regmap_init_mmio(&pdev->dev, regs_ahub, 5958c2ecf20Sopenharmony_ci &tegra30_ahub_ahub_regmap_config); 5968c2ecf20Sopenharmony_ci if (IS_ERR(ahub->regmap_ahub)) { 5978c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "ahub regmap init failed\n"); 5988c2ecf20Sopenharmony_ci ret = PTR_ERR(ahub->regmap_ahub); 5998c2ecf20Sopenharmony_ci return ret; 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci regcache_cache_only(ahub->regmap_ahub, true); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 6048c2ecf20Sopenharmony_ci if (!pm_runtime_enabled(&pdev->dev)) { 6058c2ecf20Sopenharmony_ci ret = tegra30_ahub_runtime_resume(&pdev->dev); 6068c2ecf20Sopenharmony_ci if (ret) 6078c2ecf20Sopenharmony_ci goto err_pm_disable; 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci return 0; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_cierr_pm_disable: 6158c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci return ret; 6188c2ecf20Sopenharmony_ci} 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_cistatic int tegra30_ahub_remove(struct platform_device *pdev) 6218c2ecf20Sopenharmony_ci{ 6228c2ecf20Sopenharmony_ci if (!ahub) 6238c2ecf20Sopenharmony_ci return -ENODEV; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 6268c2ecf20Sopenharmony_ci if (!pm_runtime_status_suspended(&pdev->dev)) 6278c2ecf20Sopenharmony_ci tegra30_ahub_runtime_suspend(&pdev->dev); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci return 0; 6308c2ecf20Sopenharmony_ci} 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 6338c2ecf20Sopenharmony_cistatic int tegra30_ahub_suspend(struct device *dev) 6348c2ecf20Sopenharmony_ci{ 6358c2ecf20Sopenharmony_ci regcache_mark_dirty(ahub->regmap_ahub); 6368c2ecf20Sopenharmony_ci regcache_mark_dirty(ahub->regmap_apbif); 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci return 0; 6398c2ecf20Sopenharmony_ci} 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_cistatic int tegra30_ahub_resume(struct device *dev) 6428c2ecf20Sopenharmony_ci{ 6438c2ecf20Sopenharmony_ci int ret; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(dev); 6468c2ecf20Sopenharmony_ci if (ret < 0) { 6478c2ecf20Sopenharmony_ci pm_runtime_put(dev); 6488c2ecf20Sopenharmony_ci return ret; 6498c2ecf20Sopenharmony_ci } 6508c2ecf20Sopenharmony_ci ret = regcache_sync(ahub->regmap_ahub); 6518c2ecf20Sopenharmony_ci ret |= regcache_sync(ahub->regmap_apbif); 6528c2ecf20Sopenharmony_ci pm_runtime_put(dev); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci return ret; 6558c2ecf20Sopenharmony_ci} 6568c2ecf20Sopenharmony_ci#endif 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_cistatic const struct dev_pm_ops tegra30_ahub_pm_ops = { 6598c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(tegra30_ahub_runtime_suspend, 6608c2ecf20Sopenharmony_ci tegra30_ahub_runtime_resume, NULL) 6618c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(tegra30_ahub_suspend, tegra30_ahub_resume) 6628c2ecf20Sopenharmony_ci}; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_cistatic struct platform_driver tegra30_ahub_driver = { 6658c2ecf20Sopenharmony_ci .probe = tegra30_ahub_probe, 6668c2ecf20Sopenharmony_ci .remove = tegra30_ahub_remove, 6678c2ecf20Sopenharmony_ci .driver = { 6688c2ecf20Sopenharmony_ci .name = DRV_NAME, 6698c2ecf20Sopenharmony_ci .of_match_table = tegra30_ahub_of_match, 6708c2ecf20Sopenharmony_ci .pm = &tegra30_ahub_pm_ops, 6718c2ecf20Sopenharmony_ci }, 6728c2ecf20Sopenharmony_ci}; 6738c2ecf20Sopenharmony_cimodule_platform_driver(tegra30_ahub_driver); 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_civoid tegra30_ahub_set_cif(struct regmap *regmap, unsigned int reg, 6768c2ecf20Sopenharmony_ci struct tegra30_ahub_cif_conf *conf) 6778c2ecf20Sopenharmony_ci{ 6788c2ecf20Sopenharmony_ci unsigned int value; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci value = (conf->threshold << 6818c2ecf20Sopenharmony_ci TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) | 6828c2ecf20Sopenharmony_ci ((conf->audio_channels - 1) << 6838c2ecf20Sopenharmony_ci TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) | 6848c2ecf20Sopenharmony_ci ((conf->client_channels - 1) << 6858c2ecf20Sopenharmony_ci TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) | 6868c2ecf20Sopenharmony_ci (conf->audio_bits << 6878c2ecf20Sopenharmony_ci TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) | 6888c2ecf20Sopenharmony_ci (conf->client_bits << 6898c2ecf20Sopenharmony_ci TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT) | 6908c2ecf20Sopenharmony_ci (conf->expand << 6918c2ecf20Sopenharmony_ci TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT) | 6928c2ecf20Sopenharmony_ci (conf->stereo_conv << 6938c2ecf20Sopenharmony_ci TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT) | 6948c2ecf20Sopenharmony_ci (conf->replicate << 6958c2ecf20Sopenharmony_ci TEGRA30_AUDIOCIF_CTRL_REPLICATE_SHIFT) | 6968c2ecf20Sopenharmony_ci (conf->direction << 6978c2ecf20Sopenharmony_ci TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT) | 6988c2ecf20Sopenharmony_ci (conf->truncate << 6998c2ecf20Sopenharmony_ci TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT) | 7008c2ecf20Sopenharmony_ci (conf->mono_conv << 7018c2ecf20Sopenharmony_ci TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci regmap_write(regmap, reg, value); 7048c2ecf20Sopenharmony_ci} 7058c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tegra30_ahub_set_cif); 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_civoid tegra124_ahub_set_cif(struct regmap *regmap, unsigned int reg, 7088c2ecf20Sopenharmony_ci struct tegra30_ahub_cif_conf *conf) 7098c2ecf20Sopenharmony_ci{ 7108c2ecf20Sopenharmony_ci unsigned int value; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci value = (conf->threshold << 7138c2ecf20Sopenharmony_ci TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) | 7148c2ecf20Sopenharmony_ci ((conf->audio_channels - 1) << 7158c2ecf20Sopenharmony_ci TEGRA124_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) | 7168c2ecf20Sopenharmony_ci ((conf->client_channels - 1) << 7178c2ecf20Sopenharmony_ci TEGRA124_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) | 7188c2ecf20Sopenharmony_ci (conf->audio_bits << 7198c2ecf20Sopenharmony_ci TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) | 7208c2ecf20Sopenharmony_ci (conf->client_bits << 7218c2ecf20Sopenharmony_ci TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT) | 7228c2ecf20Sopenharmony_ci (conf->expand << 7238c2ecf20Sopenharmony_ci TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT) | 7248c2ecf20Sopenharmony_ci (conf->stereo_conv << 7258c2ecf20Sopenharmony_ci TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT) | 7268c2ecf20Sopenharmony_ci (conf->replicate << 7278c2ecf20Sopenharmony_ci TEGRA30_AUDIOCIF_CTRL_REPLICATE_SHIFT) | 7288c2ecf20Sopenharmony_ci (conf->direction << 7298c2ecf20Sopenharmony_ci TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT) | 7308c2ecf20Sopenharmony_ci (conf->truncate << 7318c2ecf20Sopenharmony_ci TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT) | 7328c2ecf20Sopenharmony_ci (conf->mono_conv << 7338c2ecf20Sopenharmony_ci TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT); 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci regmap_write(regmap, reg, value); 7368c2ecf20Sopenharmony_ci} 7378c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tegra124_ahub_set_cif); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ciMODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); 7408c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Tegra30 AHUB driver"); 7418c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 7428c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:" DRV_NAME); 7438c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, tegra30_ahub_of_match); 744