2023-08-04 11:46:02 +01:00
// SPDX-License-Identifier: GPL-2.0
//
// CS42L43 CODEC driver jack handling
//
// Copyright (C) 2022-2023 Cirrus Logic, Inc. and
// Cirrus Logic International Semiconductor Ltd.
# include <linux/build_bug.h>
# include <linux/delay.h>
# include <linux/errno.h>
# include <linux/irq.h>
# include <linux/jiffies.h>
# include <linux/mfd/cs42l43.h>
# include <linux/mfd/cs42l43-regs.h>
# include <linux/pm_runtime.h>
# include <linux/property.h>
# include <sound/control.h>
# include <sound/jack.h>
# include <sound/pcm.h>
# include <sound/pcm_params.h>
# include <sound/soc-component.h>
# include <sound/soc.h>
# include "cs42l43.h"
static const unsigned int cs42l43_accdet_us [ ] = {
20 , 100 , 1000 , 10000 , 50000 , 75000 , 100000 , 200000
} ;
static const unsigned int cs42l43_accdet_db_ms [ ] = {
0 , 125 , 250 , 500 , 750 , 1000 , 1250 , 1500
} ;
static const unsigned int cs42l43_accdet_ramp_ms [ ] = { 10 , 40 , 90 , 170 } ;
static const unsigned int cs42l43_accdet_bias_sense [ ] = {
2023-10-10 11:24:25 +01:00
14 , 24 , 43 , 52 , 61 , 71 , 90 , 99 , 0 ,
2023-08-04 11:46:02 +01:00
} ;
static int cs42l43_find_index ( struct cs42l43_codec * priv , const char * const prop ,
unsigned int defval , unsigned int * val ,
const unsigned int * values , const int nvalues )
{
struct cs42l43 * cs42l43 = priv - > core ;
int i , ret ;
ret = device_property_read_u32 ( cs42l43 - > dev , prop , & defval ) ;
if ( ret ! = - EINVAL & & ret < 0 ) {
dev_err ( priv - > dev , " Property %s malformed: %d \n " , prop , ret ) ;
return ret ;
}
if ( val )
* val = defval ;
for ( i = 0 ; i < nvalues ; i + + )
if ( defval = = values [ i ] )
return i ;
dev_err ( priv - > dev , " Invalid value for property %s: %d \n " , prop , defval ) ;
return - EINVAL ;
}
int cs42l43_set_jack ( struct snd_soc_component * component ,
struct snd_soc_jack * jack , void * d )
{
struct cs42l43_codec * priv = snd_soc_component_get_drvdata ( component ) ;
struct cs42l43 * cs42l43 = priv - > core ;
/* This tip sense invert is always set, HW wants an inverted signal */
unsigned int tip_deb = CS42L43_TIPSENSE_INV_MASK ;
unsigned int hs2 = 0x2 < < CS42L43_HSDET_MODE_SHIFT ;
unsigned int autocontrol = 0 , pdncntl = 0 ;
int ret ;
dev_dbg ( priv - > dev , " Configure accessory detect \n " ) ;
ret = pm_runtime_resume_and_get ( priv - > dev ) ;
if ( ret ) {
dev_err ( priv - > dev , " Failed to resume for jack config: %d \n " , ret ) ;
return ret ;
}
mutex_lock ( & priv - > jack_lock ) ;
priv - > jack_hp = jack ;
if ( ! jack )
goto done ;
ret = device_property_count_u32 ( cs42l43 - > dev , " cirrus,buttons-ohms " ) ;
if ( ret ! = - EINVAL ) {
if ( ret < 0 ) {
dev_err ( priv - > dev , " Property cirrus,buttons-ohms malformed: %d \n " ,
ret ) ;
goto error ;
}
if ( ret > CS42L43_N_BUTTONS ) {
ret = - EINVAL ;
dev_err ( priv - > dev , " Property cirrus,buttons-ohms too many entries \n " ) ;
goto error ;
}
device_property_read_u32_array ( cs42l43 - > dev , " cirrus,buttons-ohms " ,
priv - > buttons , ret ) ;
} else {
priv - > buttons [ 0 ] = 70 ;
priv - > buttons [ 1 ] = 185 ;
priv - > buttons [ 2 ] = 355 ;
priv - > buttons [ 3 ] = 735 ;
}
ret = cs42l43_find_index ( priv , " cirrus,detect-us " , 10000 , & priv - > detect_us ,
cs42l43_accdet_us , ARRAY_SIZE ( cs42l43_accdet_us ) ) ;
if ( ret < 0 )
goto error ;
hs2 | = ret < < CS42L43_AUTO_HSDET_TIME_SHIFT ;
priv - > bias_low = device_property_read_bool ( cs42l43 - > dev , " cirrus,bias-low " ) ;
ret = cs42l43_find_index ( priv , " cirrus,bias-ramp-ms " , 170 ,
& priv - > bias_ramp_ms , cs42l43_accdet_ramp_ms ,
ARRAY_SIZE ( cs42l43_accdet_ramp_ms ) ) ;
if ( ret < 0 )
goto error ;
hs2 | = ret < < CS42L43_HSBIAS_RAMP_SHIFT ;
ret = cs42l43_find_index ( priv , " cirrus,bias-sense-microamp " , 0 ,
& priv - > bias_sense_ua , cs42l43_accdet_bias_sense ,
ARRAY_SIZE ( cs42l43_accdet_bias_sense ) ) ;
if ( ret < 0 )
goto error ;
if ( priv - > bias_sense_ua )
autocontrol | = ret < < CS42L43_HSBIAS_SENSE_TRIP_SHIFT ;
if ( ! device_property_read_bool ( cs42l43 - > dev , " cirrus,button-automute " ) )
autocontrol | = CS42L43_S0_AUTO_ADCMUTE_DISABLE_MASK ;
ret = device_property_read_u32 ( cs42l43 - > dev , " cirrus,tip-debounce-ms " ,
& priv - > tip_debounce_ms ) ;
if ( ret < 0 & & ret ! = - EINVAL ) {
dev_err ( priv - > dev , " Property cirrus,tip-debounce-ms malformed: %d \n " , ret ) ;
goto error ;
}
/* This tip sense invert is set normally, as TIPSENSE_INV already inverted */
if ( device_property_read_bool ( cs42l43 - > dev , " cirrus,tip-invert " ) )
autocontrol | = 0x1 < < CS42L43_JACKDET_INV_SHIFT ;
if ( device_property_read_bool ( cs42l43 - > dev , " cirrus,tip-disable-pullup " ) )
autocontrol | = 0x1 < < CS42L43_JACKDET_MODE_SHIFT ;
else
autocontrol | = 0x3 < < CS42L43_JACKDET_MODE_SHIFT ;
ret = cs42l43_find_index ( priv , " cirrus,tip-fall-db-ms " , 500 ,
NULL , cs42l43_accdet_db_ms ,
ARRAY_SIZE ( cs42l43_accdet_db_ms ) ) ;
if ( ret < 0 )
goto error ;
tip_deb | = ret < < CS42L43_TIPSENSE_FALLING_DB_TIME_SHIFT ;
ret = cs42l43_find_index ( priv , " cirrus,tip-rise-db-ms " , 500 ,
NULL , cs42l43_accdet_db_ms ,
ARRAY_SIZE ( cs42l43_accdet_db_ms ) ) ;
if ( ret < 0 )
goto error ;
tip_deb | = ret < < CS42L43_TIPSENSE_RISING_DB_TIME_SHIFT ;
if ( device_property_read_bool ( cs42l43 - > dev , " cirrus,use-ring-sense " ) ) {
unsigned int ring_deb = 0 ;
priv - > use_ring_sense = true ;
/* HW wants an inverted signal, so invert the invert */
if ( ! device_property_read_bool ( cs42l43 - > dev , " cirrus,ring-invert " ) )
ring_deb | = CS42L43_RINGSENSE_INV_MASK ;
if ( ! device_property_read_bool ( cs42l43 - > dev ,
" cirrus,ring-disable-pullup " ) )
ring_deb | = CS42L43_RINGSENSE_PULLUP_PDNB_MASK ;
ret = cs42l43_find_index ( priv , " cirrus,ring-fall-db-ms " , 500 ,
NULL , cs42l43_accdet_db_ms ,
ARRAY_SIZE ( cs42l43_accdet_db_ms ) ) ;
if ( ret < 0 )
goto error ;
ring_deb | = ret < < CS42L43_RINGSENSE_FALLING_DB_TIME_SHIFT ;
ret = cs42l43_find_index ( priv , " cirrus,ring-rise-db-ms " , 500 ,
NULL , cs42l43_accdet_db_ms ,
ARRAY_SIZE ( cs42l43_accdet_db_ms ) ) ;
if ( ret < 0 )
goto error ;
ring_deb | = ret < < CS42L43_RINGSENSE_RISING_DB_TIME_SHIFT ;
pdncntl | = CS42L43_RING_SENSE_EN_MASK ;
regmap_update_bits ( cs42l43 - > regmap , CS42L43_RINGSENSE_DEB_CTRL ,
CS42L43_RINGSENSE_INV_MASK |
CS42L43_RINGSENSE_PULLUP_PDNB_MASK |
CS42L43_RINGSENSE_FALLING_DB_TIME_MASK |
CS42L43_RINGSENSE_RISING_DB_TIME_MASK ,
ring_deb ) ;
}
regmap_update_bits ( cs42l43 - > regmap , CS42L43_TIPSENSE_DEB_CTRL ,
CS42L43_TIPSENSE_INV_MASK |
CS42L43_TIPSENSE_FALLING_DB_TIME_MASK |
CS42L43_TIPSENSE_RISING_DB_TIME_MASK , tip_deb ) ;
regmap_update_bits ( cs42l43 - > regmap , CS42L43_HS2 ,
CS42L43_HSBIAS_RAMP_MASK | CS42L43_HSDET_MODE_MASK |
CS42L43_AUTO_HSDET_TIME_MASK , hs2 ) ;
done :
ret = 0 ;
regmap_update_bits ( cs42l43 - > regmap , CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL ,
CS42L43_JACKDET_MODE_MASK | CS42L43_S0_AUTO_ADCMUTE_DISABLE_MASK |
CS42L43_HSBIAS_SENSE_TRIP_MASK , autocontrol ) ;
regmap_update_bits ( cs42l43 - > regmap , CS42L43_PDNCNTL ,
CS42L43_RING_SENSE_EN_MASK , pdncntl ) ;
dev_dbg ( priv - > dev , " Successfully configured accessory detect \n " ) ;
error :
mutex_unlock ( & priv - > jack_lock ) ;
pm_runtime_mark_last_busy ( priv - > dev ) ;
pm_runtime_put_autosuspend ( priv - > dev ) ;
return ret ;
}
static void cs42l43_start_hs_bias ( struct cs42l43_codec * priv , bool force_high )
{
struct cs42l43 * cs42l43 = priv - > core ;
unsigned int val = 0x3 < < CS42L43_HSBIAS_MODE_SHIFT ;
dev_dbg ( priv - > dev , " Start headset bias \n " ) ;
regmap_update_bits ( cs42l43 - > regmap , CS42L43_HS2 ,
CS42L43_HS_CLAMP_DISABLE_MASK , CS42L43_HS_CLAMP_DISABLE_MASK ) ;
if ( ! force_high & & priv - > bias_low )
val = 0x2 < < CS42L43_HSBIAS_MODE_SHIFT ;
regmap_update_bits ( cs42l43 - > regmap , CS42L43_MIC_DETECT_CONTROL_1 ,
CS42L43_HSBIAS_MODE_MASK , val ) ;
msleep ( priv - > bias_ramp_ms ) ;
}
static void cs42l43_stop_hs_bias ( struct cs42l43_codec * priv )
{
struct cs42l43 * cs42l43 = priv - > core ;
dev_dbg ( priv - > dev , " Stop headset bias \n " ) ;
regmap_update_bits ( cs42l43 - > regmap , CS42L43_MIC_DETECT_CONTROL_1 ,
CS42L43_HSBIAS_MODE_MASK , 0x1 < < CS42L43_HSBIAS_MODE_SHIFT ) ;
regmap_update_bits ( cs42l43 - > regmap , CS42L43_HS2 ,
CS42L43_HS_CLAMP_DISABLE_MASK , 0 ) ;
}
irqreturn_t cs42l43_bias_detect_clamp ( int irq , void * data )
{
struct cs42l43_codec * priv = data ;
queue_delayed_work ( system_wq , & priv - > bias_sense_timeout ,
msecs_to_jiffies ( 250 ) ) ;
return IRQ_HANDLED ;
}
# define CS42L43_JACK_PRESENT 0x3
# define CS42L43_JACK_ABSENT 0x0
# define CS42L43_JACK_OPTICAL (SND_JACK_MECHANICAL | SND_JACK_AVOUT)
# define CS42L43_JACK_HEADPHONE (SND_JACK_MECHANICAL | SND_JACK_HEADPHONE)
# define CS42L43_JACK_HEADSET (SND_JACK_MECHANICAL | SND_JACK_HEADSET)
# define CS42L43_JACK_LINEOUT (SND_JACK_MECHANICAL | SND_JACK_LINEOUT)
# define CS42L43_JACK_LINEIN (SND_JACK_MECHANICAL | SND_JACK_LINEIN)
# define CS42L43_JACK_EXTENSION (SND_JACK_MECHANICAL)
# define CS42L43_JACK_BUTTONS (SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | \
SND_JACK_BTN_3 | SND_JACK_BTN_4 | SND_JACK_BTN_5 )
static inline bool cs42l43_jack_present ( struct cs42l43_codec * priv )
{
struct cs42l43 * cs42l43 = priv - > core ;
unsigned int sts = 0 ;
regmap_read ( cs42l43 - > regmap , CS42L43_TIP_RING_SENSE_INTERRUPT_STATUS , & sts ) ;
sts = ( sts > > CS42L43_TIPSENSE_PLUG_DB_STS_SHIFT ) & CS42L43_JACK_PRESENT ;
return sts = = CS42L43_JACK_PRESENT ;
}
static void cs42l43_start_button_detect ( struct cs42l43_codec * priv )
{
struct cs42l43 * cs42l43 = priv - > core ;
unsigned int val = 0x3 < < CS42L43_BUTTON_DETECT_MODE_SHIFT ;
dev_dbg ( priv - > dev , " Start button detect \n " ) ;
priv - > button_detect_running = true ;
if ( priv - > bias_low )
val = 0x1 < < CS42L43_BUTTON_DETECT_MODE_SHIFT ;
regmap_update_bits ( cs42l43 - > regmap , CS42L43_MIC_DETECT_CONTROL_1 ,
CS42L43_BUTTON_DETECT_MODE_MASK |
CS42L43_MIC_LVL_DET_DISABLE_MASK , val ) ;
if ( priv - > bias_sense_ua ) {
regmap_update_bits ( cs42l43 - > regmap ,
CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL ,
CS42L43_HSBIAS_SENSE_EN_MASK |
CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK ,
CS42L43_HSBIAS_SENSE_EN_MASK |
CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK ) ;
}
}
static void cs42l43_stop_button_detect ( struct cs42l43_codec * priv )
{
struct cs42l43 * cs42l43 = priv - > core ;
dev_dbg ( priv - > dev , " Stop button detect \n " ) ;
if ( priv - > bias_sense_ua ) {
regmap_update_bits ( cs42l43 - > regmap ,
CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL ,
CS42L43_HSBIAS_SENSE_EN_MASK |
CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK , 0 ) ;
}
regmap_update_bits ( cs42l43 - > regmap , CS42L43_MIC_DETECT_CONTROL_1 ,
CS42L43_BUTTON_DETECT_MODE_MASK |
CS42L43_MIC_LVL_DET_DISABLE_MASK ,
CS42L43_MIC_LVL_DET_DISABLE_MASK ) ;
priv - > button_detect_running = false ;
}
# define CS42L43_BUTTON_COMB_MAX 512
# define CS42L43_BUTTON_ROUT 2210
void cs42l43_button_press_work ( struct work_struct * work )
{
struct cs42l43_codec * priv = container_of ( work , struct cs42l43_codec ,
button_press_work . work ) ;
struct cs42l43 * cs42l43 = priv - > core ;
unsigned int buttons = 0 ;
unsigned int val = 0 ;
int i , ret ;
ret = pm_runtime_resume_and_get ( priv - > dev ) ;
if ( ret ) {
dev_err ( priv - > dev , " Failed to resume for button press: %d \n " , ret ) ;
return ;
}
mutex_lock ( & priv - > jack_lock ) ;
if ( ! priv - > button_detect_running ) {
dev_dbg ( priv - > dev , " Spurious button press IRQ \n " ) ;
goto error ;
}
regmap_read ( cs42l43 - > regmap , CS42L43_DETECT_STATUS_1 , & val ) ;
/* Bail if jack removed, the button is irrelevant and likely invalid */
if ( ! cs42l43_jack_present ( priv ) ) {
dev_dbg ( priv - > dev , " Button ignored due to removal \n " ) ;
goto error ;
}
if ( val & CS42L43_HSBIAS_CLAMP_STS_MASK ) {
dev_dbg ( priv - > dev , " Button ignored due to bias sense \n " ) ;
goto error ;
}
val = ( val & CS42L43_HSDET_DC_STS_MASK ) > > CS42L43_HSDET_DC_STS_SHIFT ;
val = ( ( CS42L43_BUTTON_COMB_MAX < < 20 ) / ( val + 1 ) ) - ( 1 < < 20 ) ;
if ( val )
val = ( CS42L43_BUTTON_ROUT < < 20 ) / val ;
else
val = UINT_MAX ;
for ( i = 0 ; i < CS42L43_N_BUTTONS ; i + + ) {
if ( val < priv - > buttons [ i ] ) {
buttons = SND_JACK_BTN_0 > > i ;
dev_dbg ( priv - > dev , " Detected button %d at %d Ohms \n " , i , val ) ;
break ;
}
}
if ( ! buttons )
dev_dbg ( priv - > dev , " Unrecognised button: %d Ohms \n " , val ) ;
snd_soc_jack_report ( priv - > jack_hp , buttons , CS42L43_JACK_BUTTONS ) ;
error :
mutex_unlock ( & priv - > jack_lock ) ;
pm_runtime_mark_last_busy ( priv - > dev ) ;
pm_runtime_put_autosuspend ( priv - > dev ) ;
}
irqreturn_t cs42l43_button_press ( int irq , void * data )
{
struct cs42l43_codec * priv = data ;
// Wait for 2 full cycles of comb filter to ensure good reading
queue_delayed_work ( system_wq , & priv - > button_press_work ,
msecs_to_jiffies ( 10 ) ) ;
return IRQ_HANDLED ;
}
void cs42l43_button_release_work ( struct work_struct * work )
{
struct cs42l43_codec * priv = container_of ( work , struct cs42l43_codec ,
button_release_work ) ;
int ret ;
ret = pm_runtime_resume_and_get ( priv - > dev ) ;
if ( ret ) {
dev_err ( priv - > dev , " Failed to resume for button release: %d \n " , ret ) ;
return ;
}
mutex_lock ( & priv - > jack_lock ) ;
if ( priv - > button_detect_running ) {
dev_dbg ( priv - > dev , " Button release IRQ \n " ) ;
snd_soc_jack_report ( priv - > jack_hp , 0 , CS42L43_JACK_BUTTONS ) ;
} else {
dev_dbg ( priv - > dev , " Spurious button release IRQ \n " ) ;
}
mutex_unlock ( & priv - > jack_lock ) ;
pm_runtime_mark_last_busy ( priv - > dev ) ;
pm_runtime_put_autosuspend ( priv - > dev ) ;
}
irqreturn_t cs42l43_button_release ( int irq , void * data )
{
struct cs42l43_codec * priv = data ;
queue_work ( system_wq , & priv - > button_release_work ) ;
return IRQ_HANDLED ;
}
void cs42l43_bias_sense_timeout ( struct work_struct * work )
{
struct cs42l43_codec * priv = container_of ( work , struct cs42l43_codec ,
bias_sense_timeout . work ) ;
struct cs42l43 * cs42l43 = priv - > core ;
int ret ;
ret = pm_runtime_resume_and_get ( priv - > dev ) ;
if ( ret ) {
dev_err ( priv - > dev , " Failed to resume for bias sense: %d \n " , ret ) ;
return ;
}
mutex_lock ( & priv - > jack_lock ) ;
if ( cs42l43_jack_present ( priv ) & & priv - > button_detect_running ) {
dev_dbg ( priv - > dev , " Bias sense timeout out, restore bias \n " ) ;
regmap_update_bits ( cs42l43 - > regmap ,
CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL ,
CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK , 0 ) ;
regmap_update_bits ( cs42l43 - > regmap ,
CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL ,
CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK ,
CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK ) ;
}
mutex_unlock ( & priv - > jack_lock ) ;
pm_runtime_mark_last_busy ( priv - > dev ) ;
pm_runtime_put_autosuspend ( priv - > dev ) ;
}
static void cs42l43_start_load_detect ( struct cs42l43_codec * priv )
{
struct cs42l43 * cs42l43 = priv - > core ;
dev_dbg ( priv - > dev , " Start load detect \n " ) ;
snd_soc_dapm_mutex_lock ( snd_soc_component_get_dapm ( priv - > component ) ) ;
priv - > load_detect_running = true ;
if ( priv - > hp_ena ) {
unsigned long time_left ;
reinit_completion ( & priv - > hp_shutdown ) ;
regmap_update_bits ( cs42l43 - > regmap , CS42L43_BLOCK_EN8 ,
CS42L43_HP_EN_MASK , 0 ) ;
time_left = wait_for_completion_timeout ( & priv - > hp_shutdown ,
msecs_to_jiffies ( CS42L43_HP_TIMEOUT_MS ) ) ;
if ( ! time_left )
dev_err ( priv - > dev , " Load detect HP power down timed out \n " ) ;
}
regmap_update_bits ( cs42l43 - > regmap , CS42L43_BLOCK_EN3 ,
CS42L43_ADC1_EN_MASK | CS42L43_ADC2_EN_MASK , 0 ) ;
regmap_update_bits ( cs42l43 - > regmap , CS42L43_DACCNFG2 , CS42L43_HP_HPF_EN_MASK , 0 ) ;
regmap_update_bits ( cs42l43 - > regmap , CS42L43_MIC_DETECT_CONTROL_1 ,
CS42L43_HSBIAS_MODE_MASK , 0 ) ;
regmap_update_bits ( cs42l43 - > regmap , CS42L43_CTRL ,
CS42L43_ADPTPWR_MODE_MASK , 0x4 < < CS42L43_ADPTPWR_MODE_SHIFT ) ;
regmap_update_bits ( cs42l43 - > regmap , CS42L43_PGAVOL ,
CS42L43_HP_DIG_VOL_RAMP_MASK | CS42L43_HP_ANA_VOL_RAMP_MASK , 0x6 ) ;
regmap_update_bits ( cs42l43 - > regmap , CS42L43_DACCNFG1 ,
CS42L43_HP_MSTR_VOL_CTRL_EN_MASK , 0 ) ;
regmap_update_bits ( cs42l43 - > regmap , CS42L43_HS2 ,
CS42L43_HS_CLAMP_DISABLE_MASK , CS42L43_HS_CLAMP_DISABLE_MASK ) ;
regmap_update_bits ( cs42l43 - > regmap , CS42L43_LOADDETENA ,
CS42L43_HPLOAD_DET_EN_MASK ,
CS42L43_HPLOAD_DET_EN_MASK ) ;
snd_soc_dapm_mutex_unlock ( snd_soc_component_get_dapm ( priv - > component ) ) ;
}
static void cs42l43_stop_load_detect ( struct cs42l43_codec * priv )
{
struct cs42l43 * cs42l43 = priv - > core ;
dev_dbg ( priv - > dev , " Stop load detect \n " ) ;
snd_soc_dapm_mutex_lock ( snd_soc_component_get_dapm ( priv - > component ) ) ;
regmap_update_bits ( cs42l43 - > regmap , CS42L43_LOADDETENA ,
CS42L43_HPLOAD_DET_EN_MASK , 0 ) ;
regmap_update_bits ( cs42l43 - > regmap , CS42L43_HS2 ,
CS42L43_HS_CLAMP_DISABLE_MASK , 0 ) ;
regmap_update_bits ( cs42l43 - > regmap , CS42L43_DACCNFG1 ,
CS42L43_HP_MSTR_VOL_CTRL_EN_MASK ,
CS42L43_HP_MSTR_VOL_CTRL_EN_MASK ) ;
regmap_update_bits ( cs42l43 - > regmap , CS42L43_PGAVOL ,
CS42L43_HP_DIG_VOL_RAMP_MASK | CS42L43_HP_ANA_VOL_RAMP_MASK ,
0x4 < < CS42L43_HP_DIG_VOL_RAMP_SHIFT ) ;
regmap_update_bits ( cs42l43 - > regmap , CS42L43_CTRL ,
CS42L43_ADPTPWR_MODE_MASK , 0x7 < < CS42L43_ADPTPWR_MODE_SHIFT ) ;
regmap_update_bits ( cs42l43 - > regmap , CS42L43_MIC_DETECT_CONTROL_1 ,
CS42L43_HSBIAS_MODE_MASK , 0x1 < < CS42L43_HSBIAS_MODE_SHIFT ) ;
regmap_update_bits ( cs42l43 - > regmap , CS42L43_DACCNFG2 ,
CS42L43_HP_HPF_EN_MASK , CS42L43_HP_HPF_EN_MASK ) ;
regmap_update_bits ( cs42l43 - > regmap , CS42L43_BLOCK_EN3 ,
CS42L43_ADC1_EN_MASK | CS42L43_ADC2_EN_MASK ,
priv - > adc_ena ) ;
if ( priv - > hp_ena ) {
unsigned long time_left ;
reinit_completion ( & priv - > hp_startup ) ;
regmap_update_bits ( cs42l43 - > regmap , CS42L43_BLOCK_EN8 ,
CS42L43_HP_EN_MASK , priv - > hp_ena ) ;
time_left = wait_for_completion_timeout ( & priv - > hp_startup ,
msecs_to_jiffies ( CS42L43_HP_TIMEOUT_MS ) ) ;
if ( ! time_left )
dev_err ( priv - > dev , " Load detect HP restore timed out \n " ) ;
}
priv - > load_detect_running = false ;
snd_soc_dapm_mutex_unlock ( snd_soc_component_get_dapm ( priv - > component ) ) ;
}
static int cs42l43_run_load_detect ( struct cs42l43_codec * priv , bool mic )
{
struct cs42l43 * cs42l43 = priv - > core ;
unsigned int val = 0 ;
unsigned long time_left ;
reinit_completion ( & priv - > load_detect ) ;
cs42l43_start_load_detect ( priv ) ;
time_left = wait_for_completion_timeout ( & priv - > load_detect ,
msecs_to_jiffies ( CS42L43_LOAD_TIMEOUT_MS ) ) ;
cs42l43_stop_load_detect ( priv ) ;
if ( ! time_left )
return - ETIMEDOUT ;
regmap_read ( cs42l43 - > regmap , CS42L43_LOADDETRESULTS , & val ) ;
dev_dbg ( priv - > dev , " Headphone load detect: 0x%x \n " , val ) ;
/* Bail if jack removed, the load is irrelevant and likely invalid */
if ( ! cs42l43_jack_present ( priv ) )
return - ENODEV ;
if ( mic ) {
cs42l43_start_hs_bias ( priv , false ) ;
cs42l43_start_button_detect ( priv ) ;
return CS42L43_JACK_HEADSET ;
}
switch ( val & CS42L43_AMP3_RES_DET_MASK ) {
case 0x0 : // low impedance
case 0x1 : // high impedance
return CS42L43_JACK_HEADPHONE ;
case 0x2 : // lineout
case 0x3 : // Open circuit
return CS42L43_JACK_LINEOUT ;
default :
return - EINVAL ;
}
}
static int cs42l43_run_type_detect ( struct cs42l43_codec * priv )
{
struct cs42l43 * cs42l43 = priv - > core ;
int timeout_ms = ( ( 2 * priv - > detect_us ) / 1000 ) + 200 ;
unsigned int type = 0xff ;
unsigned long time_left ;
reinit_completion ( & priv - > type_detect ) ;
cs42l43_start_hs_bias ( priv , true ) ;
regmap_update_bits ( cs42l43 - > regmap , CS42L43_HS2 ,
CS42L43_HSDET_MODE_MASK , 0x3 < < CS42L43_HSDET_MODE_SHIFT ) ;
time_left = wait_for_completion_timeout ( & priv - > type_detect ,
msecs_to_jiffies ( timeout_ms ) ) ;
regmap_update_bits ( cs42l43 - > regmap , CS42L43_HS2 ,
CS42L43_HSDET_MODE_MASK , 0x2 < < CS42L43_HSDET_MODE_SHIFT ) ;
cs42l43_stop_hs_bias ( priv ) ;
if ( ! time_left )
return - ETIMEDOUT ;
regmap_read ( cs42l43 - > regmap , CS42L43_HS_STAT , & type ) ;
dev_dbg ( priv - > dev , " Type detect: 0x%x \n " , type ) ;
/* Bail if jack removed, the type is irrelevant and likely invalid */
if ( ! cs42l43_jack_present ( priv ) )
return - ENODEV ;
switch ( type & CS42L43_HSDET_TYPE_STS_MASK ) {
case 0x0 : // CTIA
case 0x1 : // OMTP
return cs42l43_run_load_detect ( priv , true ) ;
case 0x2 : // 3-pole
return cs42l43_run_load_detect ( priv , false ) ;
case 0x3 : // Open-circuit
return CS42L43_JACK_EXTENSION ;
default :
return - EINVAL ;
}
}
static void cs42l43_clear_jack ( struct cs42l43_codec * priv )
{
struct cs42l43 * cs42l43 = priv - > core ;
cs42l43_stop_button_detect ( priv ) ;
cs42l43_stop_hs_bias ( priv ) ;
regmap_update_bits ( cs42l43 - > regmap , CS42L43_ADC_B_CTRL1 ,
CS42L43_PGA_WIDESWING_MODE_EN_MASK , 0 ) ;
regmap_update_bits ( cs42l43 - > regmap , CS42L43_ADC_B_CTRL2 ,
CS42L43_PGA_WIDESWING_MODE_EN_MASK , 0 ) ;
regmap_update_bits ( cs42l43 - > regmap , CS42L43_STEREO_MIC_CTRL ,
CS42L43_JACK_STEREO_CONFIG_MASK , 0 ) ;
regmap_update_bits ( cs42l43 - > regmap , CS42L43_HS2 ,
CS42L43_HSDET_MODE_MASK | CS42L43_HSDET_MANUAL_MODE_MASK ,
0x2 < < CS42L43_HSDET_MODE_SHIFT ) ;
snd_soc_jack_report ( priv - > jack_hp , 0 , 0xFFFF ) ;
}
void cs42l43_tip_sense_work ( struct work_struct * work )
{
struct cs42l43_codec * priv = container_of ( work , struct cs42l43_codec ,
tip_sense_work . work ) ;
struct cs42l43 * cs42l43 = priv - > core ;
unsigned int sts = 0 ;
unsigned int tip , ring ;
int ret , report ;
ret = pm_runtime_resume_and_get ( priv - > dev ) ;
if ( ret ) {
dev_err ( priv - > dev , " Failed to resume for tip work: %d \n " , ret ) ;
return ;
}
mutex_lock ( & priv - > jack_lock ) ;
regmap_read ( cs42l43 - > regmap , CS42L43_TIP_RING_SENSE_INTERRUPT_STATUS , & sts ) ;
dev_dbg ( priv - > dev , " Tip sense: 0x%x \n " , sts ) ;
tip = ( sts > > CS42L43_TIPSENSE_PLUG_DB_STS_SHIFT ) & CS42L43_JACK_PRESENT ;
ring = ( sts > > CS42L43_RINGSENSE_PLUG_DB_STS_SHIFT ) & CS42L43_JACK_PRESENT ;
if ( tip = = CS42L43_JACK_PRESENT ) {
if ( cs42l43 - > sdw & & ! priv - > jack_present ) {
priv - > jack_present = true ;
pm_runtime_get ( priv - > dev ) ;
}
if ( priv - > use_ring_sense & & ring = = CS42L43_JACK_ABSENT ) {
report = CS42L43_JACK_OPTICAL ;
} else {
report = cs42l43_run_type_detect ( priv ) ;
if ( report < 0 ) {
dev_err ( priv - > dev , " Jack detect failed: %d \n " , report ) ;
goto error ;
}
}
snd_soc_jack_report ( priv - > jack_hp , report , report ) ;
} else {
priv - > jack_override = 0 ;
cs42l43_clear_jack ( priv ) ;
if ( cs42l43 - > sdw & & priv - > jack_present ) {
pm_runtime_put ( priv - > dev ) ;
priv - > jack_present = false ;
}
}
error :
mutex_unlock ( & priv - > jack_lock ) ;
pm_runtime_mark_last_busy ( priv - > dev ) ;
pm_runtime_put_autosuspend ( priv - > dev ) ;
}
irqreturn_t cs42l43_tip_sense ( int irq , void * data )
{
struct cs42l43_codec * priv = data ;
cancel_delayed_work ( & priv - > bias_sense_timeout ) ;
cancel_delayed_work ( & priv - > tip_sense_work ) ;
cancel_delayed_work ( & priv - > button_press_work ) ;
cancel_work ( & priv - > button_release_work ) ;
queue_delayed_work ( system_long_wq , & priv - > tip_sense_work ,
msecs_to_jiffies ( priv - > tip_debounce_ms ) ) ;
return IRQ_HANDLED ;
}
enum cs42l43_raw_jack {
CS42L43_JACK_RAW_CTIA = 0 ,
CS42L43_JACK_RAW_OMTP ,
CS42L43_JACK_RAW_HEADPHONE ,
CS42L43_JACK_RAW_LINE_OUT ,
CS42L43_JACK_RAW_LINE_IN ,
CS42L43_JACK_RAW_MICROPHONE ,
CS42L43_JACK_RAW_OPTICAL ,
} ;
# define CS42L43_JACK_3_POLE_SWITCHES ((0x2 << CS42L43_HSDET_MANUAL_MODE_SHIFT) | \
CS42L43_AMP3_4_GNDREF_HS3_SEL_MASK | \
CS42L43_AMP3_4_GNDREF_HS4_SEL_MASK | \
CS42L43_HSBIAS_GNDREF_HS3_SEL_MASK | \
CS42L43_HSBIAS_GNDREF_HS4_SEL_MASK | \
CS42L43_HSGND_HS3_SEL_MASK | \
CS42L43_HSGND_HS4_SEL_MASK )
static const struct cs42l43_jack_override_mode {
unsigned int hsdet_mode ;
unsigned int mic_ctrl ;
unsigned int clamp_ctrl ;
int report ;
} cs42l43_jack_override_modes [ ] = {
[ CS42L43_JACK_RAW_CTIA ] = {
. hsdet_mode = CS42L43_AMP3_4_GNDREF_HS3_SEL_MASK |
CS42L43_HSBIAS_GNDREF_HS3_SEL_MASK |
CS42L43_HSBIAS_OUT_HS4_SEL_MASK |
CS42L43_HSGND_HS3_SEL_MASK ,
. clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK ,
. report = CS42L43_JACK_HEADSET ,
} ,
[ CS42L43_JACK_RAW_OMTP ] = {
. hsdet_mode = ( 0x1 < < CS42L43_HSDET_MANUAL_MODE_SHIFT ) |
CS42L43_AMP3_4_GNDREF_HS4_SEL_MASK |
CS42L43_HSBIAS_GNDREF_HS4_SEL_MASK |
CS42L43_HSBIAS_OUT_HS3_SEL_MASK |
CS42L43_HSGND_HS4_SEL_MASK ,
. clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK ,
. report = CS42L43_JACK_HEADSET ,
} ,
[ CS42L43_JACK_RAW_HEADPHONE ] = {
. hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES ,
. clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK ,
. report = CS42L43_JACK_HEADPHONE ,
} ,
[ CS42L43_JACK_RAW_LINE_OUT ] = {
. hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES ,
. clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK ,
. report = CS42L43_JACK_LINEOUT ,
} ,
[ CS42L43_JACK_RAW_LINE_IN ] = {
. hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES ,
. mic_ctrl = 0x2 < < CS42L43_JACK_STEREO_CONFIG_SHIFT ,
. report = CS42L43_JACK_LINEIN ,
} ,
[ CS42L43_JACK_RAW_MICROPHONE ] = {
. hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES ,
. mic_ctrl = ( 0x3 < < CS42L43_JACK_STEREO_CONFIG_SHIFT ) |
CS42L43_HS1_BIAS_EN_MASK | CS42L43_HS2_BIAS_EN_MASK ,
. report = CS42L43_JACK_LINEIN ,
} ,
[ CS42L43_JACK_RAW_OPTICAL ] = {
. hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES ,
. clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK ,
. report = CS42L43_JACK_OPTICAL ,
} ,
} ;
static const char * const cs42l43_jack_text [ ] = {
" None " , " CTIA " , " OMTP " , " Headphone " , " Line-Out " ,
" Line-In " , " Microphone " , " Optical " ,
} ;
SOC_ENUM_SINGLE_VIRT_DECL ( cs42l43_jack_enum , cs42l43_jack_text ) ;
int cs42l43_jack_get ( struct snd_kcontrol * kcontrol , struct snd_ctl_elem_value * ucontrol )
{
struct snd_soc_component * component = snd_soc_kcontrol_component ( kcontrol ) ;
struct cs42l43_codec * priv = snd_soc_component_get_drvdata ( component ) ;
mutex_lock ( & priv - > jack_lock ) ;
ucontrol - > value . integer . value [ 0 ] = priv - > jack_override ;
mutex_unlock ( & priv - > jack_lock ) ;
return 0 ;
}
int cs42l43_jack_put ( struct snd_kcontrol * kcontrol , struct snd_ctl_elem_value * ucontrol )
{
struct snd_soc_component * component = snd_soc_kcontrol_component ( kcontrol ) ;
struct cs42l43_codec * priv = snd_soc_component_get_drvdata ( component ) ;
struct cs42l43 * cs42l43 = priv - > core ;
struct soc_enum * e = ( struct soc_enum * ) kcontrol - > private_value ;
unsigned int override = ucontrol - > value . integer . value [ 0 ] ;
BUILD_BUG_ON ( ARRAY_SIZE ( cs42l43_jack_override_modes ) ! =
ARRAY_SIZE ( cs42l43_jack_text ) - 1 ) ;
if ( override > = e - > items )
return - EINVAL ;
mutex_lock ( & priv - > jack_lock ) ;
if ( ! cs42l43_jack_present ( priv ) ) {
mutex_unlock ( & priv - > jack_lock ) ;
return - EBUSY ;
}
if ( override = = priv - > jack_override ) {
mutex_unlock ( & priv - > jack_lock ) ;
return 0 ;
}
priv - > jack_override = override ;
cs42l43_clear_jack ( priv ) ;
if ( ! override ) {
queue_delayed_work ( system_long_wq , & priv - > tip_sense_work , 0 ) ;
} else {
override - - ;
regmap_update_bits ( cs42l43 - > regmap , CS42L43_HS2 ,
CS42L43_HSDET_MODE_MASK |
CS42L43_HSDET_MANUAL_MODE_MASK |
CS42L43_AMP3_4_GNDREF_HS3_SEL_MASK |
CS42L43_AMP3_4_GNDREF_HS4_SEL_MASK |
CS42L43_HSBIAS_GNDREF_HS3_SEL_MASK |
CS42L43_HSBIAS_GNDREF_HS4_SEL_MASK |
CS42L43_HSBIAS_OUT_HS3_SEL_MASK |
CS42L43_HSBIAS_OUT_HS4_SEL_MASK |
CS42L43_HSGND_HS3_SEL_MASK |
CS42L43_HSGND_HS4_SEL_MASK ,
cs42l43_jack_override_modes [ override ] . hsdet_mode ) ;
regmap_update_bits ( cs42l43 - > regmap , CS42L43_STEREO_MIC_CTRL ,
CS42L43_HS2_BIAS_EN_MASK | CS42L43_HS1_BIAS_EN_MASK |
CS42L43_JACK_STEREO_CONFIG_MASK ,
cs42l43_jack_override_modes [ override ] . mic_ctrl ) ;
regmap_update_bits ( cs42l43 - > regmap , CS42L43_STEREO_MIC_CLAMP_CTRL ,
CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK ,
cs42l43_jack_override_modes [ override ] . clamp_ctrl ) ;
switch ( override ) {
case CS42L43_JACK_RAW_CTIA :
case CS42L43_JACK_RAW_OMTP :
cs42l43_start_hs_bias ( priv , false ) ;
cs42l43_start_button_detect ( priv ) ;
break ;
case CS42L43_JACK_RAW_LINE_IN :
regmap_update_bits ( cs42l43 - > regmap , CS42L43_ADC_B_CTRL1 ,
CS42L43_PGA_WIDESWING_MODE_EN_MASK ,
CS42L43_PGA_WIDESWING_MODE_EN_MASK ) ;
regmap_update_bits ( cs42l43 - > regmap , CS42L43_ADC_B_CTRL2 ,
CS42L43_PGA_WIDESWING_MODE_EN_MASK ,
CS42L43_PGA_WIDESWING_MODE_EN_MASK ) ;
break ;
case CS42L43_JACK_RAW_MICROPHONE :
cs42l43_start_hs_bias ( priv , false ) ;
break ;
default :
break ;
}
snd_soc_jack_report ( priv - > jack_hp ,
cs42l43_jack_override_modes [ override ] . report ,
cs42l43_jack_override_modes [ override ] . report ) ;
}
mutex_unlock ( & priv - > jack_lock ) ;
return 1 ;
}