2019-05-28 09:57:21 -07:00
// SPDX-License-Identifier: GPL-2.0-only
2015-06-22 16:31:11 +02:00
/*
* Copyright ( C ) STMicroelectronics SA 2015
* Authors : Arnaud Pouliquen < arnaud . pouliquen @ st . com >
* for STMicroelectronics .
*/
# include <linux/io.h>
# include <linux/module.h>
# include <linux/regmap.h>
# include <linux/reset.h>
# include <linux/mfd/syscon.h>
# include <sound/soc.h>
# include <sound/soc-dapm.h>
/* DAC definitions */
/* stih407 DAC registers */
/* sysconf 5041: Audio-Gue-Control */
# define STIH407_AUDIO_GLUE_CTRL 0x000000A4
/* sysconf 5042: Audio-DAC-Control */
# define STIH407_AUDIO_DAC_CTRL 0x000000A8
/* DAC definitions */
# define STIH407_DAC_SOFTMUTE 0x0
# define STIH407_DAC_STANDBY_ANA 0x1
# define STIH407_DAC_STANDBY 0x2
# define STIH407_DAC_SOFTMUTE_MASK BIT(STIH407_DAC_SOFTMUTE)
# define STIH407_DAC_STANDBY_ANA_MASK BIT(STIH407_DAC_STANDBY_ANA)
# define STIH407_DAC_STANDBY_MASK BIT(STIH407_DAC_STANDBY)
/* SPDIF definitions */
# define SPDIF_BIPHASE_ENABLE 0x6
# define SPDIF_BIPHASE_IDLE 0x7
# define SPDIF_BIPHASE_ENABLE_MASK BIT(SPDIF_BIPHASE_ENABLE)
# define SPDIF_BIPHASE_IDLE_MASK BIT(SPDIF_BIPHASE_IDLE)
enum {
STI_SAS_DAI_SPDIF_OUT ,
STI_SAS_DAI_ANALOG_OUT ,
} ;
static const struct reg_default stih407_sas_reg_defaults [ ] = {
2016-10-24 16:42:55 +02:00
{ STIH407_AUDIO_DAC_CTRL , 0x000000000 } ,
{ STIH407_AUDIO_GLUE_CTRL , 0x00000040 } ,
2015-06-22 16:31:11 +02:00
} ;
struct sti_dac_audio {
struct regmap * regmap ;
struct regmap * virt_regmap ;
struct regmap_field * * field ;
struct reset_control * rst ;
int mclk ;
} ;
struct sti_spdif_audio {
struct regmap * regmap ;
struct regmap_field * * field ;
int mclk ;
} ;
/* device data structure */
struct sti_sas_dev_data {
const struct regmap_config * regmap ;
const struct snd_soc_dai_ops * dac_ops ; /* DAC function callbacks */
const struct snd_soc_dapm_widget * dapm_widgets ; /* dapms declaration */
const int num_dapm_widgets ; /* dapms declaration */
const struct snd_soc_dapm_route * dapm_routes ; /* route declaration */
const int num_dapm_routes ; /* route declaration */
} ;
/* driver data structure */
struct sti_sas_data {
struct device * dev ;
const struct sti_sas_dev_data * dev_data ;
struct sti_dac_audio dac ;
struct sti_spdif_audio spdif ;
} ;
/* Read a register from the sysconf reg bank */
static int sti_sas_read_reg ( void * context , unsigned int reg ,
unsigned int * value )
{
struct sti_sas_data * drvdata = context ;
int status ;
u32 val ;
status = regmap_read ( drvdata - > dac . regmap , reg , & val ) ;
* value = ( unsigned int ) val ;
return status ;
}
/* Read a register from the sysconf reg bank */
static int sti_sas_write_reg ( void * context , unsigned int reg ,
unsigned int value )
{
struct sti_sas_data * drvdata = context ;
int status ;
status = regmap_write ( drvdata - > dac . regmap , reg , value ) ;
return status ;
}
2018-01-29 04:40:56 +00:00
static int sti_sas_init_sas_registers ( struct snd_soc_component * component ,
2015-06-22 16:31:11 +02:00
struct sti_sas_data * data )
{
int ret ;
/*
* DAC and SPDIF are activated by default
* put them in IDLE to save power
*/
/* Initialise bi-phase formatter to disabled */
2018-01-29 04:40:56 +00:00
ret = snd_soc_component_update_bits ( component , STIH407_AUDIO_GLUE_CTRL ,
2015-06-22 16:31:11 +02:00
SPDIF_BIPHASE_ENABLE_MASK , 0 ) ;
if ( ! ret )
/* Initialise bi-phase formatter idle value to 0 */
2018-01-29 04:40:56 +00:00
ret = snd_soc_component_update_bits ( component , STIH407_AUDIO_GLUE_CTRL ,
2015-06-22 16:31:11 +02:00
SPDIF_BIPHASE_IDLE_MASK , 0 ) ;
if ( ret < 0 ) {
2018-01-29 04:40:56 +00:00
dev_err ( component - > dev , " Failed to update SPDIF registers \n " ) ;
2015-06-22 16:31:11 +02:00
return ret ;
}
/* Init DAC configuration */
2016-10-24 16:42:55 +02:00
/* init configuration */
2018-01-29 04:40:56 +00:00
ret = snd_soc_component_update_bits ( component , STIH407_AUDIO_DAC_CTRL ,
2016-10-24 16:42:55 +02:00
STIH407_DAC_STANDBY_MASK ,
STIH407_DAC_STANDBY_MASK ) ;
if ( ! ret )
2018-01-29 04:40:56 +00:00
ret = snd_soc_component_update_bits ( component , STIH407_AUDIO_DAC_CTRL ,
2016-10-24 16:42:55 +02:00
STIH407_DAC_STANDBY_ANA_MASK ,
STIH407_DAC_STANDBY_ANA_MASK ) ;
if ( ! ret )
2018-01-29 04:40:56 +00:00
ret = snd_soc_component_update_bits ( component , STIH407_AUDIO_DAC_CTRL ,
2016-10-24 16:42:55 +02:00
STIH407_DAC_SOFTMUTE_MASK ,
STIH407_DAC_SOFTMUTE_MASK ) ;
2015-06-22 16:31:11 +02:00
if ( ret < 0 ) {
2018-01-29 04:40:56 +00:00
dev_err ( component - > dev , " Failed to update DAC registers \n " ) ;
2015-06-22 16:31:11 +02:00
return ret ;
}
return ret ;
}
/*
* DAC
*/
static int sti_sas_dac_set_fmt ( struct snd_soc_dai * dai , unsigned int fmt )
{
/* Sanity check only */
if ( ( fmt & SND_SOC_DAIFMT_MASTER_MASK ) ! = SND_SOC_DAIFMT_CBS_CFS ) {
2018-01-29 04:40:56 +00:00
dev_err ( dai - > component - > dev ,
2015-06-22 16:31:11 +02:00
" %s: ERROR: Unsupporter master mask 0x%x \n " ,
__func__ , fmt & SND_SOC_DAIFMT_MASTER_MASK ) ;
return - EINVAL ;
}
return 0 ;
}
2015-07-13 13:26:45 +08:00
static const struct snd_soc_dapm_widget stih407_sas_dapm_widgets [ ] = {
2015-06-22 16:31:11 +02:00
SND_SOC_DAPM_OUT_DRV ( " DAC standby ana " , STIH407_AUDIO_DAC_CTRL ,
STIH407_DAC_STANDBY_ANA , 1 , NULL , 0 ) ,
SND_SOC_DAPM_DAC ( " DAC standby " , " dac_p " , STIH407_AUDIO_DAC_CTRL ,
STIH407_DAC_STANDBY , 1 ) ,
SND_SOC_DAPM_OUTPUT ( " DAC Output " ) ,
} ;
2015-07-13 13:26:45 +08:00
static const struct snd_soc_dapm_route stih407_sas_route [ ] = {
2015-06-22 16:31:11 +02:00
{ " DAC Output " , NULL , " DAC standby ana " } ,
{ " DAC standby ana " , NULL , " DAC standby " } ,
} ;
static int stih407_sas_dac_mute ( struct snd_soc_dai * dai , int mute , int stream )
{
2018-01-29 04:40:56 +00:00
struct snd_soc_component * component = dai - > component ;
2015-06-22 16:31:11 +02:00
if ( mute ) {
2018-01-29 04:40:56 +00:00
return snd_soc_component_update_bits ( component , STIH407_AUDIO_DAC_CTRL ,
2015-06-22 16:31:11 +02:00
STIH407_DAC_SOFTMUTE_MASK ,
STIH407_DAC_SOFTMUTE_MASK ) ;
} else {
2018-01-29 04:40:56 +00:00
return snd_soc_component_update_bits ( component , STIH407_AUDIO_DAC_CTRL ,
2015-06-22 16:31:11 +02:00
STIH407_DAC_SOFTMUTE_MASK ,
0 ) ;
}
}
/*
* SPDIF
*/
static int sti_sas_spdif_set_fmt ( struct snd_soc_dai * dai ,
unsigned int fmt )
{
if ( ( fmt & SND_SOC_DAIFMT_MASTER_MASK ) ! = SND_SOC_DAIFMT_CBS_CFS ) {
2018-01-29 04:40:56 +00:00
dev_err ( dai - > component - > dev ,
2015-06-22 16:31:11 +02:00
" %s: ERROR: Unsupporter master mask 0x%x \n " ,
__func__ , fmt & SND_SOC_DAIFMT_MASTER_MASK ) ;
return - EINVAL ;
}
return 0 ;
}
/*
* sti_sas_spdif_trigger :
* Trigger function is used to ensure that BiPhase Formater is disabled
* before CPU dai is stopped .
* This is mandatory to avoid that BPF is stalled
*/
static int sti_sas_spdif_trigger ( struct snd_pcm_substream * substream , int cmd ,
struct snd_soc_dai * dai )
{
2018-01-29 04:40:56 +00:00
struct snd_soc_component * component = dai - > component ;
2015-06-22 16:31:11 +02:00
switch ( cmd ) {
case SNDRV_PCM_TRIGGER_START :
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE :
2018-01-29 04:40:56 +00:00
return snd_soc_component_update_bits ( component , STIH407_AUDIO_GLUE_CTRL ,
2015-06-22 16:31:11 +02:00
SPDIF_BIPHASE_ENABLE_MASK ,
SPDIF_BIPHASE_ENABLE_MASK ) ;
case SNDRV_PCM_TRIGGER_RESUME :
case SNDRV_PCM_TRIGGER_PAUSE_PUSH :
case SNDRV_PCM_TRIGGER_STOP :
case SNDRV_PCM_TRIGGER_SUSPEND :
2018-01-29 04:40:56 +00:00
return snd_soc_component_update_bits ( component , STIH407_AUDIO_GLUE_CTRL ,
2015-06-22 16:31:11 +02:00
SPDIF_BIPHASE_ENABLE_MASK ,
0 ) ;
default :
return - EINVAL ;
}
}
static bool sti_sas_volatile_register ( struct device * dev , unsigned int reg )
{
if ( reg = = STIH407_AUDIO_GLUE_CTRL )
return true ;
return false ;
}
/*
* CODEC DAIS
*/
/*
* sti_sas_set_sysclk :
* get MCLK input frequency to check that MCLK - FS ratio is coherent
*/
static int sti_sas_set_sysclk ( struct snd_soc_dai * dai , int clk_id ,
unsigned int freq , int dir )
{
2018-01-29 04:40:56 +00:00
struct snd_soc_component * component = dai - > component ;
struct sti_sas_data * drvdata = dev_get_drvdata ( component - > dev ) ;
2015-06-22 16:31:11 +02:00
if ( dir = = SND_SOC_CLOCK_OUT )
return 0 ;
if ( clk_id ! = 0 )
return - EINVAL ;
switch ( dai - > id ) {
case STI_SAS_DAI_SPDIF_OUT :
drvdata - > spdif . mclk = freq ;
break ;
case STI_SAS_DAI_ANALOG_OUT :
drvdata - > dac . mclk = freq ;
break ;
}
return 0 ;
}
static int sti_sas_prepare ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai )
{
2018-01-29 04:40:56 +00:00
struct snd_soc_component * component = dai - > component ;
struct sti_sas_data * drvdata = dev_get_drvdata ( component - > dev ) ;
2015-06-22 16:31:11 +02:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
switch ( dai - > id ) {
case STI_SAS_DAI_SPDIF_OUT :
if ( ( drvdata - > spdif . mclk / runtime - > rate ) ! = 128 ) {
2018-01-29 04:40:56 +00:00
dev_err ( component - > dev , " unexpected mclk-fs ratio \n " ) ;
2015-06-22 16:31:11 +02:00
return - EINVAL ;
}
break ;
case STI_SAS_DAI_ANALOG_OUT :
if ( ( drvdata - > dac . mclk / runtime - > rate ) ! = 256 ) {
2018-01-29 04:40:56 +00:00
dev_err ( component - > dev , " unexpected mclk-fs ratio \n " ) ;
2015-06-22 16:31:11 +02:00
return - EINVAL ;
}
break ;
}
return 0 ;
}
2015-07-13 13:26:45 +08:00
static const struct snd_soc_dai_ops stih407_dac_ops = {
2015-06-22 16:31:11 +02:00
. set_fmt = sti_sas_dac_set_fmt ,
. mute_stream = stih407_sas_dac_mute ,
. prepare = sti_sas_prepare ,
. set_sysclk = sti_sas_set_sysclk ,
} ;
2015-07-13 13:26:45 +08:00
static const struct regmap_config stih407_sas_regmap = {
2015-06-22 16:31:11 +02:00
. reg_bits = 32 ,
. val_bits = 32 ,
2016-10-24 16:42:57 +02:00
. fast_io = true ,
2015-06-22 16:31:11 +02:00
. max_register = STIH407_AUDIO_DAC_CTRL ,
. reg_defaults = stih407_sas_reg_defaults ,
. num_reg_defaults = ARRAY_SIZE ( stih407_sas_reg_defaults ) ,
. volatile_reg = sti_sas_volatile_register ,
. cache_type = REGCACHE_RBTREE ,
. reg_read = sti_sas_read_reg ,
. reg_write = sti_sas_write_reg ,
} ;
2015-07-13 13:26:45 +08:00
static const struct sti_sas_dev_data stih407_data = {
2015-06-22 16:31:11 +02:00
. regmap = & stih407_sas_regmap ,
. dac_ops = & stih407_dac_ops ,
. dapm_widgets = stih407_sas_dapm_widgets ,
. num_dapm_widgets = ARRAY_SIZE ( stih407_sas_dapm_widgets ) ,
. dapm_routes = stih407_sas_route ,
. num_dapm_routes = ARRAY_SIZE ( stih407_sas_route ) ,
} ;
static struct snd_soc_dai_driver sti_sas_dai [ ] = {
{
. name = " sas-dai-spdif-out " ,
. id = STI_SAS_DAI_SPDIF_OUT ,
. playback = {
. stream_name = " spdif_p " ,
. channels_min = 2 ,
. channels_max = 2 ,
. rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 |
SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
SNDRV_PCM_RATE_192000 ,
. formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S32_LE ,
} ,
. ops = ( struct snd_soc_dai_ops [ ] ) {
{
. set_fmt = sti_sas_spdif_set_fmt ,
. trigger = sti_sas_spdif_trigger ,
. set_sysclk = sti_sas_set_sysclk ,
. prepare = sti_sas_prepare ,
}
} ,
} ,
{
. name = " sas-dai-dac " ,
. id = STI_SAS_DAI_ANALOG_OUT ,
. playback = {
. stream_name = " dac_p " ,
. channels_min = 2 ,
. channels_max = 2 ,
. rates = SNDRV_PCM_RATE_8000_48000 ,
. formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S32_LE ,
} ,
} ,
} ;
# ifdef CONFIG_PM_SLEEP
2018-01-29 04:40:56 +00:00
static int sti_sas_resume ( struct snd_soc_component * component )
2015-06-22 16:31:11 +02:00
{
2018-01-29 04:40:56 +00:00
struct sti_sas_data * drvdata = dev_get_drvdata ( component - > dev ) ;
2015-06-22 16:31:11 +02:00
2018-01-29 04:40:56 +00:00
return sti_sas_init_sas_registers ( component , drvdata ) ;
2015-06-22 16:31:11 +02:00
}
# else
# define sti_sas_resume NULL
# endif
2018-01-29 04:40:56 +00:00
static int sti_sas_component_probe ( struct snd_soc_component * component )
2015-06-22 16:31:11 +02:00
{
2018-01-29 04:40:56 +00:00
struct sti_sas_data * drvdata = dev_get_drvdata ( component - > dev ) ;
2015-06-22 16:31:11 +02:00
int ret ;
2018-01-29 04:40:56 +00:00
ret = sti_sas_init_sas_registers ( component , drvdata ) ;
2015-06-22 16:31:11 +02:00
return ret ;
}
2018-01-29 04:40:56 +00:00
static struct snd_soc_component_driver sti_sas_driver = {
. probe = sti_sas_component_probe ,
. resume = sti_sas_resume ,
. idle_bias_on = 1 ,
. use_pmdown_time = 1 ,
. endianness = 1 ,
. non_legacy_dai_naming = 1 ,
2015-06-22 16:31:11 +02:00
} ;
static const struct of_device_id sti_sas_dev_match [ ] = {
{
. compatible = " st,stih407-sas-codec " ,
. data = & stih407_data ,
} ,
{ } ,
} ;
static int sti_sas_driver_probe ( struct platform_device * pdev )
{
struct device_node * pnode = pdev - > dev . of_node ;
struct sti_sas_data * drvdata ;
2015-07-16 10:43:20 +02:00
const struct of_device_id * of_id ;
2015-06-22 16:31:11 +02:00
/* Allocate device structure */
drvdata = devm_kzalloc ( & pdev - > dev , sizeof ( struct sti_sas_data ) ,
GFP_KERNEL ) ;
if ( ! drvdata )
return - ENOMEM ;
/* Populate data structure depending on compatibility */
2015-07-16 10:43:20 +02:00
of_id = of_match_node ( sti_sas_dev_match , pnode ) ;
if ( ! of_id - > data ) {
2016-10-24 16:42:56 +02:00
dev_err ( & pdev - > dev , " data associated to device is missing \n " ) ;
2015-06-22 16:31:11 +02:00
return - EINVAL ;
}
2015-07-16 10:43:20 +02:00
drvdata - > dev_data = ( struct sti_sas_dev_data * ) of_id - > data ;
2015-06-22 16:31:11 +02:00
/* Initialise device structure */
drvdata - > dev = & pdev - > dev ;
/* Request the DAC & SPDIF registers memory region */
drvdata - > dac . virt_regmap = devm_regmap_init ( & pdev - > dev , NULL , drvdata ,
drvdata - > dev_data - > regmap ) ;
2015-07-13 13:25:34 +08:00
if ( IS_ERR ( drvdata - > dac . virt_regmap ) ) {
2015-06-22 16:31:11 +02:00
dev_err ( & pdev - > dev , " audio registers not enabled \n " ) ;
2015-07-13 13:25:34 +08:00
return PTR_ERR ( drvdata - > dac . virt_regmap ) ;
2015-06-22 16:31:11 +02:00
}
/* Request the syscon region */
drvdata - > dac . regmap =
syscon_regmap_lookup_by_phandle ( pnode , " st,syscfg " ) ;
2015-07-13 13:25:34 +08:00
if ( IS_ERR ( drvdata - > dac . regmap ) ) {
2015-06-22 16:31:11 +02:00
dev_err ( & pdev - > dev , " syscon registers not available \n " ) ;
2015-07-13 13:25:34 +08:00
return PTR_ERR ( drvdata - > dac . regmap ) ;
2015-06-22 16:31:11 +02:00
}
drvdata - > spdif . regmap = drvdata - > dac . regmap ;
sti_sas_dai [ STI_SAS_DAI_ANALOG_OUT ] . ops = drvdata - > dev_data - > dac_ops ;
/* Set dapms*/
2018-01-29 04:40:56 +00:00
sti_sas_driver . dapm_widgets = drvdata - > dev_data - > dapm_widgets ;
sti_sas_driver . num_dapm_widgets = drvdata - > dev_data - > num_dapm_widgets ;
2015-06-22 16:31:11 +02:00
2018-01-29 04:40:56 +00:00
sti_sas_driver . dapm_routes = drvdata - > dev_data - > dapm_routes ;
sti_sas_driver . num_dapm_routes = drvdata - > dev_data - > num_dapm_routes ;
2015-06-22 16:31:11 +02:00
/* Store context */
dev_set_drvdata ( & pdev - > dev , drvdata ) ;
2018-01-29 04:40:56 +00:00
return devm_snd_soc_register_component ( & pdev - > dev , & sti_sas_driver ,
2015-06-22 16:31:11 +02:00
sti_sas_dai ,
ARRAY_SIZE ( sti_sas_dai ) ) ;
}
static struct platform_driver sti_sas_platform_driver = {
. driver = {
. name = " sti-sas-codec " ,
. of_match_table = sti_sas_dev_match ,
} ,
. probe = sti_sas_driver_probe ,
} ;
module_platform_driver ( sti_sas_platform_driver ) ;
MODULE_DESCRIPTION ( " audio codec for STMicroelectronics sti platforms " ) ;
MODULE_AUTHOR ( " Arnaud.pouliquen@st.com " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;