2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2012-04-28 00:45:52 +04:00
/*
* cs42l52 . c - - CS42L52 ALSA SoC audio driver
*
* Copyright 2012 CirrusLogic , Inc .
*
* Author : Georgi Vlaev < joe @ nucleusys . com >
* Author : Brian Austin < brian . austin @ cirrus . com >
*/
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/delay.h>
2013-11-15 19:35:33 +04:00
# include <linux/of_gpio.h>
2012-04-28 00:45:52 +04:00
# include <linux/pm.h>
# include <linux/i2c.h>
# include <linux/input.h>
# include <linux/regmap.h>
# include <linux/slab.h>
# include <linux/workqueue.h>
# include <linux/platform_device.h>
# include <sound/core.h>
# include <sound/pcm.h>
# include <sound/pcm_params.h>
# include <sound/soc.h>
# include <sound/soc-dapm.h>
# include <sound/initval.h>
# include <sound/tlv.h>
# include <sound/cs42l52.h>
# include "cs42l52.h"
struct sp_config {
u8 spc , format , spfs ;
u32 srate ;
} ;
struct cs42l52_private {
struct regmap * regmap ;
2018-01-29 06:57:55 +03:00
struct snd_soc_component * component ;
2012-04-28 00:45:52 +04:00
struct device * dev ;
struct sp_config config ;
struct cs42l52_platform_data pdata ;
u32 sysclk ;
u8 mclksel ;
u32 mclk ;
u8 flags ;
struct input_dev * beep ;
struct work_struct beep_work ;
int beep_rate ;
} ;
static const struct reg_default cs42l52_reg_defaults [ ] = {
{ CS42L52_PWRCTL1 , 0x9F } , /* r02 PWRCTL 1 */
{ CS42L52_PWRCTL2 , 0x07 } , /* r03 PWRCTL 2 */
{ CS42L52_PWRCTL3 , 0xFF } , /* r04 PWRCTL 3 */
{ CS42L52_CLK_CTL , 0xA0 } , /* r05 Clocking Ctl */
{ CS42L52_IFACE_CTL1 , 0x00 } , /* r06 Interface Ctl 1 */
{ CS42L52_ADC_PGA_A , 0x80 } , /* r08 Input A Select */
{ CS42L52_ADC_PGA_B , 0x80 } , /* r09 Input B Select */
{ CS42L52_ANALOG_HPF_CTL , 0xA5 } , /* r0A Analog HPF Ctl */
{ CS42L52_ADC_HPF_FREQ , 0x00 } , /* r0B ADC HPF Corner Freq */
{ CS42L52_ADC_MISC_CTL , 0x00 } , /* r0C Misc. ADC Ctl */
{ CS42L52_PB_CTL1 , 0x60 } , /* r0D Playback Ctl 1 */
{ CS42L52_MISC_CTL , 0x02 } , /* r0E Misc. Ctl */
{ CS42L52_PB_CTL2 , 0x00 } , /* r0F Playback Ctl 2 */
{ CS42L52_MICA_CTL , 0x00 } , /* r10 MICA Amp Ctl */
{ CS42L52_MICB_CTL , 0x00 } , /* r11 MICB Amp Ctl */
{ CS42L52_PGAA_CTL , 0x00 } , /* r12 PGAA Vol, Misc. */
{ CS42L52_PGAB_CTL , 0x00 } , /* r13 PGAB Vol, Misc. */
{ CS42L52_PASSTHRUA_VOL , 0x00 } , /* r14 Bypass A Vol */
{ CS42L52_PASSTHRUB_VOL , 0x00 } , /* r15 Bypass B Vol */
{ CS42L52_ADCA_VOL , 0x00 } , /* r16 ADCA Volume */
{ CS42L52_ADCB_VOL , 0x00 } , /* r17 ADCB Volume */
{ CS42L52_ADCA_MIXER_VOL , 0x80 } , /* r18 ADCA Mixer Volume */
{ CS42L52_ADCB_MIXER_VOL , 0x80 } , /* r19 ADCB Mixer Volume */
{ CS42L52_PCMA_MIXER_VOL , 0x00 } , /* r1A PCMA Mixer Volume */
{ CS42L52_PCMB_MIXER_VOL , 0x00 } , /* r1B PCMB Mixer Volume */
{ CS42L52_BEEP_FREQ , 0x00 } , /* r1C Beep Freq on Time */
{ CS42L52_BEEP_VOL , 0x00 } , /* r1D Beep Volume off Time */
{ CS42L52_BEEP_TONE_CTL , 0x00 } , /* r1E Beep Tone Cfg. */
{ CS42L52_TONE_CTL , 0x00 } , /* r1F Tone Ctl */
2013-05-23 18:53:02 +04:00
{ CS42L52_MASTERA_VOL , 0x00 } , /* r20 Master A Volume */
2012-04-28 00:45:52 +04:00
{ CS42L52_MASTERB_VOL , 0x00 } , /* r21 Master B Volume */
{ CS42L52_HPA_VOL , 0x00 } , /* r22 Headphone A Volume */
{ CS42L52_HPB_VOL , 0x00 } , /* r23 Headphone B Volume */
{ CS42L52_SPKA_VOL , 0x00 } , /* r24 Speaker A Volume */
{ CS42L52_SPKB_VOL , 0x00 } , /* r25 Speaker B Volume */
{ CS42L52_ADC_PCM_MIXER , 0x00 } , /* r26 Channel Mixer and Swap */
{ CS42L52_LIMITER_CTL1 , 0x00 } , /* r27 Limit Ctl 1 Thresholds */
{ CS42L52_LIMITER_CTL2 , 0x7F } , /* r28 Limit Ctl 2 Release Rate */
{ CS42L52_LIMITER_AT_RATE , 0xC0 } , /* r29 Limiter Attack Rate */
{ CS42L52_ALC_CTL , 0x00 } , /* r2A ALC Ctl 1 Attack Rate */
{ CS42L52_ALC_RATE , 0x3F } , /* r2B ALC Release Rate */
{ CS42L52_ALC_THRESHOLD , 0x3f } , /* r2C ALC Thresholds */
{ CS42L52_NOISE_GATE_CTL , 0x00 } , /* r2D Noise Gate Ctl */
{ CS42L52_CLK_STATUS , 0x00 } , /* r2E Overflow and Clock Status */
{ CS42L52_BATT_COMPEN , 0x00 } , /* r2F battery Compensation */
{ CS42L52_BATT_LEVEL , 0x00 } , /* r30 VP Battery Level */
{ CS42L52_SPK_STATUS , 0x00 } , /* r31 Speaker Status */
{ CS42L52_TEM_CTL , 0x3B } , /* r32 Temp Ctl */
{ CS42L52_THE_FOLDBACK , 0x00 } , /* r33 Foldback */
} ;
static bool cs42l52_readable_register ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
2015-08-12 06:08:37 +03:00
case CS42L52_CHIP . . . CS42L52_CHARGE_PUMP :
2012-04-28 00:45:52 +04:00
return true ;
default :
return false ;
}
}
static bool cs42l52_volatile_register ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
case CS42L52_IFACE_CTL2 :
case CS42L52_CLK_STATUS :
case CS42L52_BATT_LEVEL :
case CS42L52_SPK_STATUS :
case CS42L52_CHARGE_PUMP :
2014-08-28 19:02:41 +04:00
return true ;
2012-04-28 00:45:52 +04:00
default :
2014-08-28 19:02:41 +04:00
return false ;
2012-04-28 00:45:52 +04:00
}
}
static DECLARE_TLV_DB_SCALE ( hl_tlv , - 10200 , 50 , 0 ) ;
static DECLARE_TLV_DB_SCALE ( hpd_tlv , - 9600 , 50 , 1 ) ;
static DECLARE_TLV_DB_SCALE ( ipd_tlv , - 9600 , 100 , 0 ) ;
static DECLARE_TLV_DB_SCALE ( mic_tlv , 1600 , 100 , 0 ) ;
static DECLARE_TLV_DB_SCALE ( pga_tlv , - 600 , 50 , 0 ) ;
2022-06-02 19:21:17 +03:00
static DECLARE_TLV_DB_SCALE ( pass_tlv , - 6000 , 50 , 0 ) ;
2022-06-02 19:21:14 +03:00
static DECLARE_TLV_DB_SCALE ( mix_tlv , - 5150 , 50 , 0 ) ;
2013-05-29 22:01:19 +04:00
2013-08-06 21:57:22 +04:00
static DECLARE_TLV_DB_SCALE ( beep_tlv , - 56 , 200 , 0 ) ;
2015-08-02 18:19:34 +03:00
static const DECLARE_TLV_DB_RANGE ( limiter_tlv ,
2012-04-28 00:45:52 +04:00
0 , 2 , TLV_DB_SCALE_ITEM ( - 3000 , 600 , 0 ) ,
2015-08-02 18:19:34 +03:00
3 , 7 , TLV_DB_SCALE_ITEM ( - 1200 , 300 , 0 )
) ;
2012-04-28 00:45:52 +04:00
static const char * const cs42l52_adca_text [ ] = {
" Input1A " , " Input2A " , " Input3A " , " Input4A " , " PGA Input Left " } ;
static const char * const cs42l52_adcb_text [ ] = {
" Input1B " , " Input2B " , " Input3B " , " Input4B " , " PGA Input Right " } ;
2014-02-18 13:05:01 +04:00
static SOC_ENUM_SINGLE_DECL ( adca_enum ,
CS42L52_ADC_PGA_A , 5 , cs42l52_adca_text ) ;
2012-04-28 00:45:52 +04:00
2014-02-18 13:05:01 +04:00
static SOC_ENUM_SINGLE_DECL ( adcb_enum ,
CS42L52_ADC_PGA_B , 5 , cs42l52_adcb_text ) ;
2012-04-28 00:45:52 +04:00
static const struct snd_kcontrol_new adca_mux =
SOC_DAPM_ENUM ( " Left ADC Input Capture Mux " , adca_enum ) ;
static const struct snd_kcontrol_new adcb_mux =
SOC_DAPM_ENUM ( " Right ADC Input Capture Mux " , adcb_enum ) ;
static const char * const mic_bias_level_text [ ] = {
" 0.5 +VA " , " 0.6 +VA " , " 0.7 +VA " ,
" 0.8 +VA " , " 0.83 +VA " , " 0.91 +VA "
} ;
2014-02-18 13:05:01 +04:00
static SOC_ENUM_SINGLE_DECL ( mic_bias_level_enum ,
CS42L52_IFACE_CTL2 , 0 , mic_bias_level_text ) ;
2012-04-28 00:45:52 +04:00
2013-11-14 02:05:40 +04:00
static const char * const cs42l52_mic_text [ ] = { " MIC1 " , " MIC2 " } ;
2012-04-28 00:45:52 +04:00
2014-02-18 13:05:01 +04:00
static SOC_ENUM_SINGLE_DECL ( mica_enum ,
CS42L52_MICA_CTL , 5 , cs42l52_mic_text ) ;
2012-04-28 00:45:52 +04:00
2014-02-18 13:05:01 +04:00
static SOC_ENUM_SINGLE_DECL ( micb_enum ,
CS42L52_MICB_CTL , 5 , cs42l52_mic_text ) ;
2012-04-28 00:45:52 +04:00
static const char * const digital_output_mux_text [ ] = { " ADC " , " DSP " } ;
2014-02-18 13:05:01 +04:00
static SOC_ENUM_SINGLE_DECL ( digital_output_mux_enum ,
CS42L52_ADC_MISC_CTL , 6 ,
digital_output_mux_text ) ;
2012-04-28 00:45:52 +04:00
static const struct snd_kcontrol_new digital_output_mux =
SOC_DAPM_ENUM ( " Digital Output Mux " , digital_output_mux_enum ) ;
static const char * const hp_gain_num_text [ ] = {
" 0.3959 " , " 0.4571 " , " 0.5111 " , " 0.6047 " ,
" 0.7099 " , " 0.8399 " , " 1.000 " , " 1.1430 "
} ;
2014-02-18 13:05:01 +04:00
static SOC_ENUM_SINGLE_DECL ( hp_gain_enum ,
CS42L52_PB_CTL1 , 5 ,
hp_gain_num_text ) ;
2012-04-28 00:45:52 +04:00
static const char * const beep_pitch_text [ ] = {
" C4 " , " C5 " , " D5 " , " E5 " , " F5 " , " G5 " , " A5 " , " B5 " ,
" C6 " , " D6 " , " E6 " , " F6 " , " G6 " , " A6 " , " B6 " , " C7 "
} ;
2014-02-18 13:05:01 +04:00
static SOC_ENUM_SINGLE_DECL ( beep_pitch_enum ,
CS42L52_BEEP_FREQ , 4 ,
beep_pitch_text ) ;
2012-04-28 00:45:52 +04:00
static const char * const beep_ontime_text [ ] = {
" 86 ms " , " 430 ms " , " 780 ms " , " 1.20 s " , " 1.50 s " ,
" 1.80 s " , " 2.20 s " , " 2.50 s " , " 2.80 s " , " 3.20 s " ,
" 3.50 s " , " 3.80 s " , " 4.20 s " , " 4.50 s " , " 4.80 s " , " 5.20 s "
} ;
2014-02-18 13:05:01 +04:00
static SOC_ENUM_SINGLE_DECL ( beep_ontime_enum ,
CS42L52_BEEP_FREQ , 0 ,
beep_ontime_text ) ;
2012-04-28 00:45:52 +04:00
static const char * const beep_offtime_text [ ] = {
" 1.23 s " , " 2.58 s " , " 3.90 s " , " 5.20 s " ,
" 6.60 s " , " 8.05 s " , " 9.35 s " , " 10.80 s "
} ;
2014-02-18 13:05:01 +04:00
static SOC_ENUM_SINGLE_DECL ( beep_offtime_enum ,
CS42L52_BEEP_VOL , 5 ,
beep_offtime_text ) ;
2012-04-28 00:45:52 +04:00
static const char * const beep_config_text [ ] = {
" Off " , " Single " , " Multiple " , " Continuous "
} ;
2014-02-18 13:05:01 +04:00
static SOC_ENUM_SINGLE_DECL ( beep_config_enum ,
CS42L52_BEEP_TONE_CTL , 6 ,
beep_config_text ) ;
2012-04-28 00:45:52 +04:00
static const char * const beep_bass_text [ ] = {
" 50 Hz " , " 100 Hz " , " 200 Hz " , " 250 Hz "
} ;
2014-02-18 13:05:01 +04:00
static SOC_ENUM_SINGLE_DECL ( beep_bass_enum ,
CS42L52_BEEP_TONE_CTL , 1 ,
beep_bass_text ) ;
2012-04-28 00:45:52 +04:00
static const char * const beep_treble_text [ ] = {
" 5 kHz " , " 7 kHz " , " 10 kHz " , " 15 kHz "
} ;
2014-02-18 13:05:01 +04:00
static SOC_ENUM_SINGLE_DECL ( beep_treble_enum ,
CS42L52_BEEP_TONE_CTL , 3 ,
beep_treble_text ) ;
2012-04-28 00:45:52 +04:00
static const char * const ng_threshold_text [ ] = {
" -34dB " , " -37dB " , " -40dB " , " -43dB " ,
" -46dB " , " -52dB " , " -58dB " , " -64dB "
} ;
2014-02-18 13:05:01 +04:00
static SOC_ENUM_SINGLE_DECL ( ng_threshold_enum ,
CS42L52_NOISE_GATE_CTL , 2 ,
ng_threshold_text ) ;
2012-04-28 00:45:52 +04:00
static const char * const cs42l52_ng_delay_text [ ] = {
" 50ms " , " 100ms " , " 150ms " , " 200ms " } ;
2014-02-18 13:05:01 +04:00
static SOC_ENUM_SINGLE_DECL ( ng_delay_enum ,
CS42L52_NOISE_GATE_CTL , 0 ,
cs42l52_ng_delay_text ) ;
2012-04-28 00:45:52 +04:00
static const char * const cs42l52_ng_type_text [ ] = {
" Apply Specific " , " Apply All "
} ;
2014-02-18 13:05:01 +04:00
static SOC_ENUM_SINGLE_DECL ( ng_type_enum ,
CS42L52_NOISE_GATE_CTL , 6 ,
cs42l52_ng_type_text ) ;
2012-04-28 00:45:52 +04:00
static const char * const left_swap_text [ ] = {
" Left " , " LR 2 " , " Right " } ;
static const char * const right_swap_text [ ] = {
" Right " , " LR 2 " , " Left " } ;
static const unsigned int swap_values [ ] = { 0 , 1 , 3 } ;
static const struct soc_enum adca_swap_enum =
2014-03-18 23:01:47 +04:00
SOC_VALUE_ENUM_SINGLE ( CS42L52_ADC_PCM_MIXER , 2 , 3 ,
2012-04-28 00:45:52 +04:00
ARRAY_SIZE ( left_swap_text ) ,
left_swap_text ,
swap_values ) ;
static const struct snd_kcontrol_new adca_mixer =
SOC_DAPM_ENUM ( " Route " , adca_swap_enum ) ;
static const struct soc_enum pcma_swap_enum =
2014-03-18 23:01:47 +04:00
SOC_VALUE_ENUM_SINGLE ( CS42L52_ADC_PCM_MIXER , 6 , 3 ,
2012-04-28 00:45:52 +04:00
ARRAY_SIZE ( left_swap_text ) ,
left_swap_text ,
swap_values ) ;
static const struct snd_kcontrol_new pcma_mixer =
SOC_DAPM_ENUM ( " Route " , pcma_swap_enum ) ;
static const struct soc_enum adcb_swap_enum =
2014-03-18 23:01:47 +04:00
SOC_VALUE_ENUM_SINGLE ( CS42L52_ADC_PCM_MIXER , 0 , 3 ,
2012-04-28 00:45:52 +04:00
ARRAY_SIZE ( right_swap_text ) ,
right_swap_text ,
swap_values ) ;
static const struct snd_kcontrol_new adcb_mixer =
SOC_DAPM_ENUM ( " Route " , adcb_swap_enum ) ;
static const struct soc_enum pcmb_swap_enum =
2014-03-18 23:01:47 +04:00
SOC_VALUE_ENUM_SINGLE ( CS42L52_ADC_PCM_MIXER , 4 , 3 ,
2012-04-28 00:45:52 +04:00
ARRAY_SIZE ( right_swap_text ) ,
right_swap_text ,
swap_values ) ;
static const struct snd_kcontrol_new pcmb_mixer =
SOC_DAPM_ENUM ( " Route " , pcmb_swap_enum ) ;
static const struct snd_kcontrol_new passthrul_ctl =
SOC_DAPM_SINGLE ( " Switch " , CS42L52_MISC_CTL , 6 , 1 , 0 ) ;
static const struct snd_kcontrol_new passthrur_ctl =
SOC_DAPM_SINGLE ( " Switch " , CS42L52_MISC_CTL , 7 , 1 , 0 ) ;
static const struct snd_kcontrol_new spkl_ctl =
SOC_DAPM_SINGLE ( " Switch " , CS42L52_PWRCTL3 , 0 , 1 , 1 ) ;
static const struct snd_kcontrol_new spkr_ctl =
SOC_DAPM_SINGLE ( " Switch " , CS42L52_PWRCTL3 , 2 , 1 , 1 ) ;
static const struct snd_kcontrol_new hpl_ctl =
SOC_DAPM_SINGLE ( " Switch " , CS42L52_PWRCTL3 , 4 , 1 , 1 ) ;
static const struct snd_kcontrol_new hpr_ctl =
SOC_DAPM_SINGLE ( " Switch " , CS42L52_PWRCTL3 , 6 , 1 , 1 ) ;
static const struct snd_kcontrol_new cs42l52_snd_controls [ ] = {
SOC_DOUBLE_R_SX_TLV ( " Master Volume " , CS42L52_MASTERA_VOL ,
CS42L52_MASTERB_VOL , 0 , 0x34 , 0xE4 , hl_tlv ) ,
SOC_DOUBLE_R_SX_TLV ( " Headphone Volume " , CS42L52_HPA_VOL ,
2014-07-17 22:16:55 +04:00
CS42L52_HPB_VOL , 0 , 0x34 , 0xC0 , hpd_tlv ) ,
2012-04-28 00:45:52 +04:00
SOC_ENUM ( " Headphone Analog Gain " , hp_gain_enum ) ,
SOC_DOUBLE_R_SX_TLV ( " Speaker Volume " , CS42L52_SPKA_VOL ,
2014-07-17 22:16:55 +04:00
CS42L52_SPKB_VOL , 0 , 0x40 , 0xC0 , hl_tlv ) ,
2012-04-28 00:45:52 +04:00
SOC_DOUBLE_R_SX_TLV ( " Bypass Volume " , CS42L52_PASSTHRUA_VOL ,
2022-06-02 19:21:17 +03:00
CS42L52_PASSTHRUB_VOL , 0 , 0x88 , 0x90 , pass_tlv ) ,
2012-04-28 00:45:52 +04:00
SOC_DOUBLE ( " Bypass Mute " , CS42L52_MISC_CTL , 4 , 5 , 1 , 0 ) ,
SOC_DOUBLE_R_TLV ( " MIC Gain Volume " , CS42L52_MICA_CTL ,
CS42L52_MICB_CTL , 0 , 0x10 , 0 , mic_tlv ) ,
SOC_ENUM ( " MIC Bias Level " , mic_bias_level_enum ) ,
SOC_DOUBLE_R_SX_TLV ( " ADC Volume " , CS42L52_ADCA_VOL ,
2014-07-17 22:16:55 +04:00
CS42L52_ADCB_VOL , 0 , 0xA0 , 0x78 , ipd_tlv ) ,
2012-04-28 00:45:52 +04:00
SOC_DOUBLE_R_SX_TLV ( " ADC Mixer Volume " ,
CS42L52_ADCA_MIXER_VOL , CS42L52_ADCB_MIXER_VOL ,
2022-06-02 19:21:14 +03:00
0 , 0x19 , 0x7F , mix_tlv ) ,
2012-04-28 00:45:52 +04:00
SOC_DOUBLE ( " ADC Switch " , CS42L52_ADC_MISC_CTL , 0 , 1 , 1 , 0 ) ,
SOC_DOUBLE_R ( " ADC Mixer Switch " , CS42L52_ADCA_MIXER_VOL ,
CS42L52_ADCB_MIXER_VOL , 7 , 1 , 1 ) ,
SOC_DOUBLE_R_SX_TLV ( " PGA Volume " , CS42L52_PGAA_CTL ,
2014-07-17 22:16:55 +04:00
CS42L52_PGAB_CTL , 0 , 0x28 , 0x24 , pga_tlv ) ,
2012-04-28 00:45:52 +04:00
SOC_DOUBLE_R_SX_TLV ( " PCM Mixer Volume " ,
CS42L52_PCMA_MIXER_VOL , CS42L52_PCMB_MIXER_VOL ,
2014-07-17 22:16:55 +04:00
0 , 0x19 , 0x7f , mix_tlv ) ,
2012-04-28 00:45:52 +04:00
SOC_DOUBLE_R ( " PCM Mixer Switch " ,
CS42L52_PCMA_MIXER_VOL , CS42L52_PCMB_MIXER_VOL , 7 , 1 , 1 ) ,
SOC_ENUM ( " Beep Config " , beep_config_enum ) ,
SOC_ENUM ( " Beep Pitch " , beep_pitch_enum ) ,
SOC_ENUM ( " Beep on Time " , beep_ontime_enum ) ,
SOC_ENUM ( " Beep off Time " , beep_offtime_enum ) ,
2013-08-06 21:57:22 +04:00
SOC_SINGLE_SX_TLV ( " Beep Volume " , CS42L52_BEEP_VOL ,
0 , 0x07 , 0x1f , beep_tlv ) ,
2012-04-28 00:45:52 +04:00
SOC_SINGLE ( " Beep Mixer Switch " , CS42L52_BEEP_TONE_CTL , 5 , 1 , 1 ) ,
SOC_ENUM ( " Beep Treble Corner Freq " , beep_treble_enum ) ,
SOC_ENUM ( " Beep Bass Corner Freq " , beep_bass_enum ) ,
SOC_SINGLE ( " Tone Control Switch " , CS42L52_BEEP_TONE_CTL , 0 , 1 , 1 ) ,
SOC_SINGLE_TLV ( " Treble Gain Volume " ,
CS42L52_TONE_CTL , 4 , 15 , 1 , hl_tlv ) ,
SOC_SINGLE_TLV ( " Bass Gain Volume " ,
CS42L52_TONE_CTL , 0 , 15 , 1 , hl_tlv ) ,
/* Limiter */
SOC_SINGLE_TLV ( " Limiter Max Threshold Volume " ,
CS42L52_LIMITER_CTL1 , 5 , 7 , 0 , limiter_tlv ) ,
SOC_SINGLE_TLV ( " Limiter Cushion Threshold Volume " ,
CS42L52_LIMITER_CTL1 , 2 , 7 , 0 , limiter_tlv ) ,
SOC_SINGLE_TLV ( " Limiter Release Rate Volume " ,
CS42L52_LIMITER_CTL2 , 0 , 63 , 0 , limiter_tlv ) ,
SOC_SINGLE_TLV ( " Limiter Attack Rate Volume " ,
CS42L52_LIMITER_AT_RATE , 0 , 63 , 0 , limiter_tlv ) ,
SOC_SINGLE ( " Limiter SR Switch " , CS42L52_LIMITER_CTL1 , 1 , 1 , 0 ) ,
SOC_SINGLE ( " Limiter ZC Switch " , CS42L52_LIMITER_CTL1 , 0 , 1 , 0 ) ,
SOC_SINGLE ( " Limiter Switch " , CS42L52_LIMITER_CTL2 , 7 , 1 , 0 ) ,
/* ALC */
SOC_SINGLE_TLV ( " ALC Attack Rate Volume " , CS42L52_ALC_CTL ,
0 , 63 , 0 , limiter_tlv ) ,
SOC_SINGLE_TLV ( " ALC Release Rate Volume " , CS42L52_ALC_RATE ,
0 , 63 , 0 , limiter_tlv ) ,
SOC_SINGLE_TLV ( " ALC Max Threshold Volume " , CS42L52_ALC_THRESHOLD ,
5 , 7 , 0 , limiter_tlv ) ,
SOC_SINGLE_TLV ( " ALC Min Threshold Volume " , CS42L52_ALC_THRESHOLD ,
2 , 7 , 0 , limiter_tlv ) ,
SOC_DOUBLE_R ( " ALC SR Capture Switch " , CS42L52_PGAA_CTL ,
CS42L52_PGAB_CTL , 7 , 1 , 1 ) ,
SOC_DOUBLE_R ( " ALC ZC Capture Switch " , CS42L52_PGAA_CTL ,
CS42L52_PGAB_CTL , 6 , 1 , 1 ) ,
SOC_DOUBLE ( " ALC Capture Switch " , CS42L52_ALC_CTL , 6 , 7 , 1 , 0 ) ,
/* Noise gate */
SOC_ENUM ( " NG Type Switch " , ng_type_enum ) ,
SOC_SINGLE ( " NG Enable Switch " , CS42L52_NOISE_GATE_CTL , 6 , 1 , 0 ) ,
SOC_SINGLE ( " NG Boost Switch " , CS42L52_NOISE_GATE_CTL , 5 , 1 , 1 ) ,
SOC_ENUM ( " NG Threshold " , ng_threshold_enum ) ,
SOC_ENUM ( " NG Delay " , ng_delay_enum ) ,
SOC_DOUBLE ( " HPF Switch " , CS42L52_ANALOG_HPF_CTL , 5 , 7 , 1 , 0 ) ,
SOC_DOUBLE ( " Analog SR Switch " , CS42L52_ANALOG_HPF_CTL , 1 , 3 , 1 , 1 ) ,
SOC_DOUBLE ( " Analog ZC Switch " , CS42L52_ANALOG_HPF_CTL , 0 , 2 , 1 , 1 ) ,
SOC_SINGLE ( " Digital SR Switch " , CS42L52_MISC_CTL , 1 , 1 , 0 ) ,
SOC_SINGLE ( " Digital ZC Switch " , CS42L52_MISC_CTL , 0 , 1 , 0 ) ,
SOC_SINGLE ( " Deemphasis Switch " , CS42L52_MISC_CTL , 2 , 1 , 0 ) ,
SOC_SINGLE ( " Batt Compensation Switch " , CS42L52_BATT_COMPEN , 7 , 1 , 0 ) ,
SOC_SINGLE ( " Batt VP Monitor Switch " , CS42L52_BATT_COMPEN , 6 , 1 , 0 ) ,
SOC_SINGLE ( " Batt VP ref " , CS42L52_BATT_COMPEN , 0 , 0x0f , 0 ) ,
SOC_SINGLE ( " PGA AIN1L Switch " , CS42L52_ADC_PGA_A , 0 , 1 , 0 ) ,
SOC_SINGLE ( " PGA AIN1R Switch " , CS42L52_ADC_PGA_B , 0 , 1 , 0 ) ,
SOC_SINGLE ( " PGA AIN2L Switch " , CS42L52_ADC_PGA_A , 1 , 1 , 0 ) ,
SOC_SINGLE ( " PGA AIN2R Switch " , CS42L52_ADC_PGA_B , 1 , 1 , 0 ) ,
SOC_SINGLE ( " PGA AIN3L Switch " , CS42L52_ADC_PGA_A , 2 , 1 , 0 ) ,
SOC_SINGLE ( " PGA AIN3R Switch " , CS42L52_ADC_PGA_B , 2 , 1 , 0 ) ,
SOC_SINGLE ( " PGA AIN4L Switch " , CS42L52_ADC_PGA_A , 3 , 1 , 0 ) ,
SOC_SINGLE ( " PGA AIN4R Switch " , CS42L52_ADC_PGA_B , 3 , 1 , 0 ) ,
SOC_SINGLE ( " PGA MICA Switch " , CS42L52_ADC_PGA_A , 4 , 1 , 0 ) ,
SOC_SINGLE ( " PGA MICB Switch " , CS42L52_ADC_PGA_B , 4 , 1 , 0 ) ,
} ;
2013-11-14 21:46:11 +04:00
static const struct snd_kcontrol_new cs42l52_mica_controls [ ] = {
SOC_ENUM ( " MICA Select " , mica_enum ) ,
} ;
static const struct snd_kcontrol_new cs42l52_micb_controls [ ] = {
SOC_ENUM ( " MICB Select " , micb_enum ) ,
} ;
2018-01-29 06:57:55 +03:00
static int cs42l52_add_mic_controls ( struct snd_soc_component * component )
2013-11-14 21:46:11 +04:00
{
2018-01-29 06:57:55 +03:00
struct cs42l52_private * cs42l52 = snd_soc_component_get_drvdata ( component ) ;
2013-11-14 21:46:11 +04:00
struct cs42l52_platform_data * pdata = & cs42l52 - > pdata ;
if ( ! pdata - > mica_diff_cfg )
2018-01-29 06:57:55 +03:00
snd_soc_add_component_controls ( component , cs42l52_mica_controls ,
2013-11-14 21:46:11 +04:00
ARRAY_SIZE ( cs42l52_mica_controls ) ) ;
if ( ! pdata - > micb_diff_cfg )
2018-01-29 06:57:55 +03:00
snd_soc_add_component_controls ( component , cs42l52_micb_controls ,
2013-11-14 21:46:11 +04:00
ARRAY_SIZE ( cs42l52_micb_controls ) ) ;
return 0 ;
}
2012-04-28 00:45:52 +04:00
static const struct snd_soc_dapm_widget cs42l52_dapm_widgets [ ] = {
SND_SOC_DAPM_INPUT ( " AIN1L " ) ,
SND_SOC_DAPM_INPUT ( " AIN1R " ) ,
SND_SOC_DAPM_INPUT ( " AIN2L " ) ,
SND_SOC_DAPM_INPUT ( " AIN2R " ) ,
SND_SOC_DAPM_INPUT ( " AIN3L " ) ,
SND_SOC_DAPM_INPUT ( " AIN3R " ) ,
SND_SOC_DAPM_INPUT ( " AIN4L " ) ,
SND_SOC_DAPM_INPUT ( " AIN4R " ) ,
SND_SOC_DAPM_INPUT ( " MICA " ) ,
SND_SOC_DAPM_INPUT ( " MICB " ) ,
SND_SOC_DAPM_SIGGEN ( " Beep " ) ,
SND_SOC_DAPM_AIF_OUT ( " AIFOUTL " , NULL , 0 ,
SND_SOC_NOPM , 0 , 0 ) ,
SND_SOC_DAPM_AIF_OUT ( " AIFOUTR " , NULL , 0 ,
SND_SOC_NOPM , 0 , 0 ) ,
SND_SOC_DAPM_ADC ( " ADC Left " , NULL , CS42L52_PWRCTL1 , 1 , 1 ) ,
SND_SOC_DAPM_ADC ( " ADC Right " , NULL , CS42L52_PWRCTL1 , 2 , 1 ) ,
SND_SOC_DAPM_PGA ( " PGA Left " , CS42L52_PWRCTL1 , 3 , 1 , NULL , 0 ) ,
SND_SOC_DAPM_PGA ( " PGA Right " , CS42L52_PWRCTL1 , 4 , 1 , NULL , 0 ) ,
SND_SOC_DAPM_MUX ( " ADC Left Mux " , SND_SOC_NOPM , 0 , 0 , & adca_mux ) ,
SND_SOC_DAPM_MUX ( " ADC Right Mux " , SND_SOC_NOPM , 0 , 0 , & adcb_mux ) ,
SND_SOC_DAPM_MUX ( " ADC Left Swap " , SND_SOC_NOPM ,
0 , 0 , & adca_mixer ) ,
SND_SOC_DAPM_MUX ( " ADC Right Swap " , SND_SOC_NOPM ,
0 , 0 , & adcb_mixer ) ,
SND_SOC_DAPM_MUX ( " Output Mux " , SND_SOC_NOPM ,
0 , 0 , & digital_output_mux ) ,
SND_SOC_DAPM_PGA ( " PGA MICA " , CS42L52_PWRCTL2 , 1 , 1 , NULL , 0 ) ,
SND_SOC_DAPM_PGA ( " PGA MICB " , CS42L52_PWRCTL2 , 2 , 1 , NULL , 0 ) ,
SND_SOC_DAPM_SUPPLY ( " Mic Bias " , CS42L52_PWRCTL2 , 0 , 1 , NULL , 0 ) ,
SND_SOC_DAPM_SUPPLY ( " Charge Pump " , CS42L52_PWRCTL1 , 7 , 1 , NULL , 0 ) ,
SND_SOC_DAPM_AIF_IN ( " AIFINL " , NULL , 0 ,
SND_SOC_NOPM , 0 , 0 ) ,
SND_SOC_DAPM_AIF_IN ( " AIFINR " , NULL , 0 ,
SND_SOC_NOPM , 0 , 0 ) ,
SND_SOC_DAPM_DAC ( " DAC Left " , NULL , SND_SOC_NOPM , 0 , 0 ) ,
SND_SOC_DAPM_DAC ( " DAC Right " , NULL , SND_SOC_NOPM , 0 , 0 ) ,
SND_SOC_DAPM_SWITCH ( " Bypass Left " , CS42L52_MISC_CTL ,
6 , 0 , & passthrul_ctl ) ,
SND_SOC_DAPM_SWITCH ( " Bypass Right " , CS42L52_MISC_CTL ,
7 , 0 , & passthrur_ctl ) ,
SND_SOC_DAPM_MUX ( " PCM Left Swap " , SND_SOC_NOPM ,
0 , 0 , & pcma_mixer ) ,
SND_SOC_DAPM_MUX ( " PCM Right Swap " , SND_SOC_NOPM ,
0 , 0 , & pcmb_mixer ) ,
SND_SOC_DAPM_SWITCH ( " HP Left Amp " , SND_SOC_NOPM , 0 , 0 , & hpl_ctl ) ,
SND_SOC_DAPM_SWITCH ( " HP Right Amp " , SND_SOC_NOPM , 0 , 0 , & hpr_ctl ) ,
SND_SOC_DAPM_SWITCH ( " SPK Left Amp " , SND_SOC_NOPM , 0 , 0 , & spkl_ctl ) ,
SND_SOC_DAPM_SWITCH ( " SPK Right Amp " , SND_SOC_NOPM , 0 , 0 , & spkr_ctl ) ,
SND_SOC_DAPM_OUTPUT ( " HPOUTA " ) ,
SND_SOC_DAPM_OUTPUT ( " HPOUTB " ) ,
SND_SOC_DAPM_OUTPUT ( " SPKOUTA " ) ,
SND_SOC_DAPM_OUTPUT ( " SPKOUTB " ) ,
} ;
static const struct snd_soc_dapm_route cs42l52_audio_map [ ] = {
{ " Capture " , NULL , " AIFOUTL " } ,
{ " Capture " , NULL , " AIFOUTL " } ,
{ " AIFOUTL " , NULL , " Output Mux " } ,
{ " AIFOUTR " , NULL , " Output Mux " } ,
{ " Output Mux " , " ADC " , " ADC Left " } ,
{ " Output Mux " , " ADC " , " ADC Right " } ,
{ " ADC Left " , NULL , " Charge Pump " } ,
{ " ADC Right " , NULL , " Charge Pump " } ,
{ " Charge Pump " , NULL , " ADC Left Mux " } ,
{ " Charge Pump " , NULL , " ADC Right Mux " } ,
{ " ADC Left Mux " , " Input1A " , " AIN1L " } ,
{ " ADC Right Mux " , " Input1B " , " AIN1R " } ,
{ " ADC Left Mux " , " Input2A " , " AIN2L " } ,
{ " ADC Right Mux " , " Input2B " , " AIN2R " } ,
{ " ADC Left Mux " , " Input3A " , " AIN3L " } ,
{ " ADC Right Mux " , " Input3B " , " AIN3R " } ,
{ " ADC Left Mux " , " Input4A " , " AIN4L " } ,
{ " ADC Right Mux " , " Input4B " , " AIN4R " } ,
{ " ADC Left Mux " , " PGA Input Left " , " PGA Left " } ,
{ " ADC Right Mux " , " PGA Input Right " , " PGA Right " } ,
{ " PGA Left " , " Switch " , " AIN1L " } ,
{ " PGA Right " , " Switch " , " AIN1R " } ,
{ " PGA Left " , " Switch " , " AIN2L " } ,
{ " PGA Right " , " Switch " , " AIN2R " } ,
{ " PGA Left " , " Switch " , " AIN3L " } ,
{ " PGA Right " , " Switch " , " AIN3R " } ,
{ " PGA Left " , " Switch " , " AIN4L " } ,
{ " PGA Right " , " Switch " , " AIN4R " } ,
{ " PGA Left " , " Switch " , " PGA MICA " } ,
{ " PGA MICA " , NULL , " MICA " } ,
{ " PGA Right " , " Switch " , " PGA MICB " } ,
{ " PGA MICB " , NULL , " MICB " } ,
{ " HPOUTA " , NULL , " HP Left Amp " } ,
{ " HPOUTB " , NULL , " HP Right Amp " } ,
{ " HP Left Amp " , NULL , " Bypass Left " } ,
{ " HP Right Amp " , NULL , " Bypass Right " } ,
{ " Bypass Left " , " Switch " , " PGA Left " } ,
{ " Bypass Right " , " Switch " , " PGA Right " } ,
{ " HP Left Amp " , " Switch " , " DAC Left " } ,
{ " HP Right Amp " , " Switch " , " DAC Right " } ,
{ " SPKOUTA " , NULL , " SPK Left Amp " } ,
{ " SPKOUTB " , NULL , " SPK Right Amp " } ,
{ " SPK Left Amp " , NULL , " Beep " } ,
{ " SPK Right Amp " , NULL , " Beep " } ,
{ " SPK Left Amp " , " Switch " , " Playback " } ,
{ " SPK Right Amp " , " Switch " , " Playback " } ,
{ " DAC Left " , NULL , " Beep " } ,
{ " DAC Right " , NULL , " Beep " } ,
{ " DAC Left " , NULL , " Playback " } ,
{ " DAC Right " , NULL , " Playback " } ,
{ " Output Mux " , " DSP " , " Playback " } ,
{ " Output Mux " , " DSP " , " Playback " } ,
{ " AIFINL " , NULL , " Playback " } ,
{ " AIFINR " , NULL , " Playback " } ,
} ;
struct cs42l52_clk_para {
u32 mclk ;
u32 rate ;
u8 speed ;
u8 group ;
u8 videoclk ;
u8 ratio ;
u8 mclkdiv2 ;
} ;
static const struct cs42l52_clk_para clk_map_table [ ] = {
/*8k*/
{ 12288000 , 8000 , CLK_QS_MODE , CLK_32K , CLK_NO_27M , CLK_R_128 , 0 } ,
{ 18432000 , 8000 , CLK_QS_MODE , CLK_32K , CLK_NO_27M , CLK_R_128 , 0 } ,
{ 12000000 , 8000 , CLK_QS_MODE , CLK_32K , CLK_NO_27M , CLK_R_125 , 0 } ,
{ 24000000 , 8000 , CLK_QS_MODE , CLK_32K , CLK_NO_27M , CLK_R_125 , 1 } ,
{ 27000000 , 8000 , CLK_QS_MODE , CLK_32K , CLK_27M_MCLK , CLK_R_125 , 0 } ,
/*11.025k*/
{ 11289600 , 11025 , CLK_QS_MODE , CLK_NO_32K , CLK_NO_27M , CLK_R_128 , 0 } ,
{ 16934400 , 11025 , CLK_QS_MODE , CLK_NO_32K , CLK_NO_27M , CLK_R_128 , 0 } ,
/*16k*/
{ 12288000 , 16000 , CLK_HS_MODE , CLK_32K , CLK_NO_27M , CLK_R_128 , 0 } ,
{ 18432000 , 16000 , CLK_HS_MODE , CLK_32K , CLK_NO_27M , CLK_R_128 , 0 } ,
{ 12000000 , 16000 , CLK_HS_MODE , CLK_32K , CLK_NO_27M , CLK_R_125 , 0 } ,
{ 24000000 , 16000 , CLK_HS_MODE , CLK_32K , CLK_NO_27M , CLK_R_125 , 1 } ,
{ 27000000 , 16000 , CLK_HS_MODE , CLK_32K , CLK_27M_MCLK , CLK_R_125 , 1 } ,
/*22.05k*/
{ 11289600 , 22050 , CLK_HS_MODE , CLK_NO_32K , CLK_NO_27M , CLK_R_128 , 0 } ,
{ 16934400 , 22050 , CLK_HS_MODE , CLK_NO_32K , CLK_NO_27M , CLK_R_128 , 0 } ,
/* 32k */
{ 12288000 , 32000 , CLK_SS_MODE , CLK_32K , CLK_NO_27M , CLK_R_128 , 0 } ,
{ 18432000 , 32000 , CLK_SS_MODE , CLK_32K , CLK_NO_27M , CLK_R_128 , 0 } ,
{ 12000000 , 32000 , CLK_SS_MODE , CLK_32K , CLK_NO_27M , CLK_R_125 , 0 } ,
{ 24000000 , 32000 , CLK_SS_MODE , CLK_32K , CLK_NO_27M , CLK_R_125 , 1 } ,
{ 27000000 , 32000 , CLK_SS_MODE , CLK_32K , CLK_27M_MCLK , CLK_R_125 , 0 } ,
/* 44.1k */
{ 11289600 , 44100 , CLK_SS_MODE , CLK_NO_32K , CLK_NO_27M , CLK_R_128 , 0 } ,
{ 16934400 , 44100 , CLK_SS_MODE , CLK_NO_32K , CLK_NO_27M , CLK_R_128 , 0 } ,
/* 48k */
{ 12288000 , 48000 , CLK_SS_MODE , CLK_NO_32K , CLK_NO_27M , CLK_R_128 , 0 } ,
{ 18432000 , 48000 , CLK_SS_MODE , CLK_NO_32K , CLK_NO_27M , CLK_R_128 , 0 } ,
{ 12000000 , 48000 , CLK_SS_MODE , CLK_NO_32K , CLK_NO_27M , CLK_R_125 , 0 } ,
{ 24000000 , 48000 , CLK_SS_MODE , CLK_NO_32K , CLK_NO_27M , CLK_R_125 , 1 } ,
{ 27000000 , 48000 , CLK_SS_MODE , CLK_NO_32K , CLK_27M_MCLK , CLK_R_125 , 1 } ,
/* 88.2k */
{ 11289600 , 88200 , CLK_DS_MODE , CLK_NO_32K , CLK_NO_27M , CLK_R_128 , 0 } ,
{ 16934400 , 88200 , CLK_DS_MODE , CLK_NO_32K , CLK_NO_27M , CLK_R_128 , 0 } ,
/* 96k */
{ 12288000 , 96000 , CLK_DS_MODE , CLK_NO_32K , CLK_NO_27M , CLK_R_128 , 0 } ,
{ 18432000 , 96000 , CLK_DS_MODE , CLK_NO_32K , CLK_NO_27M , CLK_R_128 , 0 } ,
{ 12000000 , 96000 , CLK_DS_MODE , CLK_NO_32K , CLK_NO_27M , CLK_R_125 , 0 } ,
{ 24000000 , 96000 , CLK_DS_MODE , CLK_NO_32K , CLK_NO_27M , CLK_R_125 , 1 } ,
} ;
static int cs42l52_get_clk ( int mclk , int rate )
{
2012-12-20 12:53:16 +04:00
int i , ret = - EINVAL ;
2012-04-28 00:45:52 +04:00
u_int mclk1 , mclk2 = 0 ;
for ( i = 0 ; i < ARRAY_SIZE ( clk_map_table ) ; i + + ) {
if ( clk_map_table [ i ] . rate = = rate ) {
mclk1 = clk_map_table [ i ] . mclk ;
if ( abs ( mclk - mclk1 ) < abs ( mclk - mclk2 ) ) {
mclk2 = mclk1 ;
ret = i ;
}
}
}
return ret ;
}
static int cs42l52_set_sysclk ( struct snd_soc_dai * codec_dai ,
int clk_id , unsigned int freq , int dir )
{
2018-01-29 06:57:55 +03:00
struct snd_soc_component * component = codec_dai - > component ;
struct cs42l52_private * cs42l52 = snd_soc_component_get_drvdata ( component ) ;
2012-04-28 00:45:52 +04:00
if ( ( freq > = CS42L52_MIN_CLK ) & & ( freq < = CS42L52_MAX_CLK ) ) {
cs42l52 - > sysclk = freq ;
} else {
2018-01-29 06:57:55 +03:00
dev_err ( component - > dev , " Invalid freq parameter \n " ) ;
2012-04-28 00:45:52 +04:00
return - EINVAL ;
}
return 0 ;
}
static int cs42l52_set_fmt ( struct snd_soc_dai * codec_dai , unsigned int fmt )
{
2018-01-29 06:57:55 +03:00
struct snd_soc_component * component = codec_dai - > component ;
struct cs42l52_private * cs42l52 = snd_soc_component_get_drvdata ( component ) ;
2012-04-28 00:45:52 +04:00
u8 iface = 0 ;
switch ( fmt & SND_SOC_DAIFMT_MASTER_MASK ) {
case SND_SOC_DAIFMT_CBM_CFM :
iface = CS42L52_IFACE_CTL1_MASTER ;
break ;
case SND_SOC_DAIFMT_CBS_CFS :
iface = CS42L52_IFACE_CTL1_SLAVE ;
break ;
default :
return - EINVAL ;
}
/* interface format */
switch ( fmt & SND_SOC_DAIFMT_FORMAT_MASK ) {
case SND_SOC_DAIFMT_I2S :
iface | = CS42L52_IFACE_CTL1_ADC_FMT_I2S |
CS42L52_IFACE_CTL1_DAC_FMT_I2S ;
break ;
case SND_SOC_DAIFMT_RIGHT_J :
iface | = CS42L52_IFACE_CTL1_DAC_FMT_RIGHT_J ;
break ;
case SND_SOC_DAIFMT_LEFT_J :
iface | = CS42L52_IFACE_CTL1_ADC_FMT_LEFT_J |
CS42L52_IFACE_CTL1_DAC_FMT_LEFT_J ;
break ;
case SND_SOC_DAIFMT_DSP_A :
iface | = CS42L52_IFACE_CTL1_DSP_MODE_EN ;
break ;
case SND_SOC_DAIFMT_DSP_B :
break ;
default :
return - EINVAL ;
}
/* clock inversion */
switch ( fmt & SND_SOC_DAIFMT_INV_MASK ) {
case SND_SOC_DAIFMT_NB_NF :
break ;
case SND_SOC_DAIFMT_IB_IF :
iface | = CS42L52_IFACE_CTL1_INV_SCLK ;
break ;
case SND_SOC_DAIFMT_IB_NF :
iface | = CS42L52_IFACE_CTL1_INV_SCLK ;
break ;
case SND_SOC_DAIFMT_NB_IF :
break ;
default :
2012-11-07 16:38:35 +04:00
return - EINVAL ;
2012-04-28 00:45:52 +04:00
}
cs42l52 - > config . format = iface ;
2018-01-29 06:57:55 +03:00
snd_soc_component_write ( component , CS42L52_IFACE_CTL1 , cs42l52 - > config . format ) ;
2012-04-28 00:45:52 +04:00
return 0 ;
}
2020-07-09 04:57:06 +03:00
static int cs42l52_mute ( struct snd_soc_dai * dai , int mute , int direction )
2012-04-28 00:45:52 +04:00
{
2018-01-29 06:57:55 +03:00
struct snd_soc_component * component = dai - > component ;
2012-04-28 00:45:52 +04:00
if ( mute )
2018-01-29 06:57:55 +03:00
snd_soc_component_update_bits ( component , CS42L52_PB_CTL1 ,
2012-04-28 00:45:52 +04:00
CS42L52_PB_CTL1_MUTE_MASK ,
CS42L52_PB_CTL1_MUTE ) ;
else
2018-01-29 06:57:55 +03:00
snd_soc_component_update_bits ( component , CS42L52_PB_CTL1 ,
2012-04-28 00:45:52 +04:00
CS42L52_PB_CTL1_MUTE_MASK ,
CS42L52_PB_CTL1_UNMUTE ) ;
return 0 ;
}
static int cs42l52_pcm_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params ,
struct snd_soc_dai * dai )
{
2018-01-29 06:57:55 +03:00
struct snd_soc_component * component = dai - > component ;
struct cs42l52_private * cs42l52 = snd_soc_component_get_drvdata ( component ) ;
2012-04-28 00:45:52 +04:00
u32 clk = 0 ;
int index ;
index = cs42l52_get_clk ( cs42l52 - > sysclk , params_rate ( params ) ) ;
if ( index > = 0 ) {
cs42l52 - > sysclk = clk_map_table [ index ] . mclk ;
clk | = ( clk_map_table [ index ] . speed < < CLK_SPEED_SHIFT ) |
( clk_map_table [ index ] . group < < CLK_32K_SR_SHIFT ) |
( clk_map_table [ index ] . videoclk < < CLK_27M_MCLK_SHIFT ) |
( clk_map_table [ index ] . ratio < < CLK_RATIO_SHIFT ) |
clk_map_table [ index ] . mclkdiv2 ;
2018-01-29 06:57:55 +03:00
snd_soc_component_write ( component , CS42L52_CLK_CTL , clk ) ;
2012-04-28 00:45:52 +04:00
} else {
2018-01-29 06:57:55 +03:00
dev_err ( component - > dev , " can't get correct mclk \n " ) ;
2012-04-28 00:45:52 +04:00
return - EINVAL ;
}
return 0 ;
}
2018-01-29 06:57:55 +03:00
static int cs42l52_set_bias_level ( struct snd_soc_component * component ,
2012-04-28 00:45:52 +04:00
enum snd_soc_bias_level level )
{
2018-01-29 06:57:55 +03:00
struct cs42l52_private * cs42l52 = snd_soc_component_get_drvdata ( component ) ;
2012-04-28 00:45:52 +04:00
switch ( level ) {
case SND_SOC_BIAS_ON :
break ;
case SND_SOC_BIAS_PREPARE :
2018-01-29 06:57:55 +03:00
snd_soc_component_update_bits ( component , CS42L52_PWRCTL1 ,
2012-04-28 00:45:52 +04:00
CS42L52_PWRCTL1_PDN_CODEC , 0 ) ;
break ;
case SND_SOC_BIAS_STANDBY :
2018-01-29 06:57:55 +03:00
if ( snd_soc_component_get_bias_level ( component ) = = SND_SOC_BIAS_OFF ) {
2012-04-28 00:45:52 +04:00
regcache_cache_only ( cs42l52 - > regmap , false ) ;
regcache_sync ( cs42l52 - > regmap ) ;
}
2018-01-29 06:57:55 +03:00
snd_soc_component_write ( component , CS42L52_PWRCTL1 , CS42L52_PWRCTL1_PDN_ALL ) ;
2012-04-28 00:45:52 +04:00
break ;
case SND_SOC_BIAS_OFF :
2018-01-29 06:57:55 +03:00
snd_soc_component_write ( component , CS42L52_PWRCTL1 , CS42L52_PWRCTL1_PDN_ALL ) ;
2012-04-28 00:45:52 +04:00
regcache_cache_only ( cs42l52 - > regmap , true ) ;
break ;
}
return 0 ;
}
# define CS42L52_RATES (SNDRV_PCM_RATE_8000_96000)
# define CS42L52_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE | \
SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_U18_3LE | \
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_U20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE )
2015-07-15 10:38:14 +03:00
static const struct snd_soc_dai_ops cs42l52_ops = {
2012-04-28 00:45:52 +04:00
. hw_params = cs42l52_pcm_hw_params ,
2020-07-09 04:57:06 +03:00
. mute_stream = cs42l52_mute ,
2012-04-28 00:45:52 +04:00
. set_fmt = cs42l52_set_fmt ,
. set_sysclk = cs42l52_set_sysclk ,
2020-07-09 04:57:06 +03:00
. no_capture_mute = 1 ,
2012-04-28 00:45:52 +04:00
} ;
2012-05-10 01:14:41 +04:00
static struct snd_soc_dai_driver cs42l52_dai = {
2012-04-28 00:45:52 +04:00
. name = " cs42l52 " ,
. playback = {
. stream_name = " Playback " ,
. channels_min = 1 ,
. channels_max = 2 ,
. rates = CS42L52_RATES ,
. formats = CS42L52_FORMATS ,
} ,
. capture = {
. stream_name = " Capture " ,
. channels_min = 1 ,
. channels_max = 2 ,
. rates = CS42L52_RATES ,
. formats = CS42L52_FORMATS ,
} ,
. ops = & cs42l52_ops ,
} ;
static int beep_rates [ ] = {
261 , 522 , 585 , 667 , 706 , 774 , 889 , 1000 ,
1043 , 1200 , 1333 , 1412 , 1600 , 1714 , 2000 , 2182
} ;
static void cs42l52_beep_work ( struct work_struct * work )
{
struct cs42l52_private * cs42l52 =
container_of ( work , struct cs42l52_private , beep_work ) ;
2018-01-29 06:57:55 +03:00
struct snd_soc_component * component = cs42l52 - > component ;
struct snd_soc_dapm_context * dapm = snd_soc_component_get_dapm ( component ) ;
2012-04-28 00:45:52 +04:00
int i ;
int val = 0 ;
int best = 0 ;
if ( cs42l52 - > beep_rate ) {
for ( i = 0 ; i < ARRAY_SIZE ( beep_rates ) ; i + + ) {
if ( abs ( cs42l52 - > beep_rate - beep_rates [ i ] ) <
abs ( cs42l52 - > beep_rate - beep_rates [ best ] ) )
best = i ;
}
2018-01-29 06:57:55 +03:00
dev_dbg ( component - > dev , " Set beep rate %dHz for requested %dHz \n " ,
2012-04-28 00:45:52 +04:00
beep_rates [ best ] , cs42l52 - > beep_rate ) ;
val = ( best < < CS42L52_BEEP_RATE_SHIFT ) ;
snd_soc_dapm_enable_pin ( dapm , " Beep " ) ;
} else {
2018-01-29 06:57:55 +03:00
dev_dbg ( component - > dev , " Disabling beep \n " ) ;
2012-04-28 00:45:52 +04:00
snd_soc_dapm_disable_pin ( dapm , " Beep " ) ;
}
2018-01-29 06:57:55 +03:00
snd_soc_component_update_bits ( component , CS42L52_BEEP_FREQ ,
2012-04-28 00:45:52 +04:00
CS42L52_BEEP_RATE_MASK , val ) ;
snd_soc_dapm_sync ( dapm ) ;
}
/* For usability define a way of injecting beep events for the device -
* many systems will not have a keyboard .
*/
static int cs42l52_beep_event ( struct input_dev * dev , unsigned int type ,
unsigned int code , int hz )
{
2018-01-29 06:57:55 +03:00
struct snd_soc_component * component = input_get_drvdata ( dev ) ;
struct cs42l52_private * cs42l52 = snd_soc_component_get_drvdata ( component ) ;
2012-04-28 00:45:52 +04:00
2018-01-29 06:57:55 +03:00
dev_dbg ( component - > dev , " Beep event %x %x \n " , code , hz ) ;
2012-04-28 00:45:52 +04:00
switch ( code ) {
case SND_BELL :
if ( hz )
hz = 261 ;
2020-11-20 21:23:50 +03:00
break ;
2012-04-28 00:45:52 +04:00
case SND_TONE :
break ;
default :
return - 1 ;
}
/* Kick the beep from a workqueue */
cs42l52 - > beep_rate = hz ;
schedule_work ( & cs42l52 - > beep_work ) ;
return 0 ;
}
2021-05-24 14:42:39 +03:00
static ssize_t beep_store ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
2012-04-28 00:45:52 +04:00
{
struct cs42l52_private * cs42l52 = dev_get_drvdata ( dev ) ;
long int time ;
int ret ;
ret = kstrtol ( buf , 10 , & time ) ;
if ( ret ! = 0 )
return ret ;
input_event ( cs42l52 - > beep , EV_SND , SND_TONE , time ) ;
return count ;
}
2021-05-24 14:42:39 +03:00
static DEVICE_ATTR_WO ( beep ) ;
2012-04-28 00:45:52 +04:00
2018-01-29 06:57:55 +03:00
static void cs42l52_init_beep ( struct snd_soc_component * component )
2012-04-28 00:45:52 +04:00
{
2018-01-29 06:57:55 +03:00
struct cs42l52_private * cs42l52 = snd_soc_component_get_drvdata ( component ) ;
2012-04-28 00:45:52 +04:00
int ret ;
2018-01-29 06:57:55 +03:00
cs42l52 - > beep = devm_input_allocate_device ( component - > dev ) ;
2012-04-28 00:45:52 +04:00
if ( ! cs42l52 - > beep ) {
2018-01-29 06:57:55 +03:00
dev_err ( component - > dev , " Failed to allocate beep device \n " ) ;
2012-04-28 00:45:52 +04:00
return ;
}
INIT_WORK ( & cs42l52 - > beep_work , cs42l52_beep_work ) ;
cs42l52 - > beep_rate = 0 ;
cs42l52 - > beep - > name = " CS42L52 Beep Generator " ;
2018-01-29 06:57:55 +03:00
cs42l52 - > beep - > phys = dev_name ( component - > dev ) ;
2012-04-28 00:45:52 +04:00
cs42l52 - > beep - > id . bustype = BUS_I2C ;
cs42l52 - > beep - > evbit [ 0 ] = BIT_MASK ( EV_SND ) ;
cs42l52 - > beep - > sndbit [ 0 ] = BIT_MASK ( SND_BELL ) | BIT_MASK ( SND_TONE ) ;
cs42l52 - > beep - > event = cs42l52_beep_event ;
2018-01-29 06:57:55 +03:00
cs42l52 - > beep - > dev . parent = component - > dev ;
input_set_drvdata ( cs42l52 - > beep , component ) ;
2012-04-28 00:45:52 +04:00
ret = input_register_device ( cs42l52 - > beep ) ;
if ( ret ! = 0 ) {
cs42l52 - > beep = NULL ;
2018-01-29 06:57:55 +03:00
dev_err ( component - > dev , " Failed to register beep device \n " ) ;
2012-04-28 00:45:52 +04:00
}
2018-01-29 06:57:55 +03:00
ret = device_create_file ( component - > dev , & dev_attr_beep ) ;
2012-04-28 00:45:52 +04:00
if ( ret ! = 0 ) {
2018-01-29 06:57:55 +03:00
dev_err ( component - > dev , " Failed to create keyclick file: %d \n " ,
2012-04-28 00:45:52 +04:00
ret ) ;
}
}
2018-01-29 06:57:55 +03:00
static void cs42l52_free_beep ( struct snd_soc_component * component )
2012-04-28 00:45:52 +04:00
{
2018-01-29 06:57:55 +03:00
struct cs42l52_private * cs42l52 = snd_soc_component_get_drvdata ( component ) ;
2012-04-28 00:45:52 +04:00
2018-01-29 06:57:55 +03:00
device_remove_file ( component - > dev , & dev_attr_beep ) ;
2012-04-28 00:45:52 +04:00
cancel_work_sync ( & cs42l52 - > beep_work ) ;
cs42l52 - > beep = NULL ;
2018-01-29 06:57:55 +03:00
snd_soc_component_update_bits ( component , CS42L52_BEEP_TONE_CTL ,
2012-04-28 00:45:52 +04:00
CS42L52_BEEP_EN_MASK , 0 ) ;
}
2018-01-29 06:57:55 +03:00
static int cs42l52_probe ( struct snd_soc_component * component )
2012-04-28 00:45:52 +04:00
{
2018-01-29 06:57:55 +03:00
struct cs42l52_private * cs42l52 = snd_soc_component_get_drvdata ( component ) ;
2012-04-28 00:45:52 +04:00
regcache_cache_only ( cs42l52 - > regmap , true ) ;
2018-01-29 06:57:55 +03:00
cs42l52_add_mic_controls ( component ) ;
2013-11-14 21:46:11 +04:00
2018-01-29 06:57:55 +03:00
cs42l52_init_beep ( component ) ;
2012-04-28 00:45:52 +04:00
cs42l52 - > sysclk = CS42L52_DEFAULT_CLK ;
cs42l52 - > config . format = CS42L52_DEFAULT_FORMAT ;
2014-03-11 08:43:20 +04:00
return 0 ;
2012-04-28 00:45:52 +04:00
}
2018-01-29 06:57:55 +03:00
static void cs42l52_remove ( struct snd_soc_component * component )
2012-04-28 00:45:52 +04:00
{
2018-01-29 06:57:55 +03:00
cs42l52_free_beep ( component ) ;
2012-04-28 00:45:52 +04:00
}
2018-01-29 06:57:55 +03:00
static const struct snd_soc_component_driver soc_component_dev_cs42l52 = {
. probe = cs42l52_probe ,
. remove = cs42l52_remove ,
. set_bias_level = cs42l52_set_bias_level ,
. controls = cs42l52_snd_controls ,
. num_controls = ARRAY_SIZE ( cs42l52_snd_controls ) ,
. dapm_widgets = cs42l52_dapm_widgets ,
. num_dapm_widgets = ARRAY_SIZE ( cs42l52_dapm_widgets ) ,
. dapm_routes = cs42l52_audio_map ,
. num_dapm_routes = ARRAY_SIZE ( cs42l52_audio_map ) ,
. suspend_bias_off = 1 ,
. idle_bias_on = 1 ,
. use_pmdown_time = 1 ,
. endianness = 1 ,
2012-04-28 00:45:52 +04:00
} ;
/* Current and threshold powerup sequence Pg37 */
2015-07-16 18:36:21 +03:00
static const struct reg_sequence cs42l52_threshold_patch [ ] = {
2012-04-28 00:45:52 +04:00
{ 0x00 , 0x99 } ,
{ 0x3E , 0xBA } ,
{ 0x47 , 0x80 } ,
{ 0x32 , 0xBB } ,
{ 0x32 , 0x3B } ,
{ 0x00 , 0x00 } ,
} ;
2015-01-05 12:18:23 +03:00
static const struct regmap_config cs42l52_regmap = {
2012-04-28 00:45:52 +04:00
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = CS42L52_MAX_REGISTER ,
. reg_defaults = cs42l52_reg_defaults ,
. num_reg_defaults = ARRAY_SIZE ( cs42l52_reg_defaults ) ,
. readable_reg = cs42l52_readable_register ,
. volatile_reg = cs42l52_volatile_register ,
. cache_type = REGCACHE_RBTREE ,
} ;
2022-03-25 20:07:34 +03:00
static int cs42l52_i2c_probe ( struct i2c_client * i2c_client )
2012-04-28 00:45:52 +04:00
{
struct cs42l52_private * cs42l52 ;
2013-10-25 19:01:14 +04:00
struct cs42l52_platform_data * pdata = dev_get_platdata ( & i2c_client - > dev ) ;
2012-04-28 00:45:52 +04:00
int ret ;
2021-05-11 13:10:51 +03:00
unsigned int devid ;
2012-04-28 00:45:52 +04:00
unsigned int reg ;
2013-11-15 19:35:33 +04:00
u32 val32 ;
2012-04-28 00:45:52 +04:00
2017-11-23 00:11:30 +03:00
cs42l52 = devm_kzalloc ( & i2c_client - > dev , sizeof ( * cs42l52 ) , GFP_KERNEL ) ;
2012-04-28 00:45:52 +04:00
if ( cs42l52 = = NULL )
return - ENOMEM ;
cs42l52 - > dev = & i2c_client - > dev ;
2012-06-04 22:19:42 +04:00
cs42l52 - > regmap = devm_regmap_init_i2c ( i2c_client , & cs42l52_regmap ) ;
2012-04-28 00:45:52 +04:00
if ( IS_ERR ( cs42l52 - > regmap ) ) {
ret = PTR_ERR ( cs42l52 - > regmap ) ;
dev_err ( & i2c_client - > dev , " regmap_init() failed: %d \n " , ret ) ;
2012-06-04 22:19:42 +04:00
return ret ;
2012-04-28 00:45:52 +04:00
}
2013-11-15 19:35:33 +04:00
if ( pdata ) {
cs42l52 - > pdata = * pdata ;
} else {
2017-11-23 00:11:30 +03:00
pdata = devm_kzalloc ( & i2c_client - > dev , sizeof ( * pdata ) ,
GFP_KERNEL ) ;
2017-11-23 00:08:06 +03:00
if ( ! pdata )
2013-11-15 19:35:33 +04:00
return - ENOMEM ;
2017-11-23 00:08:06 +03:00
2013-11-15 19:35:33 +04:00
if ( i2c_client - > dev . of_node ) {
if ( of_property_read_bool ( i2c_client - > dev . of_node ,
" cirrus,mica-differential-cfg " ) )
pdata - > mica_diff_cfg = true ;
if ( of_property_read_bool ( i2c_client - > dev . of_node ,
" cirrus,micb-differential-cfg " ) )
pdata - > micb_diff_cfg = true ;
if ( of_property_read_u32 ( i2c_client - > dev . of_node ,
" cirrus,micbias-lvl " , & val32 ) > = 0 )
pdata - > micbias_lvl = val32 ;
if ( of_property_read_u32 ( i2c_client - > dev . of_node ,
" cirrus,chgfreq-divisor " , & val32 ) > = 0 )
2013-11-29 15:42:18 +04:00
pdata - > chgfreq = val32 ;
2013-11-15 19:35:33 +04:00
pdata - > reset_gpio =
of_get_named_gpio ( i2c_client - > dev . of_node ,
" cirrus,reset-gpio " , 0 ) ;
}
2013-10-25 19:01:14 +04:00
cs42l52 - > pdata = * pdata ;
2013-11-15 19:35:33 +04:00
}
2012-04-28 00:45:52 +04:00
2013-10-25 19:01:14 +04:00
if ( cs42l52 - > pdata . reset_gpio ) {
2014-04-08 18:10:33 +04:00
ret = devm_gpio_request_one ( & i2c_client - > dev ,
cs42l52 - > pdata . reset_gpio ,
GPIOF_OUT_INIT_HIGH ,
" CS42L52 /RST " ) ;
2013-10-25 19:01:14 +04:00
if ( ret < 0 ) {
dev_err ( & i2c_client - > dev , " Failed to request /RST %d: %d \n " ,
cs42l52 - > pdata . reset_gpio , ret ) ;
return ret ;
}
gpio_set_value_cansleep ( cs42l52 - > pdata . reset_gpio , 0 ) ;
gpio_set_value_cansleep ( cs42l52 - > pdata . reset_gpio , 1 ) ;
}
i2c_set_clientdata ( i2c_client , cs42l52 ) ;
2012-04-28 00:45:52 +04:00
ret = regmap_register_patch ( cs42l52 - > regmap , cs42l52_threshold_patch ,
ARRAY_SIZE ( cs42l52_threshold_patch ) ) ;
if ( ret ! = 0 )
dev_warn ( cs42l52 - > dev , " Failed to apply regmap patch: %d \n " ,
ret ) ;
ret = regmap_read ( cs42l52 - > regmap , CS42L52_CHIP , & reg ) ;
2021-05-11 13:10:51 +03:00
if ( ret ) {
dev_err ( & i2c_client - > dev , " Failed to read chip ID: %d \n " , ret ) ;
return ret ;
}
2012-04-28 00:45:52 +04:00
devid = reg & CS42L52_CHIP_ID_MASK ;
if ( devid ! = CS42L52_CHIP_ID ) {
ret = - ENODEV ;
dev_err ( & i2c_client - > dev ,
" CS42L52 Device ID (%X). Expected %X \n " ,
devid , CS42L52_CHIP_ID ) ;
2012-06-04 22:19:42 +04:00
return ret ;
2012-04-28 00:45:52 +04:00
}
2013-10-25 19:01:17 +04:00
dev_info ( & i2c_client - > dev , " Cirrus Logic CS42L52, Revision: %02X \n " ,
2014-04-05 20:04:35 +04:00
reg & CS42L52_CHIP_REV_MASK ) ;
2013-10-25 19:01:17 +04:00
2013-10-25 19:01:16 +04:00
/* Set Platform Data */
2013-11-14 21:46:11 +04:00
if ( cs42l52 - > pdata . mica_diff_cfg )
2013-10-25 19:01:16 +04:00
regmap_update_bits ( cs42l52 - > regmap , CS42L52_MICA_CTL ,
CS42L52_MIC_CTL_TYPE_MASK ,
2013-11-14 21:46:11 +04:00
cs42l52 - > pdata . mica_diff_cfg < <
2013-10-25 19:01:16 +04:00
CS42L52_MIC_CTL_TYPE_SHIFT ) ;
2013-11-14 21:46:11 +04:00
if ( cs42l52 - > pdata . micb_diff_cfg )
2013-10-25 19:01:16 +04:00
regmap_update_bits ( cs42l52 - > regmap , CS42L52_MICB_CTL ,
CS42L52_MIC_CTL_TYPE_MASK ,
2013-11-14 21:46:11 +04:00
cs42l52 - > pdata . micb_diff_cfg < <
2013-10-25 19:01:16 +04:00
CS42L52_MIC_CTL_TYPE_SHIFT ) ;
if ( cs42l52 - > pdata . chgfreq )
regmap_update_bits ( cs42l52 - > regmap , CS42L52_CHARGE_PUMP ,
CS42L52_CHARGE_PUMP_MASK ,
cs42l52 - > pdata . chgfreq < <
CS42L52_CHARGE_PUMP_SHIFT ) ;
if ( cs42l52 - > pdata . micbias_lvl )
regmap_update_bits ( cs42l52 - > regmap , CS42L52_IFACE_CTL2 ,
CS42L52_IFACE_CTL2_BIAS_LVL ,
cs42l52 - > pdata . micbias_lvl ) ;
2012-04-28 00:45:52 +04:00
2021-05-11 13:10:51 +03:00
return devm_snd_soc_register_component ( & i2c_client - > dev ,
2018-01-29 06:57:55 +03:00
& soc_component_dev_cs42l52 , & cs42l52_dai , 1 ) ;
2012-04-28 00:45:52 +04:00
}
2013-11-15 19:35:33 +04:00
static const struct of_device_id cs42l52_of_match [ ] = {
{ . compatible = " cirrus,cs42l52 " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , cs42l52_of_match ) ;
2012-04-28 00:45:52 +04:00
static const struct i2c_device_id cs42l52_id [ ] = {
{ " cs42l52 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , cs42l52_id ) ;
static struct i2c_driver cs42l52_i2c_driver = {
. driver = {
. name = " cs42l52 " ,
2013-11-15 19:35:33 +04:00
. of_match_table = cs42l52_of_match ,
2012-04-28 00:45:52 +04:00
} ,
. id_table = cs42l52_id ,
2023-04-25 12:57:16 +03:00
. probe = cs42l52_i2c_probe ,
2012-04-28 00:45:52 +04:00
} ;
module_i2c_driver ( cs42l52_i2c_driver ) ;
MODULE_DESCRIPTION ( " ASoC CS42L52 driver " ) ;
MODULE_AUTHOR ( " Georgi Vlaev, Nucleus Systems Ltd, <joe@nucleusys.com> " ) ;
MODULE_AUTHOR ( " Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;