2019-10-08 13:15:17 -05:00
// SPDX-License-Identifier: GPL-2.0
//
// Driver for the Texas Instruments TAS2562 CODEC
// Copyright (C) 2019 Texas Instruments Inc.
# include <linux/module.h>
# include <linux/errno.h>
# include <linux/device.h>
# include <linux/i2c.h>
# include <linux/pm_runtime.h>
# include <linux/regmap.h>
# include <linux/slab.h>
# include <linux/gpio/consumer.h>
# include <linux/regulator/consumer.h>
# include <linux/delay.h>
# include <sound/pcm.h>
# include <sound/pcm_params.h>
# include <sound/soc.h>
# include <sound/soc-dapm.h>
# include <sound/tlv.h>
# include "tas2562.h"
# define TAS2562_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\
SNDRV_PCM_FORMAT_S32_LE )
struct tas2562_data {
struct snd_soc_component * component ;
struct gpio_desc * sdz_gpio ;
struct regmap * regmap ;
struct device * dev ;
struct i2c_client * client ;
int v_sense_slot ;
int i_sense_slot ;
} ;
static int tas2562_set_bias_level ( struct snd_soc_component * component ,
enum snd_soc_bias_level level )
{
struct tas2562_data * tas2562 =
snd_soc_component_get_drvdata ( component ) ;
switch ( level ) {
case SND_SOC_BIAS_ON :
snd_soc_component_update_bits ( component ,
TAS2562_PWR_CTRL ,
TAS2562_MODE_MASK , TAS2562_ACTIVE ) ;
break ;
case SND_SOC_BIAS_STANDBY :
case SND_SOC_BIAS_PREPARE :
snd_soc_component_update_bits ( component ,
TAS2562_PWR_CTRL ,
TAS2562_MODE_MASK , TAS2562_MUTE ) ;
break ;
case SND_SOC_BIAS_OFF :
snd_soc_component_update_bits ( component ,
TAS2562_PWR_CTRL ,
TAS2562_MODE_MASK , TAS2562_SHUTDOWN ) ;
break ;
default :
dev_err ( tas2562 - > dev ,
" wrong power level setting %d \n " , level ) ;
return - EINVAL ;
}
return 0 ;
}
static int tas2562_set_samplerate ( struct tas2562_data * tas2562 , int samplerate )
{
int samp_rate ;
int ramp_rate ;
switch ( samplerate ) {
case 7350 :
ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1 ;
samp_rate = TAS2562_TDM_CFG0_SAMPRATE_7305_8KHZ ;
break ;
case 8000 :
ramp_rate = 0 ;
samp_rate = TAS2562_TDM_CFG0_SAMPRATE_7305_8KHZ ;
break ;
case 14700 :
ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1 ;
samp_rate = TAS2562_TDM_CFG0_SAMPRATE_14_7_16KHZ ;
break ;
case 16000 :
ramp_rate = 0 ;
samp_rate = TAS2562_TDM_CFG0_SAMPRATE_14_7_16KHZ ;
break ;
case 22050 :
ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1 ;
samp_rate = TAS2562_TDM_CFG0_SAMPRATE_22_05_24KHZ ;
break ;
case 24000 :
ramp_rate = 0 ;
samp_rate = TAS2562_TDM_CFG0_SAMPRATE_22_05_24KHZ ;
break ;
case 29400 :
ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1 ;
samp_rate = TAS2562_TDM_CFG0_SAMPRATE_29_4_32KHZ ;
break ;
case 32000 :
ramp_rate = 0 ;
samp_rate = TAS2562_TDM_CFG0_SAMPRATE_29_4_32KHZ ;
break ;
case 44100 :
ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1 ;
samp_rate = TAS2562_TDM_CFG0_SAMPRATE_44_1_48KHZ ;
break ;
case 48000 :
ramp_rate = 0 ;
samp_rate = TAS2562_TDM_CFG0_SAMPRATE_44_1_48KHZ ;
break ;
case 88200 :
ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1 ;
samp_rate = TAS2562_TDM_CFG0_SAMPRATE_88_2_96KHZ ;
break ;
case 96000 :
ramp_rate = 0 ;
samp_rate = TAS2562_TDM_CFG0_SAMPRATE_88_2_96KHZ ;
break ;
case 176400 :
ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1 ;
samp_rate = TAS2562_TDM_CFG0_SAMPRATE_176_4_192KHZ ;
break ;
case 192000 :
ramp_rate = 0 ;
samp_rate = TAS2562_TDM_CFG0_SAMPRATE_176_4_192KHZ ;
break ;
default :
dev_info ( tas2562 - > dev , " %s, unsupported sample rate, %d \n " ,
__func__ , samplerate ) ;
return - EINVAL ;
}
snd_soc_component_update_bits ( tas2562 - > component , TAS2562_TDM_CFG0 ,
TAS2562_TDM_CFG0_RAMPRATE_MASK , ramp_rate ) ;
snd_soc_component_update_bits ( tas2562 - > component , TAS2562_TDM_CFG0 ,
TAS2562_TDM_CFG0_SAMPRATE_MASK , samp_rate ) ;
return 0 ;
}
static int tas2562_set_dai_tdm_slot ( struct snd_soc_dai * dai ,
unsigned int tx_mask , unsigned int rx_mask ,
int slots , int slot_width )
{
struct snd_soc_component * component = dai - > component ;
struct tas2562_data * tas2562 = snd_soc_component_get_drvdata ( component ) ;
int ret = 0 ;
switch ( slot_width ) {
case 16 :
ret = snd_soc_component_update_bits ( component ,
TAS2562_TDM_CFG2 ,
TAS2562_TDM_CFG2_RXLEN_MASK ,
TAS2562_TDM_CFG2_RXLEN_16B ) ;
break ;
case 24 :
ret = snd_soc_component_update_bits ( component ,
TAS2562_TDM_CFG2 ,
TAS2562_TDM_CFG2_RXLEN_MASK ,
TAS2562_TDM_CFG2_RXLEN_24B ) ;
break ;
case 32 :
ret = snd_soc_component_update_bits ( component ,
TAS2562_TDM_CFG2 ,
TAS2562_TDM_CFG2_RXLEN_MASK ,
TAS2562_TDM_CFG2_RXLEN_32B ) ;
break ;
case 0 :
/* Do not change slot width */
break ;
default :
dev_err ( tas2562 - > dev , " slot width not supported " ) ;
ret = - EINVAL ;
}
if ( ret < 0 )
return ret ;
return 0 ;
}
static int tas2562_set_bitwidth ( struct tas2562_data * tas2562 , int bitwidth )
{
int ret ;
switch ( bitwidth ) {
case SNDRV_PCM_FORMAT_S16_LE :
snd_soc_component_update_bits ( tas2562 - > component ,
TAS2562_TDM_CFG2 ,
TAS2562_TDM_CFG2_RXWLEN_MASK ,
TAS2562_TDM_CFG2_RXWLEN_16B ) ;
tas2562 - > v_sense_slot = tas2562 - > i_sense_slot + 2 ;
break ;
case SNDRV_PCM_FORMAT_S24_LE :
snd_soc_component_update_bits ( tas2562 - > component ,
TAS2562_TDM_CFG2 ,
TAS2562_TDM_CFG2_RXWLEN_MASK ,
TAS2562_TDM_CFG2_RXWLEN_24B ) ;
tas2562 - > v_sense_slot = tas2562 - > i_sense_slot + 4 ;
break ;
case SNDRV_PCM_FORMAT_S32_LE :
snd_soc_component_update_bits ( tas2562 - > component ,
TAS2562_TDM_CFG2 ,
TAS2562_TDM_CFG2_RXWLEN_MASK ,
TAS2562_TDM_CFG2_RXWLEN_32B ) ;
tas2562 - > v_sense_slot = tas2562 - > i_sense_slot + 4 ;
break ;
default :
2020-02-18 12:52:52 -06:00
dev_info ( tas2562 - > dev , " Unsupported bitwidth format \n " ) ;
return - EINVAL ;
2019-10-08 13:15:17 -05:00
}
ret = snd_soc_component_update_bits ( tas2562 - > component ,
TAS2562_TDM_CFG5 ,
TAS2562_TDM_CFG5_VSNS_EN | TAS2562_TDM_CFG5_VSNS_SLOT_MASK ,
TAS2562_TDM_CFG5_VSNS_EN | tas2562 - > v_sense_slot ) ;
if ( ret < 0 )
return ret ;
ret = snd_soc_component_update_bits ( tas2562 - > component ,
TAS2562_TDM_CFG6 ,
TAS2562_TDM_CFG6_ISNS_EN | TAS2562_TDM_CFG6_ISNS_SLOT_MASK ,
TAS2562_TDM_CFG6_ISNS_EN | tas2562 - > i_sense_slot ) ;
if ( ret < 0 )
return ret ;
return 0 ;
}
static int tas2562_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params ,
struct snd_soc_dai * dai )
{
struct snd_soc_component * component = dai - > component ;
struct tas2562_data * tas2562 = snd_soc_component_get_drvdata ( component ) ;
int ret ;
ret = tas2562_set_bitwidth ( tas2562 , params_format ( params ) ) ;
if ( ret ) {
dev_err ( tas2562 - > dev , " set bitwidth failed, %d \n " , ret ) ;
return ret ;
}
ret = tas2562_set_samplerate ( tas2562 , params_rate ( params ) ) ;
if ( ret )
2020-02-26 07:03:05 -06:00
dev_err ( tas2562 - > dev , " set sample rate failed, %d \n " , ret ) ;
2019-10-08 13:15:17 -05:00
return ret ;
}
static int tas2562_set_dai_fmt ( struct snd_soc_dai * dai , unsigned int fmt )
{
struct snd_soc_component * component = dai - > component ;
struct tas2562_data * tas2562 = snd_soc_component_get_drvdata ( component ) ;
u8 tdm_rx_start_slot = 0 , asi_cfg_1 = 0 ;
int ret ;
switch ( fmt & SND_SOC_DAIFMT_INV_MASK ) {
case SND_SOC_DAIFMT_NB_NF :
asi_cfg_1 = 0 ;
break ;
case SND_SOC_DAIFMT_IB_NF :
asi_cfg_1 | = TAS2562_TDM_CFG1_RX_FALLING ;
break ;
default :
dev_err ( tas2562 - > dev , " ASI format Inverse is not found \n " ) ;
return - EINVAL ;
}
ret = snd_soc_component_update_bits ( component , TAS2562_TDM_CFG1 ,
TAS2562_TDM_CFG1_RX_EDGE_MASK ,
asi_cfg_1 ) ;
if ( ret < 0 ) {
dev_err ( tas2562 - > dev , " Failed to set RX edge \n " ) ;
return ret ;
}
switch ( fmt & SND_SOC_DAIFMT_FORMAT_MASK ) {
case ( SND_SOC_DAIFMT_I2S ) :
case ( SND_SOC_DAIFMT_DSP_A ) :
case ( SND_SOC_DAIFMT_DSP_B ) :
tdm_rx_start_slot = BIT ( 1 ) ;
break ;
case ( SND_SOC_DAIFMT_LEFT_J ) :
tdm_rx_start_slot = 0 ;
break ;
default :
dev_err ( tas2562 - > dev , " DAI Format is not found, fmt=0x%x \n " ,
fmt ) ;
ret = - EINVAL ;
break ;
}
ret = snd_soc_component_update_bits ( component , TAS2562_TDM_CFG1 ,
TAS2562_TDM_CFG1_RX_OFFSET_MASK ,
tdm_rx_start_slot ) ;
if ( ret < 0 )
return ret ;
return 0 ;
}
static int tas2562_mute ( struct snd_soc_dai * dai , int mute )
{
struct snd_soc_component * component = dai - > component ;
return snd_soc_component_update_bits ( component , TAS2562_PWR_CTRL ,
TAS2562_MODE_MASK ,
mute ? TAS2562_MUTE : 0 ) ;
}
static int tas2562_codec_probe ( struct snd_soc_component * component )
{
struct tas2562_data * tas2562 = snd_soc_component_get_drvdata ( component ) ;
int ret ;
tas2562 - > component = component ;
if ( tas2562 - > sdz_gpio )
gpiod_set_value_cansleep ( tas2562 - > sdz_gpio , 1 ) ;
ret = snd_soc_component_update_bits ( component , TAS2562_PWR_CTRL ,
TAS2562_MODE_MASK , TAS2562_MUTE ) ;
if ( ret < 0 )
return ret ;
return 0 ;
}
# ifdef CONFIG_PM
static int tas2562_suspend ( struct snd_soc_component * component )
{
struct tas2562_data * tas2562 = snd_soc_component_get_drvdata ( component ) ;
regcache_cache_only ( tas2562 - > regmap , true ) ;
regcache_mark_dirty ( tas2562 - > regmap ) ;
if ( tas2562 - > sdz_gpio )
gpiod_set_value_cansleep ( tas2562 - > sdz_gpio , 0 ) ;
return 0 ;
}
static int tas2562_resume ( struct snd_soc_component * component )
{
struct tas2562_data * tas2562 = snd_soc_component_get_drvdata ( component ) ;
if ( tas2562 - > sdz_gpio )
gpiod_set_value_cansleep ( tas2562 - > sdz_gpio , 1 ) ;
regcache_cache_only ( tas2562 - > regmap , false ) ;
return regcache_sync ( tas2562 - > regmap ) ;
}
# else
# define tas2562_suspend NULL
# define tas2562_resume NULL
# endif
static const char * const tas2562_ASI1_src [ ] = {
" I2C offset " , " Left " , " Right " , " LeftRightDiv2 " ,
} ;
static SOC_ENUM_SINGLE_DECL ( tas2562_ASI1_src_enum , TAS2562_TDM_CFG2 , 4 ,
tas2562_ASI1_src ) ;
static const struct snd_kcontrol_new tas2562_asi1_mux =
SOC_DAPM_ENUM ( " ASI1 Source " , tas2562_ASI1_src_enum ) ;
static int tas2562_dac_event ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * kcontrol , int event )
{
struct snd_soc_component * component =
snd_soc_dapm_to_component ( w - > dapm ) ;
struct tas2562_data * tas2562 = snd_soc_component_get_drvdata ( component ) ;
switch ( event ) {
case SND_SOC_DAPM_POST_PMU :
dev_info ( tas2562 - > dev , " SND_SOC_DAPM_POST_PMU \n " ) ;
break ;
case SND_SOC_DAPM_PRE_PMD :
dev_info ( tas2562 - > dev , " SND_SOC_DAPM_PRE_PMD \n " ) ;
break ;
default :
break ;
}
return 0 ;
}
static DECLARE_TLV_DB_SCALE ( tas2562_dac_tlv , 850 , 50 , 0 ) ;
static const struct snd_kcontrol_new isense_switch =
SOC_DAPM_SINGLE ( " Switch " , TAS2562_PWR_CTRL , TAS2562_ISENSE_POWER_EN ,
1 , 1 ) ;
static const struct snd_kcontrol_new vsense_switch =
SOC_DAPM_SINGLE ( " Switch " , TAS2562_PWR_CTRL , TAS2562_VSENSE_POWER_EN ,
1 , 1 ) ;
static const struct snd_kcontrol_new tas2562_snd_controls [ ] = {
SOC_SINGLE_TLV ( " Amp Gain Volume " , TAS2562_PB_CFG1 , 0 , 0x1c , 0 ,
tas2562_dac_tlv ) ,
} ;
static const struct snd_soc_dapm_widget tas2562_dapm_widgets [ ] = {
SND_SOC_DAPM_AIF_IN ( " ASI1 " , " ASI1 Playback " , 0 , SND_SOC_NOPM , 0 , 0 ) ,
SND_SOC_DAPM_MUX ( " ASI1 Sel " , SND_SOC_NOPM , 0 , 0 , & tas2562_asi1_mux ) ,
SND_SOC_DAPM_AIF_IN ( " DAC IN " , " Playback " , 0 , SND_SOC_NOPM , 0 , 0 ) ,
SND_SOC_DAPM_DAC_E ( " DAC " , NULL , SND_SOC_NOPM , 0 , 0 , tas2562_dac_event ,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD ) ,
SND_SOC_DAPM_SWITCH ( " ISENSE " , TAS2562_PWR_CTRL , 3 , 1 , & isense_switch ) ,
SND_SOC_DAPM_SWITCH ( " VSENSE " , TAS2562_PWR_CTRL , 2 , 1 , & vsense_switch ) ,
SND_SOC_DAPM_SIGGEN ( " VMON " ) ,
SND_SOC_DAPM_SIGGEN ( " IMON " ) ,
SND_SOC_DAPM_OUTPUT ( " OUT " ) ,
} ;
static const struct snd_soc_dapm_route tas2562_audio_map [ ] = {
{ " ASI1 Sel " , " I2C offset " , " ASI1 " } ,
{ " ASI1 Sel " , " Left " , " ASI1 " } ,
{ " ASI1 Sel " , " Right " , " ASI1 " } ,
{ " ASI1 Sel " , " LeftRightDiv2 " , " ASI1 " } ,
{ " DAC " , NULL , " DAC IN " } ,
{ " OUT " , NULL , " DAC " } ,
{ " ISENSE " , " Switch " , " IMON " } ,
{ " VSENSE " , " Switch " , " VMON " } ,
} ;
static const struct snd_soc_component_driver soc_component_dev_tas2562 = {
. probe = tas2562_codec_probe ,
. suspend = tas2562_suspend ,
. resume = tas2562_resume ,
. set_bias_level = tas2562_set_bias_level ,
. controls = tas2562_snd_controls ,
. num_controls = ARRAY_SIZE ( tas2562_snd_controls ) ,
. dapm_widgets = tas2562_dapm_widgets ,
. num_dapm_widgets = ARRAY_SIZE ( tas2562_dapm_widgets ) ,
. dapm_routes = tas2562_audio_map ,
. num_dapm_routes = ARRAY_SIZE ( tas2562_audio_map ) ,
. idle_bias_on = 1 ,
. use_pmdown_time = 1 ,
. endianness = 1 ,
. non_legacy_dai_naming = 1 ,
} ;
static const struct snd_soc_dai_ops tas2562_speaker_dai_ops = {
. hw_params = tas2562_hw_params ,
. set_fmt = tas2562_set_dai_fmt ,
. set_tdm_slot = tas2562_set_dai_tdm_slot ,
. digital_mute = tas2562_mute ,
} ;
static struct snd_soc_dai_driver tas2562_dai [ ] = {
{
. name = " tas2562-amplifier " ,
. id = 0 ,
. playback = {
. stream_name = " ASI1 Playback " ,
. channels_min = 2 ,
. channels_max = 2 ,
. rates = SNDRV_PCM_RATE_8000_192000 ,
. formats = TAS2562_FORMATS ,
} ,
. ops = & tas2562_speaker_dai_ops ,
} ,
} ;
static const struct regmap_range_cfg tas2562_ranges [ ] = {
{
. range_min = 0 ,
. range_max = 5 * 128 ,
. selector_reg = TAS2562_PAGE_CTRL ,
. selector_mask = 0xff ,
. selector_shift = 0 ,
. window_start = 0 ,
. window_len = 128 ,
} ,
} ;
static const struct reg_default tas2562_reg_defaults [ ] = {
{ TAS2562_PAGE_CTRL , 0x00 } ,
{ TAS2562_SW_RESET , 0x00 } ,
{ TAS2562_PWR_CTRL , 0x0e } ,
{ TAS2562_PB_CFG1 , 0x20 } ,
{ TAS2562_TDM_CFG0 , 0x09 } ,
{ TAS2562_TDM_CFG1 , 0x02 } ,
} ;
static const struct regmap_config tas2562_regmap_config = {
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = 5 * 128 ,
. cache_type = REGCACHE_RBTREE ,
. reg_defaults = tas2562_reg_defaults ,
. num_reg_defaults = ARRAY_SIZE ( tas2562_reg_defaults ) ,
. ranges = tas2562_ranges ,
. num_ranges = ARRAY_SIZE ( tas2562_ranges ) ,
} ;
static int tas2562_parse_dt ( struct tas2562_data * tas2562 )
{
struct device * dev = tas2562 - > dev ;
int ret = 0 ;
tas2562 - > sdz_gpio = devm_gpiod_get_optional ( dev , " shut-down-gpio " ,
GPIOD_OUT_HIGH ) ;
if ( IS_ERR ( tas2562 - > sdz_gpio ) ) {
if ( PTR_ERR ( tas2562 - > sdz_gpio ) = = - EPROBE_DEFER ) {
tas2562 - > sdz_gpio = NULL ;
return - EPROBE_DEFER ;
}
}
ret = fwnode_property_read_u32 ( dev - > fwnode , " ti,imon-slot-no " ,
& tas2562 - > i_sense_slot ) ;
if ( ret )
dev_err ( dev , " Looking up %s property failed %d \n " ,
" ti,imon-slot-no " , ret ) ;
return ret ;
}
static int tas2562_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct device * dev = & client - > dev ;
struct tas2562_data * data ;
int ret ;
data = devm_kzalloc ( dev , sizeof ( * data ) , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
data - > client = client ;
data - > dev = & client - > dev ;
tas2562_parse_dt ( data ) ;
data - > regmap = devm_regmap_init_i2c ( client , & tas2562_regmap_config ) ;
if ( IS_ERR ( data - > regmap ) ) {
ret = PTR_ERR ( data - > regmap ) ;
dev_err ( dev , " failed to allocate register map: %d \n " , ret ) ;
return ret ;
}
dev_set_drvdata ( & client - > dev , data ) ;
return devm_snd_soc_register_component ( dev , & soc_component_dev_tas2562 ,
tas2562_dai ,
ARRAY_SIZE ( tas2562_dai ) ) ;
}
static const struct i2c_device_id tas2562_id [ ] = {
{ " tas2562 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , tas2562_id ) ;
static const struct of_device_id tas2562_of_match [ ] = {
{ . compatible = " ti,tas2562 " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , tas2562_of_match ) ;
static struct i2c_driver tas2562_i2c_driver = {
. driver = {
. name = " tas2562 " ,
. of_match_table = of_match_ptr ( tas2562_of_match ) ,
} ,
. probe = tas2562_probe ,
. id_table = tas2562_id ,
} ;
module_i2c_driver ( tas2562_i2c_driver ) ;
MODULE_AUTHOR ( " Dan Murphy <dmurphy@ti.com> " ) ;
MODULE_DESCRIPTION ( " TAS2562 Audio amplifier driver " ) ;
MODULE_LICENSE ( " GPL " ) ;