2019-05-29 07:17:59 -07:00
// SPDX-License-Identifier: GPL-2.0-only
2013-02-18 19:59:35 -08:00
/*
* sound / soc / codecs / si476x . c - - Codec driver for SI476X chips
*
* Copyright ( C ) 2012 Innovative Converged Devices ( ICD )
* Copyright ( C ) 2013 Andrey Smirnov
*
* Author : Andrey Smirnov < andrew . smirnov @ gmail . com >
*/
2012-10-05 18:55:02 -07:00
# include <linux/module.h>
# include <linux/slab.h>
# include <sound/pcm.h>
# include <sound/pcm_params.h>
2014-03-11 12:43:21 +08:00
# include <linux/regmap.h>
2012-10-05 18:55:02 -07:00
# include <sound/soc.h>
# include <sound/initval.h>
# include <linux/i2c.h>
# include <linux/mfd/si476x-core.h>
enum si476x_audio_registers {
SI476X_DIGITAL_IO_OUTPUT_FORMAT = 0x0203 ,
SI476X_DIGITAL_IO_OUTPUT_SAMPLE_RATE = 0x0202 ,
} ;
enum si476x_digital_io_output_format {
SI476X_DIGITAL_IO_SLOT_SIZE_SHIFT = 11 ,
SI476X_DIGITAL_IO_SAMPLE_SIZE_SHIFT = 8 ,
} ;
2013-07-03 15:00:44 -07:00
# define SI476X_DIGITAL_IO_OUTPUT_WIDTH_MASK ((0x7 << SI476X_DIGITAL_IO_SLOT_SIZE_SHIFT) | \
( 0x7 < < SI476X_DIGITAL_IO_SAMPLE_SIZE_SHIFT ) )
# define SI476X_DIGITAL_IO_OUTPUT_FORMAT_MASK (0x7e)
2012-10-05 18:55:02 -07:00
enum si476x_daudio_formats {
SI476X_DAUDIO_MODE_I2S = ( 0x0 < < 1 ) ,
SI476X_DAUDIO_MODE_DSP_A = ( 0x6 < < 1 ) ,
SI476X_DAUDIO_MODE_DSP_B = ( 0x7 < < 1 ) ,
SI476X_DAUDIO_MODE_LEFT_J = ( 0x8 < < 1 ) ,
SI476X_DAUDIO_MODE_RIGHT_J = ( 0x9 < < 1 ) ,
SI476X_DAUDIO_MODE_IB = ( 1 < < 5 ) ,
SI476X_DAUDIO_MODE_IF = ( 1 < < 6 ) ,
} ;
enum si476x_pcm_format {
SI476X_PCM_FORMAT_S8 = 2 ,
SI476X_PCM_FORMAT_S16_LE = 4 ,
SI476X_PCM_FORMAT_S20_3LE = 5 ,
SI476X_PCM_FORMAT_S24_LE = 6 ,
} ;
2013-08-16 11:54:51 +01:00
static const struct snd_soc_dapm_widget si476x_dapm_widgets [ ] = {
SND_SOC_DAPM_OUTPUT ( " LOUT " ) ,
SND_SOC_DAPM_OUTPUT ( " ROUT " ) ,
} ;
static const struct snd_soc_dapm_route si476x_dapm_routes [ ] = {
{ " Capture " , NULL , " LOUT " } ,
{ " Capture " , NULL , " ROUT " } ,
} ;
2012-10-05 18:55:02 -07:00
static int si476x_codec_set_dai_fmt ( struct snd_soc_dai * codec_dai ,
unsigned int fmt )
{
2013-10-20 18:14:20 +01:00
struct si476x_core * core = i2c_mfd_cell_to_core ( codec_dai - > dev ) ;
2012-10-05 18:55:02 -07:00
int err ;
u16 format = 0 ;
2022-02-23 00:17:37 +00:00
if ( ( fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK ) ! = SND_SOC_DAIFMT_CBC_CFC )
2012-10-05 18:55:02 -07:00
return - EINVAL ;
switch ( fmt & SND_SOC_DAIFMT_FORMAT_MASK ) {
case SND_SOC_DAIFMT_DSP_A :
format | = SI476X_DAUDIO_MODE_DSP_A ;
break ;
case SND_SOC_DAIFMT_DSP_B :
format | = SI476X_DAUDIO_MODE_DSP_B ;
break ;
case SND_SOC_DAIFMT_I2S :
format | = SI476X_DAUDIO_MODE_I2S ;
break ;
case SND_SOC_DAIFMT_RIGHT_J :
format | = SI476X_DAUDIO_MODE_RIGHT_J ;
break ;
case SND_SOC_DAIFMT_LEFT_J :
format | = SI476X_DAUDIO_MODE_LEFT_J ;
break ;
default :
return - EINVAL ;
}
switch ( fmt & SND_SOC_DAIFMT_FORMAT_MASK ) {
case SND_SOC_DAIFMT_DSP_A :
case SND_SOC_DAIFMT_DSP_B :
switch ( fmt & SND_SOC_DAIFMT_INV_MASK ) {
case SND_SOC_DAIFMT_NB_NF :
break ;
case SND_SOC_DAIFMT_IB_NF :
format | = SI476X_DAUDIO_MODE_IB ;
break ;
default :
return - EINVAL ;
}
break ;
case SND_SOC_DAIFMT_I2S :
case SND_SOC_DAIFMT_RIGHT_J :
case SND_SOC_DAIFMT_LEFT_J :
switch ( fmt & SND_SOC_DAIFMT_INV_MASK ) {
case SND_SOC_DAIFMT_NB_NF :
break ;
case SND_SOC_DAIFMT_IB_IF :
format | = SI476X_DAUDIO_MODE_IB |
SI476X_DAUDIO_MODE_IF ;
break ;
case SND_SOC_DAIFMT_IB_NF :
format | = SI476X_DAUDIO_MODE_IB ;
break ;
case SND_SOC_DAIFMT_NB_IF :
format | = SI476X_DAUDIO_MODE_IF ;
break ;
default :
return - EINVAL ;
}
break ;
default :
return - EINVAL ;
}
2013-10-20 18:14:20 +01:00
si476x_core_lock ( core ) ;
2018-01-29 04:45:14 +00:00
err = snd_soc_component_update_bits ( codec_dai - > component , SI476X_DIGITAL_IO_OUTPUT_FORMAT ,
2012-10-05 18:55:02 -07:00
SI476X_DIGITAL_IO_OUTPUT_FORMAT_MASK ,
format ) ;
2013-10-20 18:14:20 +01:00
si476x_core_unlock ( core ) ;
2012-10-05 18:55:02 -07:00
if ( err < 0 ) {
2018-01-29 04:45:14 +00:00
dev_err ( codec_dai - > component - > dev , " Failed to set output format \n " ) ;
2012-10-05 18:55:02 -07:00
return err ;
}
2013-02-18 19:59:35 -08:00
2012-10-05 18:55:02 -07:00
return 0 ;
}
static int si476x_codec_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params ,
struct snd_soc_dai * dai )
{
2013-10-20 18:14:20 +01:00
struct si476x_core * core = i2c_mfd_cell_to_core ( dai - > dev ) ;
2012-10-05 18:55:02 -07:00
int rate , width , err ;
rate = params_rate ( params ) ;
if ( rate < 32000 | | rate > 48000 ) {
2018-01-29 04:45:14 +00:00
dev_err ( dai - > component - > dev , " Rate: %d is not supported \n " , rate ) ;
2012-10-05 18:55:02 -07:00
return - EINVAL ;
}
2014-07-31 12:46:36 +01:00
switch ( params_width ( params ) ) {
case 8 :
2012-10-05 18:55:02 -07:00
width = SI476X_PCM_FORMAT_S8 ;
2012-10-31 16:07:43 +08:00
break ;
2014-07-31 12:46:36 +01:00
case 16 :
2012-10-05 18:55:02 -07:00
width = SI476X_PCM_FORMAT_S16_LE ;
break ;
2014-07-31 12:46:36 +01:00
case 20 :
2012-10-05 18:55:02 -07:00
width = SI476X_PCM_FORMAT_S20_3LE ;
break ;
2014-07-31 12:46:36 +01:00
case 24 :
2012-10-05 18:55:02 -07:00
width = SI476X_PCM_FORMAT_S24_LE ;
break ;
default :
return - EINVAL ;
}
2013-10-20 18:14:20 +01:00
si476x_core_lock ( core ) ;
2018-01-29 04:45:14 +00:00
err = snd_soc_component_write ( dai - > component , SI476X_DIGITAL_IO_OUTPUT_SAMPLE_RATE ,
2012-10-05 18:55:02 -07:00
rate ) ;
if ( err < 0 ) {
2018-01-29 04:45:14 +00:00
dev_err ( dai - > component - > dev , " Failed to set sample rate \n " ) ;
2013-10-20 18:14:20 +01:00
goto out ;
2012-10-05 18:55:02 -07:00
}
2018-01-29 04:45:14 +00:00
err = snd_soc_component_update_bits ( dai - > component , SI476X_DIGITAL_IO_OUTPUT_FORMAT ,
2012-10-05 18:55:02 -07:00
SI476X_DIGITAL_IO_OUTPUT_WIDTH_MASK ,
2013-02-18 19:59:35 -08:00
( width < < SI476X_DIGITAL_IO_SLOT_SIZE_SHIFT ) |
2012-10-05 18:55:02 -07:00
( width < < SI476X_DIGITAL_IO_SAMPLE_SIZE_SHIFT ) ) ;
if ( err < 0 ) {
2018-01-29 04:45:14 +00:00
dev_err ( dai - > component - > dev , " Failed to set output width \n " ) ;
2013-10-20 18:14:20 +01:00
goto out ;
2012-10-05 18:55:02 -07:00
}
2013-10-20 18:14:20 +01:00
out :
si476x_core_unlock ( core ) ;
return err ;
2012-10-05 18:55:02 -07:00
}
2015-07-15 15:38:14 +08:00
static const struct snd_soc_dai_ops si476x_dai_ops = {
2012-10-05 18:55:02 -07:00
. hw_params = si476x_codec_hw_params ,
. set_fmt = si476x_codec_set_dai_fmt ,
} ;
static struct snd_soc_dai_driver si476x_dai = {
. name = " si476x-codec " ,
. capture = {
. stream_name = " Capture " ,
. channels_min = 2 ,
. channels_max = 2 ,
. rates = SNDRV_PCM_RATE_32000 |
SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_48000 ,
. formats = SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S20_3LE |
SNDRV_PCM_FMTBIT_S24_LE
} ,
. ops = & si476x_dai_ops ,
} ;
2017-11-28 06:05:15 +00:00
static int si476x_probe ( struct snd_soc_component * component )
2014-03-26 13:40:27 +08:00
{
2017-11-28 06:05:15 +00:00
snd_soc_component_init_regmap ( component ,
dev_get_regmap ( component - > dev - > parent , NULL ) ) ;
return 0 ;
2014-03-26 13:40:27 +08:00
}
2018-01-29 04:45:14 +00:00
static const struct snd_soc_component_driver soc_component_dev_si476x = {
. probe = si476x_probe ,
. dapm_widgets = si476x_dapm_widgets ,
. num_dapm_widgets = ARRAY_SIZE ( si476x_dapm_widgets ) ,
. dapm_routes = si476x_dapm_routes ,
. num_dapm_routes = ARRAY_SIZE ( si476x_dapm_routes ) ,
. idle_bias_on = 1 ,
. use_pmdown_time = 1 ,
. endianness = 1 ,
2012-10-05 18:55:02 -07:00
} ;
2012-12-07 09:26:37 -05:00
static int si476x_platform_probe ( struct platform_device * pdev )
2012-10-05 18:55:02 -07:00
{
2018-01-29 04:45:14 +00:00
return devm_snd_soc_register_component ( & pdev - > dev ,
& soc_component_dev_si476x ,
2012-10-05 18:55:02 -07:00
& si476x_dai , 1 ) ;
}
MODULE_ALIAS ( " platform:si476x-codec " ) ;
static struct platform_driver si476x_platform_driver = {
. driver = {
. name = " si476x-codec " ,
} ,
. probe = si476x_platform_probe ,
} ;
module_platform_driver ( si476x_platform_driver ) ;
2013-02-18 19:59:35 -08:00
MODULE_AUTHOR ( " Andrey Smirnov <andrew.smirnov@gmail.com> " ) ;
2012-10-05 18:55:02 -07:00
MODULE_DESCRIPTION ( " ASoC Si4761/64 codec driver " ) ;
MODULE_LICENSE ( " GPL " ) ;