2019-05-30 02:57:50 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2010-03-19 14:25:51 +03:00
/*
* ALSA SoC TWL6040 codec driver
*
* Author : Misael Lopez Cruz < x0052729 @ ti . com >
*/
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/init.h>
# include <linux/delay.h>
# include <linux/pm.h>
# include <linux/platform_device.h>
2010-03-29 10:55:51 +04:00
# include <linux/slab.h>
2011-05-02 06:27:00 +04:00
# include <linux/mfd/twl6040.h>
2010-03-19 14:25:51 +03:00
# include <sound/core.h>
# include <sound/pcm.h>
# include <sound/pcm_params.h>
# include <sound/soc.h>
2012-01-11 16:43:24 +04:00
# include <sound/soc-dapm.h>
2010-03-19 14:25:51 +03:00
# include <sound/initval.h>
# include <sound/tlv.h>
# include "twl6040.h"
2013-06-24 17:42:05 +04:00
enum twl6040_dai_id {
TWL6040_DAI_LEGACY = 0 ,
TWL6040_DAI_UL ,
TWL6040_DAI_DL1 ,
TWL6040_DAI_DL2 ,
TWL6040_DAI_VIB ,
} ;
2010-12-11 06:05:58 +03:00
# define TWL6040_RATES SNDRV_PCM_RATE_8000_96000
2010-12-15 04:00:21 +03:00
# define TWL6040_FORMATS (SNDRV_PCM_FMTBIT_S32_LE)
# define TWL6040_OUTHS_0dB 0x00
# define TWL6040_OUTHS_M30dB 0x0F
# define TWL6040_OUTHF_0dB 0x03
# define TWL6040_OUTHF_M52dB 0x1D
2013-10-06 15:43:51 +04:00
# define TWL6040_CACHEREGNUM (TWL6040_REG_STATUS + 1)
2011-09-22 12:05:49 +04:00
2010-12-11 05:45:17 +03:00
struct twl6040_jack_data {
struct snd_soc_jack * jack ;
2011-09-26 17:26:27 +04:00
struct delayed_work work ;
2010-12-11 05:45:17 +03:00
int report ;
} ;
2010-03-19 14:25:51 +03:00
/* codec private data */
struct twl6040_data {
2011-07-04 20:52:26 +04:00
int plug_irq ;
2010-03-19 14:25:51 +03:00
int codec_powered ;
int pll ;
2011-06-27 18:03:14 +04:00
int pll_power_mode ;
2011-02-12 02:51:05 +03:00
int hs_power_mode ;
int hs_power_mode_locked ;
2013-06-24 17:42:06 +04:00
bool dl1_unmuted ;
bool dl2_unmuted ;
2013-11-29 18:03:44 +04:00
u8 dl12_cache [ TWL6040_REG_HFRCTL - TWL6040_REG_HSLCTL + 1 ] ;
2011-05-02 06:27:00 +04:00
unsigned int clk_in ;
2010-03-19 14:25:51 +03:00
unsigned int sysclk ;
2010-12-11 05:45:17 +03:00
struct twl6040_jack_data hs_jack ;
2018-01-29 07:20:09 +03:00
struct snd_soc_component * component ;
2010-12-11 05:45:17 +03:00
struct mutex mutex ;
2010-03-19 14:25:51 +03:00
} ;
2011-06-27 18:03:14 +04:00
/* set of rates for each pll: low-power and high-performance */
2014-02-06 00:54:34 +04:00
static const unsigned int lp_rates [ ] = {
2011-06-27 18:03:14 +04:00
8000 ,
11250 ,
16000 ,
22500 ,
32000 ,
44100 ,
48000 ,
88200 ,
96000 ,
} ;
2014-02-06 00:54:34 +04:00
static const unsigned int hp_rates [ ] = {
2011-06-27 18:03:14 +04:00
8000 ,
16000 ,
32000 ,
48000 ,
96000 ,
} ;
2014-02-06 00:54:34 +04:00
static const struct snd_pcm_hw_constraint_list sysclk_constraints [ ] = {
2011-06-29 14:28:18 +04:00
{ . count = ARRAY_SIZE ( lp_rates ) , . list = lp_rates , } ,
{ . count = ARRAY_SIZE ( hp_rates ) , . list = hp_rates , } ,
2011-06-27 18:03:14 +04:00
} ;
2018-01-29 07:20:09 +03:00
# define to_twl6040(component) dev_get_drvdata((component)->dev->parent)
2017-11-09 04:04:37 +03:00
2018-01-29 07:20:09 +03:00
static unsigned int twl6040_read ( struct snd_soc_component * component , unsigned int reg )
2013-11-29 18:03:44 +04:00
{
2018-01-29 07:20:09 +03:00
struct twl6040_data * priv = snd_soc_component_get_drvdata ( component ) ;
struct twl6040 * twl6040 = to_twl6040 ( component ) ;
2013-11-29 18:03:47 +04:00
u8 value ;
if ( reg > = TWL6040_CACHEREGNUM )
return - EIO ;
2013-11-29 18:03:44 +04:00
switch ( reg ) {
case TWL6040_REG_HSLCTL :
case TWL6040_REG_HSRCTL :
case TWL6040_REG_EARCTL :
case TWL6040_REG_HFLCTL :
case TWL6040_REG_HFRCTL :
2013-11-29 18:03:47 +04:00
value = priv - > dl12_cache [ reg - TWL6040_REG_HSLCTL ] ;
2013-11-29 18:03:44 +04:00
break ;
default :
2013-11-29 18:03:47 +04:00
value = twl6040_reg_read ( twl6040 , reg ) ;
2013-11-29 18:03:44 +04:00
break ;
}
return value ;
}
2018-01-29 07:20:09 +03:00
static bool twl6040_can_write_to_chip ( struct snd_soc_component * component ,
2013-11-29 18:03:47 +04:00
unsigned int reg )
2013-11-29 18:03:44 +04:00
{
2018-01-29 07:20:09 +03:00
struct twl6040_data * priv = snd_soc_component_get_drvdata ( component ) ;
2013-11-29 18:03:44 +04:00
switch ( reg ) {
case TWL6040_REG_HSLCTL :
case TWL6040_REG_HSRCTL :
case TWL6040_REG_EARCTL :
2013-11-29 18:03:47 +04:00
/* DL1 path */
return priv - > dl1_unmuted ;
2013-11-29 18:03:44 +04:00
case TWL6040_REG_HFLCTL :
case TWL6040_REG_HFRCTL :
2013-11-29 18:03:47 +04:00
return priv - > dl2_unmuted ;
2013-11-29 18:03:44 +04:00
default :
2018-08-05 00:53:38 +03:00
return true ;
2013-11-29 18:03:44 +04:00
}
2010-03-19 14:25:51 +03:00
}
2018-01-29 07:20:09 +03:00
static inline void twl6040_update_dl12_cache ( struct snd_soc_component * component ,
2013-11-29 18:03:47 +04:00
u8 reg , u8 value )
2013-06-24 17:42:06 +04:00
{
2018-01-29 07:20:09 +03:00
struct twl6040_data * priv = snd_soc_component_get_drvdata ( component ) ;
2013-06-24 17:42:06 +04:00
switch ( reg ) {
case TWL6040_REG_HSLCTL :
case TWL6040_REG_HSRCTL :
case TWL6040_REG_EARCTL :
case TWL6040_REG_HFLCTL :
case TWL6040_REG_HFRCTL :
2013-11-29 18:03:47 +04:00
priv - > dl12_cache [ reg - TWL6040_REG_HSLCTL ] = value ;
break ;
2013-06-24 17:42:06 +04:00
default :
2013-11-29 18:03:47 +04:00
break ;
2013-09-13 14:46:17 +04:00
}
2013-06-24 17:42:06 +04:00
}
2018-01-29 07:20:09 +03:00
static int twl6040_write ( struct snd_soc_component * component ,
2010-03-19 14:25:51 +03:00
unsigned int reg , unsigned int value )
{
2018-01-29 07:20:09 +03:00
struct twl6040 * twl6040 = to_twl6040 ( component ) ;
2011-05-02 06:27:00 +04:00
2010-03-19 14:25:51 +03:00
if ( reg > = TWL6040_CACHEREGNUM )
return - EIO ;
2018-01-29 07:20:09 +03:00
twl6040_update_dl12_cache ( component , reg , value ) ;
if ( twl6040_can_write_to_chip ( component , reg ) )
2011-09-22 12:05:48 +04:00
return twl6040_reg_write ( twl6040 , reg , value ) ;
else
return 0 ;
2010-03-19 14:25:51 +03:00
}
2018-01-29 07:20:09 +03:00
static void twl6040_init_chip ( struct snd_soc_component * component )
2010-03-19 14:25:51 +03:00
{
2018-01-29 07:20:09 +03:00
twl6040_read ( component , TWL6040_REG_TRIM1 ) ;
twl6040_read ( component , TWL6040_REG_TRIM2 ) ;
twl6040_read ( component , TWL6040_REG_TRIM3 ) ;
twl6040_read ( component , TWL6040_REG_HSOTRIM ) ;
twl6040_read ( component , TWL6040_REG_HFOTRIM ) ;
2011-09-26 17:05:56 +04:00
2011-09-15 16:39:28 +04:00
/* Change chip defaults */
/* No imput selected for microphone amplifiers */
2018-01-29 07:20:09 +03:00
twl6040_write ( component , TWL6040_REG_MICLCTL , 0x18 ) ;
twl6040_write ( component , TWL6040_REG_MICRCTL , 0x18 ) ;
2011-09-22 12:05:45 +04:00
/*
* We need to lower the default gain values , so the ramp code
* can work correctly for the first playback .
* This reduces the pop noise heard at the first playback .
*/
2018-01-29 07:20:09 +03:00
twl6040_write ( component , TWL6040_REG_HSGAIN , 0xff ) ;
twl6040_write ( component , TWL6040_REG_EARCTL , 0x1e ) ;
twl6040_write ( component , TWL6040_REG_HFLGAIN , 0x1d ) ;
twl6040_write ( component , TWL6040_REG_HFRGAIN , 0x1d ) ;
twl6040_write ( component , TWL6040_REG_LINEGAIN , 0 ) ;
2010-03-19 14:25:51 +03:00
}
/* set headset dac and driver power mode */
2018-01-29 07:20:09 +03:00
static int headset_power_mode ( struct snd_soc_component * component , int high_perf )
2010-03-19 14:25:51 +03:00
{
int hslctl , hsrctl ;
2011-09-22 12:05:54 +04:00
int mask = TWL6040_HSDRVMODE | TWL6040_HSDACMODE ;
2010-03-19 14:25:51 +03:00
2018-01-29 07:20:09 +03:00
hslctl = twl6040_read ( component , TWL6040_REG_HSLCTL ) ;
hsrctl = twl6040_read ( component , TWL6040_REG_HSRCTL ) ;
2010-03-19 14:25:51 +03:00
if ( high_perf ) {
hslctl & = ~ mask ;
hsrctl & = ~ mask ;
} else {
hslctl | = mask ;
hsrctl | = mask ;
}
2018-01-29 07:20:09 +03:00
twl6040_write ( component , TWL6040_REG_HSLCTL , hslctl ) ;
twl6040_write ( component , TWL6040_REG_HSRCTL , hsrctl ) ;
2010-03-19 14:25:51 +03:00
return 0 ;
}
2010-07-15 20:38:01 +04:00
static int twl6040_hs_dac_event ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * kcontrol , int event )
{
2018-01-29 07:20:09 +03:00
struct snd_soc_component * component = snd_soc_dapm_to_component ( w - > dapm ) ;
2011-10-12 15:46:02 +04:00
u8 hslctl , hsrctl ;
/*
* Workaround for Headset DC offset caused pop noise :
* Both HS DAC need to be turned on ( before the HS driver ) and off at
* the same time .
*/
2018-01-29 07:20:09 +03:00
hslctl = twl6040_read ( component , TWL6040_REG_HSLCTL ) ;
hsrctl = twl6040_read ( component , TWL6040_REG_HSRCTL ) ;
2011-10-12 15:46:02 +04:00
if ( SND_SOC_DAPM_EVENT_ON ( event ) ) {
hslctl | = TWL6040_HSDACENA ;
hsrctl | = TWL6040_HSDACENA ;
} else {
hslctl & = ~ TWL6040_HSDACENA ;
hsrctl & = ~ TWL6040_HSDACENA ;
}
2018-01-29 07:20:09 +03:00
twl6040_write ( component , TWL6040_REG_HSLCTL , hslctl ) ;
twl6040_write ( component , TWL6040_REG_HSRCTL , hsrctl ) ;
2011-10-12 15:46:02 +04:00
2010-07-15 20:38:01 +04:00
msleep ( 1 ) ;
return 0 ;
}
2011-10-13 16:05:44 +04:00
static int twl6040_ep_drv_event ( struct snd_soc_dapm_widget * w ,
2010-03-19 14:25:51 +03:00
struct snd_kcontrol * kcontrol , int event )
{
2018-01-29 07:20:09 +03:00
struct snd_soc_component * component = snd_soc_dapm_to_component ( w - > dapm ) ;
struct twl6040_data * priv = snd_soc_component_get_drvdata ( component ) ;
2011-02-12 02:51:05 +03:00
int ret = 0 ;
2010-03-19 14:25:51 +03:00
2011-02-12 02:51:05 +03:00
if ( SND_SOC_DAPM_EVENT_ON ( event ) ) {
2011-10-13 16:05:42 +04:00
/* Earphone doesn't support low power mode */
priv - > hs_power_mode_locked = 1 ;
2018-01-29 07:20:09 +03:00
ret = headset_power_mode ( component , 1 ) ;
2011-02-12 02:51:05 +03:00
} else {
2011-10-13 16:05:42 +04:00
priv - > hs_power_mode_locked = 0 ;
2018-01-29 07:20:09 +03:00
ret = headset_power_mode ( component , priv - > hs_power_mode ) ;
2011-02-12 02:51:05 +03:00
}
2010-03-19 14:25:51 +03:00
2010-07-15 20:38:01 +04:00
msleep ( 1 ) ;
2011-02-12 02:51:05 +03:00
return ret ;
2010-03-19 14:25:51 +03:00
}
2018-01-29 07:20:09 +03:00
static void twl6040_hs_jack_report ( struct snd_soc_component * component ,
2011-01-21 00:43:44 +03:00
struct snd_soc_jack * jack , int report )
2010-12-11 05:45:17 +03:00
{
2018-01-29 07:20:09 +03:00
struct twl6040_data * priv = snd_soc_component_get_drvdata ( component ) ;
2010-12-11 05:45:17 +03:00
int status ;
mutex_lock ( & priv - > mutex ) ;
/* Sync status */
2018-01-29 07:20:09 +03:00
status = twl6040_read ( component , TWL6040_REG_STATUS ) ;
2010-12-11 05:45:17 +03:00
if ( status & TWL6040_PLUGCOMP )
snd_soc_jack_report ( jack , report , report ) ;
else
snd_soc_jack_report ( jack , 0 , report ) ;
mutex_unlock ( & priv - > mutex ) ;
}
2018-01-29 07:20:09 +03:00
void twl6040_hs_jack_detect ( struct snd_soc_component * component ,
2010-12-11 05:45:17 +03:00
struct snd_soc_jack * jack , int report )
{
2018-01-29 07:20:09 +03:00
struct twl6040_data * priv = snd_soc_component_get_drvdata ( component ) ;
2010-12-11 05:45:17 +03:00
struct twl6040_jack_data * hs_jack = & priv - > hs_jack ;
hs_jack - > jack = jack ;
hs_jack - > report = report ;
2018-01-29 07:20:09 +03:00
twl6040_hs_jack_report ( component , hs_jack - > jack , hs_jack - > report ) ;
2010-12-11 05:45:17 +03:00
}
EXPORT_SYMBOL_GPL ( twl6040_hs_jack_detect ) ;
static void twl6040_accessory_work ( struct work_struct * work )
{
struct twl6040_data * priv = container_of ( work ,
2011-09-26 17:26:27 +04:00
struct twl6040_data , hs_jack . work . work ) ;
2018-01-29 07:20:09 +03:00
struct snd_soc_component * component = priv - > component ;
2010-12-11 05:45:17 +03:00
struct twl6040_jack_data * hs_jack = & priv - > hs_jack ;
2018-01-29 07:20:09 +03:00
twl6040_hs_jack_report ( component , hs_jack - > jack , hs_jack - > report ) ;
2010-12-11 05:45:17 +03:00
}
2010-03-19 14:25:51 +03:00
/* audio interrupt handler */
2011-05-02 06:27:00 +04:00
static irqreturn_t twl6040_audio_handler ( int irq , void * data )
2010-03-19 14:25:51 +03:00
{
2018-01-29 07:20:09 +03:00
struct snd_soc_component * component = data ;
struct twl6040_data * priv = snd_soc_component_get_drvdata ( component ) ;
2010-12-11 06:05:30 +03:00
2013-07-19 01:44:03 +04:00
queue_delayed_work ( system_power_efficient_wq ,
& priv - > hs_jack . work , msecs_to_jiffies ( 200 ) ) ;
2010-12-11 06:05:30 +03:00
2010-03-19 14:25:51 +03:00
return IRQ_HANDLED ;
}
2011-10-12 12:57:57 +04:00
static int twl6040_soc_dapm_put_vibra_enum ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
2018-01-29 07:20:09 +03:00
struct snd_soc_component * component = snd_soc_dapm_kcontrol_component ( kcontrol ) ;
2011-10-12 12:57:57 +04:00
struct soc_enum * e = ( struct soc_enum * ) kcontrol - > private_value ;
unsigned int val ;
/* Do not allow changes while Input/FF efect is running */
2018-01-29 07:20:09 +03:00
val = twl6040_read ( component , e - > reg ) ;
2011-10-12 12:57:57 +04:00
if ( val & TWL6040_VIBENA & & ! ( val & TWL6040_VIBSEL ) )
return - EBUSY ;
return snd_soc_dapm_put_enum_double ( kcontrol , ucontrol ) ;
}
2010-03-19 14:25:51 +03:00
/*
* MICATT volume control :
* from - 6 to 0 dB in 6 dB steps
*/
static DECLARE_TLV_DB_SCALE ( mic_preamp_tlv , - 600 , 600 , 0 ) ;
/*
* MICGAIN volume control :
2011-05-01 18:35:55 +04:00
* from 6 to 30 dB in 6 dB steps
2010-03-19 14:25:51 +03:00
*/
2011-05-01 18:35:55 +04:00
static DECLARE_TLV_DB_SCALE ( mic_amp_tlv , 600 , 600 , 0 ) ;
2010-03-19 14:25:51 +03:00
2010-12-11 06:05:32 +03:00
/*
* AFMGAIN volume control :
2011-03-28 22:23:23 +04:00
* from - 18 to 24 dB in 6 dB steps
2010-12-11 06:05:32 +03:00
*/
2011-03-28 22:23:23 +04:00
static DECLARE_TLV_DB_SCALE ( afm_amp_tlv , - 1800 , 600 , 0 ) ;
2010-12-11 06:05:32 +03:00
2010-03-19 14:25:51 +03:00
/*
* HSGAIN volume control :
* from - 30 to 0 dB in 2 dB steps
*/
static DECLARE_TLV_DB_SCALE ( hs_tlv , - 3000 , 200 , 0 ) ;
/*
* HFGAIN volume control :
* from - 52 to 6 dB in 2 dB steps
*/
static DECLARE_TLV_DB_SCALE ( hf_tlv , - 5200 , 200 , 0 ) ;
2010-05-18 21:44:18 +04:00
/*
* EPGAIN volume control :
* from - 24 to 6 dB in 2 dB steps
*/
static DECLARE_TLV_DB_SCALE ( ep_tlv , - 2400 , 200 , 0 ) ;
2010-03-19 14:25:51 +03:00
/* Left analog microphone selection */
static const char * twl6040_amicl_texts [ ] =
{ " Headset Mic " , " Main Mic " , " Aux/FM Left " , " Off " } ;
/* Right analog microphone selection */
static const char * twl6040_amicr_texts [ ] =
{ " Headset Mic " , " Sub Mic " , " Aux/FM Right " , " Off " } ;
static const struct soc_enum twl6040_enum [ ] = {
2014-02-18 13:30:20 +04:00
SOC_ENUM_SINGLE ( TWL6040_REG_MICLCTL , 3 ,
ARRAY_SIZE ( twl6040_amicl_texts ) , twl6040_amicl_texts ) ,
SOC_ENUM_SINGLE ( TWL6040_REG_MICRCTL , 3 ,
ARRAY_SIZE ( twl6040_amicr_texts ) , twl6040_amicr_texts ) ,
2010-03-19 14:25:51 +03:00
} ;
2010-12-11 06:05:32 +03:00
static const char * twl6040_hs_texts [ ] = {
" Off " , " HS DAC " , " Line-In amp "
} ;
static const struct soc_enum twl6040_hs_enum [ ] = {
SOC_ENUM_SINGLE ( TWL6040_REG_HSLCTL , 5 , ARRAY_SIZE ( twl6040_hs_texts ) ,
twl6040_hs_texts ) ,
SOC_ENUM_SINGLE ( TWL6040_REG_HSRCTL , 5 , ARRAY_SIZE ( twl6040_hs_texts ) ,
twl6040_hs_texts ) ,
} ;
static const char * twl6040_hf_texts [ ] = {
" Off " , " HF DAC " , " Line-In amp "
} ;
static const struct soc_enum twl6040_hf_enum [ ] = {
SOC_ENUM_SINGLE ( TWL6040_REG_HFLCTL , 2 , ARRAY_SIZE ( twl6040_hf_texts ) ,
twl6040_hf_texts ) ,
SOC_ENUM_SINGLE ( TWL6040_REG_HFRCTL , 2 , ARRAY_SIZE ( twl6040_hf_texts ) ,
twl6040_hf_texts ) ,
} ;
2011-10-12 12:57:57 +04:00
static const char * twl6040_vibrapath_texts [ ] = {
" Input FF " , " Audio PDM "
} ;
static const struct soc_enum twl6040_vibra_enum [ ] = {
SOC_ENUM_SINGLE ( TWL6040_REG_VIBCTLL , 1 ,
ARRAY_SIZE ( twl6040_vibrapath_texts ) ,
twl6040_vibrapath_texts ) ,
SOC_ENUM_SINGLE ( TWL6040_REG_VIBCTLR , 1 ,
ARRAY_SIZE ( twl6040_vibrapath_texts ) ,
twl6040_vibrapath_texts ) ,
} ;
2010-03-19 14:25:51 +03:00
static const struct snd_kcontrol_new amicl_control =
SOC_DAPM_ENUM ( " Route " , twl6040_enum [ 0 ] ) ;
static const struct snd_kcontrol_new amicr_control =
SOC_DAPM_ENUM ( " Route " , twl6040_enum [ 1 ] ) ;
/* Headset DAC playback switches */
2010-12-11 06:05:32 +03:00
static const struct snd_kcontrol_new hsl_mux_controls =
SOC_DAPM_ENUM ( " Route " , twl6040_hs_enum [ 0 ] ) ;
2010-03-19 14:25:51 +03:00
2010-12-11 06:05:32 +03:00
static const struct snd_kcontrol_new hsr_mux_controls =
SOC_DAPM_ENUM ( " Route " , twl6040_hs_enum [ 1 ] ) ;
2010-03-19 14:25:51 +03:00
/* Handsfree DAC playback switches */
2010-12-11 06:05:32 +03:00
static const struct snd_kcontrol_new hfl_mux_controls =
SOC_DAPM_ENUM ( " Route " , twl6040_hf_enum [ 0 ] ) ;
2010-03-19 14:25:51 +03:00
2010-12-11 06:05:32 +03:00
static const struct snd_kcontrol_new hfr_mux_controls =
SOC_DAPM_ENUM ( " Route " , twl6040_hf_enum [ 1 ] ) ;
2010-03-19 14:25:51 +03:00
2011-09-22 12:05:49 +04:00
static const struct snd_kcontrol_new ep_path_enable_control =
2013-10-06 15:43:51 +04:00
SOC_DAPM_SINGLE_VIRT ( " Switch " , 1 ) ;
2010-05-18 21:44:18 +04:00
2011-09-22 12:05:52 +04:00
static const struct snd_kcontrol_new auxl_switch_control =
SOC_DAPM_SINGLE ( " Switch " , TWL6040_REG_HFLCTL , 6 , 1 , 0 ) ;
static const struct snd_kcontrol_new auxr_switch_control =
SOC_DAPM_SINGLE ( " Switch " , TWL6040_REG_HFRCTL , 6 , 1 , 0 ) ;
2011-10-12 12:57:57 +04:00
/* Vibra playback switches */
static const struct snd_kcontrol_new vibral_mux_controls =
SOC_DAPM_ENUM_EXT ( " Route " , twl6040_vibra_enum [ 0 ] ,
snd_soc_dapm_get_enum_double ,
twl6040_soc_dapm_put_vibra_enum ) ;
static const struct snd_kcontrol_new vibrar_mux_controls =
SOC_DAPM_ENUM_EXT ( " Route " , twl6040_vibra_enum [ 1 ] ,
snd_soc_dapm_get_enum_double ,
twl6040_soc_dapm_put_vibra_enum ) ;
2011-02-12 02:51:05 +03:00
/* Headset power mode */
2011-06-27 14:33:14 +04:00
static const char * twl6040_power_mode_texts [ ] = {
2012-07-06 20:04:17 +04:00
" Low-Power " , " High-Performance " ,
2011-02-12 02:51:05 +03:00
} ;
2014-02-18 13:30:20 +04:00
static SOC_ENUM_SINGLE_EXT_DECL ( twl6040_power_mode_enum ,
twl6040_power_mode_texts ) ;
2011-02-12 02:51:05 +03:00
static int twl6040_headset_power_get_enum ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
2018-01-29 07:20:09 +03:00
struct snd_soc_component * component = snd_soc_kcontrol_component ( kcontrol ) ;
struct twl6040_data * priv = snd_soc_component_get_drvdata ( component ) ;
2011-02-12 02:51:05 +03:00
ucontrol - > value . enumerated . item [ 0 ] = priv - > hs_power_mode ;
return 0 ;
}
static int twl6040_headset_power_put_enum ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
2018-01-29 07:20:09 +03:00
struct snd_soc_component * component = snd_soc_kcontrol_component ( kcontrol ) ;
struct twl6040_data * priv = snd_soc_component_get_drvdata ( component ) ;
2011-02-12 02:51:05 +03:00
int high_perf = ucontrol - > value . enumerated . item [ 0 ] ;
int ret = 0 ;
if ( ! priv - > hs_power_mode_locked )
2018-01-29 07:20:09 +03:00
ret = headset_power_mode ( component , high_perf ) ;
2011-02-12 02:51:05 +03:00
if ( ! ret )
priv - > hs_power_mode = high_perf ;
return ret ;
}
2011-06-27 18:03:14 +04:00
static int twl6040_pll_get_enum ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
2018-01-29 07:20:09 +03:00
struct snd_soc_component * component = snd_soc_kcontrol_component ( kcontrol ) ;
struct twl6040_data * priv = snd_soc_component_get_drvdata ( component ) ;
2011-06-27 18:03:14 +04:00
ucontrol - > value . enumerated . item [ 0 ] = priv - > pll_power_mode ;
return 0 ;
}
static int twl6040_pll_put_enum ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
2018-01-29 07:20:09 +03:00
struct snd_soc_component * component = snd_soc_kcontrol_component ( kcontrol ) ;
struct twl6040_data * priv = snd_soc_component_get_drvdata ( component ) ;
2011-06-27 18:03:14 +04:00
priv - > pll_power_mode = ucontrol - > value . enumerated . item [ 0 ] ;
return 0 ;
}
2018-01-29 07:20:09 +03:00
int twl6040_get_dl1_gain ( struct snd_soc_component * component )
2012-01-11 16:43:24 +04:00
{
2018-01-29 07:20:09 +03:00
struct snd_soc_dapm_context * dapm = snd_soc_component_get_dapm ( component ) ;
2012-01-11 16:43:24 +04:00
if ( snd_soc_dapm_get_pin_status ( dapm , " EP " ) )
return - 1 ; /* -1dB */
if ( snd_soc_dapm_get_pin_status ( dapm , " HSOR " ) | |
snd_soc_dapm_get_pin_status ( dapm , " HSOL " ) ) {
2018-01-29 07:20:09 +03:00
u8 val = twl6040_read ( component , TWL6040_REG_HSLCTL ) ;
2012-01-11 16:43:24 +04:00
if ( val & TWL6040_HSDACMODE )
/* HSDACL in LP mode */
return - 8 ; /* -8dB */
else
/* HSDACL in HP mode */
return - 1 ; /* -1dB */
}
return 0 ; /* 0dB */
}
EXPORT_SYMBOL_GPL ( twl6040_get_dl1_gain ) ;
2018-01-29 07:20:09 +03:00
int twl6040_get_clk_id ( struct snd_soc_component * component )
2011-06-27 18:03:14 +04:00
{
2018-01-29 07:20:09 +03:00
struct twl6040_data * priv = snd_soc_component_get_drvdata ( component ) ;
2011-06-27 18:03:14 +04:00
2011-07-04 11:35:23 +04:00
return priv - > pll_power_mode ;
2011-06-27 18:03:14 +04:00
}
EXPORT_SYMBOL_GPL ( twl6040_get_clk_id ) ;
2018-01-29 07:20:09 +03:00
int twl6040_get_trim_value ( struct snd_soc_component * component , enum twl6040_trim trim )
2011-09-26 17:05:57 +04:00
{
if ( unlikely ( trim > = TWL6040_TRIM_INVAL ) )
return - EINVAL ;
2018-01-29 07:20:09 +03:00
return twl6040_read ( component , TWL6040_REG_TRIM1 + trim ) ;
2011-09-26 17:05:57 +04:00
}
EXPORT_SYMBOL_GPL ( twl6040_get_trim_value ) ;
2018-01-29 07:20:09 +03:00
int twl6040_get_hs_step_size ( struct snd_soc_component * component )
2012-01-09 16:10:16 +04:00
{
2018-01-29 07:20:09 +03:00
struct twl6040 * twl6040 = to_twl6040 ( component ) ;
2012-01-09 16:10:16 +04:00
2012-07-16 13:49:43 +04:00
if ( twl6040_get_revid ( twl6040 ) < TWL6040_REV_ES1_3 )
2012-01-09 16:10:16 +04:00
/* For ES under ES_1.3 HS step is 2 mV */
return 2 ;
else
/* For ES_1.3 HS step is 1 mV */
return 1 ;
}
EXPORT_SYMBOL_GPL ( twl6040_get_hs_step_size ) ;
2010-03-19 14:25:51 +03:00
static const struct snd_kcontrol_new twl6040_snd_controls [ ] = {
/* Capture gains */
SOC_DOUBLE_TLV ( " Capture Preamplifier Volume " ,
TWL6040_REG_MICGAIN , 6 , 7 , 1 , 1 , mic_preamp_tlv ) ,
SOC_DOUBLE_TLV ( " Capture Volume " ,
TWL6040_REG_MICGAIN , 0 , 3 , 4 , 0 , mic_amp_tlv ) ,
2010-12-11 06:05:32 +03:00
/* AFM gains */
SOC_DOUBLE_TLV ( " Aux FM Volume " ,
2011-03-28 22:23:23 +04:00
TWL6040_REG_LINEGAIN , 0 , 3 , 7 , 0 , afm_amp_tlv ) ,
2010-12-11 06:05:32 +03:00
2010-03-19 14:25:51 +03:00
/* Playback gains */
2012-05-04 16:17:20 +04:00
SOC_DOUBLE_TLV ( " Headset Playback Volume " ,
TWL6040_REG_HSGAIN , 0 , 4 , 0xF , 1 , hs_tlv ) ,
SOC_DOUBLE_R_TLV ( " Handsfree Playback Volume " ,
TWL6040_REG_HFLGAIN , TWL6040_REG_HFRGAIN , 0 , 0x1D , 1 , hf_tlv ) ,
2010-05-18 21:44:18 +04:00
SOC_SINGLE_TLV ( " Earphone Playback Volume " ,
TWL6040_REG_EARCTL , 1 , 0xF , 1 , ep_tlv ) ,
2011-02-12 02:51:05 +03:00
2011-06-27 14:33:14 +04:00
SOC_ENUM_EXT ( " Headset Power Mode " , twl6040_power_mode_enum ,
2011-02-12 02:51:05 +03:00
twl6040_headset_power_get_enum ,
twl6040_headset_power_put_enum ) ,
2011-06-27 18:03:14 +04:00
2017-04-03 13:12:39 +03:00
/* Left HS PDM data routed to Right HSDAC */
SOC_SINGLE ( " Headset Mono to Stereo Playback Switch " ,
TWL6040_REG_HSRCTL , 7 , 1 , 0 ) ,
/* Left HF PDM data routed to Right HFDAC */
SOC_SINGLE ( " Handsfree Mono to Stereo Playback Switch " ,
TWL6040_REG_HFRCTL , 5 , 1 , 0 ) ,
2011-06-27 18:03:14 +04:00
SOC_ENUM_EXT ( " PLL Selection " , twl6040_power_mode_enum ,
twl6040_pll_get_enum , twl6040_pll_put_enum ) ,
2010-03-19 14:25:51 +03:00
} ;
static const struct snd_soc_dapm_widget twl6040_dapm_widgets [ ] = {
/* Inputs */
SND_SOC_DAPM_INPUT ( " MAINMIC " ) ,
SND_SOC_DAPM_INPUT ( " HSMIC " ) ,
SND_SOC_DAPM_INPUT ( " SUBMIC " ) ,
SND_SOC_DAPM_INPUT ( " AFML " ) ,
SND_SOC_DAPM_INPUT ( " AFMR " ) ,
/* Outputs */
SND_SOC_DAPM_OUTPUT ( " HSOL " ) ,
SND_SOC_DAPM_OUTPUT ( " HSOR " ) ,
SND_SOC_DAPM_OUTPUT ( " HFL " ) ,
SND_SOC_DAPM_OUTPUT ( " HFR " ) ,
2010-05-18 21:44:18 +04:00
SND_SOC_DAPM_OUTPUT ( " EP " ) ,
2011-09-22 12:05:52 +04:00
SND_SOC_DAPM_OUTPUT ( " AUXL " ) ,
SND_SOC_DAPM_OUTPUT ( " AUXR " ) ,
2011-10-12 12:57:57 +04:00
SND_SOC_DAPM_OUTPUT ( " VIBRAL " ) ,
SND_SOC_DAPM_OUTPUT ( " VIBRAR " ) ,
2010-03-19 14:25:51 +03:00
/* Analog input muxes for the capture amplifiers */
SND_SOC_DAPM_MUX ( " Analog Left Capture Route " ,
SND_SOC_NOPM , 0 , 0 , & amicl_control ) ,
SND_SOC_DAPM_MUX ( " Analog Right Capture Route " ,
SND_SOC_NOPM , 0 , 0 , & amicr_control ) ,
/* Analog capture PGAs */
SND_SOC_DAPM_PGA ( " MicAmpL " ,
TWL6040_REG_MICLCTL , 0 , 0 , NULL , 0 ) ,
SND_SOC_DAPM_PGA ( " MicAmpR " ,
TWL6040_REG_MICRCTL , 0 , 0 , NULL , 0 ) ,
2010-12-11 06:05:32 +03:00
/* Auxiliary FM PGAs */
SND_SOC_DAPM_PGA ( " AFMAmpL " ,
TWL6040_REG_MICLCTL , 1 , 0 , NULL , 0 ) ,
SND_SOC_DAPM_PGA ( " AFMAmpR " ,
TWL6040_REG_MICRCTL , 1 , 0 , NULL , 0 ) ,
2010-03-19 14:25:51 +03:00
/* ADCs */
2012-09-20 17:32:15 +04:00
SND_SOC_DAPM_ADC ( " ADC Left " , NULL , TWL6040_REG_MICLCTL , 2 , 0 ) ,
SND_SOC_DAPM_ADC ( " ADC Right " , NULL , TWL6040_REG_MICRCTL , 2 , 0 ) ,
2010-03-19 14:25:51 +03:00
/* Microphone bias */
2011-12-22 14:47:21 +04:00
SND_SOC_DAPM_SUPPLY ( " Headset Mic Bias " ,
TWL6040_REG_AMICBCTL , 0 , 0 , NULL , 0 ) ,
SND_SOC_DAPM_SUPPLY ( " Main Mic Bias " ,
TWL6040_REG_AMICBCTL , 4 , 0 , NULL , 0 ) ,
SND_SOC_DAPM_SUPPLY ( " Digital Mic1 Bias " ,
TWL6040_REG_DMICBCTL , 0 , 0 , NULL , 0 ) ,
SND_SOC_DAPM_SUPPLY ( " Digital Mic2 Bias " ,
TWL6040_REG_DMICBCTL , 4 , 0 , NULL , 0 ) ,
2010-03-19 14:25:51 +03:00
/* DACs */
2012-09-20 17:32:15 +04:00
SND_SOC_DAPM_DAC ( " HSDAC Left " , NULL , SND_SOC_NOPM , 0 , 0 ) ,
SND_SOC_DAPM_DAC ( " HSDAC Right " , NULL , SND_SOC_NOPM , 0 , 0 ) ,
SND_SOC_DAPM_DAC ( " HFDAC Left " , NULL , TWL6040_REG_HFLCTL , 0 , 0 ) ,
SND_SOC_DAPM_DAC ( " HFDAC Right " , NULL , TWL6040_REG_HFRCTL , 0 , 0 ) ,
2011-10-12 12:57:57 +04:00
/* Virtual DAC for vibra path (DL4 channel) */
2012-09-20 17:32:15 +04:00
SND_SOC_DAPM_DAC ( " VIBRA DAC " , NULL , SND_SOC_NOPM , 0 , 0 ) ,
2010-03-19 14:25:51 +03:00
2011-09-22 12:05:50 +04:00
SND_SOC_DAPM_MUX ( " Handsfree Left Playback " ,
2010-12-11 06:05:32 +03:00
SND_SOC_NOPM , 0 , 0 , & hfl_mux_controls ) ,
2011-09-22 12:05:50 +04:00
SND_SOC_DAPM_MUX ( " Handsfree Right Playback " ,
2010-12-11 06:05:32 +03:00
SND_SOC_NOPM , 0 , 0 , & hfr_mux_controls ) ,
/* Analog playback Muxes */
2011-09-22 12:05:51 +04:00
SND_SOC_DAPM_MUX ( " Headset Left Playback " ,
2010-12-11 06:05:32 +03:00
SND_SOC_NOPM , 0 , 0 , & hsl_mux_controls ) ,
2011-09-22 12:05:51 +04:00
SND_SOC_DAPM_MUX ( " Headset Right Playback " ,
2010-12-11 06:05:32 +03:00
SND_SOC_NOPM , 0 , 0 , & hsr_mux_controls ) ,
2010-03-19 14:25:51 +03:00
2011-10-12 12:57:57 +04:00
SND_SOC_DAPM_MUX ( " Vibra Left Playback " , SND_SOC_NOPM , 0 , 0 ,
& vibral_mux_controls ) ,
SND_SOC_DAPM_MUX ( " Vibra Right Playback " , SND_SOC_NOPM , 0 , 0 ,
& vibrar_mux_controls ) ,
2011-09-22 12:05:49 +04:00
SND_SOC_DAPM_SWITCH ( " Earphone Playback " , SND_SOC_NOPM , 0 , 0 ,
& ep_path_enable_control ) ,
2011-09-22 12:05:52 +04:00
SND_SOC_DAPM_SWITCH ( " AUXL Playback " , SND_SOC_NOPM , 0 , 0 ,
& auxl_switch_control ) ,
SND_SOC_DAPM_SWITCH ( " AUXR Playback " , SND_SOC_NOPM , 0 , 0 ,
& auxr_switch_control ) ,
2011-09-22 12:05:49 +04:00
2010-07-15 20:38:01 +04:00
/* Analog playback drivers */
2012-05-04 16:17:20 +04:00
SND_SOC_DAPM_OUT_DRV ( " HF Left Driver " ,
TWL6040_REG_HFLCTL , 4 , 0 , NULL , 0 ) ,
SND_SOC_DAPM_OUT_DRV ( " HF Right Driver " ,
TWL6040_REG_HFRCTL , 4 , 0 , NULL , 0 ) ,
SND_SOC_DAPM_OUT_DRV ( " HS Left Driver " ,
TWL6040_REG_HSLCTL , 2 , 0 , NULL , 0 ) ,
SND_SOC_DAPM_OUT_DRV ( " HS Right Driver " ,
TWL6040_REG_HSRCTL , 2 , 0 , NULL , 0 ) ,
2011-09-22 12:05:49 +04:00
SND_SOC_DAPM_OUT_DRV_E ( " Earphone Driver " ,
TWL6040_REG_EARCTL , 0 , 0 , NULL , 0 ,
2011-10-13 16:05:44 +04:00
twl6040_ep_drv_event ,
2011-10-13 16:05:43 +04:00
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD ) ,
2011-10-12 12:57:57 +04:00
SND_SOC_DAPM_OUT_DRV ( " Vibra Left Driver " ,
TWL6040_REG_VIBCTLL , 0 , 0 , NULL , 0 ) ,
SND_SOC_DAPM_OUT_DRV ( " Vibra Right Driver " ,
TWL6040_REG_VIBCTLR , 0 , 0 , NULL , 0 ) ,
SND_SOC_DAPM_SUPPLY ( " Vibra Left Control " , TWL6040_REG_VIBCTLL , 2 , 0 ,
NULL , 0 ) ,
SND_SOC_DAPM_SUPPLY ( " Vibra Right Control " , TWL6040_REG_VIBCTLR , 2 , 0 ,
NULL , 0 ) ,
2011-10-12 15:46:02 +04:00
SND_SOC_DAPM_SUPPLY_S ( " HSDAC Power " , 1 , SND_SOC_NOPM , 0 , 0 ,
twl6040_hs_dac_event ,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD ) ,
2010-03-19 14:25:51 +03:00
/* Analog playback PGAs */
2011-09-22 12:05:50 +04:00
SND_SOC_DAPM_PGA ( " HF Left PGA " ,
2010-03-19 14:25:51 +03:00
TWL6040_REG_HFLCTL , 1 , 0 , NULL , 0 ) ,
2011-09-22 12:05:50 +04:00
SND_SOC_DAPM_PGA ( " HF Right PGA " ,
2010-03-19 14:25:51 +03:00
TWL6040_REG_HFRCTL , 1 , 0 , NULL , 0 ) ,
} ;
static const struct snd_soc_dapm_route intercon [ ] = {
2012-09-20 17:32:15 +04:00
/* Stream -> DAC mapping */
{ " HSDAC Left " , NULL , " Legacy Playback " } ,
{ " HSDAC Left " , NULL , " Headset Playback " } ,
{ " HSDAC Right " , NULL , " Legacy Playback " } ,
{ " HSDAC Right " , NULL , " Headset Playback " } ,
{ " HFDAC Left " , NULL , " Legacy Playback " } ,
{ " HFDAC Left " , NULL , " Handsfree Playback " } ,
{ " HFDAC Right " , NULL , " Legacy Playback " } ,
{ " HFDAC Right " , NULL , " Handsfree Playback " } ,
{ " VIBRA DAC " , NULL , " Legacy Playback " } ,
{ " VIBRA DAC " , NULL , " Vibra Playback " } ,
/* ADC -> Stream mapping */
2012-10-04 15:15:02 +04:00
{ " Legacy Capture " , NULL , " ADC Left " } ,
{ " Capture " , NULL , " ADC Left " } ,
{ " Legacy Capture " , NULL , " ADC Right " } ,
{ " Capture " , NULL , " ADC Right " } ,
2012-09-20 17:32:15 +04:00
2010-03-19 14:25:51 +03:00
/* Capture path */
{ " Analog Left Capture Route " , " Headset Mic " , " HSMIC " } ,
{ " Analog Left Capture Route " , " Main Mic " , " MAINMIC " } ,
{ " Analog Left Capture Route " , " Aux/FM Left " , " AFML " } ,
{ " Analog Right Capture Route " , " Headset Mic " , " HSMIC " } ,
{ " Analog Right Capture Route " , " Sub Mic " , " SUBMIC " } ,
{ " Analog Right Capture Route " , " Aux/FM Right " , " AFMR " } ,
{ " MicAmpL " , NULL , " Analog Left Capture Route " } ,
{ " MicAmpR " , NULL , " Analog Right Capture Route " } ,
{ " ADC Left " , NULL , " MicAmpL " } ,
{ " ADC Right " , NULL , " MicAmpR " } ,
2010-12-11 06:05:32 +03:00
/* AFM path */
2011-09-22 12:05:47 +04:00
{ " AFMAmpL " , NULL , " AFML " } ,
{ " AFMAmpR " , NULL , " AFMR " } ,
2010-12-11 06:05:32 +03:00
2011-10-12 15:46:02 +04:00
{ " HSDAC Left " , NULL , " HSDAC Power " } ,
{ " HSDAC Right " , NULL , " HSDAC Power " } ,
2011-09-22 12:05:51 +04:00
{ " Headset Left Playback " , " HS DAC " , " HSDAC Left " } ,
{ " Headset Left Playback " , " Line-In amp " , " AFMAmpL " } ,
2010-03-19 14:25:51 +03:00
2011-09-22 12:05:51 +04:00
{ " Headset Right Playback " , " HS DAC " , " HSDAC Right " } ,
{ " Headset Right Playback " , " Line-In amp " , " AFMAmpR " } ,
2010-12-11 06:05:32 +03:00
2011-09-22 12:05:51 +04:00
{ " HS Left Driver " , NULL , " Headset Left Playback " } ,
{ " HS Right Driver " , NULL , " Headset Right Playback " } ,
2010-03-19 14:25:51 +03:00
2011-09-22 12:05:51 +04:00
{ " HSOL " , NULL , " HS Left Driver " } ,
{ " HSOR " , NULL , " HS Right Driver " } ,
2010-03-19 14:25:51 +03:00
2010-05-18 21:44:18 +04:00
/* Earphone playback path */
2011-09-22 12:05:49 +04:00
{ " Earphone Playback " , " Switch " , " HSDAC Left " } ,
{ " Earphone Driver " , NULL , " Earphone Playback " } ,
2010-05-18 21:44:18 +04:00
{ " EP " , NULL , " Earphone Driver " } ,
2011-09-22 12:05:50 +04:00
{ " Handsfree Left Playback " , " HF DAC " , " HFDAC Left " } ,
{ " Handsfree Left Playback " , " Line-In amp " , " AFMAmpL " } ,
2010-12-11 06:05:32 +03:00
2011-09-22 12:05:50 +04:00
{ " Handsfree Right Playback " , " HF DAC " , " HFDAC Right " } ,
{ " Handsfree Right Playback " , " Line-In amp " , " AFMAmpR " } ,
2010-03-19 14:25:51 +03:00
2011-09-22 12:05:50 +04:00
{ " HF Left PGA " , NULL , " Handsfree Left Playback " } ,
{ " HF Right PGA " , NULL , " Handsfree Right Playback " } ,
2010-03-19 14:25:51 +03:00
2011-09-22 12:05:50 +04:00
{ " HF Left Driver " , NULL , " HF Left PGA " } ,
{ " HF Right Driver " , NULL , " HF Right PGA " } ,
2010-03-19 14:25:51 +03:00
2011-09-22 12:05:50 +04:00
{ " HFL " , NULL , " HF Left Driver " } ,
{ " HFR " , NULL , " HF Right Driver " } ,
2011-09-22 12:05:52 +04:00
{ " AUXL Playback " , " Switch " , " HF Left PGA " } ,
{ " AUXR Playback " , " Switch " , " HF Right PGA " } ,
{ " AUXL " , NULL , " AUXL Playback " } ,
{ " AUXR " , NULL , " AUXR Playback " } ,
2011-10-12 12:57:57 +04:00
/* Vibrator paths */
{ " Vibra Left Playback " , " Audio PDM " , " VIBRA DAC " } ,
{ " Vibra Right Playback " , " Audio PDM " , " VIBRA DAC " } ,
{ " Vibra Left Driver " , NULL , " Vibra Left Playback " } ,
{ " Vibra Right Driver " , NULL , " Vibra Right Playback " } ,
{ " Vibra Left Driver " , NULL , " Vibra Left Control " } ,
{ " Vibra Right Driver " , NULL , " Vibra Right Control " } ,
{ " VIBRAL " , NULL , " Vibra Left Driver " } ,
{ " VIBRAR " , NULL , " Vibra Right Driver " } ,
2010-03-19 14:25:51 +03:00
} ;
2018-01-29 07:20:09 +03:00
static int twl6040_set_bias_level ( struct snd_soc_component * component ,
2010-03-19 14:25:51 +03:00
enum snd_soc_bias_level level )
{
2018-01-29 07:20:09 +03:00
struct twl6040 * twl6040 = to_twl6040 ( component ) ;
struct twl6040_data * priv = snd_soc_component_get_drvdata ( component ) ;
2016-05-11 14:14:05 +03:00
int ret = 0 ;
2010-03-19 14:25:51 +03:00
switch ( level ) {
case SND_SOC_BIAS_ON :
break ;
case SND_SOC_BIAS_PREPARE :
break ;
case SND_SOC_BIAS_STANDBY :
2016-05-11 14:14:05 +03:00
if ( priv - > codec_powered ) {
/* Select low power PLL in standby */
ret = twl6040_set_pll ( twl6040 , TWL6040_SYSCLK_SEL_LPPLL ,
32768 , 19200000 ) ;
2010-03-19 14:25:51 +03:00
break ;
2016-05-11 14:14:05 +03:00
}
2010-03-19 14:25:51 +03:00
2011-05-02 06:27:00 +04:00
ret = twl6040_power ( twl6040 , 1 ) ;
if ( ret )
2016-05-11 14:14:05 +03:00
break ;
2010-03-19 14:25:51 +03:00
2011-05-02 06:27:00 +04:00
priv - > codec_powered = 1 ;
2010-03-19 14:25:51 +03:00
2010-12-15 04:18:36 +03:00
/* Set external boost GPO */
2018-01-29 07:20:09 +03:00
twl6040_write ( component , TWL6040_REG_GPOCTL , 0x02 ) ;
2010-03-19 14:25:51 +03:00
break ;
case SND_SOC_BIAS_OFF :
if ( ! priv - > codec_powered )
break ;
2011-05-02 06:27:00 +04:00
twl6040_power ( twl6040 , 0 ) ;
2010-03-19 14:25:51 +03:00
priv - > codec_powered = 0 ;
break ;
}
2016-05-11 14:14:05 +03:00
return ret ;
2010-03-19 14:25:51 +03:00
}
static int twl6040_startup ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai )
{
2018-01-29 07:20:09 +03:00
struct snd_soc_component * component = dai - > component ;
struct twl6040_data * priv = snd_soc_component_get_drvdata ( component ) ;
2010-03-19 14:25:51 +03:00
snd_pcm_hw_constraint_list ( substream - > runtime , 0 ,
SNDRV_PCM_HW_PARAM_RATE ,
2011-06-29 14:28:18 +04:00
& sysclk_constraints [ priv - > pll_power_mode ] ) ;
2010-03-19 14:25:51 +03:00
return 0 ;
}
static int twl6040_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params ,
struct snd_soc_dai * dai )
{
2018-01-29 07:20:09 +03:00
struct snd_soc_component * component = dai - > component ;
struct twl6040_data * priv = snd_soc_component_get_drvdata ( component ) ;
2010-03-19 14:25:51 +03:00
int rate ;
rate = params_rate ( params ) ;
switch ( rate ) {
2010-12-11 06:05:58 +03:00
case 11250 :
case 22500 :
case 44100 :
2010-03-19 14:25:51 +03:00
case 88200 :
2011-07-03 03:06:07 +04:00
/* These rates are not supported when HPPLL is in use */
if ( unlikely ( priv - > pll = = TWL6040_SYSCLK_SEL_HPPLL ) ) {
2018-01-29 07:20:09 +03:00
dev_err ( component - > dev , " HPPLL does not support rate %d \n " ,
2011-07-03 03:06:07 +04:00
rate ) ;
return - EINVAL ;
}
2010-03-19 14:25:51 +03:00
priv - > sysclk = 17640000 ;
break ;
2010-12-11 06:05:58 +03:00
case 8000 :
case 16000 :
case 32000 :
case 48000 :
2010-03-19 14:25:51 +03:00
case 96000 :
priv - > sysclk = 19200000 ;
break ;
default :
2018-01-29 07:20:09 +03:00
dev_err ( component - > dev , " unsupported rate %d \n " , rate ) ;
2010-03-19 14:25:51 +03:00
return - EINVAL ;
}
return 0 ;
}
2010-12-11 06:05:54 +03:00
static int twl6040_prepare ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai )
2010-03-19 14:25:51 +03:00
{
2018-01-29 07:20:09 +03:00
struct snd_soc_component * component = dai - > component ;
struct twl6040 * twl6040 = to_twl6040 ( component ) ;
struct twl6040_data * priv = snd_soc_component_get_drvdata ( component ) ;
2011-07-03 03:06:07 +04:00
int ret ;
2010-03-19 14:25:51 +03:00
2010-12-11 06:05:54 +03:00
if ( ! priv - > sysclk ) {
2018-01-29 07:20:09 +03:00
dev_err ( component - > dev ,
2010-12-11 06:05:54 +03:00
" no mclk configured, call set_sysclk() on init \n " ) ;
return - EINVAL ;
}
2011-07-03 03:06:07 +04:00
ret = twl6040_set_pll ( twl6040 , priv - > pll , priv - > clk_in , priv - > sysclk ) ;
if ( ret ) {
2018-01-29 07:20:09 +03:00
dev_err ( component - > dev , " Can not set PLL (%d) \n " , ret ) ;
2011-07-03 03:06:07 +04:00
return - EPERM ;
}
2010-03-19 14:25:51 +03:00
return 0 ;
}
static int twl6040_set_dai_sysclk ( struct snd_soc_dai * codec_dai ,
int clk_id , unsigned int freq , int dir )
{
2018-01-29 07:20:09 +03:00
struct snd_soc_component * component = codec_dai - > component ;
struct twl6040_data * priv = snd_soc_component_get_drvdata ( component ) ;
2010-03-19 14:25:51 +03:00
switch ( clk_id ) {
case TWL6040_SYSCLK_SEL_LPPLL :
case TWL6040_SYSCLK_SEL_HPPLL :
2011-07-03 03:06:07 +04:00
priv - > pll = clk_id ;
priv - > clk_in = freq ;
2010-03-19 14:25:51 +03:00
break ;
default :
2018-01-29 07:20:09 +03:00
dev_err ( component - > dev , " unknown clk_id %d \n " , clk_id ) ;
2010-03-19 14:25:51 +03:00
return - EINVAL ;
}
return 0 ;
}
2018-01-29 07:20:09 +03:00
static void twl6040_mute_path ( struct snd_soc_component * component , enum twl6040_dai_id id ,
2013-06-24 17:42:06 +04:00
int mute )
{
2018-01-29 07:20:09 +03:00
struct twl6040 * twl6040 = to_twl6040 ( component ) ;
struct twl6040_data * priv = snd_soc_component_get_drvdata ( component ) ;
2013-06-24 17:42:06 +04:00
int hslctl , hsrctl , earctl ;
int hflctl , hfrctl ;
switch ( id ) {
case TWL6040_DAI_DL1 :
2018-01-29 07:20:09 +03:00
hslctl = twl6040_read ( component , TWL6040_REG_HSLCTL ) ;
hsrctl = twl6040_read ( component , TWL6040_REG_HSRCTL ) ;
earctl = twl6040_read ( component , TWL6040_REG_EARCTL ) ;
2013-06-24 17:42:06 +04:00
if ( mute ) {
/* Power down drivers and DACs */
earctl & = ~ 0x01 ;
hslctl & = ~ ( TWL6040_HSDRVENA | TWL6040_HSDACENA ) ;
hsrctl & = ~ ( TWL6040_HSDRVENA | TWL6040_HSDACENA ) ;
}
twl6040_reg_write ( twl6040 , TWL6040_REG_EARCTL , earctl ) ;
twl6040_reg_write ( twl6040 , TWL6040_REG_HSLCTL , hslctl ) ;
twl6040_reg_write ( twl6040 , TWL6040_REG_HSRCTL , hsrctl ) ;
priv - > dl1_unmuted = ! mute ;
break ;
case TWL6040_DAI_DL2 :
2018-01-29 07:20:09 +03:00
hflctl = twl6040_read ( component , TWL6040_REG_HFLCTL ) ;
hfrctl = twl6040_read ( component , TWL6040_REG_HFRCTL ) ;
2013-06-24 17:42:06 +04:00
if ( mute ) {
/* Power down drivers and DACs */
hflctl & = ~ ( TWL6040_HFDACENA | TWL6040_HFPGAENA |
2016-05-18 16:19:01 +03:00
TWL6040_HFDRVENA | TWL6040_HFSWENA ) ;
2013-06-24 17:42:06 +04:00
hfrctl & = ~ ( TWL6040_HFDACENA | TWL6040_HFPGAENA |
2016-05-18 16:19:01 +03:00
TWL6040_HFDRVENA | TWL6040_HFSWENA ) ;
2013-06-24 17:42:06 +04:00
}
twl6040_reg_write ( twl6040 , TWL6040_REG_HFLCTL , hflctl ) ;
twl6040_reg_write ( twl6040 , TWL6040_REG_HFRCTL , hfrctl ) ;
priv - > dl2_unmuted = ! mute ;
break ;
default :
break ;
2013-09-13 14:46:17 +04:00
}
2013-06-24 17:42:06 +04:00
}
2020-07-09 04:56:20 +03:00
static int twl6040_mute_stream ( struct snd_soc_dai * dai , int mute , int direction )
2013-06-24 17:42:06 +04:00
{
switch ( dai - > id ) {
case TWL6040_DAI_LEGACY :
2018-01-29 07:20:09 +03:00
twl6040_mute_path ( dai - > component , TWL6040_DAI_DL1 , mute ) ;
twl6040_mute_path ( dai - > component , TWL6040_DAI_DL2 , mute ) ;
2013-06-24 17:42:06 +04:00
break ;
case TWL6040_DAI_DL1 :
case TWL6040_DAI_DL2 :
2018-01-29 07:20:09 +03:00
twl6040_mute_path ( dai - > component , dai - > id , mute ) ;
2013-06-24 17:42:06 +04:00
break ;
default :
break ;
}
return 0 ;
}
2011-11-23 14:40:40 +04:00
static const struct snd_soc_dai_ops twl6040_dai_ops = {
2010-03-19 14:25:51 +03:00
. startup = twl6040_startup ,
. hw_params = twl6040_hw_params ,
2010-12-11 06:05:54 +03:00
. prepare = twl6040_prepare ,
2010-03-19 14:25:51 +03:00
. set_sysclk = twl6040_set_dai_sysclk ,
2020-07-09 04:56:20 +03:00
. mute_stream = twl6040_mute_stream ,
. no_capture_mute = 1 ,
2010-03-19 14:25:51 +03:00
} ;
2011-02-11 20:37:51 +03:00
static struct snd_soc_dai_driver twl6040_dai [ ] = {
2011-07-05 23:35:53 +04:00
{
2011-09-22 12:05:53 +04:00
. name = " twl6040-legacy " ,
2013-06-24 17:42:05 +04:00
. id = TWL6040_DAI_LEGACY ,
2010-03-19 14:25:51 +03:00
. playback = {
2012-09-20 17:32:15 +04:00
. stream_name = " Legacy Playback " ,
2010-03-19 14:25:51 +03:00
. channels_min = 1 ,
2011-09-15 16:59:19 +04:00
. channels_max = 5 ,
2011-07-05 23:35:53 +04:00
. rates = TWL6040_RATES ,
. formats = TWL6040_FORMATS ,
} ,
. capture = {
2012-09-20 17:32:15 +04:00
. stream_name = " Legacy Capture " ,
2011-07-05 23:35:53 +04:00
. channels_min = 1 ,
. channels_max = 2 ,
2010-03-19 14:25:51 +03:00
. rates = TWL6040_RATES ,
. formats = TWL6040_FORMATS ,
} ,
2011-07-05 23:35:53 +04:00
. ops = & twl6040_dai_ops ,
} ,
2011-02-11 20:37:51 +03:00
{
. name = " twl6040-ul " ,
2013-06-24 17:42:05 +04:00
. id = TWL6040_DAI_UL ,
2010-03-19 14:25:51 +03:00
. capture = {
. stream_name = " Capture " ,
. channels_min = 1 ,
. channels_max = 2 ,
. rates = TWL6040_RATES ,
. formats = TWL6040_FORMATS ,
} ,
. ops = & twl6040_dai_ops ,
2011-02-11 20:37:51 +03:00
} ,
{
. name = " twl6040-dl1 " ,
2013-06-24 17:42:05 +04:00
. id = TWL6040_DAI_DL1 ,
2010-03-19 14:25:51 +03:00
. playback = {
2011-02-11 20:37:51 +03:00
. stream_name = " Headset Playback " ,
2010-03-19 14:25:51 +03:00
. channels_min = 1 ,
2011-02-11 20:37:51 +03:00
. channels_max = 2 ,
2010-03-19 14:25:51 +03:00
. rates = TWL6040_RATES ,
. formats = TWL6040_FORMATS ,
} ,
2011-02-11 20:37:51 +03:00
. ops = & twl6040_dai_ops ,
} ,
{
. name = " twl6040-dl2 " ,
2013-06-24 17:42:05 +04:00
. id = TWL6040_DAI_DL2 ,
2011-02-11 20:37:51 +03:00
. playback = {
. stream_name = " Handsfree Playback " ,
2010-03-19 14:25:51 +03:00
. channels_min = 1 ,
. channels_max = 2 ,
. rates = TWL6040_RATES ,
. formats = TWL6040_FORMATS ,
} ,
. ops = & twl6040_dai_ops ,
2011-02-11 20:37:51 +03:00
} ,
{
. name = " twl6040-vib " ,
2013-06-24 17:42:05 +04:00
. id = TWL6040_DAI_VIB ,
2011-02-11 20:37:51 +03:00
. playback = {
. stream_name = " Vibra Playback " ,
2011-09-15 16:59:18 +04:00
. channels_min = 1 ,
. channels_max = 1 ,
2011-02-11 20:37:51 +03:00
. rates = SNDRV_PCM_RATE_CONTINUOUS ,
. formats = TWL6040_FORMATS ,
} ,
. ops = & twl6040_dai_ops ,
} ,
2010-03-19 14:25:51 +03:00
} ;
2018-01-29 07:20:09 +03:00
static int twl6040_probe ( struct snd_soc_component * component )
2010-03-19 14:25:51 +03:00
{
struct twl6040_data * priv ;
2018-01-29 07:20:09 +03:00
struct platform_device * pdev = to_platform_device ( component - > dev ) ;
2010-03-19 14:25:51 +03:00
int ret = 0 ;
2018-01-29 07:20:09 +03:00
priv = devm_kzalloc ( component - > dev , sizeof ( * priv ) , GFP_KERNEL ) ;
2010-03-19 14:25:51 +03:00
if ( priv = = NULL )
return - ENOMEM ;
2013-01-11 20:01:01 +04:00
2018-01-29 07:20:09 +03:00
snd_soc_component_set_drvdata ( component , priv ) ;
2010-03-19 14:25:51 +03:00
2018-01-29 07:20:09 +03:00
priv - > component = component ;
2010-12-11 05:45:17 +03:00
2011-07-04 20:52:26 +04:00
priv - > plug_irq = platform_get_irq ( pdev , 0 ) ;
2019-07-30 21:15:49 +03:00
if ( priv - > plug_irq < 0 )
2017-07-01 01:26:48 +03:00
return priv - > plug_irq ;
2010-03-19 14:25:51 +03:00
2011-09-26 17:26:27 +04:00
INIT_DELAYED_WORK ( & priv - > hs_jack . work , twl6040_accessory_work ) ;
2010-12-11 05:45:17 +03:00
mutex_init ( & priv - > mutex ) ;
2010-03-19 14:25:51 +03:00
2013-06-24 17:42:03 +04:00
ret = request_threaded_irq ( priv - > plug_irq , NULL ,
2015-05-12 07:22:59 +03:00
twl6040_audio_handler ,
IRQF_NO_SUSPEND | IRQF_ONESHOT ,
2018-01-29 07:20:09 +03:00
" twl6040_irq_plug " , component ) ;
2011-05-02 06:27:00 +04:00
if ( ret ) {
2018-01-29 07:20:09 +03:00
dev_err ( component - > dev , " PLUG IRQ request failed: %d \n " , ret ) ;
2013-01-11 20:01:02 +04:00
return ret ;
2011-05-02 06:27:00 +04:00
}
2018-01-29 07:20:09 +03:00
snd_soc_component_force_bias_level ( component , SND_SOC_BIAS_STANDBY ) ;
twl6040_init_chip ( component ) ;
2011-05-02 06:27:00 +04:00
2013-11-29 18:03:47 +04:00
return 0 ;
2010-03-19 14:25:51 +03:00
}
2018-01-29 07:20:09 +03:00
static void twl6040_remove ( struct snd_soc_component * component )
2010-03-19 14:25:51 +03:00
{
2018-01-29 07:20:09 +03:00
struct twl6040_data * priv = snd_soc_component_get_drvdata ( component ) ;
2013-06-24 17:42:03 +04:00
2018-01-29 07:20:09 +03:00
free_irq ( priv - > plug_irq , component ) ;
2010-03-17 23:15:21 +03:00
}
2010-03-19 14:25:51 +03:00
2018-01-29 07:20:09 +03:00
static const struct snd_soc_component_driver soc_component_dev_twl6040 = {
. probe = twl6040_probe ,
. remove = twl6040_remove ,
. read = twl6040_read ,
2018-03-12 17:24:22 +03:00
. write = twl6040_write ,
2018-01-29 07:20:09 +03:00
. set_bias_level = twl6040_set_bias_level ,
. controls = twl6040_snd_controls ,
. num_controls = ARRAY_SIZE ( twl6040_snd_controls ) ,
. dapm_widgets = twl6040_dapm_widgets ,
. num_dapm_widgets = ARRAY_SIZE ( twl6040_dapm_widgets ) ,
. dapm_routes = intercon ,
. num_dapm_routes = ARRAY_SIZE ( intercon ) ,
. suspend_bias_off = 1 ,
. idle_bias_on = 1 ,
. endianness = 1 ,
2010-03-17 23:15:21 +03:00
} ;
2012-12-07 18:26:37 +04:00
static int twl6040_codec_probe ( struct platform_device * pdev )
2010-03-17 23:15:21 +03:00
{
2018-01-29 07:20:09 +03:00
return devm_snd_soc_register_component ( & pdev - > dev ,
& soc_component_dev_twl6040 ,
2011-02-11 20:37:51 +03:00
twl6040_dai , ARRAY_SIZE ( twl6040_dai ) ) ;
2010-03-17 23:15:21 +03:00
}
2010-03-19 14:25:51 +03:00
static struct platform_driver twl6040_codec_driver = {
. driver = {
2010-03-17 23:15:21 +03:00
. name = " twl6040-codec " ,
2010-03-19 14:25:51 +03:00
} ,
. probe = twl6040_codec_probe ,
} ;
2011-11-24 02:52:08 +04:00
module_platform_driver ( twl6040_codec_driver ) ;
2010-03-19 14:25:51 +03:00
MODULE_DESCRIPTION ( " ASoC TWL6040 codec driver " ) ;
MODULE_AUTHOR ( " Misael Lopez Cruz " ) ;
MODULE_LICENSE ( " GPL " ) ;