2012-06-24 15:09:45 +04:00
/*
* extcon - arizona . c - Extcon driver Wolfson Arizona devices
*
* Copyright ( C ) 2012 Wolfson Microelectronics plc
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/i2c.h>
# include <linux/slab.h>
# include <linux/interrupt.h>
# include <linux/err.h>
# include <linux/gpio.h>
2012-07-20 20:07:29 +04:00
# include <linux/input.h>
2012-06-24 15:09:45 +04:00
# include <linux/platform_device.h>
# include <linux/pm_runtime.h>
# include <linux/regulator/consumer.h>
# include <linux/extcon.h>
# include <linux/mfd/arizona/core.h>
# include <linux/mfd/arizona/pdata.h>
# include <linux/mfd/arizona/registers.h>
2013-01-11 03:55:43 +04:00
# define ARIZONA_DEFAULT_HP 32
2012-07-20 20:07:29 +04:00
# define ARIZONA_NUM_BUTTONS 6
2013-01-11 03:55:43 +04:00
# define ARIZONA_ACCDET_MODE_MIC 0
# define ARIZONA_ACCDET_MODE_HPL 1
# define ARIZONA_ACCDET_MODE_HPR 2
2012-06-24 15:09:45 +04:00
struct arizona_extcon_info {
struct device * dev ;
struct arizona * arizona ;
struct mutex lock ;
struct regulator * micvdd ;
2012-07-20 20:07:29 +04:00
struct input_dev * input ;
2012-06-24 15:09:45 +04:00
int micd_mode ;
const struct arizona_micd_config * micd_modes ;
int micd_num_modes ;
bool micd_reva ;
2013-01-11 03:55:36 +04:00
bool micd_clamp ;
2012-06-24 15:09:45 +04:00
2013-01-11 03:55:43 +04:00
bool hpdet_active ;
2012-06-24 15:09:45 +04:00
bool mic ;
bool detecting ;
int jack_flips ;
2013-01-11 03:55:43 +04:00
int hpdet_ip ;
2012-06-24 15:09:45 +04:00
struct extcon_dev edev ;
} ;
static const struct arizona_micd_config micd_default_modes [ ] = {
{ ARIZONA_ACCDET_SRC , 1 < < ARIZONA_MICD_BIAS_SRC_SHIFT , 0 } ,
{ 0 , 2 < < ARIZONA_MICD_BIAS_SRC_SHIFT , 1 } ,
} ;
2012-07-20 20:07:29 +04:00
static struct {
u16 status ;
int report ;
} arizona_lvl_to_key [ ARIZONA_NUM_BUTTONS ] = {
{ 0x1 , BTN_0 } ,
{ 0x2 , BTN_1 } ,
{ 0x4 , BTN_2 } ,
{ 0x8 , BTN_3 } ,
{ 0x10 , BTN_4 } ,
{ 0x20 , BTN_5 } ,
} ;
2012-06-28 16:08:30 +04:00
# define ARIZONA_CABLE_MECHANICAL 0
# define ARIZONA_CABLE_MICROPHONE 1
# define ARIZONA_CABLE_HEADPHONE 2
2013-01-11 03:55:43 +04:00
# define ARIZONA_CABLE_LINEOUT 3
2012-06-24 15:09:45 +04:00
static const char * arizona_cable [ ] = {
2012-06-28 16:08:30 +04:00
" Mechanical " ,
" Microphone " ,
" Headphone " ,
2013-01-11 03:55:43 +04:00
" Line-out " ,
2012-06-24 15:09:45 +04:00
NULL ,
} ;
static void arizona_extcon_set_mode ( struct arizona_extcon_info * info , int mode )
{
struct arizona * arizona = info - > arizona ;
2012-11-27 11:14:26 +04:00
if ( arizona - > pdata . micd_pol_gpio > 0 )
gpio_set_value_cansleep ( arizona - > pdata . micd_pol_gpio ,
info - > micd_modes [ mode ] . gpio ) ;
2012-06-24 15:09:45 +04:00
regmap_update_bits ( arizona - > regmap , ARIZONA_MIC_DETECT_1 ,
ARIZONA_MICD_BIAS_SRC_MASK ,
info - > micd_modes [ mode ] . bias ) ;
regmap_update_bits ( arizona - > regmap , ARIZONA_ACCESSORY_DETECT_MODE_1 ,
ARIZONA_ACCDET_SRC , info - > micd_modes [ mode ] . src ) ;
info - > micd_mode = mode ;
dev_dbg ( arizona - > dev , " Set jack polarity to %d \n " , mode ) ;
}
2013-01-11 03:55:46 +04:00
static void arizona_start_mic ( struct arizona_extcon_info * info )
{
struct arizona * arizona = info - > arizona ;
bool change ;
int ret ;
info - > detecting = true ;
info - > mic = false ;
info - > jack_flips = 0 ;
/* Microphone detection can't use idle mode */
pm_runtime_get ( info - > dev ) ;
ret = regulator_enable ( info - > micvdd ) ;
if ( ret ! = 0 ) {
dev_err ( arizona - > dev , " Failed to enable MICVDD: %d \n " ,
ret ) ;
}
if ( info - > micd_reva ) {
regmap_write ( arizona - > regmap , 0x80 , 0x3 ) ;
regmap_write ( arizona - > regmap , 0x294 , 0 ) ;
regmap_write ( arizona - > regmap , 0x80 , 0x0 ) ;
}
regmap_update_bits ( arizona - > regmap ,
ARIZONA_ACCESSORY_DETECT_MODE_1 ,
ARIZONA_ACCDET_MODE_MASK , ARIZONA_ACCDET_MODE_MIC ) ;
regmap_update_bits_check ( arizona - > regmap , ARIZONA_MIC_DETECT_1 ,
ARIZONA_MICD_ENA , ARIZONA_MICD_ENA ,
& change ) ;
if ( ! change ) {
regulator_disable ( info - > micvdd ) ;
pm_runtime_put_autosuspend ( info - > dev ) ;
}
}
static void arizona_stop_mic ( struct arizona_extcon_info * info )
{
struct arizona * arizona = info - > arizona ;
bool change ;
regmap_update_bits_check ( arizona - > regmap , ARIZONA_MIC_DETECT_1 ,
ARIZONA_MICD_ENA , 0 ,
& change ) ;
if ( info - > micd_reva ) {
regmap_write ( arizona - > regmap , 0x80 , 0x3 ) ;
regmap_write ( arizona - > regmap , 0x294 , 2 ) ;
regmap_write ( arizona - > regmap , 0x80 , 0x0 ) ;
}
if ( change ) {
regulator_disable ( info - > micvdd ) ;
pm_runtime_mark_last_busy ( info - > dev ) ;
pm_runtime_put_autosuspend ( info - > dev ) ;
}
}
2013-01-11 03:55:43 +04:00
static struct {
unsigned int factor_a ;
unsigned int factor_b ;
} arizona_hpdet_b_ranges [ ] = {
{ 5528 , 362464 } ,
{ 11084 , 6186851 } ,
{ 11065 , 65460395 } ,
} ;
static struct {
int min ;
int max ;
} arizona_hpdet_c_ranges [ ] = {
{ 0 , 30 } ,
{ 8 , 100 } ,
{ 100 , 1000 } ,
{ 1000 , 10000 } ,
} ;
static int arizona_hpdet_read ( struct arizona_extcon_info * info )
{
struct arizona * arizona = info - > arizona ;
unsigned int val , range ;
int ret ;
ret = regmap_read ( arizona - > regmap , ARIZONA_HEADPHONE_DETECT_2 , & val ) ;
if ( ret ! = 0 ) {
dev_err ( arizona - > dev , " Failed to read HPDET status: %d \n " ,
ret ) ;
return ret ;
}
switch ( info - > hpdet_ip ) {
case 0 :
if ( ! ( val & ARIZONA_HP_DONE ) ) {
dev_err ( arizona - > dev , " HPDET did not complete: %x \n " ,
val ) ;
val = ARIZONA_DEFAULT_HP ;
}
val & = ARIZONA_HP_LVL_MASK ;
break ;
case 1 :
if ( ! ( val & ARIZONA_HP_DONE_B ) ) {
dev_err ( arizona - > dev , " HPDET did not complete: %x \n " ,
val ) ;
return ARIZONA_DEFAULT_HP ;
}
ret = regmap_read ( arizona - > regmap , ARIZONA_HP_DACVAL , & val ) ;
if ( ret ! = 0 ) {
dev_err ( arizona - > dev , " Failed to read HP value: %d \n " ,
ret ) ;
return ARIZONA_DEFAULT_HP ;
}
regmap_read ( arizona - > regmap , ARIZONA_HEADPHONE_DETECT_1 ,
& range ) ;
range = ( range & ARIZONA_HP_IMPEDANCE_RANGE_MASK )
> > ARIZONA_HP_IMPEDANCE_RANGE_SHIFT ;
if ( range < ARRAY_SIZE ( arizona_hpdet_b_ranges ) - 1 & &
( val < 100 | | val > 0x3fb ) ) {
range + + ;
dev_dbg ( arizona - > dev , " Moving to HPDET range %d \n " ,
range ) ;
regmap_update_bits ( arizona - > regmap ,
ARIZONA_HEADPHONE_DETECT_1 ,
ARIZONA_HP_IMPEDANCE_RANGE_MASK ,
range < <
ARIZONA_HP_IMPEDANCE_RANGE_SHIFT ) ;
return - EAGAIN ;
}
/* If we go out of range report top of range */
if ( val < 100 | | val > 0x3fb ) {
dev_dbg ( arizona - > dev , " Measurement out of range \n " ) ;
return 10000 ;
}
dev_dbg ( arizona - > dev , " HPDET read %d in range %d \n " ,
val , range ) ;
val = arizona_hpdet_b_ranges [ range ] . factor_b
/ ( ( val * 100 ) -
arizona_hpdet_b_ranges [ range ] . factor_a ) ;
break ;
default :
dev_warn ( arizona - > dev , " Unknown HPDET IP revision %d \n " ,
info - > hpdet_ip ) ;
case 2 :
if ( ! ( val & ARIZONA_HP_DONE_B ) ) {
dev_err ( arizona - > dev , " HPDET did not complete: %x \n " ,
val ) ;
return ARIZONA_DEFAULT_HP ;
}
val & = ARIZONA_HP_LVL_B_MASK ;
regmap_read ( arizona - > regmap , ARIZONA_HEADPHONE_DETECT_1 ,
& range ) ;
range = ( range & ARIZONA_HP_IMPEDANCE_RANGE_MASK )
> > ARIZONA_HP_IMPEDANCE_RANGE_SHIFT ;
/* Skip up or down a range? */
if ( range & & ( val < arizona_hpdet_c_ranges [ range ] . min ) ) {
range - - ;
dev_dbg ( arizona - > dev , " Moving to HPDET range %d-%d \n " ,
arizona_hpdet_c_ranges [ range ] . min ,
arizona_hpdet_c_ranges [ range ] . max ) ;
regmap_update_bits ( arizona - > regmap ,
ARIZONA_HEADPHONE_DETECT_1 ,
ARIZONA_HP_IMPEDANCE_RANGE_MASK ,
range < <
ARIZONA_HP_IMPEDANCE_RANGE_SHIFT ) ;
return - EAGAIN ;
}
if ( range < ARRAY_SIZE ( arizona_hpdet_c_ranges ) - 1 & &
( val > = arizona_hpdet_c_ranges [ range ] . max ) ) {
range + + ;
dev_dbg ( arizona - > dev , " Moving to HPDET range %d-%d \n " ,
arizona_hpdet_c_ranges [ range ] . min ,
arizona_hpdet_c_ranges [ range ] . max ) ;
regmap_update_bits ( arizona - > regmap ,
ARIZONA_HEADPHONE_DETECT_1 ,
ARIZONA_HP_IMPEDANCE_RANGE_MASK ,
range < <
ARIZONA_HP_IMPEDANCE_RANGE_SHIFT ) ;
return - EAGAIN ;
}
}
dev_dbg ( arizona - > dev , " HP impedance %d ohms \n " , val ) ;
return val ;
}
static irqreturn_t arizona_hpdet_irq ( int irq , void * data )
{
struct arizona_extcon_info * info = data ;
struct arizona * arizona = info - > arizona ;
int report = ARIZONA_CABLE_HEADPHONE ;
int ret ;
mutex_lock ( & info - > lock ) ;
/* If we got a spurious IRQ for some reason then ignore it */
if ( ! info - > hpdet_active ) {
dev_warn ( arizona - > dev , " Spurious HPDET IRQ \n " ) ;
mutex_unlock ( & info - > lock ) ;
return IRQ_NONE ;
}
/* If the cable was removed while measuring ignore the result */
ret = extcon_get_cable_state_ ( & info - > edev , ARIZONA_CABLE_MECHANICAL ) ;
if ( ret < 0 ) {
dev_err ( arizona - > dev , " Failed to check cable state: %d \n " ,
ret ) ;
goto out ;
} else if ( ! ret ) {
dev_dbg ( arizona - > dev , " Ignoring HPDET for removed cable \n " ) ;
goto done ;
}
ret = arizona_hpdet_read ( info ) ;
if ( ret = = - EAGAIN ) {
goto out ;
} else if ( ret < 0 ) {
goto done ;
}
/* Reset back to starting range */
regmap_update_bits ( arizona - > regmap ,
ARIZONA_HEADPHONE_DETECT_1 ,
ARIZONA_HP_IMPEDANCE_RANGE_MASK , 0 ) ;
/* Report high impedence cables as line outputs */
if ( ret > = 5000 )
report = ARIZONA_CABLE_LINEOUT ;
else
report = ARIZONA_CABLE_HEADPHONE ;
ret = extcon_set_cable_state_ ( & info - > edev , report , true ) ;
if ( ret ! = 0 )
dev_err ( arizona - > dev , " Failed to report HP/line: %d \n " ,
ret ) ;
ret = regmap_update_bits ( arizona - > regmap , 0x225 , 0x4000 , 0 ) ;
if ( ret ! = 0 )
dev_warn ( arizona - > dev , " Failed to undo magic: %d \n " , ret ) ;
ret = regmap_update_bits ( arizona - > regmap , 0x226 , 0x4000 , 0 ) ;
if ( ret ! = 0 )
dev_warn ( arizona - > dev , " Failed to undo magic: %d \n " , ret ) ;
done :
regmap_update_bits ( arizona - > regmap , ARIZONA_HEADPHONE_DETECT_1 ,
ARIZONA_HP_POLL , 0 ) ;
/* Revert back to MICDET mode */
regmap_update_bits ( arizona - > regmap ,
ARIZONA_ACCESSORY_DETECT_MODE_1 ,
ARIZONA_ACCDET_MODE_MASK , ARIZONA_ACCDET_MODE_MIC ) ;
/* If we have a mic then reenable MICDET */
if ( info - > mic )
arizona_start_mic ( info ) ;
if ( info - > hpdet_active ) {
pm_runtime_put_autosuspend ( info - > dev ) ;
info - > hpdet_active = false ;
}
out :
mutex_unlock ( & info - > lock ) ;
return IRQ_HANDLED ;
}
static void arizona_identify_headphone ( struct arizona_extcon_info * info )
{
struct arizona * arizona = info - > arizona ;
int ret ;
dev_dbg ( arizona - > dev , " Starting HPDET \n " ) ;
/* Make sure we keep the device enabled during the measurement */
pm_runtime_get ( info - > dev ) ;
info - > hpdet_active = true ;
if ( info - > mic )
arizona_stop_mic ( info ) ;
ret = regmap_update_bits ( arizona - > regmap , 0x225 , 0x4000 , 0x4000 ) ;
if ( ret ! = 0 )
dev_warn ( arizona - > dev , " Failed to do magic: %d \n " , ret ) ;
ret = regmap_update_bits ( arizona - > regmap , 0x226 , 0x4000 , 0x4000 ) ;
if ( ret ! = 0 )
dev_warn ( arizona - > dev , " Failed to do magic: %d \n " , ret ) ;
ret = regmap_update_bits ( arizona - > regmap ,
ARIZONA_ACCESSORY_DETECT_MODE_1 ,
ARIZONA_ACCDET_MODE_MASK ,
ARIZONA_ACCDET_MODE_HPL ) ;
if ( ret ! = 0 ) {
dev_err ( arizona - > dev , " Failed to set HPDETL mode: %d \n " , ret ) ;
goto err ;
}
ret = regmap_update_bits ( arizona - > regmap , ARIZONA_HEADPHONE_DETECT_1 ,
ARIZONA_HP_POLL , ARIZONA_HP_POLL ) ;
if ( ret ! = 0 ) {
dev_err ( arizona - > dev , " Can't start HPDETL measurement: %d \n " ,
ret ) ;
goto err ;
}
return ;
err :
regmap_update_bits ( arizona - > regmap , ARIZONA_ACCESSORY_DETECT_MODE_1 ,
ARIZONA_ACCDET_MODE_MASK , ARIZONA_ACCDET_MODE_MIC ) ;
/* Just report headphone */
ret = extcon_update_state ( & info - > edev ,
1 < < ARIZONA_CABLE_HEADPHONE ,
1 < < ARIZONA_CABLE_HEADPHONE ) ;
if ( ret ! = 0 )
dev_err ( arizona - > dev , " Failed to report headphone: %d \n " , ret ) ;
if ( info - > mic )
arizona_start_mic ( info ) ;
info - > hpdet_active = false ;
}
}
info - > hpdet_active = false ;
}
2012-06-24 15:09:45 +04:00
static irqreturn_t arizona_micdet ( int irq , void * data )
{
struct arizona_extcon_info * info = data ;
struct arizona * arizona = info - > arizona ;
2012-07-20 20:07:29 +04:00
unsigned int val , lvl ;
int ret , i ;
2012-06-24 15:09:45 +04:00
mutex_lock ( & info - > lock ) ;
ret = regmap_read ( arizona - > regmap , ARIZONA_MIC_DETECT_3 , & val ) ;
if ( ret ! = 0 ) {
dev_err ( arizona - > dev , " Failed to read MICDET: %d \n " , ret ) ;
2012-11-05 12:11:41 +04:00
mutex_unlock ( & info - > lock ) ;
2012-06-24 15:09:45 +04:00
return IRQ_NONE ;
}
dev_dbg ( arizona - > dev , " MICDET: %x \n " , val ) ;
if ( ! ( val & ARIZONA_MICD_VALID ) ) {
dev_warn ( arizona - > dev , " Microphone detection state invalid \n " ) ;
mutex_unlock ( & info - > lock ) ;
return IRQ_NONE ;
}
/* Due to jack detect this should never happen */
if ( ! ( val & ARIZONA_MICD_STS ) ) {
dev_warn ( arizona - > dev , " Detected open circuit \n " ) ;
info - > detecting = false ;
goto handled ;
}
/* If we got a high impedence we should have a headset, report it. */
if ( info - > detecting & & ( val & 0x400 ) ) {
2013-01-11 03:55:43 +04:00
arizona_identify_headphone ( info ) ;
2012-06-28 16:08:30 +04:00
ret = extcon_update_state ( & info - > edev ,
2013-01-11 03:55:43 +04:00
1 < < ARIZONA_CABLE_MICROPHONE ,
1 < < ARIZONA_CABLE_MICROPHONE ) ;
2012-06-24 15:09:45 +04:00
if ( ret ! = 0 )
dev_err ( arizona - > dev , " Headset report failed: %d \n " ,
ret ) ;
info - > mic = true ;
info - > detecting = false ;
goto handled ;
}
/* If we detected a lower impedence during initial startup
* then we probably have the wrong polarity , flip it . Don ' t
* do this for the lowest impedences to speed up detection of
* plain headphones . If both polarities report a low
* impedence then give up and report headphones .
*/
if ( info - > detecting & & ( val & 0x3f8 ) ) {
info - > jack_flips + + ;
if ( info - > jack_flips > = info - > micd_num_modes ) {
2013-01-11 03:55:43 +04:00
dev_dbg ( arizona - > dev , " Detected HP/line \n " ) ;
arizona_identify_headphone ( info ) ;
2012-06-24 15:09:45 +04:00
info - > detecting = false ;
2012-06-28 16:08:31 +04:00
2013-01-11 03:55:43 +04:00
arizona_stop_mic ( info ) ;
2012-06-24 15:09:45 +04:00
} else {
info - > micd_mode + + ;
if ( info - > micd_mode = = info - > micd_num_modes )
info - > micd_mode = 0 ;
arizona_extcon_set_mode ( info , info - > micd_mode ) ;
info - > jack_flips + + ;
}
goto handled ;
}
/*
* If we ' re still detecting and we detect a short then we ' ve
2012-07-20 20:07:29 +04:00
* got a headphone . Otherwise it ' s a button press .
2012-06-24 15:09:45 +04:00
*/
if ( val & 0x3fc ) {
if ( info - > mic ) {
dev_dbg ( arizona - > dev , " Mic button detected \n " ) ;
2012-07-20 20:07:29 +04:00
lvl = val & ARIZONA_MICD_LVL_MASK ;
lvl > > = ARIZONA_MICD_LVL_SHIFT ;
for ( i = 0 ; i < ARIZONA_NUM_BUTTONS ; i + + )
if ( lvl & arizona_lvl_to_key [ i ] . status )
input_report_key ( info - > input ,
arizona_lvl_to_key [ i ] . report ,
1 ) ;
input_sync ( info - > input ) ;
2012-06-24 15:09:45 +04:00
} else if ( info - > detecting ) {
dev_dbg ( arizona - > dev , " Headphone detected \n " ) ;
info - > detecting = false ;
arizona_stop_mic ( info ) ;
2013-01-11 03:55:43 +04:00
arizona_identify_headphone ( info ) ;
2012-06-24 15:09:45 +04:00
} else {
dev_warn ( arizona - > dev , " Button with no mic: %x \n " ,
val ) ;
}
} else {
dev_dbg ( arizona - > dev , " Mic button released \n " ) ;
2012-07-20 20:07:29 +04:00
for ( i = 0 ; i < ARIZONA_NUM_BUTTONS ; i + + )
input_report_key ( info - > input ,
arizona_lvl_to_key [ i ] . report , 0 ) ;
input_sync ( info - > input ) ;
2012-06-24 15:09:45 +04:00
}
handled :
pm_runtime_mark_last_busy ( info - > dev ) ;
mutex_unlock ( & info - > lock ) ;
return IRQ_HANDLED ;
}
static irqreturn_t arizona_jackdet ( int irq , void * data )
{
struct arizona_extcon_info * info = data ;
struct arizona * arizona = info - > arizona ;
2013-01-11 03:55:39 +04:00
unsigned int val , present , mask ;
2012-07-20 20:07:29 +04:00
int ret , i ;
2012-06-24 15:09:45 +04:00
pm_runtime_get_sync ( info - > dev ) ;
mutex_lock ( & info - > lock ) ;
2013-01-11 03:55:39 +04:00
if ( arizona - > pdata . jd_gpio5 ) {
mask = ARIZONA_MICD_CLAMP_STS ;
present = 0 ;
} else {
mask = ARIZONA_JD1_STS ;
present = ARIZONA_JD1_STS ;
}
2012-06-24 15:09:45 +04:00
ret = regmap_read ( arizona - > regmap , ARIZONA_AOD_IRQ_RAW_STATUS , & val ) ;
if ( ret ! = 0 ) {
dev_err ( arizona - > dev , " Failed to read jackdet status: %d \n " ,
ret ) ;
mutex_unlock ( & info - > lock ) ;
pm_runtime_put_autosuspend ( info - > dev ) ;
return IRQ_NONE ;
}
2013-01-11 03:55:39 +04:00
if ( ( val & mask ) = = present ) {
2012-06-24 15:09:45 +04:00
dev_dbg ( arizona - > dev , " Detected jack \n " ) ;
2012-06-28 16:08:30 +04:00
ret = extcon_set_cable_state_ ( & info - > edev ,
ARIZONA_CABLE_MECHANICAL , true ) ;
2012-06-24 15:09:45 +04:00
if ( ret ! = 0 )
dev_err ( arizona - > dev , " Mechanical report failed: %d \n " ,
ret ) ;
arizona_start_mic ( info ) ;
} else {
dev_dbg ( arizona - > dev , " Detected jack removal \n " ) ;
arizona_stop_mic ( info ) ;
2013-01-11 03:55:39 +04:00
2012-07-20 20:07:29 +04:00
for ( i = 0 ; i < ARIZONA_NUM_BUTTONS ; i + + )
input_report_key ( info - > input ,
arizona_lvl_to_key [ i ] . report , 0 ) ;
input_sync ( info - > input ) ;
2012-06-24 15:09:45 +04:00
ret = extcon_update_state ( & info - > edev , 0xffffffff , 0 ) ;
if ( ret ! = 0 )
dev_err ( arizona - > dev , " Removal report failed: %d \n " ,
ret ) ;
}
mutex_unlock ( & info - > lock ) ;
pm_runtime_mark_last_busy ( info - > dev ) ;
pm_runtime_put_autosuspend ( info - > dev ) ;
return IRQ_HANDLED ;
}
2012-11-19 22:23:21 +04:00
static int arizona_extcon_probe ( struct platform_device * pdev )
2012-06-24 15:09:45 +04:00
{
struct arizona * arizona = dev_get_drvdata ( pdev - > dev . parent ) ;
struct arizona_pdata * pdata ;
struct arizona_extcon_info * info ;
2013-01-11 03:55:39 +04:00
int jack_irq_fall , jack_irq_rise ;
2012-07-20 20:07:29 +04:00
int ret , mode , i ;
2012-06-24 15:09:45 +04:00
pdata = dev_get_platdata ( arizona - > dev ) ;
info = devm_kzalloc ( & pdev - > dev , sizeof ( * info ) , GFP_KERNEL ) ;
if ( ! info ) {
2012-08-23 04:11:50 +04:00
dev_err ( & pdev - > dev , " Failed to allocate memory \n " ) ;
2012-06-24 15:09:45 +04:00
ret = - ENOMEM ;
goto err ;
}
info - > micvdd = devm_regulator_get ( arizona - > dev , " MICVDD " ) ;
if ( IS_ERR ( info - > micvdd ) ) {
ret = PTR_ERR ( info - > micvdd ) ;
dev_err ( arizona - > dev , " Failed to get MICVDD: %d \n " , ret ) ;
goto err ;
}
mutex_init ( & info - > lock ) ;
info - > arizona = arizona ;
info - > dev = & pdev - > dev ;
info - > detecting = true ;
platform_set_drvdata ( pdev , info ) ;
switch ( arizona - > type ) {
case WM5102 :
switch ( arizona - > rev ) {
case 0 :
info - > micd_reva = true ;
break ;
default :
2013-01-11 03:55:36 +04:00
info - > micd_clamp = true ;
2013-01-11 03:55:43 +04:00
info - > hpdet_ip = 1 ;
2012-06-24 15:09:45 +04:00
break ;
}
break ;
default :
break ;
}
info - > edev . name = " Headset Jack " ;
info - > edev . supported_cable = arizona_cable ;
ret = extcon_dev_register ( & info - > edev , arizona - > dev ) ;
if ( ret < 0 ) {
2012-08-23 04:11:50 +04:00
dev_err ( arizona - > dev , " extcon_dev_register() failed: %d \n " ,
2012-06-24 15:09:45 +04:00
ret ) ;
goto err ;
}
if ( pdata - > num_micd_configs ) {
info - > micd_modes = pdata - > micd_configs ;
info - > micd_num_modes = pdata - > num_micd_configs ;
} else {
info - > micd_modes = micd_default_modes ;
info - > micd_num_modes = ARRAY_SIZE ( micd_default_modes ) ;
}
if ( arizona - > pdata . micd_pol_gpio > 0 ) {
if ( info - > micd_modes [ 0 ] . gpio )
mode = GPIOF_OUT_INIT_HIGH ;
else
mode = GPIOF_OUT_INIT_LOW ;
ret = devm_gpio_request_one ( & pdev - > dev ,
arizona - > pdata . micd_pol_gpio ,
mode ,
" MICD polarity " ) ;
if ( ret ! = 0 ) {
dev_err ( arizona - > dev , " Failed to request GPIO%d: %d \n " ,
arizona - > pdata . micd_pol_gpio , ret ) ;
goto err_register ;
}
}
2013-01-11 03:55:24 +04:00
if ( arizona - > pdata . micd_bias_start_time )
regmap_update_bits ( arizona - > regmap , ARIZONA_MIC_DETECT_1 ,
ARIZONA_MICD_BIAS_STARTTIME_MASK ,
arizona - > pdata . micd_bias_start_time
< < ARIZONA_MICD_BIAS_STARTTIME_SHIFT ) ;
2013-01-11 03:55:36 +04:00
/*
2013-01-11 03:55:39 +04:00
* If we have a clamp use it , activating in conjunction with
* GPIO5 if that is connected for jack detect operation .
2013-01-11 03:55:36 +04:00
*/
if ( info - > micd_clamp ) {
2013-01-11 03:55:39 +04:00
if ( arizona - > pdata . jd_gpio5 ) {
/* Put the GPIO into input mode */
regmap_write ( arizona - > regmap , ARIZONA_GPIO5_CTRL ,
0xc101 ) ;
regmap_update_bits ( arizona - > regmap ,
ARIZONA_MICD_CLAMP_CONTROL ,
ARIZONA_MICD_CLAMP_MODE_MASK , 0x9 ) ;
} else {
regmap_update_bits ( arizona - > regmap ,
ARIZONA_MICD_CLAMP_CONTROL ,
ARIZONA_MICD_CLAMP_MODE_MASK , 0x4 ) ;
}
2013-01-11 03:55:36 +04:00
regmap_update_bits ( arizona - > regmap ,
ARIZONA_JACK_DETECT_DEBOUNCE ,
ARIZONA_MICD_CLAMP_DB ,
ARIZONA_MICD_CLAMP_DB ) ;
}
2012-06-24 15:09:45 +04:00
arizona_extcon_set_mode ( info , 0 ) ;
2013-01-11 03:51:13 +04:00
info - > input = devm_input_allocate_device ( & pdev - > dev ) ;
2012-07-20 20:07:29 +04:00
if ( ! info - > input ) {
dev_err ( arizona - > dev , " Can't allocate input dev \n " ) ;
ret = - ENOMEM ;
goto err_register ;
}
for ( i = 0 ; i < ARIZONA_NUM_BUTTONS ; i + + )
input_set_capability ( info - > input , EV_KEY ,
arizona_lvl_to_key [ i ] . report ) ;
info - > input - > name = " Headset " ;
info - > input - > phys = " arizona/extcon " ;
info - > input - > dev . parent = & pdev - > dev ;
2012-06-24 15:09:45 +04:00
pm_runtime_enable ( & pdev - > dev ) ;
pm_runtime_idle ( & pdev - > dev ) ;
pm_runtime_get_sync ( & pdev - > dev ) ;
2013-01-11 03:55:39 +04:00
if ( arizona - > pdata . jd_gpio5 ) {
jack_irq_rise = ARIZONA_IRQ_MICD_CLAMP_RISE ;
jack_irq_fall = ARIZONA_IRQ_MICD_CLAMP_FALL ;
} else {
jack_irq_rise = ARIZONA_IRQ_JD_RISE ;
jack_irq_fall = ARIZONA_IRQ_JD_FALL ;
}
ret = arizona_request_irq ( arizona , jack_irq_rise ,
2012-06-24 15:09:45 +04:00
" JACKDET rise " , arizona_jackdet , info ) ;
if ( ret ! = 0 ) {
dev_err ( & pdev - > dev , " Failed to get JACKDET rise IRQ: %d \n " ,
ret ) ;
2012-07-20 20:07:29 +04:00
goto err_input ;
2012-06-24 15:09:45 +04:00
}
2013-01-11 03:55:39 +04:00
ret = arizona_set_irq_wake ( arizona , jack_irq_rise , 1 ) ;
2012-06-24 15:09:45 +04:00
if ( ret ! = 0 ) {
dev_err ( & pdev - > dev , " Failed to set JD rise IRQ wake: %d \n " ,
ret ) ;
goto err_rise ;
}
2013-01-11 03:55:39 +04:00
ret = arizona_request_irq ( arizona , jack_irq_fall ,
2012-06-24 15:09:45 +04:00
" JACKDET fall " , arizona_jackdet , info ) ;
if ( ret ! = 0 ) {
dev_err ( & pdev - > dev , " Failed to get JD fall IRQ: %d \n " , ret ) ;
goto err_rise_wake ;
}
2013-01-11 03:55:39 +04:00
ret = arizona_set_irq_wake ( arizona , jack_irq_fall , 1 ) ;
2012-06-24 15:09:45 +04:00
if ( ret ! = 0 ) {
dev_err ( & pdev - > dev , " Failed to set JD fall IRQ wake: %d \n " ,
ret ) ;
goto err_fall ;
}
ret = arizona_request_irq ( arizona , ARIZONA_IRQ_MICDET ,
" MICDET " , arizona_micdet , info ) ;
if ( ret ! = 0 ) {
dev_err ( & pdev - > dev , " Failed to get MICDET IRQ: %d \n " , ret ) ;
goto err_fall_wake ;
}
2013-01-11 03:55:43 +04:00
ret = arizona_request_irq ( arizona , ARIZONA_IRQ_HPDET ,
" HPDET " , arizona_hpdet_irq , info ) ;
if ( ret ! = 0 ) {
dev_err ( & pdev - > dev , " Failed to get HPDET IRQ: %d \n " , ret ) ;
goto err_micdet ;
}
2012-06-24 15:09:45 +04:00
regmap_update_bits ( arizona - > regmap , ARIZONA_MIC_DETECT_1 ,
ARIZONA_MICD_RATE_MASK ,
8 < < ARIZONA_MICD_RATE_SHIFT ) ;
arizona_clk32k_enable ( arizona ) ;
regmap_update_bits ( arizona - > regmap , ARIZONA_JACK_DETECT_DEBOUNCE ,
ARIZONA_JD1_DB , ARIZONA_JD1_DB ) ;
regmap_update_bits ( arizona - > regmap , ARIZONA_JACK_DETECT_ANALOGUE ,
ARIZONA_JD1_ENA , ARIZONA_JD1_ENA ) ;
2012-09-07 13:01:15 +04:00
ret = regulator_allow_bypass ( info - > micvdd , true ) ;
if ( ret ! = 0 )
dev_warn ( arizona - > dev , " Failed to set MICVDD to bypass: %d \n " ,
ret ) ;
2012-06-24 15:09:45 +04:00
pm_runtime_put ( & pdev - > dev ) ;
2012-07-20 20:07:29 +04:00
ret = input_register_device ( info - > input ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " Can't register input device: %d \n " , ret ) ;
2013-01-11 03:55:43 +04:00
goto err_hpdet ;
2012-07-20 20:07:29 +04:00
}
2012-06-24 15:09:45 +04:00
return 0 ;
2013-01-11 03:55:43 +04:00
err_hpdet :
arizona_free_irq ( arizona , ARIZONA_IRQ_HPDET , info ) ;
2012-08-27 00:58:20 +04:00
err_micdet :
arizona_free_irq ( arizona , ARIZONA_IRQ_MICDET , info ) ;
2012-06-24 15:09:45 +04:00
err_fall_wake :
2013-01-11 03:55:39 +04:00
arizona_set_irq_wake ( arizona , jack_irq_fall , 0 ) ;
2012-06-24 15:09:45 +04:00
err_fall :
2013-01-11 03:55:39 +04:00
arizona_free_irq ( arizona , jack_irq_fall , info ) ;
2012-06-24 15:09:45 +04:00
err_rise_wake :
2013-01-11 03:55:39 +04:00
arizona_set_irq_wake ( arizona , jack_irq_rise , 0 ) ;
2012-06-24 15:09:45 +04:00
err_rise :
2013-01-11 03:55:39 +04:00
arizona_free_irq ( arizona , jack_irq_rise , info ) ;
2012-07-20 20:07:29 +04:00
err_input :
2012-06-24 15:09:45 +04:00
err_register :
pm_runtime_disable ( & pdev - > dev ) ;
extcon_dev_unregister ( & info - > edev ) ;
err :
return ret ;
}
2012-11-19 22:25:49 +04:00
static int arizona_extcon_remove ( struct platform_device * pdev )
2012-06-24 15:09:45 +04:00
{
struct arizona_extcon_info * info = platform_get_drvdata ( pdev ) ;
struct arizona * arizona = info - > arizona ;
2013-01-11 03:55:39 +04:00
int jack_irq_rise , jack_irq_fall ;
2012-06-24 15:09:45 +04:00
pm_runtime_disable ( & pdev - > dev ) ;
2013-01-11 03:55:36 +04:00
regmap_update_bits ( arizona - > regmap ,
ARIZONA_MICD_CLAMP_CONTROL ,
ARIZONA_MICD_CLAMP_MODE_MASK , 0 ) ;
2013-01-11 03:55:39 +04:00
if ( arizona - > pdata . jd_gpio5 ) {
jack_irq_rise = ARIZONA_IRQ_MICD_CLAMP_RISE ;
jack_irq_fall = ARIZONA_IRQ_MICD_CLAMP_FALL ;
} else {
jack_irq_rise = ARIZONA_IRQ_JD_RISE ;
jack_irq_fall = ARIZONA_IRQ_JD_FALL ;
}
arizona_set_irq_wake ( arizona , jack_irq_rise , 0 ) ;
arizona_set_irq_wake ( arizona , jack_irq_fall , 0 ) ;
arizona_free_irq ( arizona , ARIZONA_IRQ_HPDET , info ) ;
2012-06-24 15:09:45 +04:00
arizona_free_irq ( arizona , ARIZONA_IRQ_MICDET , info ) ;
2013-01-11 03:55:39 +04:00
arizona_free_irq ( arizona , jack_irq_rise , info ) ;
arizona_free_irq ( arizona , jack_irq_fall , info ) ;
2012-06-24 15:09:45 +04:00
regmap_update_bits ( arizona - > regmap , ARIZONA_JACK_DETECT_ANALOGUE ,
ARIZONA_JD1_ENA , 0 ) ;
arizona_clk32k_disable ( arizona ) ;
extcon_dev_unregister ( & info - > edev ) ;
return 0 ;
}
static struct platform_driver arizona_extcon_driver = {
. driver = {
. name = " arizona-extcon " ,
. owner = THIS_MODULE ,
} ,
. probe = arizona_extcon_probe ,
2012-11-19 22:20:06 +04:00
. remove = arizona_extcon_remove ,
2012-06-24 15:09:45 +04:00
} ;
module_platform_driver ( arizona_extcon_driver ) ;
MODULE_DESCRIPTION ( " Arizona Extcon driver " ) ;
MODULE_AUTHOR ( " Mark Brown <broonie@opensource.wolfsonmicro.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform:extcon-arizona " ) ;