2019-05-27 09:55:06 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2013-08-01 13:14:58 +04:00
/*
* PCM1681 ASoC codec driver
*
* Copyright ( c ) StreamUnlimited GmbH 2013
* Marek Belisko < marek . belisko @ streamunlimited . com >
*/
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/delay.h>
# include <linux/gpio.h>
# include <linux/i2c.h>
# include <linux/regmap.h>
2013-10-11 15:53:57 +04:00
# include <linux/of.h>
2013-08-01 13:14:58 +04:00
# include <linux/of_device.h>
# include <linux/of_gpio.h>
# include <sound/pcm.h>
# include <sound/pcm_params.h>
# include <sound/soc.h>
# include <sound/tlv.h>
# define PCM1681_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE )
# define PCM1681_PCM_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \
SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \
SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000 )
# define PCM1681_SOFT_MUTE_ALL 0xff
# define PCM1681_DEEMPH_RATE_MASK 0x18
# define PCM1681_DEEMPH_MASK 0x01
# define PCM1681_ATT_CONTROL(X) (X <= 6 ? X : X + 9) /* Attenuation level */
# define PCM1681_SOFT_MUTE 0x07 /* Soft mute control register */
# define PCM1681_DAC_CONTROL 0x08 /* DAC operation control */
# define PCM1681_FMT_CONTROL 0x09 /* Audio interface data format */
# define PCM1681_DEEMPH_CONTROL 0x0a /* De-emphasis control */
# define PCM1681_ZERO_DETECT_STATUS 0x0e /* Zero detect status reg */
static const struct reg_default pcm1681_reg_defaults [ ] = {
{ 0x01 , 0xff } ,
{ 0x02 , 0xff } ,
{ 0x03 , 0xff } ,
{ 0x04 , 0xff } ,
{ 0x05 , 0xff } ,
{ 0x06 , 0xff } ,
{ 0x07 , 0x00 } ,
{ 0x08 , 0x00 } ,
{ 0x09 , 0x06 } ,
{ 0x0A , 0x00 } ,
{ 0x0B , 0xff } ,
{ 0x0C , 0x0f } ,
{ 0x0D , 0x00 } ,
{ 0x10 , 0xff } ,
{ 0x11 , 0xff } ,
{ 0x12 , 0x00 } ,
{ 0x13 , 0x00 } ,
} ;
static bool pcm1681_accessible_reg ( struct device * dev , unsigned int reg )
{
return ! ( ( reg = = 0x00 ) | | ( reg = = 0x0f ) ) ;
}
2016-06-13 18:39:45 +03:00
static bool pcm1681_writeable_reg ( struct device * dev , unsigned int reg )
2013-08-01 13:14:58 +04:00
{
return pcm1681_accessible_reg ( dev , reg ) & &
( reg ! = PCM1681_ZERO_DETECT_STATUS ) ;
}
struct pcm1681_private {
struct regmap * regmap ;
unsigned int format ;
/* Current deemphasis status */
unsigned int deemph ;
/* Current rate for deemphasis control */
unsigned int rate ;
} ;
static const int pcm1681_deemph [ ] = { 44100 , 48000 , 32000 } ;
2018-01-29 07:18:12 +03:00
static int pcm1681_set_deemph ( struct snd_soc_component * component )
2013-08-01 13:14:58 +04:00
{
2018-01-29 07:18:12 +03:00
struct pcm1681_private * priv = snd_soc_component_get_drvdata ( component ) ;
2021-03-12 21:22:39 +03:00
int i , val = - 1 , enable = 0 ;
2013-08-01 13:14:58 +04:00
2015-07-23 18:23:35 +03:00
if ( priv - > deemph ) {
for ( i = 0 ; i < ARRAY_SIZE ( pcm1681_deemph ) ; i + + ) {
if ( pcm1681_deemph [ i ] = = priv - > rate ) {
2013-08-01 13:14:58 +04:00
val = i ;
2015-07-23 18:23:35 +03:00
break ;
}
}
}
2013-08-01 13:14:58 +04:00
if ( val ! = - 1 ) {
regmap_update_bits ( priv - > regmap , PCM1681_DEEMPH_CONTROL ,
2015-07-23 18:22:26 +03:00
PCM1681_DEEMPH_RATE_MASK , val < < 3 ) ;
2013-08-01 13:14:58 +04:00
enable = 1 ;
2015-07-23 18:23:35 +03:00
} else {
2013-08-01 13:14:58 +04:00
enable = 0 ;
2015-07-23 18:23:35 +03:00
}
2013-08-01 13:14:58 +04:00
/* enable/disable deemphasis functionality */
return regmap_update_bits ( priv - > regmap , PCM1681_DEEMPH_CONTROL ,
PCM1681_DEEMPH_MASK , enable ) ;
}
static int pcm1681_get_deemph ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
2018-01-29 07:18:12 +03:00
struct snd_soc_component * component = snd_soc_kcontrol_component ( kcontrol ) ;
struct pcm1681_private * priv = snd_soc_component_get_drvdata ( component ) ;
2013-08-01 13:14:58 +04:00
2015-03-10 14:39:07 +03:00
ucontrol - > value . integer . value [ 0 ] = priv - > deemph ;
2013-08-01 13:14:58 +04:00
return 0 ;
}
static int pcm1681_put_deemph ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
2018-01-29 07:18:12 +03:00
struct snd_soc_component * component = snd_soc_kcontrol_component ( kcontrol ) ;
struct pcm1681_private * priv = snd_soc_component_get_drvdata ( component ) ;
2013-08-01 13:14:58 +04:00
2015-03-10 14:39:07 +03:00
priv - > deemph = ucontrol - > value . integer . value [ 0 ] ;
2013-08-01 13:14:58 +04:00
2018-01-29 07:18:12 +03:00
return pcm1681_set_deemph ( component ) ;
2013-08-01 13:14:58 +04:00
}
static int pcm1681_set_dai_fmt ( struct snd_soc_dai * codec_dai ,
unsigned int format )
{
2018-01-29 07:18:12 +03:00
struct snd_soc_component * component = codec_dai - > component ;
struct pcm1681_private * priv = snd_soc_component_get_drvdata ( component ) ;
2013-08-01 13:14:58 +04:00
2022-02-23 04:48:43 +03:00
/* The PCM1681 can only be consumer to all clocks */
if ( ( format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK ) ! = SND_SOC_DAIFMT_CBC_CFC ) {
2018-01-29 07:18:12 +03:00
dev_err ( component - > dev , " Invalid clocking mode \n " ) ;
2013-08-01 13:14:58 +04:00
return - EINVAL ;
}
priv - > format = format ;
return 0 ;
}
2020-07-09 04:56:39 +03:00
static int pcm1681_mute ( struct snd_soc_dai * dai , int mute , int direction )
2013-08-01 13:14:58 +04:00
{
2018-01-29 07:18:12 +03:00
struct snd_soc_component * component = dai - > component ;
struct pcm1681_private * priv = snd_soc_component_get_drvdata ( component ) ;
2013-08-01 13:14:58 +04:00
int val ;
if ( mute )
val = PCM1681_SOFT_MUTE_ALL ;
else
val = 0 ;
return regmap_write ( priv - > regmap , PCM1681_SOFT_MUTE , val ) ;
}
static int pcm1681_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params ,
struct snd_soc_dai * dai )
{
2018-01-29 07:18:12 +03:00
struct snd_soc_component * component = dai - > component ;
struct pcm1681_private * priv = snd_soc_component_get_drvdata ( component ) ;
2013-08-01 13:14:58 +04:00
int val = 0 , ret ;
priv - > rate = params_rate ( params ) ;
switch ( priv - > format & SND_SOC_DAIFMT_FORMAT_MASK ) {
case SND_SOC_DAIFMT_RIGHT_J :
2014-01-24 20:07:05 +04:00
switch ( params_width ( params ) ) {
case 24 :
val = 0 ;
break ;
case 16 :
val = 3 ;
break ;
default :
return - EINVAL ;
}
2013-08-01 13:14:58 +04:00
break ;
case SND_SOC_DAIFMT_I2S :
val = 0x04 ;
break ;
case SND_SOC_DAIFMT_LEFT_J :
val = 0x05 ;
break ;
default :
2018-01-29 07:18:12 +03:00
dev_err ( component - > dev , " Invalid DAI format \n " ) ;
2013-08-01 13:14:58 +04:00
return - EINVAL ;
}
ret = regmap_update_bits ( priv - > regmap , PCM1681_FMT_CONTROL , 0x0f , val ) ;
if ( ret < 0 )
return ret ;
2018-01-29 07:18:12 +03:00
return pcm1681_set_deemph ( component ) ;
2013-08-01 13:14:58 +04:00
}
static const struct snd_soc_dai_ops pcm1681_dai_ops = {
. set_fmt = pcm1681_set_dai_fmt ,
. hw_params = pcm1681_hw_params ,
2020-07-09 04:56:39 +03:00
. mute_stream = pcm1681_mute ,
. no_capture_mute = 1 ,
2013-08-01 13:14:58 +04:00
} ;
2013-08-13 21:25:41 +04:00
static const struct snd_soc_dapm_widget pcm1681_dapm_widgets [ ] = {
SND_SOC_DAPM_OUTPUT ( " VOUT1 " ) ,
SND_SOC_DAPM_OUTPUT ( " VOUT2 " ) ,
SND_SOC_DAPM_OUTPUT ( " VOUT3 " ) ,
SND_SOC_DAPM_OUTPUT ( " VOUT4 " ) ,
SND_SOC_DAPM_OUTPUT ( " VOUT5 " ) ,
SND_SOC_DAPM_OUTPUT ( " VOUT6 " ) ,
SND_SOC_DAPM_OUTPUT ( " VOUT7 " ) ,
SND_SOC_DAPM_OUTPUT ( " VOUT8 " ) ,
} ;
static const struct snd_soc_dapm_route pcm1681_dapm_routes [ ] = {
{ " VOUT1 " , NULL , " Playback " } ,
{ " VOUT2 " , NULL , " Playback " } ,
{ " VOUT3 " , NULL , " Playback " } ,
{ " VOUT4 " , NULL , " Playback " } ,
{ " VOUT5 " , NULL , " Playback " } ,
{ " VOUT6 " , NULL , " Playback " } ,
{ " VOUT7 " , NULL , " Playback " } ,
{ " VOUT8 " , NULL , " Playback " } ,
} ;
2013-08-01 13:14:58 +04:00
static const DECLARE_TLV_DB_SCALE ( pcm1681_dac_tlv , - 6350 , 50 , 1 ) ;
static const struct snd_kcontrol_new pcm1681_controls [ ] = {
SOC_DOUBLE_R_TLV ( " Channel 1/2 Playback Volume " ,
PCM1681_ATT_CONTROL ( 1 ) , PCM1681_ATT_CONTROL ( 2 ) , 0 ,
0x7f , 0 , pcm1681_dac_tlv ) ,
SOC_DOUBLE_R_TLV ( " Channel 3/4 Playback Volume " ,
PCM1681_ATT_CONTROL ( 3 ) , PCM1681_ATT_CONTROL ( 4 ) , 0 ,
0x7f , 0 , pcm1681_dac_tlv ) ,
SOC_DOUBLE_R_TLV ( " Channel 5/6 Playback Volume " ,
PCM1681_ATT_CONTROL ( 5 ) , PCM1681_ATT_CONTROL ( 6 ) , 0 ,
0x7f , 0 , pcm1681_dac_tlv ) ,
SOC_DOUBLE_R_TLV ( " Channel 7/8 Playback Volume " ,
PCM1681_ATT_CONTROL ( 7 ) , PCM1681_ATT_CONTROL ( 8 ) , 0 ,
0x7f , 0 , pcm1681_dac_tlv ) ,
SOC_SINGLE_BOOL_EXT ( " De-emphasis Switch " , 0 ,
pcm1681_get_deemph , pcm1681_put_deemph ) ,
} ;
2013-08-08 15:25:57 +04:00
static struct snd_soc_dai_driver pcm1681_dai = {
2013-08-01 13:14:58 +04:00
. name = " pcm1681-hifi " ,
. playback = {
. stream_name = " Playback " ,
. channels_min = 2 ,
. channels_max = 8 ,
. rates = PCM1681_PCM_RATES ,
. formats = PCM1681_PCM_FORMATS ,
} ,
. ops = & pcm1681_dai_ops ,
} ;
# ifdef CONFIG_OF
static const struct of_device_id pcm1681_dt_ids [ ] = {
{ . compatible = " ti,pcm1681 " , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , pcm1681_dt_ids ) ;
# endif
static const struct regmap_config pcm1681_regmap = {
. reg_bits = 8 ,
. val_bits = 8 ,
2013-10-12 13:24:25 +04:00
. max_register = 0x13 ,
2013-08-01 13:14:58 +04:00
. reg_defaults = pcm1681_reg_defaults ,
. num_reg_defaults = ARRAY_SIZE ( pcm1681_reg_defaults ) ,
. writeable_reg = pcm1681_writeable_reg ,
. readable_reg = pcm1681_accessible_reg ,
} ;
2018-01-29 07:18:12 +03:00
static const struct snd_soc_component_driver soc_component_dev_pcm1681 = {
. controls = pcm1681_controls ,
. num_controls = ARRAY_SIZE ( pcm1681_controls ) ,
. dapm_widgets = pcm1681_dapm_widgets ,
. num_dapm_widgets = ARRAY_SIZE ( pcm1681_dapm_widgets ) ,
. dapm_routes = pcm1681_dapm_routes ,
. num_dapm_routes = ARRAY_SIZE ( pcm1681_dapm_routes ) ,
. idle_bias_on = 1 ,
. use_pmdown_time = 1 ,
. endianness = 1 ,
2013-08-01 13:14:58 +04:00
} ;
static const struct i2c_device_id pcm1681_i2c_id [ ] = {
{ " pcm1681 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , pcm1681_i2c_id ) ;
2022-04-05 19:58:30 +03:00
static int pcm1681_i2c_probe ( struct i2c_client * client )
2013-08-01 13:14:58 +04:00
{
int ret ;
struct pcm1681_private * priv ;
priv = devm_kzalloc ( & client - > dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
priv - > regmap = devm_regmap_init_i2c ( client , & pcm1681_regmap ) ;
if ( IS_ERR ( priv - > regmap ) ) {
ret = PTR_ERR ( priv - > regmap ) ;
dev_err ( & client - > dev , " Failed to create regmap: %d \n " , ret ) ;
return ret ;
}
i2c_set_clientdata ( client , priv ) ;
2018-01-29 07:18:12 +03:00
return devm_snd_soc_register_component ( & client - > dev ,
& soc_component_dev_pcm1681 ,
2013-08-01 13:14:58 +04:00
& pcm1681_dai , 1 ) ;
}
static struct i2c_driver pcm1681_i2c_driver = {
. driver = {
. name = " pcm1681 " ,
. of_match_table = of_match_ptr ( pcm1681_dt_ids ) ,
} ,
. id_table = pcm1681_i2c_id ,
2022-04-05 19:58:30 +03:00
. probe_new = pcm1681_i2c_probe ,
2013-08-01 13:14:58 +04:00
} ;
module_i2c_driver ( pcm1681_i2c_driver ) ;
MODULE_DESCRIPTION ( " Texas Instruments PCM1681 ALSA SoC Codec Driver " ) ;
MODULE_AUTHOR ( " Marek Belisko <marek.belisko@streamunlimited.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;