2019-05-27 09:55:21 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2016-04-27 01:15:57 +03:00
/*
* tas5720 . c - ALSA SoC Texas Instruments TAS5720 Mono Audio Amplifier
*
2020-07-19 18:38:22 +03:00
* Copyright ( C ) 2015 - 2016 Texas Instruments Incorporated - https : //www.ti.com
2016-04-27 01:15:57 +03:00
*
* Author : Andreas Dannenberg < dannenberg @ ti . com >
*/
# 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/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 "tas5720.h"
/* Define how often to check (and clear) the fault status register (in ms) */
# define TAS5720_FAULT_CHECK_INTERVAL 200
2017-12-11 22:01:54 +03:00
enum tas572x_type {
TAS5720 ,
TAS5722 ,
} ;
2016-04-27 01:15:57 +03:00
static const char * const tas5720_supply_names [ ] = {
" dvdd " , /* Digital power supply. Connect to 3.3-V supply. */
" pvdd " , /* Class-D amp and analog power supply (connected). */
} ;
# define TAS5720_NUM_SUPPLIES ARRAY_SIZE(tas5720_supply_names)
struct tas5720_data {
2018-01-29 07:23:00 +03:00
struct snd_soc_component * component ;
2016-04-27 01:15:57 +03:00
struct regmap * regmap ;
struct i2c_client * tas5720_client ;
2017-12-11 22:01:54 +03:00
enum tas572x_type devtype ;
2016-04-27 01:15:57 +03:00
struct regulator_bulk_data supplies [ TAS5720_NUM_SUPPLIES ] ;
struct delayed_work fault_check_work ;
unsigned int last_fault ;
} ;
static int tas5720_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params ,
struct snd_soc_dai * dai )
{
2018-01-29 07:23:00 +03:00
struct snd_soc_component * component = dai - > component ;
2016-04-27 01:15:57 +03:00
unsigned int rate = params_rate ( params ) ;
bool ssz_ds ;
int ret ;
switch ( rate ) {
case 44100 :
case 48000 :
ssz_ds = false ;
break ;
case 88200 :
case 96000 :
ssz_ds = true ;
break ;
default :
2018-01-29 07:23:00 +03:00
dev_err ( component - > dev , " unsupported sample rate: %u \n " , rate ) ;
2016-04-27 01:15:57 +03:00
return - EINVAL ;
}
2018-01-29 07:23:00 +03:00
ret = snd_soc_component_update_bits ( component , TAS5720_DIGITAL_CTRL1_REG ,
2016-04-27 01:15:57 +03:00
TAS5720_SSZ_DS , ssz_ds ) ;
if ( ret < 0 ) {
2018-01-29 07:23:00 +03:00
dev_err ( component - > dev , " error setting sample rate: %d \n " , ret ) ;
2016-04-27 01:15:57 +03:00
return ret ;
}
return 0 ;
}
static int tas5720_set_dai_fmt ( struct snd_soc_dai * dai , unsigned int fmt )
{
2018-01-29 07:23:00 +03:00
struct snd_soc_component * component = dai - > component ;
2016-04-27 01:15:57 +03:00
u8 serial_format ;
int ret ;
if ( ( fmt & SND_SOC_DAIFMT_MASTER_MASK ) ! = SND_SOC_DAIFMT_CBS_CFS ) {
2018-01-29 07:23:00 +03:00
dev_vdbg ( component - > dev , " DAI Format master is not found \n " ) ;
2016-04-27 01:15:57 +03:00
return - EINVAL ;
}
switch ( fmt & ( SND_SOC_DAIFMT_FORMAT_MASK |
SND_SOC_DAIFMT_INV_MASK ) ) {
case ( SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF ) :
/* 1st data bit occur one BCLK cycle after the frame sync */
serial_format = TAS5720_SAIF_I2S ;
break ;
case ( SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF ) :
/*
* Note that although the TAS5720 does not have a dedicated DSP
* mode it doesn ' t care about the LRCLK duty cycle during TDM
* operation . Therefore we can use the device ' s I2S mode with
* its delaying of the 1 st data bit to receive DSP_A formatted
* data . See device datasheet for additional details .
*/
serial_format = TAS5720_SAIF_I2S ;
break ;
case ( SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF ) :
/*
* Similar to DSP_A , we can use the fact that the TAS5720 does
* not care about the LRCLK duty cycle during TDM to receive
* DSP_B formatted data in LEFTJ mode ( no delaying of the 1 st
* data bit ) .
*/
serial_format = TAS5720_SAIF_LEFTJ ;
break ;
case ( SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF ) :
/* No delay after the frame sync */
serial_format = TAS5720_SAIF_LEFTJ ;
break ;
default :
2018-01-29 07:23:00 +03:00
dev_vdbg ( component - > dev , " DAI Format is not found \n " ) ;
2016-04-27 01:15:57 +03:00
return - EINVAL ;
}
2018-01-29 07:23:00 +03:00
ret = snd_soc_component_update_bits ( component , TAS5720_DIGITAL_CTRL1_REG ,
2016-04-27 01:15:57 +03:00
TAS5720_SAIF_FORMAT_MASK ,
serial_format ) ;
if ( ret < 0 ) {
2018-01-29 07:23:00 +03:00
dev_err ( component - > dev , " error setting SAIF format: %d \n " , ret ) ;
2016-04-27 01:15:57 +03:00
return ret ;
}
return 0 ;
}
static int tas5720_set_dai_tdm_slot ( struct snd_soc_dai * dai ,
unsigned int tx_mask , unsigned int rx_mask ,
int slots , int slot_width )
{
2018-01-29 07:23:00 +03:00
struct snd_soc_component * component = dai - > component ;
2018-08-31 17:47:14 +03:00
struct tas5720_data * tas5720 = snd_soc_component_get_drvdata ( component ) ;
2016-04-27 01:15:57 +03:00
unsigned int first_slot ;
int ret ;
if ( ! tx_mask ) {
2018-01-29 07:23:00 +03:00
dev_err ( component - > dev , " tx masks must not be 0 \n " ) ;
2016-04-27 01:15:57 +03:00
return - EINVAL ;
}
/*
* Determine the first slot that is being requested . We will only
* use the first slot that is found since the TAS5720 is a mono
* amplifier .
*/
first_slot = __ffs ( tx_mask ) ;
if ( first_slot > 7 ) {
2018-01-29 07:23:00 +03:00
dev_err ( component - > dev , " slot selection out of bounds (%u) \n " ,
2016-04-27 01:15:57 +03:00
first_slot ) ;
return - EINVAL ;
}
/* Enable manual TDM slot selection (instead of I2C ID based) */
2018-01-29 07:23:00 +03:00
ret = snd_soc_component_update_bits ( component , TAS5720_DIGITAL_CTRL1_REG ,
2016-04-27 01:15:57 +03:00
TAS5720_TDM_CFG_SRC , TAS5720_TDM_CFG_SRC ) ;
if ( ret < 0 )
2018-01-29 07:23:00 +03:00
goto error_snd_soc_component_update_bits ;
2016-04-27 01:15:57 +03:00
/* Configure the TDM slot to process audio from */
2018-01-29 07:23:00 +03:00
ret = snd_soc_component_update_bits ( component , TAS5720_DIGITAL_CTRL2_REG ,
2016-04-27 01:15:57 +03:00
TAS5720_TDM_SLOT_SEL_MASK , first_slot ) ;
if ( ret < 0 )
2018-01-29 07:23:00 +03:00
goto error_snd_soc_component_update_bits ;
2016-04-27 01:15:57 +03:00
2018-08-31 17:47:14 +03:00
/* Configure TDM slot width. This is only applicable to TAS5722. */
switch ( tas5720 - > devtype ) {
case TAS5722 :
ret = snd_soc_component_update_bits ( component , TAS5722_DIGITAL_CTRL2_REG ,
TAS5722_TDM_SLOT_16B ,
slot_width = = 16 ?
TAS5722_TDM_SLOT_16B : 0 ) ;
if ( ret < 0 )
goto error_snd_soc_component_update_bits ;
break ;
default :
break ;
}
2016-04-27 01:15:57 +03:00
return 0 ;
2018-01-29 07:23:00 +03:00
error_snd_soc_component_update_bits :
dev_err ( component - > dev , " error configuring TDM mode: %d \n " , ret ) ;
2016-04-27 01:15:57 +03:00
return ret ;
}
2020-07-09 04:56:30 +03:00
static int tas5720_mute ( struct snd_soc_dai * dai , int mute , int direction )
2016-04-27 01:15:57 +03:00
{
2018-01-29 07:23:00 +03:00
struct snd_soc_component * component = dai - > component ;
2016-04-27 01:15:57 +03:00
int ret ;
2018-01-29 07:23:00 +03:00
ret = snd_soc_component_update_bits ( component , TAS5720_DIGITAL_CTRL2_REG ,
2016-04-27 01:15:57 +03:00
TAS5720_MUTE , mute ? TAS5720_MUTE : 0 ) ;
if ( ret < 0 ) {
2018-01-29 07:23:00 +03:00
dev_err ( component - > dev , " error (un-)muting device: %d \n " , ret ) ;
2016-04-27 01:15:57 +03:00
return ret ;
}
return 0 ;
}
static void tas5720_fault_check_work ( struct work_struct * work )
{
struct tas5720_data * tas5720 = container_of ( work , struct tas5720_data ,
fault_check_work . work ) ;
2018-01-29 07:23:00 +03:00
struct device * dev = tas5720 - > component - > dev ;
2016-04-27 01:15:57 +03:00
unsigned int curr_fault ;
int ret ;
ret = regmap_read ( tas5720 - > regmap , TAS5720_FAULT_REG , & curr_fault ) ;
if ( ret < 0 ) {
dev_err ( dev , " failed to read FAULT register: %d \n " , ret ) ;
goto out ;
}
/* Check/handle all errors except SAIF clock errors */
curr_fault & = TAS5720_OCE | TAS5720_DCE | TAS5720_OTE ;
/*
* Only flag errors once for a given occurrence . This is needed as
* the TAS5720 will take time clearing the fault condition internally
* during which we don ' t want to bombard the system with the same
* error message over and over .
*/
if ( ( curr_fault & TAS5720_OCE ) & & ! ( tas5720 - > last_fault & TAS5720_OCE ) )
dev_crit ( dev , " experienced an over current hardware fault \n " ) ;
if ( ( curr_fault & TAS5720_DCE ) & & ! ( tas5720 - > last_fault & TAS5720_DCE ) )
dev_crit ( dev , " experienced a DC detection fault \n " ) ;
if ( ( curr_fault & TAS5720_OTE ) & & ! ( tas5720 - > last_fault & TAS5720_OTE ) )
dev_crit ( dev , " experienced an over temperature fault \n " ) ;
/* Store current fault value so we can detect any changes next time */
tas5720 - > last_fault = curr_fault ;
if ( ! curr_fault )
goto out ;
/*
* Periodically toggle SDZ ( shutdown bit ) H - > L - > H to clear any latching
* faults as long as a fault condition persists . Always going through
* the full sequence no matter the first return value to minimizes
* chances for the device to end up in shutdown mode .
*/
ret = regmap_write_bits ( tas5720 - > regmap , TAS5720_POWER_CTRL_REG ,
TAS5720_SDZ , 0 ) ;
if ( ret < 0 )
dev_err ( dev , " failed to write POWER_CTRL register: %d \n " , ret ) ;
ret = regmap_write_bits ( tas5720 - > regmap , TAS5720_POWER_CTRL_REG ,
TAS5720_SDZ , TAS5720_SDZ ) ;
if ( ret < 0 )
dev_err ( dev , " failed to write POWER_CTRL register: %d \n " , ret ) ;
out :
/* Schedule the next fault check at the specified interval */
schedule_delayed_work ( & tas5720 - > fault_check_work ,
msecs_to_jiffies ( TAS5720_FAULT_CHECK_INTERVAL ) ) ;
}
2018-01-29 07:23:00 +03:00
static int tas5720_codec_probe ( struct snd_soc_component * component )
2016-04-27 01:15:57 +03:00
{
2018-01-29 07:23:00 +03:00
struct tas5720_data * tas5720 = snd_soc_component_get_drvdata ( component ) ;
2017-12-11 22:01:54 +03:00
unsigned int device_id , expected_device_id ;
2016-04-27 01:15:57 +03:00
int ret ;
2018-01-29 07:23:00 +03:00
tas5720 - > component = component ;
2016-04-27 01:15:57 +03:00
ret = regulator_bulk_enable ( ARRAY_SIZE ( tas5720 - > supplies ) ,
tas5720 - > supplies ) ;
if ( ret ! = 0 ) {
2018-01-29 07:23:00 +03:00
dev_err ( component - > dev , " failed to enable supplies: %d \n " , ret ) ;
2016-04-27 01:15:57 +03:00
return ret ;
}
2017-12-11 22:01:54 +03:00
/*
* Take a liberal approach to checking the device ID to allow the
* driver to be used even if the device ID does not match , however
* issue a warning if there is a mismatch .
*/
2016-04-27 01:15:57 +03:00
ret = regmap_read ( tas5720 - > regmap , TAS5720_DEVICE_ID_REG , & device_id ) ;
if ( ret < 0 ) {
2018-01-29 07:23:00 +03:00
dev_err ( component - > dev , " failed to read device ID register: %d \n " ,
2016-04-27 01:15:57 +03:00
ret ) ;
goto probe_fail ;
}
2017-12-11 22:01:54 +03:00
switch ( tas5720 - > devtype ) {
case TAS5720 :
expected_device_id = TAS5720_DEVICE_ID ;
break ;
case TAS5722 :
expected_device_id = TAS5722_DEVICE_ID ;
break ;
default :
2018-01-29 07:23:00 +03:00
dev_err ( component - > dev , " unexpected private driver data \n " ) ;
2017-12-11 22:01:54 +03:00
return - EINVAL ;
2016-04-27 01:15:57 +03:00
}
2017-12-11 22:01:54 +03:00
if ( device_id ! = expected_device_id )
2018-01-29 07:23:00 +03:00
dev_warn ( component - > dev , " wrong device ID. expected: %u read: %u \n " ,
2017-12-11 22:01:54 +03:00
expected_device_id , device_id ) ;
2016-04-27 01:15:57 +03:00
/* Set device to mute */
2018-01-29 07:23:00 +03:00
ret = snd_soc_component_update_bits ( component , TAS5720_DIGITAL_CTRL2_REG ,
2016-04-27 01:15:57 +03:00
TAS5720_MUTE , TAS5720_MUTE ) ;
if ( ret < 0 )
2018-01-29 07:23:00 +03:00
goto error_snd_soc_component_update_bits ;
2016-04-27 01:15:57 +03:00
/*
* Enter shutdown mode - our default when not playing audio - to
* minimize current consumption . On the TAS5720 there is no real down
* side doing so as all device registers are preserved and the wakeup
* of the codec is rather quick which we do using a dapm widget .
*/
2018-01-29 07:23:00 +03:00
ret = snd_soc_component_update_bits ( component , TAS5720_POWER_CTRL_REG ,
2016-04-27 01:15:57 +03:00
TAS5720_SDZ , 0 ) ;
if ( ret < 0 )
2018-01-29 07:23:00 +03:00
goto error_snd_soc_component_update_bits ;
2016-04-27 01:15:57 +03:00
INIT_DELAYED_WORK ( & tas5720 - > fault_check_work , tas5720_fault_check_work ) ;
return 0 ;
2018-01-29 07:23:00 +03:00
error_snd_soc_component_update_bits :
dev_err ( component - > dev , " error configuring device registers: %d \n " , ret ) ;
2016-04-27 01:15:57 +03:00
probe_fail :
regulator_bulk_disable ( ARRAY_SIZE ( tas5720 - > supplies ) ,
tas5720 - > supplies ) ;
return ret ;
}
2018-01-29 07:23:00 +03:00
static void tas5720_codec_remove ( struct snd_soc_component * component )
2016-04-27 01:15:57 +03:00
{
2018-01-29 07:23:00 +03:00
struct tas5720_data * tas5720 = snd_soc_component_get_drvdata ( component ) ;
2016-04-27 01:15:57 +03:00
int ret ;
cancel_delayed_work_sync ( & tas5720 - > fault_check_work ) ;
ret = regulator_bulk_disable ( ARRAY_SIZE ( tas5720 - > supplies ) ,
tas5720 - > supplies ) ;
if ( ret < 0 )
2018-01-29 07:23:00 +03:00
dev_err ( component - > dev , " failed to disable supplies: %d \n " , ret ) ;
2016-04-27 01:15:57 +03:00
} ;
static int tas5720_dac_event ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * kcontrol , int event )
{
2018-01-29 07:23:00 +03:00
struct snd_soc_component * component = snd_soc_dapm_to_component ( w - > dapm ) ;
struct tas5720_data * tas5720 = snd_soc_component_get_drvdata ( component ) ;
2016-04-27 01:15:57 +03:00
int ret ;
if ( event & SND_SOC_DAPM_POST_PMU ) {
/* Take TAS5720 out of shutdown mode */
2018-01-29 07:23:00 +03:00
ret = snd_soc_component_update_bits ( component , TAS5720_POWER_CTRL_REG ,
2016-04-27 01:15:57 +03:00
TAS5720_SDZ , TAS5720_SDZ ) ;
if ( ret < 0 ) {
2018-01-29 07:23:00 +03:00
dev_err ( component - > dev , " error waking component: %d \n " , ret ) ;
2016-04-27 01:15:57 +03:00
return ret ;
}
/*
* Observe codec shutdown - to - active time . The datasheet only
* lists a nominal value however just use - it as - is without
* additional padding to minimize the delay introduced in
* starting to play audio ( actually there is other setup done
* by the ASoC framework that will provide additional delays ,
* so we should always be safe ) .
*/
msleep ( 25 ) ;
/* Turn on TAS5720 periodic fault checking/handling */
tas5720 - > last_fault = 0 ;
schedule_delayed_work ( & tas5720 - > fault_check_work ,
msecs_to_jiffies ( TAS5720_FAULT_CHECK_INTERVAL ) ) ;
} else if ( event & SND_SOC_DAPM_PRE_PMD ) {
/* Disable TAS5720 periodic fault checking/handling */
cancel_delayed_work_sync ( & tas5720 - > fault_check_work ) ;
/* Place TAS5720 in shutdown mode to minimize current draw */
2018-01-29 07:23:00 +03:00
ret = snd_soc_component_update_bits ( component , TAS5720_POWER_CTRL_REG ,
2016-04-27 01:15:57 +03:00
TAS5720_SDZ , 0 ) ;
if ( ret < 0 ) {
2018-01-29 07:23:00 +03:00
dev_err ( component - > dev , " error shutting down component: %d \n " ,
2016-04-27 01:15:57 +03:00
ret ) ;
return ret ;
}
}
return 0 ;
}
# ifdef CONFIG_PM
2018-01-29 07:23:00 +03:00
static int tas5720_suspend ( struct snd_soc_component * component )
2016-04-27 01:15:57 +03:00
{
2018-01-29 07:23:00 +03:00
struct tas5720_data * tas5720 = snd_soc_component_get_drvdata ( component ) ;
2016-04-27 01:15:57 +03:00
int ret ;
regcache_cache_only ( tas5720 - > regmap , true ) ;
regcache_mark_dirty ( tas5720 - > regmap ) ;
ret = regulator_bulk_disable ( ARRAY_SIZE ( tas5720 - > supplies ) ,
tas5720 - > supplies ) ;
if ( ret < 0 )
2018-01-29 07:23:00 +03:00
dev_err ( component - > dev , " failed to disable supplies: %d \n " , ret ) ;
2016-04-27 01:15:57 +03:00
return ret ;
}
2018-01-29 07:23:00 +03:00
static int tas5720_resume ( struct snd_soc_component * component )
2016-04-27 01:15:57 +03:00
{
2018-01-29 07:23:00 +03:00
struct tas5720_data * tas5720 = snd_soc_component_get_drvdata ( component ) ;
2016-04-27 01:15:57 +03:00
int ret ;
ret = regulator_bulk_enable ( ARRAY_SIZE ( tas5720 - > supplies ) ,
tas5720 - > supplies ) ;
if ( ret < 0 ) {
2018-01-29 07:23:00 +03:00
dev_err ( component - > dev , " failed to enable supplies: %d \n " , ret ) ;
2016-04-27 01:15:57 +03:00
return ret ;
}
regcache_cache_only ( tas5720 - > regmap , false ) ;
ret = regcache_sync ( tas5720 - > regmap ) ;
if ( ret < 0 ) {
2018-01-29 07:23:00 +03:00
dev_err ( component - > dev , " failed to sync regcache: %d \n " , ret ) ;
2016-04-27 01:15:57 +03:00
return ret ;
}
return 0 ;
}
# else
# define tas5720_suspend NULL
# define tas5720_resume NULL
# endif
static bool tas5720_is_volatile_reg ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
case TAS5720_DEVICE_ID_REG :
case TAS5720_FAULT_REG :
return true ;
default :
return false ;
}
}
static const struct regmap_config tas5720_regmap_config = {
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = TAS5720_MAX_REG ,
. cache_type = REGCACHE_RBTREE ,
. volatile_reg = tas5720_is_volatile_reg ,
} ;
2017-12-11 22:01:55 +03:00
static const struct regmap_config tas5722_regmap_config = {
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = TAS5722_MAX_REG ,
. cache_type = REGCACHE_RBTREE ,
. volatile_reg = tas5720_is_volatile_reg ,
} ;
2016-04-27 01:15:57 +03:00
/*
* DAC analog gain . There are four discrete values to select from , ranging
* from 19.2 dB to 26.3 dB .
*/
static const DECLARE_TLV_DB_RANGE ( dac_analog_tlv ,
0x0 , 0x0 , TLV_DB_SCALE_ITEM ( 1920 , 0 , 0 ) ,
0x1 , 0x1 , TLV_DB_SCALE_ITEM ( 2070 , 0 , 0 ) ,
0x2 , 0x2 , TLV_DB_SCALE_ITEM ( 2350 , 0 , 0 ) ,
0x3 , 0x3 , TLV_DB_SCALE_ITEM ( 2630 , 0 , 0 ) ,
) ;
/*
2018-08-31 17:47:13 +03:00
* DAC digital volumes . From - 103.5 to 24 dB in 0.5 dB or 0.25 dB steps
* depending on the device . Note that setting the gain below - 100 dB
* ( register value < 0x7 ) is effectively a MUTE as per device datasheet .
*
* Note that for the TAS5722 the digital volume controls are actually split
* over two registers , so we need custom getters / setters for access .
2016-04-27 01:15:57 +03:00
*/
2018-08-31 17:47:13 +03:00
static DECLARE_TLV_DB_SCALE ( tas5720_dac_tlv , - 10350 , 50 , 0 ) ;
static DECLARE_TLV_DB_SCALE ( tas5722_dac_tlv , - 10350 , 25 , 0 ) ;
static int tas5722_volume_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct snd_soc_component * component = snd_soc_kcontrol_component ( kcontrol ) ;
unsigned int val ;
ASoC: soc-component: merge snd_soc_component_read() and snd_soc_component_read32()
We had read/write function for Codec, Platform, etc,
but these has been merged into snd_soc_component_read/write().
Internally, it is using regmap or driver function.
In read case, each styles are like below
regmap
ret = regmap_read(..., reg, &val);
driver function
val = xxx->read(..., reg);
Because of this kind of different style, to keep same read style,
when we merged each read function into snd_soc_component_read(),
we created snd_soc_component_read32(), like below.
commit 738b49efe6c6 ("ASoC: add snd_soc_component_read32")
(1) val = snd_soc_component_read32(component, reg);
(2) ret = snd_soc_component_read(component, reg, &val);
Many drivers are using snd_soc_component_read32(), and
some drivers are using snd_soc_component_read() today.
In generally, we don't check read function successes,
because, we will have many other issues at initial timing
if read function didn't work.
Now we can use soc_component_err() when error case.
This means, it is easy to notice if error occurred.
This patch aggressively merge snd_soc_component_read() and _read32(),
and makes snd_soc_component_read/write() as generally style.
This patch do
1) merge snd_soc_component_read() and snd_soc_component_read32()
2) it uses soc_component_err() when error case (easy to notice)
3) keeps read32 for now by #define
4) update snd_soc_component_read() for all drivers
Because _read() user drivers are not too many, this patch changes
all user drivers.
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Reviewed-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Link: https://lore.kernel.org/r/87sgev4mfl.wl-kuninori.morimoto.gx@renesas.com
Signed-off-by: Mark Brown <broonie@kernel.org>
2020-06-16 08:19:41 +03:00
val = snd_soc_component_read ( component , TAS5720_VOLUME_CTRL_REG ) ;
2018-08-31 17:47:13 +03:00
ucontrol - > value . integer . value [ 0 ] = val < < 1 ;
ASoC: soc-component: merge snd_soc_component_read() and snd_soc_component_read32()
We had read/write function for Codec, Platform, etc,
but these has been merged into snd_soc_component_read/write().
Internally, it is using regmap or driver function.
In read case, each styles are like below
regmap
ret = regmap_read(..., reg, &val);
driver function
val = xxx->read(..., reg);
Because of this kind of different style, to keep same read style,
when we merged each read function into snd_soc_component_read(),
we created snd_soc_component_read32(), like below.
commit 738b49efe6c6 ("ASoC: add snd_soc_component_read32")
(1) val = snd_soc_component_read32(component, reg);
(2) ret = snd_soc_component_read(component, reg, &val);
Many drivers are using snd_soc_component_read32(), and
some drivers are using snd_soc_component_read() today.
In generally, we don't check read function successes,
because, we will have many other issues at initial timing
if read function didn't work.
Now we can use soc_component_err() when error case.
This means, it is easy to notice if error occurred.
This patch aggressively merge snd_soc_component_read() and _read32(),
and makes snd_soc_component_read/write() as generally style.
This patch do
1) merge snd_soc_component_read() and snd_soc_component_read32()
2) it uses soc_component_err() when error case (easy to notice)
3) keeps read32 for now by #define
4) update snd_soc_component_read() for all drivers
Because _read() user drivers are not too many, this patch changes
all user drivers.
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Reviewed-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Link: https://lore.kernel.org/r/87sgev4mfl.wl-kuninori.morimoto.gx@renesas.com
Signed-off-by: Mark Brown <broonie@kernel.org>
2020-06-16 08:19:41 +03:00
val = snd_soc_component_read ( component , TAS5722_DIGITAL_CTRL2_REG ) ;
2018-08-31 17:47:13 +03:00
ucontrol - > value . integer . value [ 0 ] | = val & TAS5722_VOL_CONTROL_LSB ;
return 0 ;
}
static int tas5722_volume_set ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct snd_soc_component * component = snd_soc_kcontrol_component ( kcontrol ) ;
unsigned int sel = ucontrol - > value . integer . value [ 0 ] ;
snd_soc_component_write ( component , TAS5720_VOLUME_CTRL_REG , sel > > 1 ) ;
snd_soc_component_update_bits ( component , TAS5722_DIGITAL_CTRL2_REG ,
TAS5722_VOL_CONTROL_LSB , sel ) ;
return 0 ;
}
2016-04-27 01:15:57 +03:00
static const struct snd_kcontrol_new tas5720_snd_controls [ ] = {
SOC_SINGLE_TLV ( " Speaker Driver Playback Volume " ,
2018-08-31 17:47:13 +03:00
TAS5720_VOLUME_CTRL_REG , 0 , 0xff , 0 , tas5720_dac_tlv ) ,
SOC_SINGLE_TLV ( " Speaker Driver Analog Gain " , TAS5720_ANALOG_CTRL_REG ,
TAS5720_ANALOG_GAIN_SHIFT , 3 , 0 , dac_analog_tlv ) ,
} ;
static const struct snd_kcontrol_new tas5722_snd_controls [ ] = {
SOC_SINGLE_EXT_TLV ( " Speaker Driver Playback Volume " ,
0 , 0 , 511 , 0 ,
tas5722_volume_get , tas5722_volume_set ,
tas5722_dac_tlv ) ,
2016-04-27 01:15:57 +03:00
SOC_SINGLE_TLV ( " Speaker Driver Analog Gain " , TAS5720_ANALOG_CTRL_REG ,
TAS5720_ANALOG_GAIN_SHIFT , 3 , 0 , dac_analog_tlv ) ,
} ;
static const struct snd_soc_dapm_widget tas5720_dapm_widgets [ ] = {
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 , tas5720_dac_event ,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD ) ,
SND_SOC_DAPM_OUTPUT ( " OUT " )
} ;
static const struct snd_soc_dapm_route tas5720_audio_map [ ] = {
{ " DAC " , NULL , " DAC IN " } ,
{ " OUT " , NULL , " DAC " } ,
} ;
2018-01-29 07:23:00 +03:00
static const struct snd_soc_component_driver soc_component_dev_tas5720 = {
. probe = tas5720_codec_probe ,
. remove = tas5720_codec_remove ,
. suspend = tas5720_suspend ,
. resume = tas5720_resume ,
. controls = tas5720_snd_controls ,
. num_controls = ARRAY_SIZE ( tas5720_snd_controls ) ,
. dapm_widgets = tas5720_dapm_widgets ,
. num_dapm_widgets = ARRAY_SIZE ( tas5720_dapm_widgets ) ,
. dapm_routes = tas5720_audio_map ,
. num_dapm_routes = ARRAY_SIZE ( tas5720_audio_map ) ,
. idle_bias_on = 1 ,
. use_pmdown_time = 1 ,
. endianness = 1 ,
. non_legacy_dai_naming = 1 ,
2016-04-27 01:15:57 +03:00
} ;
2018-08-31 17:47:13 +03:00
static const struct snd_soc_component_driver soc_component_dev_tas5722 = {
. probe = tas5720_codec_probe ,
. remove = tas5720_codec_remove ,
. suspend = tas5720_suspend ,
. resume = tas5720_resume ,
. controls = tas5722_snd_controls ,
. num_controls = ARRAY_SIZE ( tas5722_snd_controls ) ,
. dapm_widgets = tas5720_dapm_widgets ,
. num_dapm_widgets = ARRAY_SIZE ( tas5720_dapm_widgets ) ,
. dapm_routes = tas5720_audio_map ,
. num_dapm_routes = ARRAY_SIZE ( tas5720_audio_map ) ,
. idle_bias_on = 1 ,
. use_pmdown_time = 1 ,
. endianness = 1 ,
. non_legacy_dai_naming = 1 ,
} ;
2016-04-27 01:15:57 +03:00
/* PCM rates supported by the TAS5720 driver */
# define TAS5720_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 )
/* Formats supported by TAS5720 driver */
# define TAS5720_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE |\
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE )
2017-08-18 15:05:59 +03:00
static const struct snd_soc_dai_ops tas5720_speaker_dai_ops = {
2016-04-27 01:15:57 +03:00
. hw_params = tas5720_hw_params ,
. set_fmt = tas5720_set_dai_fmt ,
. set_tdm_slot = tas5720_set_dai_tdm_slot ,
2020-07-09 04:56:30 +03:00
. mute_stream = tas5720_mute ,
. no_capture_mute = 1 ,
2016-04-27 01:15:57 +03:00
} ;
/*
* TAS5720 DAI structure
*
* Note that were are advertising . playback . channels_max = 2 despite this being
* a mono amplifier . The reason for that is that some serial ports such as TI ' s
* McASP module have a minimum number of channels ( 2 ) that they can output .
* Advertising more channels than we have will allow us to interface with such
* a serial port without really any negative side effects as the TAS5720 will
* simply ignore any extra channel ( s ) asides from the one channel that is
* configured to be played back .
*/
static struct snd_soc_dai_driver tas5720_dai [ ] = {
{
. name = " tas5720-amplifier " ,
. playback = {
. stream_name = " Playback " ,
. channels_min = 1 ,
. channels_max = 2 ,
. rates = TAS5720_RATES ,
. formats = TAS5720_FORMATS ,
} ,
. ops = & tas5720_speaker_dai_ops ,
} ,
} ;
static int tas5720_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct device * dev = & client - > dev ;
struct tas5720_data * data ;
2017-12-11 22:01:55 +03:00
const struct regmap_config * regmap_config ;
2016-04-27 01:15:57 +03:00
int ret ;
int i ;
data = devm_kzalloc ( dev , sizeof ( * data ) , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
data - > tas5720_client = client ;
2017-12-11 22:01:54 +03:00
data - > devtype = id - > driver_data ;
2017-12-11 22:01:55 +03:00
switch ( id - > driver_data ) {
case TAS5720 :
regmap_config = & tas5720_regmap_config ;
break ;
case TAS5722 :
regmap_config = & tas5722_regmap_config ;
break ;
default :
dev_err ( dev , " unexpected private driver data \n " ) ;
return - EINVAL ;
}
data - > regmap = devm_regmap_init_i2c ( client , regmap_config ) ;
2016-04-27 01:15:57 +03:00
if ( IS_ERR ( data - > regmap ) ) {
ret = PTR_ERR ( data - > regmap ) ;
dev_err ( dev , " failed to allocate register map: %d \n " , ret ) ;
return ret ;
}
for ( i = 0 ; i < ARRAY_SIZE ( data - > supplies ) ; i + + )
data - > supplies [ i ] . supply = tas5720_supply_names [ i ] ;
ret = devm_regulator_bulk_get ( dev , ARRAY_SIZE ( data - > supplies ) ,
data - > supplies ) ;
if ( ret ! = 0 ) {
dev_err ( dev , " failed to request supplies: %d \n " , ret ) ;
return ret ;
}
dev_set_drvdata ( dev , data ) ;
2018-08-31 17:47:13 +03:00
switch ( id - > driver_data ) {
case TAS5720 :
ret = devm_snd_soc_register_component ( & client - > dev ,
& soc_component_dev_tas5720 ,
tas5720_dai ,
ARRAY_SIZE ( tas5720_dai ) ) ;
break ;
case TAS5722 :
ret = devm_snd_soc_register_component ( & client - > dev ,
& soc_component_dev_tas5722 ,
tas5720_dai ,
ARRAY_SIZE ( tas5720_dai ) ) ;
break ;
default :
dev_err ( dev , " unexpected private driver data \n " ) ;
return - EINVAL ;
}
2016-04-27 01:15:57 +03:00
if ( ret < 0 ) {
2018-01-29 07:23:00 +03:00
dev_err ( dev , " failed to register component: %d \n " , ret ) ;
2016-04-27 01:15:57 +03:00
return ret ;
}
return 0 ;
}
static const struct i2c_device_id tas5720_id [ ] = {
2017-12-11 22:01:54 +03:00
{ " tas5720 " , TAS5720 } ,
{ " tas5722 " , TAS5722 } ,
2016-04-27 01:15:57 +03:00
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , tas5720_id ) ;
# if IS_ENABLED(CONFIG_OF)
static const struct of_device_id tas5720_of_match [ ] = {
{ . compatible = " ti,tas5720 " , } ,
2017-12-11 22:01:54 +03:00
{ . compatible = " ti,tas5722 " , } ,
2016-04-27 01:15:57 +03:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , tas5720_of_match ) ;
# endif
static struct i2c_driver tas5720_i2c_driver = {
. driver = {
. name = " tas5720 " ,
. of_match_table = of_match_ptr ( tas5720_of_match ) ,
} ,
. probe = tas5720_probe ,
. id_table = tas5720_id ,
} ;
module_i2c_driver ( tas5720_i2c_driver ) ;
MODULE_AUTHOR ( " Andreas Dannenberg <dannenberg@ti.com> " ) ;
MODULE_DESCRIPTION ( " TAS5720 Audio amplifier driver " ) ;
MODULE_LICENSE ( " GPL " ) ;