2019-05-28 20:10:04 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2012-04-11 02:31:59 +04:00
/*
* tegra30_ahub . c - Tegra30 AHUB driver
*
* Copyright ( c ) 2011 , 2012 , NVIDIA CORPORATION . All rights reserved .
*/
# include <linux/clk.h>
# include <linux/device.h>
# include <linux/io.h>
# include <linux/module.h>
# include <linux/of_platform.h>
# include <linux/platform_device.h>
# include <linux/pm_runtime.h>
# include <linux/regmap.h>
2013-11-07 02:18:22 +04:00
# include <linux/reset.h>
2012-04-11 02:31:59 +04:00
# include <linux/slab.h>
# include <sound/soc.h>
# include "tegra30_ahub.h"
# define DRV_NAME "tegra30-ahub"
static struct tegra30_ahub * ahub ;
static inline void tegra30_apbif_write ( u32 reg , u32 val )
{
regmap_write ( ahub - > regmap_apbif , reg , val ) ;
}
static inline u32 tegra30_apbif_read ( u32 reg )
{
u32 val ;
2017-02-25 21:59:17 +03:00
2012-04-11 02:31:59 +04:00
regmap_read ( ahub - > regmap_apbif , reg , & val ) ;
return val ;
}
static inline void tegra30_audio_write ( u32 reg , u32 val )
{
regmap_write ( ahub - > regmap_ahub , reg , val ) ;
}
2021-04-22 16:34:00 +03:00
static __maybe_unused int tegra30_ahub_runtime_suspend ( struct device * dev )
2012-04-11 02:31:59 +04:00
{
regcache_cache_only ( ahub - > regmap_apbif , true ) ;
regcache_cache_only ( ahub - > regmap_ahub , true ) ;
2021-01-20 03:31:53 +03:00
clk_bulk_disable_unprepare ( ahub - > nclocks , ahub - > clocks ) ;
2012-04-11 02:31:59 +04:00
return 0 ;
}
/*
* clk_apbif isn ' t required for an I2S < - > I2S configuration where no PCM data
* is read from or sent to memory . However , that ' s not something the rest of
* the driver supports right now , so we ' ll just treat the two clocks as one
* for now .
*
* These functions should not be a plain ref - count . Instead , each active stream
* contributes some requirement to the minimum clock rate , so starting or
* stopping streams should dynamically adjust the clock as required . However ,
* this is not yet implemented .
*/
2021-04-22 16:34:00 +03:00
static __maybe_unused int tegra30_ahub_runtime_resume ( struct device * dev )
2012-04-11 02:31:59 +04:00
{
int ret ;
2021-03-14 18:44:47 +03:00
ret = reset_control_bulk_assert ( ahub - > nresets , ahub - > resets ) ;
2021-01-20 03:31:54 +03:00
if ( ret )
return ret ;
2021-01-20 03:31:53 +03:00
ret = clk_bulk_prepare_enable ( ahub - > nclocks , ahub - > clocks ) ;
if ( ret )
2012-04-11 02:31:59 +04:00
return ret ;
2021-01-20 03:31:54 +03:00
usleep_range ( 10 , 100 ) ;
2021-03-14 18:44:47 +03:00
ret = reset_control_bulk_deassert ( ahub - > nresets , ahub - > resets ) ;
2021-01-20 03:31:54 +03:00
if ( ret )
goto disable_clocks ;
2012-04-11 02:31:59 +04:00
regcache_cache_only ( ahub - > regmap_apbif , false ) ;
regcache_cache_only ( ahub - > regmap_ahub , false ) ;
2021-01-20 03:31:54 +03:00
regcache_mark_dirty ( ahub - > regmap_apbif ) ;
regcache_mark_dirty ( ahub - > regmap_ahub ) ;
ret = regcache_sync ( ahub - > regmap_apbif ) ;
if ( ret )
goto disable_clocks ;
ret = regcache_sync ( ahub - > regmap_ahub ) ;
if ( ret )
goto disable_clocks ;
2012-04-11 02:31:59 +04:00
return 0 ;
2021-01-20 03:31:54 +03:00
disable_clocks :
clk_bulk_disable_unprepare ( ahub - > nclocks , ahub - > clocks ) ;
return ret ;
2012-04-11 02:31:59 +04:00
}
int tegra30_ahub_allocate_rx_fifo ( enum tegra30_ahub_rxcif * rxcif ,
2013-11-12 02:21:01 +04:00
char * dmachan , int dmachan_len ,
dma_addr_t * fiforeg )
2012-04-11 02:31:59 +04:00
{
int channel ;
u32 reg , val ;
2013-10-12 01:43:17 +04:00
struct tegra30_ahub_cif_conf cif_conf ;
2012-04-11 02:31:59 +04:00
channel = find_first_zero_bit ( ahub - > rx_usage ,
TEGRA30_AHUB_CHANNEL_CTRL_COUNT ) ;
if ( channel > = TEGRA30_AHUB_CHANNEL_CTRL_COUNT )
return - EBUSY ;
__set_bit ( channel , ahub - > rx_usage ) ;
* rxcif = TEGRA30_AHUB_RXCIF_APBIF_RX0 + channel ;
2013-11-12 02:21:01 +04:00
snprintf ( dmachan , dmachan_len , " rx%d " , channel ) ;
2012-04-11 02:31:59 +04:00
* fiforeg = ahub - > apbif_addr + TEGRA30_AHUB_CHANNEL_RXFIFO +
( channel * TEGRA30_AHUB_CHANNEL_RXFIFO_STRIDE ) ;
2013-11-15 22:29:45 +04:00
pm_runtime_get_sync ( ahub - > dev ) ;
2012-04-11 02:31:59 +04:00
reg = TEGRA30_AHUB_CHANNEL_CTRL +
( channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE ) ;
val = tegra30_apbif_read ( reg ) ;
val & = ~ ( TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_MASK |
TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_MASK ) ;
val | = ( 7 < < TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_SHIFT ) |
TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_EN |
TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_16 ;
tegra30_apbif_write ( reg , val ) ;
2013-10-12 01:43:17 +04:00
cif_conf . threshold = 0 ;
cif_conf . audio_channels = 2 ;
cif_conf . client_channels = 2 ;
cif_conf . audio_bits = TEGRA30_AUDIOCIF_BITS_16 ;
cif_conf . client_bits = TEGRA30_AUDIOCIF_BITS_16 ;
cif_conf . expand = 0 ;
cif_conf . stereo_conv = 0 ;
cif_conf . replicate = 0 ;
cif_conf . direction = TEGRA30_AUDIOCIF_DIRECTION_RX ;
cif_conf . truncate = 0 ;
cif_conf . mono_conv = 0 ;
2012-04-11 02:31:59 +04:00
reg = TEGRA30_AHUB_CIF_RX_CTRL +
( channel * TEGRA30_AHUB_CIF_RX_CTRL_STRIDE ) ;
2013-10-12 01:43:17 +04:00
ahub - > soc_data - > set_audio_cif ( ahub - > regmap_apbif , reg , & cif_conf ) ;
2012-04-11 02:31:59 +04:00
2013-11-15 22:29:45 +04:00
pm_runtime_put ( ahub - > dev ) ;
2012-04-11 02:31:59 +04:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( tegra30_ahub_allocate_rx_fifo ) ;
int tegra30_ahub_enable_rx_fifo ( enum tegra30_ahub_rxcif rxcif )
{
int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0 ;
int reg , val ;
2013-11-15 22:29:45 +04:00
pm_runtime_get_sync ( ahub - > dev ) ;
2012-04-11 02:31:59 +04:00
reg = TEGRA30_AHUB_CHANNEL_CTRL +
( channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE ) ;
val = tegra30_apbif_read ( reg ) ;
val | = TEGRA30_AHUB_CHANNEL_CTRL_RX_EN ;
tegra30_apbif_write ( reg , val ) ;
2013-11-15 22:29:45 +04:00
pm_runtime_put ( ahub - > dev ) ;
2012-04-11 02:31:59 +04:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( tegra30_ahub_enable_rx_fifo ) ;
int tegra30_ahub_disable_rx_fifo ( enum tegra30_ahub_rxcif rxcif )
{
int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0 ;
int reg , val ;
2013-11-15 22:29:45 +04:00
pm_runtime_get_sync ( ahub - > dev ) ;
2012-04-11 02:31:59 +04:00
reg = TEGRA30_AHUB_CHANNEL_CTRL +
( channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE ) ;
val = tegra30_apbif_read ( reg ) ;
val & = ~ TEGRA30_AHUB_CHANNEL_CTRL_RX_EN ;
tegra30_apbif_write ( reg , val ) ;
2013-11-15 22:29:45 +04:00
pm_runtime_put ( ahub - > dev ) ;
2012-04-11 02:31:59 +04:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( tegra30_ahub_disable_rx_fifo ) ;
int tegra30_ahub_free_rx_fifo ( enum tegra30_ahub_rxcif rxcif )
{
int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0 ;
__clear_bit ( channel , ahub - > rx_usage ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( tegra30_ahub_free_rx_fifo ) ;
int tegra30_ahub_allocate_tx_fifo ( enum tegra30_ahub_txcif * txcif ,
2013-11-12 02:21:01 +04:00
char * dmachan , int dmachan_len ,
dma_addr_t * fiforeg )
2012-04-11 02:31:59 +04:00
{
int channel ;
u32 reg , val ;
2013-10-12 01:43:17 +04:00
struct tegra30_ahub_cif_conf cif_conf ;
2012-04-11 02:31:59 +04:00
channel = find_first_zero_bit ( ahub - > tx_usage ,
TEGRA30_AHUB_CHANNEL_CTRL_COUNT ) ;
if ( channel > = TEGRA30_AHUB_CHANNEL_CTRL_COUNT )
return - EBUSY ;
__set_bit ( channel , ahub - > tx_usage ) ;
* txcif = TEGRA30_AHUB_TXCIF_APBIF_TX0 + channel ;
2013-11-12 02:21:01 +04:00
snprintf ( dmachan , dmachan_len , " tx%d " , channel ) ;
2012-04-11 02:31:59 +04:00
* fiforeg = ahub - > apbif_addr + TEGRA30_AHUB_CHANNEL_TXFIFO +
( channel * TEGRA30_AHUB_CHANNEL_TXFIFO_STRIDE ) ;
2013-11-15 22:29:45 +04:00
pm_runtime_get_sync ( ahub - > dev ) ;
2012-04-11 02:31:59 +04:00
reg = TEGRA30_AHUB_CHANNEL_CTRL +
( channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE ) ;
val = tegra30_apbif_read ( reg ) ;
val & = ~ ( TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_MASK |
TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_MASK ) ;
val | = ( 7 < < TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_SHIFT ) |
TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_EN |
TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_16 ;
tegra30_apbif_write ( reg , val ) ;
2013-10-12 01:43:17 +04:00
cif_conf . threshold = 0 ;
cif_conf . audio_channels = 2 ;
cif_conf . client_channels = 2 ;
cif_conf . audio_bits = TEGRA30_AUDIOCIF_BITS_16 ;
cif_conf . client_bits = TEGRA30_AUDIOCIF_BITS_16 ;
cif_conf . expand = 0 ;
cif_conf . stereo_conv = 0 ;
cif_conf . replicate = 0 ;
cif_conf . direction = TEGRA30_AUDIOCIF_DIRECTION_TX ;
cif_conf . truncate = 0 ;
cif_conf . mono_conv = 0 ;
2012-04-11 02:31:59 +04:00
reg = TEGRA30_AHUB_CIF_TX_CTRL +
( channel * TEGRA30_AHUB_CIF_TX_CTRL_STRIDE ) ;
2013-10-12 01:43:17 +04:00
ahub - > soc_data - > set_audio_cif ( ahub - > regmap_apbif , reg , & cif_conf ) ;
2012-04-11 02:31:59 +04:00
2013-11-15 22:29:45 +04:00
pm_runtime_put ( ahub - > dev ) ;
2012-04-11 02:31:59 +04:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( tegra30_ahub_allocate_tx_fifo ) ;
int tegra30_ahub_enable_tx_fifo ( enum tegra30_ahub_txcif txcif )
{
int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0 ;
int reg , val ;
2013-11-15 22:29:45 +04:00
pm_runtime_get_sync ( ahub - > dev ) ;
2012-04-11 02:31:59 +04:00
reg = TEGRA30_AHUB_CHANNEL_CTRL +
( channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE ) ;
val = tegra30_apbif_read ( reg ) ;
val | = TEGRA30_AHUB_CHANNEL_CTRL_TX_EN ;
tegra30_apbif_write ( reg , val ) ;
2013-11-15 22:29:45 +04:00
pm_runtime_put ( ahub - > dev ) ;
2012-04-11 02:31:59 +04:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( tegra30_ahub_enable_tx_fifo ) ;
int tegra30_ahub_disable_tx_fifo ( enum tegra30_ahub_txcif txcif )
{
int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0 ;
int reg , val ;
2013-11-15 22:29:45 +04:00
pm_runtime_get_sync ( ahub - > dev ) ;
2012-04-11 02:31:59 +04:00
reg = TEGRA30_AHUB_CHANNEL_CTRL +
( channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE ) ;
val = tegra30_apbif_read ( reg ) ;
val & = ~ TEGRA30_AHUB_CHANNEL_CTRL_TX_EN ;
tegra30_apbif_write ( reg , val ) ;
2013-11-15 22:29:45 +04:00
pm_runtime_put ( ahub - > dev ) ;
2012-04-11 02:31:59 +04:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( tegra30_ahub_disable_tx_fifo ) ;
int tegra30_ahub_free_tx_fifo ( enum tegra30_ahub_txcif txcif )
{
int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0 ;
__clear_bit ( channel , ahub - > tx_usage ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( tegra30_ahub_free_tx_fifo ) ;
int tegra30_ahub_set_rx_cif_source ( enum tegra30_ahub_rxcif rxcif ,
enum tegra30_ahub_txcif txcif )
{
int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0 ;
int reg ;
2013-11-15 22:29:45 +04:00
pm_runtime_get_sync ( ahub - > dev ) ;
2012-04-11 02:31:59 +04:00
reg = TEGRA30_AHUB_AUDIO_RX +
( channel * TEGRA30_AHUB_AUDIO_RX_STRIDE ) ;
tegra30_audio_write ( reg , 1 < < txcif ) ;
2013-11-15 22:29:45 +04:00
pm_runtime_put ( ahub - > dev ) ;
2012-04-11 02:31:59 +04:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( tegra30_ahub_set_rx_cif_source ) ;
int tegra30_ahub_unset_rx_cif_source ( enum tegra30_ahub_rxcif rxcif )
{
int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0 ;
int reg ;
2013-11-15 22:29:45 +04:00
pm_runtime_get_sync ( ahub - > dev ) ;
2012-04-11 02:31:59 +04:00
reg = TEGRA30_AHUB_AUDIO_RX +
( channel * TEGRA30_AHUB_AUDIO_RX_STRIDE ) ;
tegra30_audio_write ( reg , 0 ) ;
2013-11-15 22:29:45 +04:00
pm_runtime_put ( ahub - > dev ) ;
2012-04-11 02:31:59 +04:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( tegra30_ahub_unset_rx_cif_source ) ;
2021-03-14 18:44:47 +03:00
static const struct reset_control_bulk_data tegra30_ahub_resets_data [ ] = {
{ " d_audio " } ,
{ " apbif " } ,
{ " i2s0 " } ,
{ " i2s1 " } ,
{ " i2s2 " } ,
{ " i2s3 " } ,
{ " i2s4 " } ,
{ " dam0 " } ,
{ " dam1 " } ,
{ " dam2 " } ,
{ " spdif " } ,
{ " amx " } , /* Tegra114+ */
{ " adx " } , /* Tegra114+ */
{ " amx1 " } , /* Tegra124 */
{ " adx1 " } , /* Tegra124 */
{ " afc0 " } , /* Tegra124 */
{ " afc1 " } , /* Tegra124 */
{ " afc2 " } , /* Tegra124 */
{ " afc3 " } , /* Tegra124 */
{ " afc4 " } , /* Tegra124 */
{ " afc5 " } , /* Tegra124 */
2012-04-11 02:31:59 +04:00
} ;
# define LAST_REG(name) \
( TEGRA30_AHUB_ # # name + \
( TEGRA30_AHUB_ # # name # # _STRIDE * TEGRA30_AHUB_ # # name # # _COUNT ) - 4 )
# define REG_IN_ARRAY(reg, name) \
( ( reg > = TEGRA30_AHUB_ # # name ) & & \
( reg < = LAST_REG ( name ) & & \
( ! ( ( reg - TEGRA30_AHUB_ # # name ) % TEGRA30_AHUB_ # # name # # _STRIDE ) ) ) )
static bool tegra30_ahub_apbif_wr_rd_reg ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
case TEGRA30_AHUB_CONFIG_LINK_CTRL :
case TEGRA30_AHUB_MISC_CTRL :
case TEGRA30_AHUB_APBDMA_LIVE_STATUS :
case TEGRA30_AHUB_I2S_LIVE_STATUS :
case TEGRA30_AHUB_SPDIF_LIVE_STATUS :
case TEGRA30_AHUB_I2S_INT_MASK :
case TEGRA30_AHUB_DAM_INT_MASK :
case TEGRA30_AHUB_SPDIF_INT_MASK :
case TEGRA30_AHUB_APBIF_INT_MASK :
case TEGRA30_AHUB_I2S_INT_STATUS :
case TEGRA30_AHUB_DAM_INT_STATUS :
case TEGRA30_AHUB_SPDIF_INT_STATUS :
case TEGRA30_AHUB_APBIF_INT_STATUS :
case TEGRA30_AHUB_I2S_INT_SOURCE :
case TEGRA30_AHUB_DAM_INT_SOURCE :
case TEGRA30_AHUB_SPDIF_INT_SOURCE :
case TEGRA30_AHUB_APBIF_INT_SOURCE :
case TEGRA30_AHUB_I2S_INT_SET :
case TEGRA30_AHUB_DAM_INT_SET :
case TEGRA30_AHUB_SPDIF_INT_SET :
case TEGRA30_AHUB_APBIF_INT_SET :
return true ;
default :
break ;
2013-10-09 02:55:45 +04:00
}
2012-04-11 02:31:59 +04:00
if ( REG_IN_ARRAY ( reg , CHANNEL_CTRL ) | |
REG_IN_ARRAY ( reg , CHANNEL_CLEAR ) | |
REG_IN_ARRAY ( reg , CHANNEL_STATUS ) | |
REG_IN_ARRAY ( reg , CHANNEL_TXFIFO ) | |
REG_IN_ARRAY ( reg , CHANNEL_RXFIFO ) | |
REG_IN_ARRAY ( reg , CIF_TX_CTRL ) | |
REG_IN_ARRAY ( reg , CIF_RX_CTRL ) | |
REG_IN_ARRAY ( reg , DAM_LIVE_STATUS ) )
return true ;
return false ;
}
static bool tegra30_ahub_apbif_volatile_reg ( struct device * dev ,
unsigned int reg )
{
switch ( reg ) {
case TEGRA30_AHUB_CONFIG_LINK_CTRL :
case TEGRA30_AHUB_MISC_CTRL :
case TEGRA30_AHUB_APBDMA_LIVE_STATUS :
case TEGRA30_AHUB_I2S_LIVE_STATUS :
case TEGRA30_AHUB_SPDIF_LIVE_STATUS :
case TEGRA30_AHUB_I2S_INT_STATUS :
case TEGRA30_AHUB_DAM_INT_STATUS :
case TEGRA30_AHUB_SPDIF_INT_STATUS :
case TEGRA30_AHUB_APBIF_INT_STATUS :
case TEGRA30_AHUB_I2S_INT_SET :
case TEGRA30_AHUB_DAM_INT_SET :
case TEGRA30_AHUB_SPDIF_INT_SET :
case TEGRA30_AHUB_APBIF_INT_SET :
return true ;
default :
break ;
2013-10-09 02:55:45 +04:00
}
2012-04-11 02:31:59 +04:00
if ( REG_IN_ARRAY ( reg , CHANNEL_CLEAR ) | |
REG_IN_ARRAY ( reg , CHANNEL_STATUS ) | |
REG_IN_ARRAY ( reg , CHANNEL_TXFIFO ) | |
REG_IN_ARRAY ( reg , CHANNEL_RXFIFO ) | |
REG_IN_ARRAY ( reg , DAM_LIVE_STATUS ) )
return true ;
return false ;
}
static bool tegra30_ahub_apbif_precious_reg ( struct device * dev ,
unsigned int reg )
{
if ( REG_IN_ARRAY ( reg , CHANNEL_TXFIFO ) | |
REG_IN_ARRAY ( reg , CHANNEL_RXFIFO ) )
return true ;
return false ;
}
static const struct regmap_config tegra30_ahub_apbif_regmap_config = {
. name = " apbif " ,
. reg_bits = 32 ,
. val_bits = 32 ,
. reg_stride = 4 ,
. max_register = TEGRA30_AHUB_APBIF_INT_SET ,
. writeable_reg = tegra30_ahub_apbif_wr_rd_reg ,
. readable_reg = tegra30_ahub_apbif_wr_rd_reg ,
. volatile_reg = tegra30_ahub_apbif_volatile_reg ,
. precious_reg = tegra30_ahub_apbif_precious_reg ,
2014-03-18 09:08:49 +04:00
. cache_type = REGCACHE_FLAT ,
2012-04-11 02:31:59 +04:00
} ;
static bool tegra30_ahub_ahub_wr_rd_reg ( struct device * dev , unsigned int reg )
{
if ( REG_IN_ARRAY ( reg , AUDIO_RX ) )
return true ;
return false ;
}
static const struct regmap_config tegra30_ahub_ahub_regmap_config = {
. name = " ahub " ,
. reg_bits = 32 ,
. val_bits = 32 ,
. reg_stride = 4 ,
. max_register = LAST_REG ( AUDIO_RX ) ,
. writeable_reg = tegra30_ahub_ahub_wr_rd_reg ,
. readable_reg = tegra30_ahub_ahub_wr_rd_reg ,
2014-03-18 09:08:49 +04:00
. cache_type = REGCACHE_FLAT ,
2012-04-11 02:31:59 +04:00
} ;
2013-03-21 23:56:41 +04:00
static struct tegra30_ahub_soc_data soc_data_tegra30 = {
2021-03-14 18:44:47 +03:00
. num_resets = 11 ,
2013-10-12 01:43:17 +04:00
. set_audio_cif = tegra30_ahub_set_cif ,
2013-03-21 23:56:41 +04:00
} ;
static struct tegra30_ahub_soc_data soc_data_tegra114 = {
2021-03-14 18:44:47 +03:00
. num_resets = 13 ,
2013-10-12 01:43:17 +04:00
. set_audio_cif = tegra30_ahub_set_cif ,
} ;
static struct tegra30_ahub_soc_data soc_data_tegra124 = {
2021-03-14 18:44:47 +03:00
. num_resets = 21 ,
2013-10-12 01:43:17 +04:00
. set_audio_cif = tegra124_ahub_set_cif ,
2013-03-21 23:56:41 +04:00
} ;
static const struct of_device_id tegra30_ahub_of_match [ ] = {
2013-10-12 01:43:17 +04:00
{ . compatible = " nvidia,tegra124-ahub " , . data = & soc_data_tegra124 } ,
2013-03-21 23:56:41 +04:00
{ . compatible = " nvidia,tegra114-ahub " , . data = & soc_data_tegra114 } ,
{ . compatible = " nvidia,tegra30-ahub " , . data = & soc_data_tegra30 } ,
{ } ,
} ;
2012-12-07 18:26:33 +04:00
static int tegra30_ahub_probe ( struct platform_device * pdev )
2012-04-11 02:31:59 +04:00
{
2013-03-21 23:56:41 +04:00
const struct tegra30_ahub_soc_data * soc_data ;
2019-09-04 11:39:09 +03:00
struct resource * res0 ;
2012-04-11 02:31:59 +04:00
void __iomem * regs_apbif , * regs_ahub ;
int ret = 0 ;
2021-08-17 00:14:51 +03:00
soc_data = of_device_get_match_data ( & pdev - > dev ) ;
if ( ! soc_data )
2013-03-21 23:56:41 +04:00
return - EINVAL ;
2012-04-11 02:31:59 +04:00
ahub = devm_kzalloc ( & pdev - > dev , sizeof ( struct tegra30_ahub ) ,
GFP_KERNEL ) ;
2017-02-25 14:18:08 +03:00
if ( ! ahub )
2015-08-03 14:57:34 +03:00
return - ENOMEM ;
2012-04-11 02:31:59 +04:00
dev_set_drvdata ( & pdev - > dev , ahub ) ;
2021-03-14 18:44:47 +03:00
BUILD_BUG_ON ( sizeof ( ahub - > resets ) ! = sizeof ( tegra30_ahub_resets_data ) ) ;
memcpy ( ahub - > resets , tegra30_ahub_resets_data , sizeof ( ahub - > resets ) ) ;
ahub - > nresets = soc_data - > num_resets ;
2013-10-12 01:43:17 +04:00
ahub - > soc_data = soc_data ;
2012-04-11 02:31:59 +04:00
ahub - > dev = & pdev - > dev ;
2021-01-20 03:31:53 +03:00
ahub - > clocks [ ahub - > nclocks + + ] . id = " apbif " ;
ahub - > clocks [ ahub - > nclocks + + ] . id = " d_audio " ;
2012-04-11 02:31:59 +04:00
2021-01-20 03:31:53 +03:00
ret = devm_clk_bulk_get ( & pdev - > dev , ahub - > nclocks , ahub - > clocks ) ;
if ( ret )
2021-03-14 18:44:57 +03:00
goto err_unset_ahub ;
2012-04-11 02:31:59 +04:00
2021-03-14 18:44:47 +03:00
ret = devm_reset_control_bulk_get_exclusive ( & pdev - > dev , ahub - > nresets ,
ahub - > resets ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " Can't get resets: %d \n " , ret ) ;
2021-03-14 18:44:57 +03:00
goto err_unset_ahub ;
2021-01-20 03:31:54 +03:00
}
2021-06-18 05:47:22 +03:00
regs_apbif = devm_platform_get_and_ioremap_resource ( pdev , 0 , & res0 ) ;
2021-03-14 18:44:57 +03:00
if ( IS_ERR ( regs_apbif ) ) {
ret = PTR_ERR ( regs_apbif ) ;
goto err_unset_ahub ;
}
2012-04-11 02:31:59 +04:00
ahub - > apbif_addr = res0 - > start ;
ahub - > regmap_apbif = devm_regmap_init_mmio ( & pdev - > dev , regs_apbif ,
& tegra30_ahub_apbif_regmap_config ) ;
if ( IS_ERR ( ahub - > regmap_apbif ) ) {
dev_err ( & pdev - > dev , " apbif regmap init failed \n " ) ;
ret = PTR_ERR ( ahub - > regmap_apbif ) ;
2021-03-14 18:44:57 +03:00
goto err_unset_ahub ;
2012-04-11 02:31:59 +04:00
}
regcache_cache_only ( ahub - > regmap_apbif , true ) ;
2019-09-04 11:39:09 +03:00
regs_ahub = devm_platform_ioremap_resource ( pdev , 1 ) ;
2021-03-14 18:44:57 +03:00
if ( IS_ERR ( regs_ahub ) ) {
ret = PTR_ERR ( regs_ahub ) ;
goto err_unset_ahub ;
}
2012-04-11 02:31:59 +04:00
ahub - > regmap_ahub = devm_regmap_init_mmio ( & pdev - > dev , regs_ahub ,
& tegra30_ahub_ahub_regmap_config ) ;
if ( IS_ERR ( ahub - > regmap_ahub ) ) {
dev_err ( & pdev - > dev , " ahub regmap init failed \n " ) ;
ret = PTR_ERR ( ahub - > regmap_ahub ) ;
2021-03-14 18:44:57 +03:00
goto err_unset_ahub ;
2012-04-11 02:31:59 +04:00
}
regcache_cache_only ( ahub - > regmap_ahub , true ) ;
pm_runtime_enable ( & pdev - > dev ) ;
2013-01-11 12:01:25 +04:00
of_platform_populate ( pdev - > dev . of_node , NULL , NULL , & pdev - > dev ) ;
2012-04-11 02:31:59 +04:00
return 0 ;
2021-03-14 18:44:57 +03:00
err_unset_ahub :
ahub = NULL ;
2015-08-03 14:57:34 +03:00
2012-04-11 02:31:59 +04:00
return ret ;
}
2023-03-15 18:07:31 +03:00
static void tegra30_ahub_remove ( struct platform_device * pdev )
2012-04-11 02:31:59 +04:00
{
pm_runtime_disable ( & pdev - > dev ) ;
2021-03-14 18:44:57 +03:00
ahub = NULL ;
2012-04-11 02:31:59 +04:00
}
2012-11-19 22:25:33 +04:00
static const struct dev_pm_ops tegra30_ahub_pm_ops = {
2012-04-11 02:31:59 +04:00
SET_RUNTIME_PM_OPS ( tegra30_ahub_runtime_suspend ,
tegra30_ahub_runtime_resume , NULL )
2021-03-14 18:44:58 +03:00
SET_SYSTEM_SLEEP_PM_OPS ( pm_runtime_force_suspend ,
pm_runtime_force_resume )
2012-04-11 02:31:59 +04:00
} ;
static struct platform_driver tegra30_ahub_driver = {
. probe = tegra30_ahub_probe ,
2023-03-15 18:07:31 +03:00
. remove_new = tegra30_ahub_remove ,
2012-04-11 02:31:59 +04:00
. driver = {
. name = DRV_NAME ,
. of_match_table = tegra30_ahub_of_match ,
. pm = & tegra30_ahub_pm_ops ,
} ,
} ;
module_platform_driver ( tegra30_ahub_driver ) ;
2013-10-12 01:43:17 +04:00
void tegra30_ahub_set_cif ( struct regmap * regmap , unsigned int reg ,
struct tegra30_ahub_cif_conf * conf )
{
unsigned int value ;
value = ( conf - > threshold < <
TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT ) |
( ( conf - > audio_channels - 1 ) < <
TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT ) |
( ( conf - > client_channels - 1 ) < <
TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT ) |
( conf - > audio_bits < <
TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT ) |
( conf - > client_bits < <
TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT ) |
( conf - > expand < <
TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT ) |
( conf - > stereo_conv < <
TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT ) |
( conf - > replicate < <
TEGRA30_AUDIOCIF_CTRL_REPLICATE_SHIFT ) |
( conf - > direction < <
TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT ) |
( conf - > truncate < <
TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT ) |
( conf - > mono_conv < <
TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT ) ;
regmap_write ( regmap , reg , value ) ;
}
EXPORT_SYMBOL_GPL ( tegra30_ahub_set_cif ) ;
void tegra124_ahub_set_cif ( struct regmap * regmap , unsigned int reg ,
struct tegra30_ahub_cif_conf * conf )
{
unsigned int value ;
value = ( conf - > threshold < <
TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT ) |
( ( conf - > audio_channels - 1 ) < <
TEGRA124_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT ) |
( ( conf - > client_channels - 1 ) < <
TEGRA124_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT ) |
( conf - > audio_bits < <
TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT ) |
( conf - > client_bits < <
TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT ) |
( conf - > expand < <
TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT ) |
( conf - > stereo_conv < <
TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT ) |
( conf - > replicate < <
TEGRA30_AUDIOCIF_CTRL_REPLICATE_SHIFT ) |
( conf - > direction < <
TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT ) |
( conf - > truncate < <
TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT ) |
( conf - > mono_conv < <
TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT ) ;
regmap_write ( regmap , reg , value ) ;
}
EXPORT_SYMBOL_GPL ( tegra124_ahub_set_cif ) ;
2012-04-11 02:31:59 +04:00
MODULE_AUTHOR ( " Stephen Warren <swarren@nvidia.com> " ) ;
MODULE_DESCRIPTION ( " Tegra30 AHUB driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_ALIAS ( " platform: " DRV_NAME ) ;
2012-06-08 03:58:31 +04:00
MODULE_DEVICE_TABLE ( of , tegra30_ahub_of_match ) ;