2020-10-13 15:17:32 +03:00
// SPDX-License-Identifier: GPL-2.0
// Copyright 2019 NXP
# include <linux/bitrev.h>
# include <linux/clk.h>
# include <linux/firmware.h>
# include <linux/interrupt.h>
# include <linux/module.h>
# include <linux/of_platform.h>
# include <linux/pm_runtime.h>
# include <linux/regmap.h>
# include <linux/reset.h>
# include <sound/dmaengine_pcm.h>
# include <sound/pcm_iec958.h>
# include <sound/pcm_params.h>
# include "fsl_xcvr.h"
# include "imx-pcm.h"
# define FSL_XCVR_CAPDS_SIZE 256
struct fsl_xcvr_soc_data {
const char * fw_name ;
} ;
struct fsl_xcvr {
const struct fsl_xcvr_soc_data * soc_data ;
struct platform_device * pdev ;
struct regmap * regmap ;
struct clk * ipg_clk ;
struct clk * pll_ipg_clk ;
struct clk * phy_clk ;
struct clk * spba_clk ;
struct reset_control * reset ;
u8 streams ;
u32 mode ;
u32 arc_mode ;
void __iomem * ram_addr ;
struct snd_dmaengine_dai_dma_data dma_prms_rx ;
struct snd_dmaengine_dai_dma_data dma_prms_tx ;
struct snd_aes_iec958 rx_iec958 ;
struct snd_aes_iec958 tx_iec958 ;
u8 cap_ds [ FSL_XCVR_CAPDS_SIZE ] ;
} ;
static const struct fsl_xcvr_pll_conf {
u8 mfi ; /* min=0x18, max=0x38 */
u32 mfn ; /* signed int, 2's compl., min=0x3FFF0000, max=0x00010000 */
u32 mfd ; /* unsigned int */
u32 fout ; /* Fout = Fref*(MFI + MFN/MFD), Fref is 24MHz */
} fsl_xcvr_pll_cfg [ ] = {
{ . mfi = 54 , . mfn = 1 , . mfd = 6 , . fout = 1300000000 , } , /* 1.3 GHz */
{ . mfi = 32 , . mfn = 96 , . mfd = 125 , . fout = 786432000 , } , /* 8000 Hz */
{ . mfi = 30 , . mfn = 66 , . mfd = 625 , . fout = 722534400 , } , /* 11025 Hz */
{ . mfi = 29 , . mfn = 1 , . mfd = 6 , . fout = 700000000 , } , /* 700 MHz */
} ;
/*
* HDMI2 .1 spec defines 6 - and 12 - channels layout for one bit audio
* stream . Todo : to check how this case can be considered below
*/
static const u32 fsl_xcvr_earc_channels [ ] = { 1 , 2 , 8 , 16 , 32 , } ;
static const struct snd_pcm_hw_constraint_list fsl_xcvr_earc_channels_constr = {
. count = ARRAY_SIZE ( fsl_xcvr_earc_channels ) ,
. list = fsl_xcvr_earc_channels ,
} ;
static const u32 fsl_xcvr_earc_rates [ ] = {
32000 , 44100 , 48000 , 64000 , 88200 , 96000 ,
128000 , 176400 , 192000 , 256000 , 352800 , 384000 ,
512000 , 705600 , 768000 , 1024000 , 1411200 , 1536000 ,
} ;
static const struct snd_pcm_hw_constraint_list fsl_xcvr_earc_rates_constr = {
. count = ARRAY_SIZE ( fsl_xcvr_earc_rates ) ,
. list = fsl_xcvr_earc_rates ,
} ;
static const u32 fsl_xcvr_spdif_channels [ ] = { 2 , } ;
static const struct snd_pcm_hw_constraint_list fsl_xcvr_spdif_channels_constr = {
. count = ARRAY_SIZE ( fsl_xcvr_spdif_channels ) ,
. list = fsl_xcvr_spdif_channels ,
} ;
static const u32 fsl_xcvr_spdif_rates [ ] = {
32000 , 44100 , 48000 , 88200 , 96000 , 176400 , 192000 ,
} ;
static const struct snd_pcm_hw_constraint_list fsl_xcvr_spdif_rates_constr = {
. count = ARRAY_SIZE ( fsl_xcvr_spdif_rates ) ,
. list = fsl_xcvr_spdif_rates ,
} ;
static int fsl_xcvr_arc_mode_put ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct snd_soc_dai * dai = snd_kcontrol_chip ( kcontrol ) ;
struct fsl_xcvr * xcvr = snd_soc_dai_get_drvdata ( dai ) ;
struct soc_enum * e = ( struct soc_enum * ) kcontrol - > private_value ;
unsigned int * item = ucontrol - > value . enumerated . item ;
xcvr - > arc_mode = snd_soc_enum_item_to_val ( e , item [ 0 ] ) ;
return 0 ;
}
static int fsl_xcvr_arc_mode_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct snd_soc_dai * dai = snd_kcontrol_chip ( kcontrol ) ;
struct fsl_xcvr * xcvr = snd_soc_dai_get_drvdata ( dai ) ;
ucontrol - > value . enumerated . item [ 0 ] = xcvr - > arc_mode ;
return 0 ;
}
static const u32 fsl_xcvr_phy_arc_cfg [ ] = {
FSL_XCVR_PHY_CTRL_ARC_MODE_SE_EN , FSL_XCVR_PHY_CTRL_ARC_MODE_CM_EN ,
} ;
static const char * const fsl_xcvr_arc_mode [ ] = { " Single Ended " , " Common " , } ;
static const struct soc_enum fsl_xcvr_arc_mode_enum =
SOC_ENUM_SINGLE_EXT ( ARRAY_SIZE ( fsl_xcvr_arc_mode ) , fsl_xcvr_arc_mode ) ;
static struct snd_kcontrol_new fsl_xcvr_arc_mode_kctl =
SOC_ENUM_EXT ( " ARC Mode " , fsl_xcvr_arc_mode_enum ,
fsl_xcvr_arc_mode_get , fsl_xcvr_arc_mode_put ) ;
/* Capabilities data structure, bytes */
static int fsl_xcvr_type_capds_bytes_info ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_info * uinfo )
{
uinfo - > type = SNDRV_CTL_ELEM_TYPE_BYTES ;
uinfo - > count = FSL_XCVR_CAPDS_SIZE ;
return 0 ;
}
static int fsl_xcvr_capds_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct snd_soc_dai * dai = snd_kcontrol_chip ( kcontrol ) ;
struct fsl_xcvr * xcvr = snd_soc_dai_get_drvdata ( dai ) ;
memcpy ( ucontrol - > value . bytes . data , xcvr - > cap_ds , FSL_XCVR_CAPDS_SIZE ) ;
return 0 ;
}
static int fsl_xcvr_capds_put ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct snd_soc_dai * dai = snd_kcontrol_chip ( kcontrol ) ;
struct fsl_xcvr * xcvr = snd_soc_dai_get_drvdata ( dai ) ;
memcpy ( xcvr - > cap_ds , ucontrol - > value . bytes . data , FSL_XCVR_CAPDS_SIZE ) ;
return 0 ;
}
static struct snd_kcontrol_new fsl_xcvr_earc_capds_kctl = {
. iface = SNDRV_CTL_ELEM_IFACE_PCM ,
. name = " Capabilities Data Structure " ,
. access = SNDRV_CTL_ELEM_ACCESS_READWRITE ,
. info = fsl_xcvr_type_capds_bytes_info ,
. get = fsl_xcvr_capds_get ,
. put = fsl_xcvr_capds_put ,
} ;
static int fsl_xcvr_activate_ctl ( struct snd_soc_dai * dai , const char * name ,
bool active )
{
struct snd_soc_card * card = dai - > component - > card ;
struct snd_kcontrol * kctl ;
bool enabled ;
kctl = snd_soc_card_get_kcontrol ( card , name ) ;
if ( kctl = = NULL )
return - ENOENT ;
enabled = ( ( kctl - > vd [ 0 ] . access & SNDRV_CTL_ELEM_ACCESS_WRITE ) ! = 0 ) ;
if ( active = = enabled )
return 0 ; /* nothing to do */
if ( active )
kctl - > vd [ 0 ] . access | = SNDRV_CTL_ELEM_ACCESS_WRITE ;
else
kctl - > vd [ 0 ] . access & = ~ SNDRV_CTL_ELEM_ACCESS_WRITE ;
snd_ctl_notify ( card - > snd_card , SNDRV_CTL_EVENT_MASK_INFO , & kctl - > id ) ;
return 1 ;
}
static int fsl_xcvr_mode_put ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct snd_soc_dai * dai = snd_kcontrol_chip ( kcontrol ) ;
struct fsl_xcvr * xcvr = snd_soc_dai_get_drvdata ( dai ) ;
struct soc_enum * e = ( struct soc_enum * ) kcontrol - > private_value ;
unsigned int * item = ucontrol - > value . enumerated . item ;
struct snd_soc_card * card = dai - > component - > card ;
struct snd_soc_pcm_runtime * rtd ;
xcvr - > mode = snd_soc_enum_item_to_val ( e , item [ 0 ] ) ;
fsl_xcvr_activate_ctl ( dai , fsl_xcvr_arc_mode_kctl . name ,
( xcvr - > mode = = FSL_XCVR_MODE_ARC ) ) ;
fsl_xcvr_activate_ctl ( dai , fsl_xcvr_earc_capds_kctl . name ,
( xcvr - > mode = = FSL_XCVR_MODE_EARC ) ) ;
/* Allow playback for SPDIF only */
rtd = snd_soc_get_pcm_runtime ( card , card - > dai_link ) ;
rtd - > pcm - > streams [ SNDRV_PCM_STREAM_PLAYBACK ] . substream_count =
( xcvr - > mode = = FSL_XCVR_MODE_SPDIF ? 1 : 0 ) ;
return 0 ;
}
static int fsl_xcvr_mode_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct snd_soc_dai * dai = snd_kcontrol_chip ( kcontrol ) ;
struct fsl_xcvr * xcvr = snd_soc_dai_get_drvdata ( dai ) ;
ucontrol - > value . enumerated . item [ 0 ] = xcvr - > mode ;
return 0 ;
}
static const char * const fsl_xcvr_mode [ ] = { " SPDIF " , " ARC RX " , " eARC " , } ;
static const struct soc_enum fsl_xcvr_mode_enum =
SOC_ENUM_SINGLE_EXT ( ARRAY_SIZE ( fsl_xcvr_mode ) , fsl_xcvr_mode ) ;
static struct snd_kcontrol_new fsl_xcvr_mode_kctl =
SOC_ENUM_EXT ( " XCVR Mode " , fsl_xcvr_mode_enum ,
fsl_xcvr_mode_get , fsl_xcvr_mode_put ) ;
/** phy: true => phy, false => pll */
static int fsl_xcvr_ai_write ( struct fsl_xcvr * xcvr , u8 reg , u32 data , bool phy )
{
struct device * dev = & xcvr - > pdev - > dev ;
u32 val , idx , tidx ;
int ret ;
idx = BIT ( phy ? 26 : 24 ) ;
tidx = BIT ( phy ? 27 : 25 ) ;
regmap_write ( xcvr - > regmap , FSL_XCVR_PHY_AI_CTRL_CLR , 0xFF ) ;
regmap_write ( xcvr - > regmap , FSL_XCVR_PHY_AI_CTRL_SET , reg ) ;
regmap_write ( xcvr - > regmap , FSL_XCVR_PHY_AI_WDATA , data ) ;
regmap_write ( xcvr - > regmap , FSL_XCVR_PHY_AI_CTRL_TOG , idx ) ;
ret = regmap_read_poll_timeout ( xcvr - > regmap , FSL_XCVR_PHY_AI_CTRL , val ,
2020-11-02 18:18:10 +02:00
( val & idx ) = = ( ( val & tidx ) > > 1 ) ,
2020-10-13 15:17:32 +03:00
10 , 10000 ) ;
if ( ret )
dev_err ( dev , " AI timeout: failed to set %s reg 0x%02x=0x%08x \n " ,
phy ? " PHY " : " PLL " , reg , data ) ;
return ret ;
}
static int fsl_xcvr_en_phy_pll ( struct fsl_xcvr * xcvr , u32 freq , bool tx )
{
struct device * dev = & xcvr - > pdev - > dev ;
u32 i , div = 0 , log2 ;
int ret ;
for ( i = 0 ; i < ARRAY_SIZE ( fsl_xcvr_pll_cfg ) ; i + + ) {
if ( fsl_xcvr_pll_cfg [ i ] . fout % freq = = 0 ) {
div = fsl_xcvr_pll_cfg [ i ] . fout / freq ;
break ;
}
}
if ( ! div | | i > = ARRAY_SIZE ( fsl_xcvr_pll_cfg ) )
return - EINVAL ;
log2 = ilog2 ( div ) ;
/* Release AI interface from reset */
ret = regmap_write ( xcvr - > regmap , FSL_XCVR_PHY_AI_CTRL_SET ,
FSL_XCVR_PHY_AI_CTRL_AI_RESETN ) ;
if ( ret < 0 ) {
dev_err ( dev , " Error while setting IER0: %d \n " , ret ) ;
return ret ;
}
/* PLL: BANDGAP_SET: EN_VBG (enable bandgap) */
fsl_xcvr_ai_write ( xcvr , FSL_XCVR_PLL_BANDGAP_SET ,
FSL_XCVR_PLL_BANDGAP_EN_VBG , 0 ) ;
/* PLL: CTRL0: DIV_INTEGER */
fsl_xcvr_ai_write ( xcvr , FSL_XCVR_PLL_CTRL0 , fsl_xcvr_pll_cfg [ i ] . mfi , 0 ) ;
/* PLL: NUMERATOR: MFN */
fsl_xcvr_ai_write ( xcvr , FSL_XCVR_PLL_NUM , fsl_xcvr_pll_cfg [ i ] . mfn , 0 ) ;
/* PLL: DENOMINATOR: MFD */
fsl_xcvr_ai_write ( xcvr , FSL_XCVR_PLL_DEN , fsl_xcvr_pll_cfg [ i ] . mfd , 0 ) ;
/* PLL: CTRL0_SET: HOLD_RING_OFF, POWER_UP */
fsl_xcvr_ai_write ( xcvr , FSL_XCVR_PLL_CTRL0_SET ,
FSL_XCVR_PLL_CTRL0_HROFF | FSL_XCVR_PLL_CTRL0_PWP , 0 ) ;
udelay ( 25 ) ;
/* PLL: CTRL0: Clear Hold Ring Off */
fsl_xcvr_ai_write ( xcvr , FSL_XCVR_PLL_CTRL0_CLR ,
FSL_XCVR_PLL_CTRL0_HROFF , 0 ) ;
udelay ( 100 ) ;
if ( tx ) { /* TX is enabled for SPDIF only */
/* PLL: POSTDIV: PDIV0 */
fsl_xcvr_ai_write ( xcvr , FSL_XCVR_PLL_PDIV ,
FSL_XCVR_PLL_PDIVx ( log2 , 0 ) , 0 ) ;
/* PLL: CTRL_SET: CLKMUX0_EN */
fsl_xcvr_ai_write ( xcvr , FSL_XCVR_PLL_CTRL0_SET ,
FSL_XCVR_PLL_CTRL0_CM0_EN , 0 ) ;
} else if ( xcvr - > mode = = FSL_XCVR_MODE_EARC ) { /* eARC RX */
/* PLL: POSTDIV: PDIV1 */
fsl_xcvr_ai_write ( xcvr , FSL_XCVR_PLL_PDIV ,
FSL_XCVR_PLL_PDIVx ( log2 , 1 ) , 0 ) ;
/* PLL: CTRL_SET: CLKMUX1_EN */
fsl_xcvr_ai_write ( xcvr , FSL_XCVR_PLL_CTRL0_SET ,
FSL_XCVR_PLL_CTRL0_CM1_EN , 0 ) ;
} else { /* SPDIF / ARC RX */
/* PLL: POSTDIV: PDIV2 */
fsl_xcvr_ai_write ( xcvr , FSL_XCVR_PLL_PDIV ,
FSL_XCVR_PLL_PDIVx ( log2 , 2 ) , 0 ) ;
/* PLL: CTRL_SET: CLKMUX2_EN */
fsl_xcvr_ai_write ( xcvr , FSL_XCVR_PLL_CTRL0_SET ,
FSL_XCVR_PLL_CTRL0_CM2_EN , 0 ) ;
}
if ( xcvr - > mode = = FSL_XCVR_MODE_EARC ) { /* eARC mode */
/* PHY: CTRL_SET: TX_DIFF_OE, PHY_EN */
fsl_xcvr_ai_write ( xcvr , FSL_XCVR_PHY_CTRL_SET ,
FSL_XCVR_PHY_CTRL_TSDIFF_OE |
FSL_XCVR_PHY_CTRL_PHY_EN , 1 ) ;
/* PHY: CTRL2_SET: EARC_TX_MODE */
fsl_xcvr_ai_write ( xcvr , FSL_XCVR_PHY_CTRL2_SET ,
FSL_XCVR_PHY_CTRL2_EARC_TXMS , 1 ) ;
} else if ( ! tx ) { /* SPDIF / ARC RX mode */
if ( xcvr - > mode = = FSL_XCVR_MODE_SPDIF )
/* PHY: CTRL_SET: SPDIF_EN */
fsl_xcvr_ai_write ( xcvr , FSL_XCVR_PHY_CTRL_SET ,
FSL_XCVR_PHY_CTRL_SPDIF_EN , 1 ) ;
else /* PHY: CTRL_SET: ARC RX setup */
fsl_xcvr_ai_write ( xcvr , FSL_XCVR_PHY_CTRL_SET ,
FSL_XCVR_PHY_CTRL_PHY_EN |
FSL_XCVR_PHY_CTRL_RX_CM_EN |
fsl_xcvr_phy_arc_cfg [ xcvr - > arc_mode ] , 1 ) ;
}
dev_dbg ( dev , " PLL Fexp: %u, Fout: %u, mfi: %u, mfn: %u, mfd: %d, div: %u, pdiv0: %u \n " ,
freq , fsl_xcvr_pll_cfg [ i ] . fout , fsl_xcvr_pll_cfg [ i ] . mfi ,
fsl_xcvr_pll_cfg [ i ] . mfn , fsl_xcvr_pll_cfg [ i ] . mfd , div , log2 ) ;
return 0 ;
}
static int fsl_xcvr_en_aud_pll ( struct fsl_xcvr * xcvr , u32 freq )
{
struct device * dev = & xcvr - > pdev - > dev ;
int ret ;
clk_disable_unprepare ( xcvr - > phy_clk ) ;
ret = clk_set_rate ( xcvr - > phy_clk , freq ) ;
if ( ret < 0 ) {
dev_err ( dev , " Error while setting AUD PLL rate: %d \n " , ret ) ;
return ret ;
}
ret = clk_prepare_enable ( xcvr - > phy_clk ) ;
if ( ret ) {
dev_err ( dev , " failed to start PHY clock: %d \n " , ret ) ;
return ret ;
}
/* Release AI interface from reset */
ret = regmap_write ( xcvr - > regmap , FSL_XCVR_PHY_AI_CTRL_SET ,
FSL_XCVR_PHY_AI_CTRL_AI_RESETN ) ;
if ( ret < 0 ) {
dev_err ( dev , " Error while setting IER0: %d \n " , ret ) ;
return ret ;
}
if ( xcvr - > mode = = FSL_XCVR_MODE_EARC ) { /* eARC mode */
/* PHY: CTRL_SET: TX_DIFF_OE, PHY_EN */
fsl_xcvr_ai_write ( xcvr , FSL_XCVR_PHY_CTRL_SET ,
FSL_XCVR_PHY_CTRL_TSDIFF_OE |
FSL_XCVR_PHY_CTRL_PHY_EN , 1 ) ;
/* PHY: CTRL2_SET: EARC_TX_MODE */
fsl_xcvr_ai_write ( xcvr , FSL_XCVR_PHY_CTRL2_SET ,
FSL_XCVR_PHY_CTRL2_EARC_TXMS , 1 ) ;
} else { /* SPDIF mode */
/* PHY: CTRL_SET: TX_CLK_AUD_SS | SPDIF_EN */
fsl_xcvr_ai_write ( xcvr , FSL_XCVR_PHY_CTRL_SET ,
FSL_XCVR_PHY_CTRL_TX_CLK_AUD_SS |
FSL_XCVR_PHY_CTRL_SPDIF_EN , 1 ) ;
}
dev_dbg ( dev , " PLL Fexp: %u \n " , freq ) ;
return 0 ;
}
# define FSL_XCVR_SPDIF_RX_FREQ 175000000
static int fsl_xcvr_prepare ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai )
{
struct fsl_xcvr * xcvr = snd_soc_dai_get_drvdata ( dai ) ;
bool tx = substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ;
u32 m_ctl = 0 , v_ctl = 0 ;
u32 r = substream - > runtime - > rate , ch = substream - > runtime - > channels ;
u32 fout = 32 * r * ch * 10 * 2 ;
int ret = 0 ;
switch ( xcvr - > mode ) {
case FSL_XCVR_MODE_SPDIF :
case FSL_XCVR_MODE_ARC :
if ( tx ) {
ret = fsl_xcvr_en_aud_pll ( xcvr , fout ) ;
if ( ret < 0 ) {
dev_err ( dai - > dev , " Failed to set TX freq %u: %d \n " ,
fout , ret ) ;
return ret ;
}
ret = regmap_write ( xcvr - > regmap , FSL_XCVR_TX_DPTH_CTRL_SET ,
FSL_XCVR_TX_DPTH_CTRL_FRM_FMT ) ;
if ( ret < 0 ) {
dev_err ( dai - > dev , " Failed to set TX_DPTH: %d \n " , ret ) ;
return ret ;
}
/**
* set SPDIF MODE - this flag is used to gate
* SPDIF output , useless for SPDIF RX
*/
m_ctl | = FSL_XCVR_EXT_CTRL_SPDIF_MODE ;
v_ctl | = FSL_XCVR_EXT_CTRL_SPDIF_MODE ;
} else {
/**
* Clear RX FIFO , flip RX FIFO bits ,
* disable eARC related HW mode detects
*/
ret = regmap_write ( xcvr - > regmap , FSL_XCVR_RX_DPTH_CTRL_SET ,
FSL_XCVR_RX_DPTH_CTRL_STORE_FMT |
FSL_XCVR_RX_DPTH_CTRL_CLR_RX_FIFO |
FSL_XCVR_RX_DPTH_CTRL_COMP |
FSL_XCVR_RX_DPTH_CTRL_LAYB_CTRL ) ;
if ( ret < 0 ) {
dev_err ( dai - > dev , " Failed to set RX_DPTH: %d \n " , ret ) ;
return ret ;
}
ret = fsl_xcvr_en_phy_pll ( xcvr , FSL_XCVR_SPDIF_RX_FREQ , tx ) ;
if ( ret < 0 ) {
dev_err ( dai - > dev , " Failed to set RX freq %u: %d \n " ,
FSL_XCVR_SPDIF_RX_FREQ , ret ) ;
return ret ;
}
}
break ;
case FSL_XCVR_MODE_EARC :
if ( ! tx ) {
/** Clear RX FIFO, flip RX FIFO bits */
ret = regmap_write ( xcvr - > regmap , FSL_XCVR_RX_DPTH_CTRL_SET ,
FSL_XCVR_RX_DPTH_CTRL_STORE_FMT |
FSL_XCVR_RX_DPTH_CTRL_CLR_RX_FIFO ) ;
if ( ret < 0 ) {
dev_err ( dai - > dev , " Failed to set RX_DPTH: %d \n " , ret ) ;
return ret ;
}
/** Enable eARC related HW mode detects */
ret = regmap_write ( xcvr - > regmap , FSL_XCVR_RX_DPTH_CTRL_CLR ,
FSL_XCVR_RX_DPTH_CTRL_COMP |
FSL_XCVR_RX_DPTH_CTRL_LAYB_CTRL ) ;
if ( ret < 0 ) {
dev_err ( dai - > dev , " Failed to clr TX_DPTH: %d \n " , ret ) ;
return ret ;
}
}
/* clear CMDC RESET */
m_ctl | = FSL_XCVR_EXT_CTRL_CMDC_RESET ( tx ) ;
/* set TX_RX_MODE */
m_ctl | = FSL_XCVR_EXT_CTRL_TX_RX_MODE ;
v_ctl | = ( tx ? FSL_XCVR_EXT_CTRL_TX_RX_MODE : 0 ) ;
break ;
}
ret = regmap_update_bits ( xcvr - > regmap , FSL_XCVR_EXT_IER0 ,
FSL_XCVR_IRQ_EARC_ALL , FSL_XCVR_IRQ_EARC_ALL ) ;
if ( ret < 0 ) {
dev_err ( dai - > dev , " Error while setting IER0: %d \n " , ret ) ;
return ret ;
}
2021-09-10 17:18:30 +08:00
/* set DPATH RESET */
2020-10-13 15:17:32 +03:00
m_ctl | = FSL_XCVR_EXT_CTRL_DPTH_RESET ( tx ) ;
2021-09-10 17:18:30 +08:00
v_ctl | = FSL_XCVR_EXT_CTRL_DPTH_RESET ( tx ) ;
2020-10-13 15:17:32 +03:00
ret = regmap_update_bits ( xcvr - > regmap , FSL_XCVR_EXT_CTRL , m_ctl , v_ctl ) ;
if ( ret < 0 ) {
dev_err ( dai - > dev , " Error while setting EXT_CTRL: %d \n " , ret ) ;
return ret ;
}
return 0 ;
}
static int fsl_xcvr_constr ( const struct snd_pcm_substream * substream ,
const struct snd_pcm_hw_constraint_list * channels ,
const struct snd_pcm_hw_constraint_list * rates )
{
struct snd_pcm_runtime * rt = substream - > runtime ;
int ret ;
ret = snd_pcm_hw_constraint_list ( rt , 0 , SNDRV_PCM_HW_PARAM_CHANNELS ,
channels ) ;
if ( ret < 0 )
return ret ;
ret = snd_pcm_hw_constraint_list ( rt , 0 , SNDRV_PCM_HW_PARAM_RATE ,
rates ) ;
if ( ret < 0 )
return ret ;
return 0 ;
}
static int fsl_xcvr_startup ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai )
{
struct fsl_xcvr * xcvr = snd_soc_dai_get_drvdata ( dai ) ;
bool tx = substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ;
int ret = 0 ;
if ( xcvr - > streams & BIT ( substream - > stream ) ) {
dev_err ( dai - > dev , " %sX busy \n " , tx ? " T " : " R " ) ;
return - EBUSY ;
}
switch ( xcvr - > mode ) {
case FSL_XCVR_MODE_SPDIF :
case FSL_XCVR_MODE_ARC :
ret = fsl_xcvr_constr ( substream , & fsl_xcvr_spdif_channels_constr ,
& fsl_xcvr_spdif_rates_constr ) ;
break ;
case FSL_XCVR_MODE_EARC :
ret = fsl_xcvr_constr ( substream , & fsl_xcvr_earc_channels_constr ,
& fsl_xcvr_earc_rates_constr ) ;
break ;
}
if ( ret < 0 )
return ret ;
xcvr - > streams | = BIT ( substream - > stream ) ;
/* Disable XCVR controls if there is stream started */
fsl_xcvr_activate_ctl ( dai , fsl_xcvr_mode_kctl . name , false ) ;
fsl_xcvr_activate_ctl ( dai , fsl_xcvr_arc_mode_kctl . name , false ) ;
fsl_xcvr_activate_ctl ( dai , fsl_xcvr_earc_capds_kctl . name , false ) ;
return 0 ;
}
static void fsl_xcvr_shutdown ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai )
{
struct fsl_xcvr * xcvr = snd_soc_dai_get_drvdata ( dai ) ;
bool tx = substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ;
u32 mask = 0 , val = 0 ;
int ret ;
xcvr - > streams & = ~ BIT ( substream - > stream ) ;
/* Enable XCVR controls if there is no stream started */
if ( ! xcvr - > streams ) {
fsl_xcvr_activate_ctl ( dai , fsl_xcvr_mode_kctl . name , true ) ;
fsl_xcvr_activate_ctl ( dai , fsl_xcvr_arc_mode_kctl . name ,
( xcvr - > mode = = FSL_XCVR_MODE_ARC ) ) ;
fsl_xcvr_activate_ctl ( dai , fsl_xcvr_earc_capds_kctl . name ,
( xcvr - > mode = = FSL_XCVR_MODE_EARC ) ) ;
ret = regmap_update_bits ( xcvr - > regmap , FSL_XCVR_EXT_IER0 ,
FSL_XCVR_IRQ_EARC_ALL , 0 ) ;
if ( ret < 0 ) {
dev_err ( dai - > dev , " Failed to set IER0: %d \n " , ret ) ;
return ;
}
/* clear SPDIF MODE */
if ( xcvr - > mode = = FSL_XCVR_MODE_SPDIF )
mask | = FSL_XCVR_EXT_CTRL_SPDIF_MODE ;
}
if ( xcvr - > mode = = FSL_XCVR_MODE_EARC ) {
/* set CMDC RESET */
mask | = FSL_XCVR_EXT_CTRL_CMDC_RESET ( tx ) ;
val | = FSL_XCVR_EXT_CTRL_CMDC_RESET ( tx ) ;
}
ret = regmap_update_bits ( xcvr - > regmap , FSL_XCVR_EXT_CTRL , mask , val ) ;
if ( ret < 0 ) {
dev_err ( dai - > dev , " Err setting DPATH RESET: %d \n " , ret ) ;
return ;
}
}
static int fsl_xcvr_trigger ( struct snd_pcm_substream * substream , int cmd ,
struct snd_soc_dai * dai )
{
struct fsl_xcvr * xcvr = snd_soc_dai_get_drvdata ( dai ) ;
bool tx = substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ;
int ret ;
switch ( cmd ) {
case SNDRV_PCM_TRIGGER_START :
case SNDRV_PCM_TRIGGER_RESUME :
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE :
if ( tx ) {
switch ( xcvr - > mode ) {
case FSL_XCVR_MODE_EARC :
/* set isr_cmdc_tx_en, w1c */
ret = regmap_write ( xcvr - > regmap ,
FSL_XCVR_ISR_SET ,
FSL_XCVR_ISR_CMDC_TX_EN ) ;
if ( ret < 0 ) {
dev_err ( dai - > dev , " err updating isr %d \n " , ret ) ;
return ret ;
}
fallthrough ;
case FSL_XCVR_MODE_SPDIF :
ret = regmap_write ( xcvr - > regmap ,
FSL_XCVR_TX_DPTH_CTRL_SET ,
FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX ) ;
if ( ret < 0 ) {
dev_err ( dai - > dev , " Failed to start DATA_TX: %d \n " , ret ) ;
return ret ;
}
break ;
}
}
/* enable DMA RD/WR */
ret = regmap_update_bits ( xcvr - > regmap , FSL_XCVR_EXT_CTRL ,
FSL_XCVR_EXT_CTRL_DMA_DIS ( tx ) , 0 ) ;
if ( ret < 0 ) {
dev_err ( dai - > dev , " Failed to enable DMA: %d \n " , ret ) ;
return ret ;
}
2021-09-10 17:18:30 +08:00
/* clear DPATH RESET */
ret = regmap_update_bits ( xcvr - > regmap , FSL_XCVR_EXT_CTRL ,
FSL_XCVR_EXT_CTRL_DPTH_RESET ( tx ) ,
0 ) ;
if ( ret < 0 ) {
dev_err ( dai - > dev , " Failed to clear DPATH RESET: %d \n " , ret ) ;
return ret ;
}
2020-10-13 15:17:32 +03:00
break ;
case SNDRV_PCM_TRIGGER_STOP :
case SNDRV_PCM_TRIGGER_SUSPEND :
case SNDRV_PCM_TRIGGER_PAUSE_PUSH :
/* disable DMA RD/WR */
ret = regmap_update_bits ( xcvr - > regmap , FSL_XCVR_EXT_CTRL ,
FSL_XCVR_EXT_CTRL_DMA_DIS ( tx ) ,
FSL_XCVR_EXT_CTRL_DMA_DIS ( tx ) ) ;
if ( ret < 0 ) {
dev_err ( dai - > dev , " Failed to disable DMA: %d \n " , ret ) ;
return ret ;
}
if ( tx ) {
switch ( xcvr - > mode ) {
case FSL_XCVR_MODE_SPDIF :
ret = regmap_write ( xcvr - > regmap ,
FSL_XCVR_TX_DPTH_CTRL_CLR ,
FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX ) ;
if ( ret < 0 ) {
dev_err ( dai - > dev , " Failed to stop DATA_TX: %d \n " , ret ) ;
return ret ;
}
fallthrough ;
case FSL_XCVR_MODE_EARC :
/* clear ISR_CMDC_TX_EN, W1C */
ret = regmap_write ( xcvr - > regmap ,
FSL_XCVR_ISR_CLR ,
FSL_XCVR_ISR_CMDC_TX_EN ) ;
if ( ret < 0 ) {
dev_err ( dai - > dev ,
" Err updating ISR %d \n " , ret ) ;
return ret ;
}
break ;
}
}
break ;
default :
return - EINVAL ;
}
return 0 ;
}
static int fsl_xcvr_load_firmware ( struct fsl_xcvr * xcvr )
{
struct device * dev = & xcvr - > pdev - > dev ;
const struct firmware * fw ;
int ret = 0 , rem , off , out , page = 0 , size = FSL_XCVR_REG_OFFSET ;
u32 mask , val ;
ret = request_firmware ( & fw , xcvr - > soc_data - > fw_name , dev ) ;
if ( ret ) {
dev_err ( dev , " failed to request firmware. \n " ) ;
return ret ;
}
rem = fw - > size ;
/* RAM is 20KiB = 16KiB code + 4KiB data => max 10 pages 2KiB each */
if ( rem > 16384 ) {
dev_err ( dev , " FW size %d is bigger than 16KiB. \n " , rem ) ;
2020-11-24 16:19:57 +02:00
release_firmware ( fw ) ;
2020-10-13 15:17:32 +03:00
return - ENOMEM ;
}
for ( page = 0 ; page < 10 ; page + + ) {
ret = regmap_update_bits ( xcvr - > regmap , FSL_XCVR_EXT_CTRL ,
FSL_XCVR_EXT_CTRL_PAGE_MASK ,
FSL_XCVR_EXT_CTRL_PAGE ( page ) ) ;
if ( ret < 0 ) {
dev_err ( dev , " FW: failed to set page %d, err=%d \n " ,
page , ret ) ;
goto err_firmware ;
}
off = page * size ;
out = min ( rem , size ) ;
/* IPG clock is assumed to be running, otherwise it will hang */
if ( out > 0 ) {
/* write firmware into code memory */
memcpy_toio ( xcvr - > ram_addr , fw - > data + off , out ) ;
rem - = out ;
if ( rem = = 0 ) {
/* last part of firmware written */
/* clean remaining part of code memory page */
memset_io ( xcvr - > ram_addr + out , 0 , size - out ) ;
}
} else {
/* clean current page, including data memory */
memset_io ( xcvr - > ram_addr , 0 , size ) ;
}
2021-05-06 10:24:52 +08:00
}
2020-10-13 15:17:32 +03:00
err_firmware :
release_firmware ( fw ) ;
if ( ret < 0 )
return ret ;
/* configure watermarks */
mask = FSL_XCVR_EXT_CTRL_RX_FWM_MASK | FSL_XCVR_EXT_CTRL_TX_FWM_MASK ;
val = FSL_XCVR_EXT_CTRL_RX_FWM ( FSL_XCVR_FIFO_WMK_RX ) ;
val | = FSL_XCVR_EXT_CTRL_TX_FWM ( FSL_XCVR_FIFO_WMK_TX ) ;
/* disable DMA RD/WR */
mask | = FSL_XCVR_EXT_CTRL_DMA_RD_DIS | FSL_XCVR_EXT_CTRL_DMA_WR_DIS ;
val | = FSL_XCVR_EXT_CTRL_DMA_RD_DIS | FSL_XCVR_EXT_CTRL_DMA_WR_DIS ;
/* Data RAM is 4KiB, last two pages: 8 and 9. Select page 8. */
mask | = FSL_XCVR_EXT_CTRL_PAGE_MASK ;
val | = FSL_XCVR_EXT_CTRL_PAGE ( 8 ) ;
ret = regmap_update_bits ( xcvr - > regmap , FSL_XCVR_EXT_CTRL , mask , val ) ;
if ( ret < 0 ) {
dev_err ( dev , " Failed to set watermarks: %d \n " , ret ) ;
return ret ;
}
/* Store Capabilities Data Structure into Data RAM */
memcpy_toio ( xcvr - > ram_addr + FSL_XCVR_CAP_DATA_STR , xcvr - > cap_ds ,
FSL_XCVR_CAPDS_SIZE ) ;
return 0 ;
}
static int fsl_xcvr_type_iec958_info ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_info * uinfo )
{
uinfo - > type = SNDRV_CTL_ELEM_TYPE_IEC958 ;
uinfo - > count = 1 ;
return 0 ;
}
static int fsl_xcvr_type_iec958_bytes_info ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_info * uinfo )
{
uinfo - > type = SNDRV_CTL_ELEM_TYPE_BYTES ;
uinfo - > count = sizeof_field ( struct snd_aes_iec958 , status ) ;
return 0 ;
}
static int fsl_xcvr_rx_cs_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct snd_soc_dai * dai = snd_kcontrol_chip ( kcontrol ) ;
struct fsl_xcvr * xcvr = snd_soc_dai_get_drvdata ( dai ) ;
memcpy ( ucontrol - > value . iec958 . status , xcvr - > rx_iec958 . status , 24 ) ;
return 0 ;
}
static int fsl_xcvr_tx_cs_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct snd_soc_dai * dai = snd_kcontrol_chip ( kcontrol ) ;
struct fsl_xcvr * xcvr = snd_soc_dai_get_drvdata ( dai ) ;
memcpy ( ucontrol - > value . iec958 . status , xcvr - > tx_iec958 . status , 24 ) ;
return 0 ;
}
static int fsl_xcvr_tx_cs_put ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct snd_soc_dai * dai = snd_kcontrol_chip ( kcontrol ) ;
struct fsl_xcvr * xcvr = snd_soc_dai_get_drvdata ( dai ) ;
memcpy ( xcvr - > tx_iec958 . status , ucontrol - > value . iec958 . status , 24 ) ;
return 0 ;
}
static struct snd_kcontrol_new fsl_xcvr_rx_ctls [ ] = {
/* Channel status controller */
{
. iface = SNDRV_CTL_ELEM_IFACE_PCM ,
. name = SNDRV_CTL_NAME_IEC958 ( " " , CAPTURE , DEFAULT ) ,
. access = SNDRV_CTL_ELEM_ACCESS_READ ,
. info = fsl_xcvr_type_iec958_info ,
. get = fsl_xcvr_rx_cs_get ,
} ,
/* Capture channel status, bytes */
{
. iface = SNDRV_CTL_ELEM_IFACE_PCM ,
. name = " Capture Channel Status " ,
. access = SNDRV_CTL_ELEM_ACCESS_READ ,
. info = fsl_xcvr_type_iec958_bytes_info ,
. get = fsl_xcvr_rx_cs_get ,
} ,
} ;
static struct snd_kcontrol_new fsl_xcvr_tx_ctls [ ] = {
/* Channel status controller */
{
. iface = SNDRV_CTL_ELEM_IFACE_PCM ,
. name = SNDRV_CTL_NAME_IEC958 ( " " , PLAYBACK , DEFAULT ) ,
. access = SNDRV_CTL_ELEM_ACCESS_READWRITE ,
. info = fsl_xcvr_type_iec958_info ,
. get = fsl_xcvr_tx_cs_get ,
. put = fsl_xcvr_tx_cs_put ,
} ,
/* Playback channel status, bytes */
{
. iface = SNDRV_CTL_ELEM_IFACE_PCM ,
. name = " Playback Channel Status " ,
. access = SNDRV_CTL_ELEM_ACCESS_READWRITE ,
. info = fsl_xcvr_type_iec958_bytes_info ,
. get = fsl_xcvr_tx_cs_get ,
. put = fsl_xcvr_tx_cs_put ,
} ,
} ;
2021-02-06 23:58:49 +01:00
static const struct snd_soc_dai_ops fsl_xcvr_dai_ops = {
2020-10-13 15:17:32 +03:00
. prepare = fsl_xcvr_prepare ,
. startup = fsl_xcvr_startup ,
. shutdown = fsl_xcvr_shutdown ,
. trigger = fsl_xcvr_trigger ,
} ;
static int fsl_xcvr_dai_probe ( struct snd_soc_dai * dai )
{
struct fsl_xcvr * xcvr = snd_soc_dai_get_drvdata ( dai ) ;
snd_soc_dai_init_dma_data ( dai , & xcvr - > dma_prms_tx , & xcvr - > dma_prms_rx ) ;
snd_soc_add_dai_controls ( dai , & fsl_xcvr_mode_kctl , 1 ) ;
snd_soc_add_dai_controls ( dai , & fsl_xcvr_arc_mode_kctl , 1 ) ;
snd_soc_add_dai_controls ( dai , & fsl_xcvr_earc_capds_kctl , 1 ) ;
snd_soc_add_dai_controls ( dai , fsl_xcvr_tx_ctls ,
ARRAY_SIZE ( fsl_xcvr_tx_ctls ) ) ;
snd_soc_add_dai_controls ( dai , fsl_xcvr_rx_ctls ,
ARRAY_SIZE ( fsl_xcvr_rx_ctls ) ) ;
return 0 ;
}
static struct snd_soc_dai_driver fsl_xcvr_dai = {
. probe = fsl_xcvr_dai_probe ,
. ops = & fsl_xcvr_dai_ops ,
. playback = {
. stream_name = " CPU-Playback " ,
. channels_min = 1 ,
. channels_max = 32 ,
. rate_min = 32000 ,
. rate_max = 1536000 ,
. rates = SNDRV_PCM_RATE_KNOT ,
. formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE ,
} ,
. capture = {
. stream_name = " CPU-Capture " ,
. channels_min = 1 ,
. channels_max = 32 ,
. rate_min = 32000 ,
. rate_max = 1536000 ,
. rates = SNDRV_PCM_RATE_KNOT ,
. formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE ,
} ,
} ;
static const struct snd_soc_component_driver fsl_xcvr_comp = {
. name = " fsl-xcvr-dai " ,
} ;
static const struct reg_default fsl_xcvr_reg_defaults [ ] = {
{ FSL_XCVR_VERSION , 0x00000000 } ,
{ FSL_XCVR_EXT_CTRL , 0xF8204040 } ,
{ FSL_XCVR_EXT_STATUS , 0x00000000 } ,
{ FSL_XCVR_EXT_IER0 , 0x00000000 } ,
{ FSL_XCVR_EXT_IER1 , 0x00000000 } ,
{ FSL_XCVR_EXT_ISR , 0x00000000 } ,
{ FSL_XCVR_EXT_ISR_SET , 0x00000000 } ,
{ FSL_XCVR_EXT_ISR_CLR , 0x00000000 } ,
{ FSL_XCVR_EXT_ISR_TOG , 0x00000000 } ,
{ FSL_XCVR_IER , 0x00000000 } ,
{ FSL_XCVR_ISR , 0x00000000 } ,
{ FSL_XCVR_ISR_SET , 0x00000000 } ,
{ FSL_XCVR_ISR_CLR , 0x00000000 } ,
{ FSL_XCVR_ISR_TOG , 0x00000000 } ,
{ FSL_XCVR_RX_DPTH_CTRL , 0x00002C89 } ,
{ FSL_XCVR_RX_DPTH_CTRL_SET , 0x00002C89 } ,
{ FSL_XCVR_RX_DPTH_CTRL_CLR , 0x00002C89 } ,
{ FSL_XCVR_RX_DPTH_CTRL_TOG , 0x00002C89 } ,
{ FSL_XCVR_TX_DPTH_CTRL , 0x00000000 } ,
{ FSL_XCVR_TX_DPTH_CTRL_SET , 0x00000000 } ,
{ FSL_XCVR_TX_DPTH_CTRL_CLR , 0x00000000 } ,
{ FSL_XCVR_TX_DPTH_CTRL_TOG , 0x00000000 } ,
{ FSL_XCVR_TX_CS_DATA_0 , 0x00000000 } ,
{ FSL_XCVR_TX_CS_DATA_1 , 0x00000000 } ,
{ FSL_XCVR_TX_CS_DATA_2 , 0x00000000 } ,
{ FSL_XCVR_TX_CS_DATA_3 , 0x00000000 } ,
{ FSL_XCVR_TX_CS_DATA_4 , 0x00000000 } ,
{ FSL_XCVR_TX_CS_DATA_5 , 0x00000000 } ,
{ FSL_XCVR_DEBUG_REG_0 , 0x00000000 } ,
{ FSL_XCVR_DEBUG_REG_1 , 0x00000000 } ,
} ;
static bool fsl_xcvr_readable_reg ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
case FSL_XCVR_VERSION :
case FSL_XCVR_EXT_CTRL :
case FSL_XCVR_EXT_STATUS :
case FSL_XCVR_EXT_IER0 :
case FSL_XCVR_EXT_IER1 :
case FSL_XCVR_EXT_ISR :
case FSL_XCVR_EXT_ISR_SET :
case FSL_XCVR_EXT_ISR_CLR :
case FSL_XCVR_EXT_ISR_TOG :
case FSL_XCVR_IER :
case FSL_XCVR_ISR :
case FSL_XCVR_ISR_SET :
case FSL_XCVR_ISR_CLR :
case FSL_XCVR_ISR_TOG :
case FSL_XCVR_PHY_AI_CTRL :
case FSL_XCVR_PHY_AI_CTRL_SET :
case FSL_XCVR_PHY_AI_CTRL_CLR :
case FSL_XCVR_PHY_AI_CTRL_TOG :
case FSL_XCVR_PHY_AI_RDATA :
case FSL_XCVR_CLK_CTRL :
case FSL_XCVR_RX_DPTH_CTRL :
case FSL_XCVR_RX_DPTH_CTRL_SET :
case FSL_XCVR_RX_DPTH_CTRL_CLR :
case FSL_XCVR_RX_DPTH_CTRL_TOG :
case FSL_XCVR_TX_DPTH_CTRL :
case FSL_XCVR_TX_DPTH_CTRL_SET :
case FSL_XCVR_TX_DPTH_CTRL_CLR :
case FSL_XCVR_TX_DPTH_CTRL_TOG :
case FSL_XCVR_TX_CS_DATA_0 :
case FSL_XCVR_TX_CS_DATA_1 :
case FSL_XCVR_TX_CS_DATA_2 :
case FSL_XCVR_TX_CS_DATA_3 :
case FSL_XCVR_TX_CS_DATA_4 :
case FSL_XCVR_TX_CS_DATA_5 :
case FSL_XCVR_DEBUG_REG_0 :
case FSL_XCVR_DEBUG_REG_1 :
return true ;
default :
return false ;
}
}
static bool fsl_xcvr_writeable_reg ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
case FSL_XCVR_EXT_CTRL :
case FSL_XCVR_EXT_IER0 :
case FSL_XCVR_EXT_IER1 :
case FSL_XCVR_EXT_ISR :
case FSL_XCVR_EXT_ISR_SET :
case FSL_XCVR_EXT_ISR_CLR :
case FSL_XCVR_EXT_ISR_TOG :
case FSL_XCVR_IER :
case FSL_XCVR_ISR_SET :
case FSL_XCVR_ISR_CLR :
case FSL_XCVR_ISR_TOG :
case FSL_XCVR_PHY_AI_CTRL :
case FSL_XCVR_PHY_AI_CTRL_SET :
case FSL_XCVR_PHY_AI_CTRL_CLR :
case FSL_XCVR_PHY_AI_CTRL_TOG :
case FSL_XCVR_PHY_AI_WDATA :
case FSL_XCVR_CLK_CTRL :
case FSL_XCVR_RX_DPTH_CTRL :
case FSL_XCVR_RX_DPTH_CTRL_SET :
case FSL_XCVR_RX_DPTH_CTRL_CLR :
case FSL_XCVR_RX_DPTH_CTRL_TOG :
case FSL_XCVR_TX_DPTH_CTRL_SET :
case FSL_XCVR_TX_DPTH_CTRL_CLR :
case FSL_XCVR_TX_DPTH_CTRL_TOG :
case FSL_XCVR_TX_CS_DATA_0 :
case FSL_XCVR_TX_CS_DATA_1 :
case FSL_XCVR_TX_CS_DATA_2 :
case FSL_XCVR_TX_CS_DATA_3 :
case FSL_XCVR_TX_CS_DATA_4 :
case FSL_XCVR_TX_CS_DATA_5 :
return true ;
default :
return false ;
}
}
static bool fsl_xcvr_volatile_reg ( struct device * dev , unsigned int reg )
{
return fsl_xcvr_readable_reg ( dev , reg ) ;
}
static const struct regmap_config fsl_xcvr_regmap_cfg = {
. reg_bits = 32 ,
. reg_stride = 4 ,
. val_bits = 32 ,
. max_register = FSL_XCVR_MAX_REG ,
. reg_defaults = fsl_xcvr_reg_defaults ,
. num_reg_defaults = ARRAY_SIZE ( fsl_xcvr_reg_defaults ) ,
. readable_reg = fsl_xcvr_readable_reg ,
. volatile_reg = fsl_xcvr_volatile_reg ,
. writeable_reg = fsl_xcvr_writeable_reg ,
. cache_type = REGCACHE_FLAT ,
} ;
static irqreturn_t irq0_isr ( int irq , void * devid )
{
struct fsl_xcvr * xcvr = ( struct fsl_xcvr * ) devid ;
struct device * dev = & xcvr - > pdev - > dev ;
struct regmap * regmap = xcvr - > regmap ;
void __iomem * reg_ctrl , * reg_buff ;
u32 isr , isr_clr = 0 , val , i ;
regmap_read ( regmap , FSL_XCVR_EXT_ISR , & isr ) ;
if ( isr & FSL_XCVR_IRQ_NEW_CS ) {
dev_dbg ( dev , " Received new CS block \n " ) ;
isr_clr | = FSL_XCVR_IRQ_NEW_CS ;
/* Data RAM is 4KiB, last two pages: 8 and 9. Select page 8. */
regmap_update_bits ( xcvr - > regmap , FSL_XCVR_EXT_CTRL ,
FSL_XCVR_EXT_CTRL_PAGE_MASK ,
FSL_XCVR_EXT_CTRL_PAGE ( 8 ) ) ;
/* Find updated CS buffer */
reg_ctrl = xcvr - > ram_addr + FSL_XCVR_RX_CS_CTRL_0 ;
reg_buff = xcvr - > ram_addr + FSL_XCVR_RX_CS_BUFF_0 ;
memcpy_fromio ( & val , reg_ctrl , sizeof ( val ) ) ;
if ( ! val ) {
reg_ctrl = xcvr - > ram_addr + FSL_XCVR_RX_CS_CTRL_1 ;
reg_buff = xcvr - > ram_addr + FSL_XCVR_RX_CS_BUFF_1 ;
memcpy_fromio ( & val , reg_ctrl , sizeof ( val ) ) ;
}
if ( val ) {
/* copy CS buffer */
memcpy_fromio ( & xcvr - > rx_iec958 . status , reg_buff ,
sizeof ( xcvr - > rx_iec958 . status ) ) ;
for ( i = 0 ; i < 6 ; i + + ) {
val = * ( u32 * ) ( xcvr - > rx_iec958 . status + i * 4 ) ;
* ( u32 * ) ( xcvr - > rx_iec958 . status + i * 4 ) =
bitrev32 ( val ) ;
}
/* clear CS control register */
memset_io ( reg_ctrl , 0 , sizeof ( val ) ) ;
}
}
if ( isr & FSL_XCVR_IRQ_NEW_UD ) {
dev_dbg ( dev , " Received new UD block \n " ) ;
isr_clr | = FSL_XCVR_IRQ_NEW_UD ;
}
if ( isr & FSL_XCVR_IRQ_MUTE ) {
dev_dbg ( dev , " HW mute bit detected \n " ) ;
isr_clr | = FSL_XCVR_IRQ_MUTE ;
}
if ( isr & FSL_XCVR_IRQ_FIFO_UOFL_ERR ) {
dev_dbg ( dev , " RX/TX FIFO full/empty \n " ) ;
isr_clr | = FSL_XCVR_IRQ_FIFO_UOFL_ERR ;
}
if ( isr & FSL_XCVR_IRQ_ARC_MODE ) {
dev_dbg ( dev , " CMDC SM falls out of eARC mode \n " ) ;
isr_clr | = FSL_XCVR_IRQ_ARC_MODE ;
}
if ( isr & FSL_XCVR_IRQ_DMA_RD_REQ ) {
dev_dbg ( dev , " DMA read request \n " ) ;
isr_clr | = FSL_XCVR_IRQ_DMA_RD_REQ ;
}
if ( isr & FSL_XCVR_IRQ_DMA_WR_REQ ) {
dev_dbg ( dev , " DMA write request \n " ) ;
isr_clr | = FSL_XCVR_IRQ_DMA_WR_REQ ;
}
if ( isr_clr ) {
regmap_write ( regmap , FSL_XCVR_EXT_ISR_CLR , isr_clr ) ;
return IRQ_HANDLED ;
}
return IRQ_NONE ;
}
static const struct fsl_xcvr_soc_data fsl_xcvr_imx8mp_data = {
. fw_name = " imx/xcvr/xcvr-imx8mp.bin " ,
} ;
static const struct of_device_id fsl_xcvr_dt_ids [ ] = {
{ . compatible = " fsl,imx8mp-xcvr " , . data = & fsl_xcvr_imx8mp_data } ,
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , fsl_xcvr_dt_ids ) ;
static int fsl_xcvr_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct fsl_xcvr * xcvr ;
2021-03-02 20:50:02 +08:00
struct resource * rx_res , * tx_res ;
2020-10-13 15:17:32 +03:00
void __iomem * regs ;
int ret , irq ;
xcvr = devm_kzalloc ( dev , sizeof ( * xcvr ) , GFP_KERNEL ) ;
if ( ! xcvr )
return - ENOMEM ;
xcvr - > pdev = pdev ;
xcvr - > soc_data = of_device_get_match_data ( & pdev - > dev ) ;
xcvr - > ipg_clk = devm_clk_get ( dev , " ipg " ) ;
if ( IS_ERR ( xcvr - > ipg_clk ) ) {
dev_err ( dev , " failed to get ipg clock \n " ) ;
return PTR_ERR ( xcvr - > ipg_clk ) ;
}
xcvr - > phy_clk = devm_clk_get ( dev , " phy " ) ;
if ( IS_ERR ( xcvr - > phy_clk ) ) {
dev_err ( dev , " failed to get phy clock \n " ) ;
return PTR_ERR ( xcvr - > phy_clk ) ;
}
xcvr - > spba_clk = devm_clk_get ( dev , " spba " ) ;
if ( IS_ERR ( xcvr - > spba_clk ) ) {
dev_err ( dev , " failed to get spba clock \n " ) ;
return PTR_ERR ( xcvr - > spba_clk ) ;
}
xcvr - > pll_ipg_clk = devm_clk_get ( dev , " pll_ipg " ) ;
if ( IS_ERR ( xcvr - > pll_ipg_clk ) ) {
dev_err ( dev , " failed to get pll_ipg clock \n " ) ;
return PTR_ERR ( xcvr - > pll_ipg_clk ) ;
}
2021-03-02 20:50:02 +08:00
xcvr - > ram_addr = devm_platform_ioremap_resource_byname ( pdev , " ram " ) ;
2020-10-13 15:17:32 +03:00
if ( IS_ERR ( xcvr - > ram_addr ) )
return PTR_ERR ( xcvr - > ram_addr ) ;
2021-03-02 20:50:02 +08:00
regs = devm_platform_ioremap_resource_byname ( pdev , " regs " ) ;
2020-10-13 15:17:32 +03:00
if ( IS_ERR ( regs ) )
return PTR_ERR ( regs ) ;
xcvr - > regmap = devm_regmap_init_mmio_clk ( dev , NULL , regs ,
& fsl_xcvr_regmap_cfg ) ;
if ( IS_ERR ( xcvr - > regmap ) ) {
dev_err ( dev , " failed to init XCVR regmap: %ld \n " ,
PTR_ERR ( xcvr - > regmap ) ) ;
return PTR_ERR ( xcvr - > regmap ) ;
}
xcvr - > reset = devm_reset_control_get_exclusive ( dev , NULL ) ;
if ( IS_ERR ( xcvr - > reset ) ) {
dev_err ( dev , " failed to get XCVR reset control \n " ) ;
return PTR_ERR ( xcvr - > reset ) ;
}
/* get IRQs */
irq = platform_get_irq ( pdev , 0 ) ;
2021-06-24 18:45:05 +08:00
if ( irq < 0 )
2020-10-13 15:17:32 +03:00
return irq ;
ret = devm_request_irq ( dev , irq , irq0_isr , 0 , pdev - > name , xcvr ) ;
if ( ret ) {
dev_err ( dev , " failed to claim IRQ0: %i \n " , ret ) ;
return ret ;
}
rx_res = platform_get_resource_byname ( pdev , IORESOURCE_MEM , " rxfifo " ) ;
tx_res = platform_get_resource_byname ( pdev , IORESOURCE_MEM , " txfifo " ) ;
2021-06-15 09:39:22 +08:00
if ( ! rx_res | | ! tx_res ) {
dev_err ( dev , " could not find rxfifo or txfifo resource \n " ) ;
return - EINVAL ;
}
2020-10-13 15:17:32 +03:00
xcvr - > dma_prms_rx . chan_name = " rx " ;
xcvr - > dma_prms_tx . chan_name = " tx " ;
xcvr - > dma_prms_rx . addr = rx_res - > start ;
xcvr - > dma_prms_tx . addr = tx_res - > start ;
xcvr - > dma_prms_rx . maxburst = FSL_XCVR_MAXBURST_RX ;
xcvr - > dma_prms_tx . maxburst = FSL_XCVR_MAXBURST_TX ;
platform_set_drvdata ( pdev , xcvr ) ;
pm_runtime_enable ( dev ) ;
regcache_cache_only ( xcvr - > regmap , true ) ;
2021-09-03 18:30:06 +08:00
/*
* Register platform component before registering cpu dai for there
* is not defer probe for platform component in snd_soc_add_pcm_runtime ( ) .
*/
ret = devm_snd_dmaengine_pcm_register ( dev , NULL , 0 ) ;
if ( ret ) {
dev_err ( dev , " failed to pcm register \n " ) ;
return ret ;
}
2020-10-13 15:17:32 +03:00
ret = devm_snd_soc_register_component ( dev , & fsl_xcvr_comp ,
& fsl_xcvr_dai , 1 ) ;
if ( ret ) {
dev_err ( dev , " failed to register component %s \n " ,
fsl_xcvr_comp . name ) ;
}
return ret ;
}
static __maybe_unused int fsl_xcvr_runtime_suspend ( struct device * dev )
{
struct fsl_xcvr * xcvr = dev_get_drvdata ( dev ) ;
int ret ;
2021-06-18 20:38:33 +08:00
/*
* Clear interrupts , when streams starts or resumes after
* suspend , interrupts are enabled in prepare ( ) , so no need
* to enable interrupts in resume ( ) .
*/
ret = regmap_update_bits ( xcvr - > regmap , FSL_XCVR_EXT_IER0 ,
FSL_XCVR_IRQ_EARC_ALL , 0 ) ;
if ( ret < 0 )
dev_err ( dev , " Failed to clear IER0: %d \n " , ret ) ;
2020-10-13 15:17:32 +03:00
/* Assert M0+ reset */
ret = regmap_update_bits ( xcvr - > regmap , FSL_XCVR_EXT_CTRL ,
FSL_XCVR_EXT_CTRL_CORE_RESET ,
FSL_XCVR_EXT_CTRL_CORE_RESET ) ;
if ( ret < 0 )
dev_err ( dev , " Failed to assert M0+ core: %d \n " , ret ) ;
regcache_cache_only ( xcvr - > regmap , true ) ;
clk_disable_unprepare ( xcvr - > spba_clk ) ;
clk_disable_unprepare ( xcvr - > phy_clk ) ;
clk_disable_unprepare ( xcvr - > pll_ipg_clk ) ;
clk_disable_unprepare ( xcvr - > ipg_clk ) ;
return 0 ;
}
static __maybe_unused int fsl_xcvr_runtime_resume ( struct device * dev )
{
struct fsl_xcvr * xcvr = dev_get_drvdata ( dev ) ;
int ret ;
2021-02-22 17:09:50 +08:00
ret = reset_control_assert ( xcvr - > reset ) ;
if ( ret < 0 ) {
dev_err ( dev , " Failed to assert M0+ reset: %d \n " , ret ) ;
return ret ;
}
2020-10-13 15:17:32 +03:00
ret = clk_prepare_enable ( xcvr - > ipg_clk ) ;
if ( ret ) {
dev_err ( dev , " failed to start IPG clock. \n " ) ;
return ret ;
}
ret = clk_prepare_enable ( xcvr - > pll_ipg_clk ) ;
if ( ret ) {
dev_err ( dev , " failed to start PLL IPG clock. \n " ) ;
goto stop_ipg_clk ;
}
ret = clk_prepare_enable ( xcvr - > phy_clk ) ;
if ( ret ) {
dev_err ( dev , " failed to start PHY clock: %d \n " , ret ) ;
goto stop_pll_ipg_clk ;
}
ret = clk_prepare_enable ( xcvr - > spba_clk ) ;
if ( ret ) {
dev_err ( dev , " failed to start SPBA clock. \n " ) ;
goto stop_phy_clk ;
}
regcache_cache_only ( xcvr - > regmap , false ) ;
regcache_mark_dirty ( xcvr - > regmap ) ;
ret = regcache_sync ( xcvr - > regmap ) ;
if ( ret ) {
dev_err ( dev , " failed to sync regcache. \n " ) ;
goto stop_spba_clk ;
}
ret = reset_control_deassert ( xcvr - > reset ) ;
if ( ret ) {
dev_err ( dev , " failed to deassert M0+ reset. \n " ) ;
goto stop_spba_clk ;
}
ret = fsl_xcvr_load_firmware ( xcvr ) ;
if ( ret ) {
dev_err ( dev , " failed to load firmware. \n " ) ;
goto stop_spba_clk ;
}
/* Release M0+ reset */
ret = regmap_update_bits ( xcvr - > regmap , FSL_XCVR_EXT_CTRL ,
FSL_XCVR_EXT_CTRL_CORE_RESET , 0 ) ;
if ( ret < 0 ) {
dev_err ( dev , " M0+ core release failed: %d \n " , ret ) ;
goto stop_spba_clk ;
}
/* Let M0+ core complete firmware initialization */
msleep ( 50 ) ;
return 0 ;
stop_spba_clk :
clk_disable_unprepare ( xcvr - > spba_clk ) ;
stop_phy_clk :
clk_disable_unprepare ( xcvr - > phy_clk ) ;
stop_pll_ipg_clk :
clk_disable_unprepare ( xcvr - > pll_ipg_clk ) ;
stop_ipg_clk :
clk_disable_unprepare ( xcvr - > ipg_clk ) ;
return ret ;
}
static const struct dev_pm_ops fsl_xcvr_pm_ops = {
SET_RUNTIME_PM_OPS ( fsl_xcvr_runtime_suspend ,
fsl_xcvr_runtime_resume ,
NULL )
SET_SYSTEM_SLEEP_PM_OPS ( pm_runtime_force_suspend ,
pm_runtime_force_resume )
} ;
static struct platform_driver fsl_xcvr_driver = {
. probe = fsl_xcvr_probe ,
. driver = {
. name = " fsl,imx8mp-audio-xcvr " ,
. pm = & fsl_xcvr_pm_ops ,
. of_match_table = fsl_xcvr_dt_ids ,
} ,
} ;
module_platform_driver ( fsl_xcvr_driver ) ;
MODULE_AUTHOR ( " Viorel Suman <viorel.suman@nxp.com> " ) ;
MODULE_DESCRIPTION ( " NXP Audio Transceiver (XCVR) driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;