2019-05-21 09:26:52 +03:00
// SPDX-License-Identifier: GPL-2.0
//
// ALSA SoC CX20721/CX20723 codec driver
//
// Copyright: (C) 2017 Conexant Systems, Inc.
// Author: Simon Ho, <Simon.ho@conexant.com>
//
// TODO: add support for TDM mode.
//
# include <linux/acpi.h>
# include <linux/clk.h>
# include <linux/delay.h>
# include <linux/gpio.h>
# include <linux/init.h>
# include <linux/i2c.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/pm.h>
# include <linux/pm_runtime.h>
# include <linux/regmap.h>
# include <linux/slab.h>
# include <sound/core.h>
# include <sound/initval.h>
# include <sound/jack.h>
# include <sound/pcm.h>
# include <sound/pcm_params.h>
# include <sound/tlv.h>
# include <sound/soc.h>
# include <sound/soc-dapm.h>
# include "cx2072x.h"
# define PLL_OUT_HZ_48 (1024 * 3 * 48000)
# define BITS_PER_SLOT 8
/* codec private data */
struct cx2072x_priv {
struct regmap * regmap ;
struct clk * mclk ;
unsigned int mclk_rate ;
struct device * dev ;
struct snd_soc_component * codec ;
struct snd_soc_jack_gpio jack_gpio ;
struct mutex lock ;
unsigned int bclk_ratio ;
bool pll_changed ;
bool i2spcm_changed ;
int sample_size ;
int frame_size ;
int sample_rate ;
unsigned int dai_fmt ;
bool en_aec_ref ;
} ;
/*
* DAC / ADC Volume
*
* max : 74 : 0 dB
* ( in 1 dB step )
* min : 0 : - 74 dB
*/
static const DECLARE_TLV_DB_SCALE ( adc_tlv , - 7400 , 100 , 0 ) ;
static const DECLARE_TLV_DB_SCALE ( dac_tlv , - 7400 , 100 , 0 ) ;
static const DECLARE_TLV_DB_SCALE ( boost_tlv , 0 , 1200 , 0 ) ;
struct cx2072x_eq_ctrl {
u8 ch ;
u8 band ;
} ;
static const DECLARE_TLV_DB_RANGE ( hpf_tlv ,
0 , 0 , TLV_DB_SCALE_ITEM ( 120 , 0 , 0 ) ,
1 , 63 , TLV_DB_SCALE_ITEM ( 30 , 30 , 0 )
) ;
/* Lookup table for PRE_DIV */
static const struct {
unsigned int mclk ;
unsigned int div ;
} mclk_pre_div [ ] = {
{ 6144000 , 1 } ,
{ 12288000 , 2 } ,
{ 19200000 , 3 } ,
{ 26000000 , 4 } ,
{ 28224000 , 5 } ,
{ 36864000 , 6 } ,
{ 36864000 , 7 } ,
{ 48000000 , 8 } ,
{ 49152000 , 8 } ,
} ;
/*
* cx2072x register cache .
*/
static const struct reg_default cx2072x_reg_defaults [ ] = {
{ CX2072X_AFG_POWER_STATE , 0x00000003 } ,
{ CX2072X_UM_RESPONSE , 0x00000000 } ,
{ CX2072X_GPIO_DATA , 0x00000000 } ,
{ CX2072X_GPIO_ENABLE , 0x00000000 } ,
{ CX2072X_GPIO_DIRECTION , 0x00000000 } ,
{ CX2072X_GPIO_WAKE , 0x00000000 } ,
{ CX2072X_GPIO_UM_ENABLE , 0x00000000 } ,
{ CX2072X_GPIO_STICKY_MASK , 0x00000000 } ,
{ CX2072X_DAC1_CONVERTER_FORMAT , 0x00000031 } ,
{ CX2072X_DAC1_AMP_GAIN_RIGHT , 0x0000004a } ,
{ CX2072X_DAC1_AMP_GAIN_LEFT , 0x0000004a } ,
{ CX2072X_DAC1_POWER_STATE , 0x00000433 } ,
{ CX2072X_DAC1_CONVERTER_STREAM_CHANNEL , 0x00000000 } ,
{ CX2072X_DAC1_EAPD_ENABLE , 0x00000000 } ,
{ CX2072X_DAC2_CONVERTER_FORMAT , 0x00000031 } ,
{ CX2072X_DAC2_AMP_GAIN_RIGHT , 0x0000004a } ,
{ CX2072X_DAC2_AMP_GAIN_LEFT , 0x0000004a } ,
{ CX2072X_DAC2_POWER_STATE , 0x00000433 } ,
{ CX2072X_DAC2_CONVERTER_STREAM_CHANNEL , 0x00000000 } ,
{ CX2072X_ADC1_CONVERTER_FORMAT , 0x00000031 } ,
{ CX2072X_ADC1_AMP_GAIN_RIGHT_0 , 0x0000004a } ,
{ CX2072X_ADC1_AMP_GAIN_LEFT_0 , 0x0000004a } ,
{ CX2072X_ADC1_AMP_GAIN_RIGHT_1 , 0x0000004a } ,
{ CX2072X_ADC1_AMP_GAIN_LEFT_1 , 0x0000004a } ,
{ CX2072X_ADC1_AMP_GAIN_RIGHT_2 , 0x0000004a } ,
{ CX2072X_ADC1_AMP_GAIN_LEFT_2 , 0x0000004a } ,
{ CX2072X_ADC1_AMP_GAIN_RIGHT_3 , 0x0000004a } ,
{ CX2072X_ADC1_AMP_GAIN_LEFT_3 , 0x0000004a } ,
{ CX2072X_ADC1_AMP_GAIN_RIGHT_4 , 0x0000004a } ,
{ CX2072X_ADC1_AMP_GAIN_LEFT_4 , 0x0000004a } ,
{ CX2072X_ADC1_AMP_GAIN_RIGHT_5 , 0x0000004a } ,
{ CX2072X_ADC1_AMP_GAIN_LEFT_5 , 0x0000004a } ,
{ CX2072X_ADC1_AMP_GAIN_RIGHT_6 , 0x0000004a } ,
{ CX2072X_ADC1_AMP_GAIN_LEFT_6 , 0x0000004a } ,
{ CX2072X_ADC1_CONNECTION_SELECT_CONTROL , 0x00000000 } ,
{ CX2072X_ADC1_POWER_STATE , 0x00000433 } ,
{ CX2072X_ADC1_CONVERTER_STREAM_CHANNEL , 0x00000000 } ,
{ CX2072X_ADC2_CONVERTER_FORMAT , 0x00000031 } ,
{ CX2072X_ADC2_AMP_GAIN_RIGHT_0 , 0x0000004a } ,
{ CX2072X_ADC2_AMP_GAIN_LEFT_0 , 0x0000004a } ,
{ CX2072X_ADC2_AMP_GAIN_RIGHT_1 , 0x0000004a } ,
{ CX2072X_ADC2_AMP_GAIN_LEFT_1 , 0x0000004a } ,
{ CX2072X_ADC2_AMP_GAIN_RIGHT_2 , 0x0000004a } ,
{ CX2072X_ADC2_AMP_GAIN_LEFT_2 , 0x0000004a } ,
{ CX2072X_ADC2_CONNECTION_SELECT_CONTROL , 0x00000000 } ,
{ CX2072X_ADC2_POWER_STATE , 0x00000433 } ,
{ CX2072X_ADC2_CONVERTER_STREAM_CHANNEL , 0x00000000 } ,
{ CX2072X_PORTA_CONNECTION_SELECT_CTRL , 0x00000000 } ,
{ CX2072X_PORTA_POWER_STATE , 0x00000433 } ,
{ CX2072X_PORTA_PIN_CTRL , 0x000000c0 } ,
{ CX2072X_PORTA_UNSOLICITED_RESPONSE , 0x00000000 } ,
{ CX2072X_PORTA_PIN_SENSE , 0x00000000 } ,
{ CX2072X_PORTA_EAPD_BTL , 0x00000002 } ,
{ CX2072X_PORTB_POWER_STATE , 0x00000433 } ,
{ CX2072X_PORTB_PIN_CTRL , 0x00000000 } ,
{ CX2072X_PORTB_UNSOLICITED_RESPONSE , 0x00000000 } ,
{ CX2072X_PORTB_PIN_SENSE , 0x00000000 } ,
{ CX2072X_PORTB_EAPD_BTL , 0x00000002 } ,
{ CX2072X_PORTB_GAIN_RIGHT , 0x00000000 } ,
{ CX2072X_PORTB_GAIN_LEFT , 0x00000000 } ,
{ CX2072X_PORTC_POWER_STATE , 0x00000433 } ,
{ CX2072X_PORTC_PIN_CTRL , 0x00000000 } ,
{ CX2072X_PORTC_GAIN_RIGHT , 0x00000000 } ,
{ CX2072X_PORTC_GAIN_LEFT , 0x00000000 } ,
{ CX2072X_PORTD_POWER_STATE , 0x00000433 } ,
{ CX2072X_PORTD_PIN_CTRL , 0x00000020 } ,
{ CX2072X_PORTD_UNSOLICITED_RESPONSE , 0x00000000 } ,
{ CX2072X_PORTD_PIN_SENSE , 0x00000000 } ,
{ CX2072X_PORTD_GAIN_RIGHT , 0x00000000 } ,
{ CX2072X_PORTD_GAIN_LEFT , 0x00000000 } ,
{ CX2072X_PORTE_CONNECTION_SELECT_CTRL , 0x00000000 } ,
{ CX2072X_PORTE_POWER_STATE , 0x00000433 } ,
{ CX2072X_PORTE_PIN_CTRL , 0x00000040 } ,
{ CX2072X_PORTE_UNSOLICITED_RESPONSE , 0x00000000 } ,
{ CX2072X_PORTE_PIN_SENSE , 0x00000000 } ,
{ CX2072X_PORTE_EAPD_BTL , 0x00000002 } ,
{ CX2072X_PORTE_GAIN_RIGHT , 0x00000000 } ,
{ CX2072X_PORTE_GAIN_LEFT , 0x00000000 } ,
{ CX2072X_PORTF_POWER_STATE , 0x00000433 } ,
{ CX2072X_PORTF_PIN_CTRL , 0x00000000 } ,
{ CX2072X_PORTF_UNSOLICITED_RESPONSE , 0x00000000 } ,
{ CX2072X_PORTF_PIN_SENSE , 0x00000000 } ,
{ CX2072X_PORTF_GAIN_RIGHT , 0x00000000 } ,
{ CX2072X_PORTF_GAIN_LEFT , 0x00000000 } ,
{ CX2072X_PORTG_POWER_STATE , 0x00000433 } ,
{ CX2072X_PORTG_PIN_CTRL , 0x00000040 } ,
{ CX2072X_PORTG_CONNECTION_SELECT_CTRL , 0x00000000 } ,
{ CX2072X_PORTG_EAPD_BTL , 0x00000002 } ,
{ CX2072X_PORTM_POWER_STATE , 0x00000433 } ,
{ CX2072X_PORTM_PIN_CTRL , 0x00000000 } ,
{ CX2072X_PORTM_CONNECTION_SELECT_CTRL , 0x00000000 } ,
{ CX2072X_PORTM_EAPD_BTL , 0x00000002 } ,
{ CX2072X_MIXER_POWER_STATE , 0x00000433 } ,
{ CX2072X_MIXER_GAIN_RIGHT_0 , 0x0000004a } ,
{ CX2072X_MIXER_GAIN_LEFT_0 , 0x0000004a } ,
{ CX2072X_MIXER_GAIN_RIGHT_1 , 0x0000004a } ,
{ CX2072X_MIXER_GAIN_LEFT_1 , 0x0000004a } ,
{ CX2072X_SPKR_DRC_ENABLE_STEP , 0x040065a4 } ,
{ CX2072X_SPKR_DRC_CONTROL , 0x007b0024 } ,
{ CX2072X_SPKR_DRC_TEST , 0x00000000 } ,
{ CX2072X_DIGITAL_BIOS_TEST0 , 0x001f008a } ,
{ CX2072X_DIGITAL_BIOS_TEST2 , 0x00990026 } ,
{ CX2072X_I2SPCM_CONTROL1 , 0x00010001 } ,
{ CX2072X_I2SPCM_CONTROL2 , 0x00000000 } ,
{ CX2072X_I2SPCM_CONTROL3 , 0x00000000 } ,
{ CX2072X_I2SPCM_CONTROL4 , 0x00000000 } ,
{ CX2072X_I2SPCM_CONTROL5 , 0x00000000 } ,
{ CX2072X_I2SPCM_CONTROL6 , 0x00000000 } ,
{ CX2072X_UM_INTERRUPT_CRTL_E , 0x00000000 } ,
{ CX2072X_CODEC_TEST2 , 0x00000000 } ,
{ CX2072X_CODEC_TEST9 , 0x00000004 } ,
{ CX2072X_CODEC_TEST20 , 0x00000600 } ,
{ CX2072X_CODEC_TEST26 , 0x00000208 } ,
{ CX2072X_ANALOG_TEST4 , 0x00000000 } ,
{ CX2072X_ANALOG_TEST5 , 0x00000000 } ,
{ CX2072X_ANALOG_TEST6 , 0x0000059a } ,
{ CX2072X_ANALOG_TEST7 , 0x000000a7 } ,
{ CX2072X_ANALOG_TEST8 , 0x00000017 } ,
{ CX2072X_ANALOG_TEST9 , 0x00000000 } ,
{ CX2072X_ANALOG_TEST10 , 0x00000285 } ,
{ CX2072X_ANALOG_TEST11 , 0x00000000 } ,
{ CX2072X_ANALOG_TEST12 , 0x00000000 } ,
{ CX2072X_ANALOG_TEST13 , 0x00000000 } ,
{ CX2072X_DIGITAL_TEST1 , 0x00000242 } ,
{ CX2072X_DIGITAL_TEST11 , 0x00000000 } ,
{ CX2072X_DIGITAL_TEST12 , 0x00000084 } ,
{ CX2072X_DIGITAL_TEST15 , 0x00000077 } ,
{ CX2072X_DIGITAL_TEST16 , 0x00000021 } ,
{ CX2072X_DIGITAL_TEST17 , 0x00000018 } ,
{ CX2072X_DIGITAL_TEST18 , 0x00000024 } ,
{ CX2072X_DIGITAL_TEST19 , 0x00000001 } ,
{ CX2072X_DIGITAL_TEST20 , 0x00000002 } ,
} ;
/*
* register initialization
*/
static const struct reg_sequence cx2072x_reg_init [ ] = {
{ CX2072X_ANALOG_TEST9 , 0x080 } , /* DC offset Calibration */
{ CX2072X_CODEC_TEST26 , 0x65f } , /* Disable the PA */
{ CX2072X_ANALOG_TEST10 , 0x289 } , /* Set the speaker output gain */
{ CX2072X_CODEC_TEST20 , 0xf05 } ,
{ CX2072X_CODEC_TESTXX , 0x380 } ,
{ CX2072X_CODEC_TEST26 , 0xb90 } ,
{ CX2072X_CODEC_TEST9 , 0x001 } , /* Enable 30 Hz High pass filter */
{ CX2072X_ANALOG_TEST3 , 0x300 } , /* Disable PCBEEP pad */
{ CX2072X_CODEC_TEST24 , 0x100 } , /* Disable SnM mode */
{ CX2072X_PORTD_PIN_CTRL , 0x020 } , /* Enable PortD input */
{ CX2072X_GPIO_ENABLE , 0x040 } , /* Enable GPIO7 pin for button */
{ CX2072X_GPIO_UM_ENABLE , 0x040 } , /* Enable UM for GPIO7 */
{ CX2072X_UM_RESPONSE , 0x080 } , /* Enable button response */
{ CX2072X_DIGITAL_TEST12 , 0x0c4 } , /* Enable headset button */
{ CX2072X_DIGITAL_TEST0 , 0x415 } , /* Power down class-D during idle */
{ CX2072X_I2SPCM_CONTROL2 , 0x00f } , /* Enable I2S TX */
{ CX2072X_I2SPCM_CONTROL3 , 0x00f } , /* Enable I2S RX */
} ;
static unsigned int cx2072x_register_size ( unsigned int reg )
{
switch ( reg ) {
case CX2072X_VENDOR_ID :
case CX2072X_REVISION_ID :
case CX2072X_PORTA_PIN_SENSE :
case CX2072X_PORTB_PIN_SENSE :
case CX2072X_PORTD_PIN_SENSE :
case CX2072X_PORTE_PIN_SENSE :
case CX2072X_PORTF_PIN_SENSE :
case CX2072X_I2SPCM_CONTROL1 :
case CX2072X_I2SPCM_CONTROL2 :
case CX2072X_I2SPCM_CONTROL3 :
case CX2072X_I2SPCM_CONTROL4 :
case CX2072X_I2SPCM_CONTROL5 :
case CX2072X_I2SPCM_CONTROL6 :
case CX2072X_UM_INTERRUPT_CRTL_E :
case CX2072X_EQ_G_COEFF :
case CX2072X_SPKR_DRC_CONTROL :
case CX2072X_SPKR_DRC_TEST :
case CX2072X_DIGITAL_BIOS_TEST0 :
case CX2072X_DIGITAL_BIOS_TEST2 :
return 4 ;
case CX2072X_EQ_ENABLE_BYPASS :
case CX2072X_EQ_B0_COEFF :
case CX2072X_EQ_B1_COEFF :
case CX2072X_EQ_B2_COEFF :
case CX2072X_EQ_A1_COEFF :
case CX2072X_EQ_A2_COEFF :
case CX2072X_DAC1_CONVERTER_FORMAT :
case CX2072X_DAC2_CONVERTER_FORMAT :
case CX2072X_ADC1_CONVERTER_FORMAT :
case CX2072X_ADC2_CONVERTER_FORMAT :
case CX2072X_CODEC_TEST2 :
case CX2072X_CODEC_TEST9 :
case CX2072X_CODEC_TEST20 :
case CX2072X_CODEC_TEST26 :
case CX2072X_ANALOG_TEST3 :
case CX2072X_ANALOG_TEST4 :
case CX2072X_ANALOG_TEST5 :
case CX2072X_ANALOG_TEST6 :
case CX2072X_ANALOG_TEST7 :
case CX2072X_ANALOG_TEST8 :
case CX2072X_ANALOG_TEST9 :
case CX2072X_ANALOG_TEST10 :
case CX2072X_ANALOG_TEST11 :
case CX2072X_ANALOG_TEST12 :
case CX2072X_ANALOG_TEST13 :
case CX2072X_DIGITAL_TEST0 :
case CX2072X_DIGITAL_TEST1 :
case CX2072X_DIGITAL_TEST11 :
case CX2072X_DIGITAL_TEST12 :
case CX2072X_DIGITAL_TEST15 :
case CX2072X_DIGITAL_TEST16 :
case CX2072X_DIGITAL_TEST17 :
case CX2072X_DIGITAL_TEST18 :
case CX2072X_DIGITAL_TEST19 :
case CX2072X_DIGITAL_TEST20 :
return 2 ;
default :
return 1 ;
}
}
static bool cx2072x_readable_register ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
case CX2072X_VENDOR_ID :
case CX2072X_REVISION_ID :
case CX2072X_CURRENT_BCLK_FREQUENCY :
case CX2072X_AFG_POWER_STATE :
case CX2072X_UM_RESPONSE :
case CX2072X_GPIO_DATA :
case CX2072X_GPIO_ENABLE :
case CX2072X_GPIO_DIRECTION :
case CX2072X_GPIO_WAKE :
case CX2072X_GPIO_UM_ENABLE :
case CX2072X_GPIO_STICKY_MASK :
case CX2072X_DAC1_CONVERTER_FORMAT :
case CX2072X_DAC1_AMP_GAIN_RIGHT :
case CX2072X_DAC1_AMP_GAIN_LEFT :
case CX2072X_DAC1_POWER_STATE :
case CX2072X_DAC1_CONVERTER_STREAM_CHANNEL :
case CX2072X_DAC1_EAPD_ENABLE :
case CX2072X_DAC2_CONVERTER_FORMAT :
case CX2072X_DAC2_AMP_GAIN_RIGHT :
case CX2072X_DAC2_AMP_GAIN_LEFT :
case CX2072X_DAC2_POWER_STATE :
case CX2072X_DAC2_CONVERTER_STREAM_CHANNEL :
case CX2072X_ADC1_CONVERTER_FORMAT :
case CX2072X_ADC1_AMP_GAIN_RIGHT_0 :
case CX2072X_ADC1_AMP_GAIN_LEFT_0 :
case CX2072X_ADC1_AMP_GAIN_RIGHT_1 :
case CX2072X_ADC1_AMP_GAIN_LEFT_1 :
case CX2072X_ADC1_AMP_GAIN_RIGHT_2 :
case CX2072X_ADC1_AMP_GAIN_LEFT_2 :
case CX2072X_ADC1_AMP_GAIN_RIGHT_3 :
case CX2072X_ADC1_AMP_GAIN_LEFT_3 :
case CX2072X_ADC1_AMP_GAIN_RIGHT_4 :
case CX2072X_ADC1_AMP_GAIN_LEFT_4 :
case CX2072X_ADC1_AMP_GAIN_RIGHT_5 :
case CX2072X_ADC1_AMP_GAIN_LEFT_5 :
case CX2072X_ADC1_AMP_GAIN_RIGHT_6 :
case CX2072X_ADC1_AMP_GAIN_LEFT_6 :
case CX2072X_ADC1_CONNECTION_SELECT_CONTROL :
case CX2072X_ADC1_POWER_STATE :
case CX2072X_ADC1_CONVERTER_STREAM_CHANNEL :
case CX2072X_ADC2_CONVERTER_FORMAT :
case CX2072X_ADC2_AMP_GAIN_RIGHT_0 :
case CX2072X_ADC2_AMP_GAIN_LEFT_0 :
case CX2072X_ADC2_AMP_GAIN_RIGHT_1 :
case CX2072X_ADC2_AMP_GAIN_LEFT_1 :
case CX2072X_ADC2_AMP_GAIN_RIGHT_2 :
case CX2072X_ADC2_AMP_GAIN_LEFT_2 :
case CX2072X_ADC2_CONNECTION_SELECT_CONTROL :
case CX2072X_ADC2_POWER_STATE :
case CX2072X_ADC2_CONVERTER_STREAM_CHANNEL :
case CX2072X_PORTA_CONNECTION_SELECT_CTRL :
case CX2072X_PORTA_POWER_STATE :
case CX2072X_PORTA_PIN_CTRL :
case CX2072X_PORTA_UNSOLICITED_RESPONSE :
case CX2072X_PORTA_PIN_SENSE :
case CX2072X_PORTA_EAPD_BTL :
case CX2072X_PORTB_POWER_STATE :
case CX2072X_PORTB_PIN_CTRL :
case CX2072X_PORTB_UNSOLICITED_RESPONSE :
case CX2072X_PORTB_PIN_SENSE :
case CX2072X_PORTB_EAPD_BTL :
case CX2072X_PORTB_GAIN_RIGHT :
case CX2072X_PORTB_GAIN_LEFT :
case CX2072X_PORTC_POWER_STATE :
case CX2072X_PORTC_PIN_CTRL :
case CX2072X_PORTC_GAIN_RIGHT :
case CX2072X_PORTC_GAIN_LEFT :
case CX2072X_PORTD_POWER_STATE :
case CX2072X_PORTD_PIN_CTRL :
case CX2072X_PORTD_UNSOLICITED_RESPONSE :
case CX2072X_PORTD_PIN_SENSE :
case CX2072X_PORTD_GAIN_RIGHT :
case CX2072X_PORTD_GAIN_LEFT :
case CX2072X_PORTE_CONNECTION_SELECT_CTRL :
case CX2072X_PORTE_POWER_STATE :
case CX2072X_PORTE_PIN_CTRL :
case CX2072X_PORTE_UNSOLICITED_RESPONSE :
case CX2072X_PORTE_PIN_SENSE :
case CX2072X_PORTE_EAPD_BTL :
case CX2072X_PORTE_GAIN_RIGHT :
case CX2072X_PORTE_GAIN_LEFT :
case CX2072X_PORTF_POWER_STATE :
case CX2072X_PORTF_PIN_CTRL :
case CX2072X_PORTF_UNSOLICITED_RESPONSE :
case CX2072X_PORTF_PIN_SENSE :
case CX2072X_PORTF_GAIN_RIGHT :
case CX2072X_PORTF_GAIN_LEFT :
case CX2072X_PORTG_POWER_STATE :
case CX2072X_PORTG_PIN_CTRL :
case CX2072X_PORTG_CONNECTION_SELECT_CTRL :
case CX2072X_PORTG_EAPD_BTL :
case CX2072X_PORTM_POWER_STATE :
case CX2072X_PORTM_PIN_CTRL :
case CX2072X_PORTM_CONNECTION_SELECT_CTRL :
case CX2072X_PORTM_EAPD_BTL :
case CX2072X_MIXER_POWER_STATE :
case CX2072X_MIXER_GAIN_RIGHT_0 :
case CX2072X_MIXER_GAIN_LEFT_0 :
case CX2072X_MIXER_GAIN_RIGHT_1 :
case CX2072X_MIXER_GAIN_LEFT_1 :
case CX2072X_EQ_ENABLE_BYPASS :
case CX2072X_EQ_B0_COEFF :
case CX2072X_EQ_B1_COEFF :
case CX2072X_EQ_B2_COEFF :
case CX2072X_EQ_A1_COEFF :
case CX2072X_EQ_A2_COEFF :
case CX2072X_EQ_G_COEFF :
case CX2072X_SPKR_DRC_ENABLE_STEP :
case CX2072X_SPKR_DRC_CONTROL :
case CX2072X_SPKR_DRC_TEST :
case CX2072X_DIGITAL_BIOS_TEST0 :
case CX2072X_DIGITAL_BIOS_TEST2 :
case CX2072X_I2SPCM_CONTROL1 :
case CX2072X_I2SPCM_CONTROL2 :
case CX2072X_I2SPCM_CONTROL3 :
case CX2072X_I2SPCM_CONTROL4 :
case CX2072X_I2SPCM_CONTROL5 :
case CX2072X_I2SPCM_CONTROL6 :
case CX2072X_UM_INTERRUPT_CRTL_E :
case CX2072X_CODEC_TEST2 :
case CX2072X_CODEC_TEST9 :
case CX2072X_CODEC_TEST20 :
case CX2072X_CODEC_TEST26 :
case CX2072X_ANALOG_TEST4 :
case CX2072X_ANALOG_TEST5 :
case CX2072X_ANALOG_TEST6 :
case CX2072X_ANALOG_TEST7 :
case CX2072X_ANALOG_TEST8 :
case CX2072X_ANALOG_TEST9 :
case CX2072X_ANALOG_TEST10 :
case CX2072X_ANALOG_TEST11 :
case CX2072X_ANALOG_TEST12 :
case CX2072X_ANALOG_TEST13 :
case CX2072X_DIGITAL_TEST0 :
case CX2072X_DIGITAL_TEST1 :
case CX2072X_DIGITAL_TEST11 :
case CX2072X_DIGITAL_TEST12 :
case CX2072X_DIGITAL_TEST15 :
case CX2072X_DIGITAL_TEST16 :
case CX2072X_DIGITAL_TEST17 :
case CX2072X_DIGITAL_TEST18 :
case CX2072X_DIGITAL_TEST19 :
case CX2072X_DIGITAL_TEST20 :
return true ;
default :
return false ;
}
}
static bool cx2072x_volatile_register ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
case CX2072X_VENDOR_ID :
case CX2072X_REVISION_ID :
case CX2072X_UM_INTERRUPT_CRTL_E :
case CX2072X_DIGITAL_TEST11 :
case CX2072X_PORTA_PIN_SENSE :
case CX2072X_PORTB_PIN_SENSE :
case CX2072X_PORTD_PIN_SENSE :
case CX2072X_PORTE_PIN_SENSE :
case CX2072X_PORTF_PIN_SENSE :
case CX2072X_EQ_G_COEFF :
case CX2072X_EQ_BAND :
return true ;
default :
return false ;
}
}
static int cx2072x_reg_raw_write ( struct i2c_client * client ,
unsigned int reg ,
const void * val , size_t val_count )
{
struct device * dev = & client - > dev ;
u8 buf [ 2 + CX2072X_MAX_EQ_COEFF ] ;
int ret ;
if ( WARN_ON ( val_count + 2 > sizeof ( buf ) ) )
return - EINVAL ;
buf [ 0 ] = reg > > 8 ;
buf [ 1 ] = reg & 0xff ;
memcpy ( buf + 2 , val , val_count ) ;
ret = i2c_master_send ( client , buf , val_count + 2 ) ;
if ( ret ! = val_count + 2 ) {
dev_err ( dev , " I2C write failed, ret = %d \n " , ret ) ;
return ret < 0 ? ret : - EIO ;
}
return 0 ;
}
static int cx2072x_reg_write ( void * context , unsigned int reg ,
unsigned int value )
{
__le32 raw_value ;
unsigned int size ;
size = cx2072x_register_size ( reg ) ;
if ( reg = = CX2072X_UM_INTERRUPT_CRTL_E ) {
/* Update the MSB byte only */
reg + = 3 ;
size = 1 ;
value > > = 24 ;
}
raw_value = cpu_to_le32 ( value ) ;
return cx2072x_reg_raw_write ( context , reg , & raw_value , size ) ;
}
static int cx2072x_reg_read ( void * context , unsigned int reg ,
unsigned int * value )
{
struct i2c_client * client = context ;
struct device * dev = & client - > dev ;
__le32 recv_buf = 0 ;
struct i2c_msg msgs [ 2 ] ;
unsigned int size ;
u8 send_buf [ 2 ] ;
int ret ;
size = cx2072x_register_size ( reg ) ;
send_buf [ 0 ] = reg > > 8 ;
send_buf [ 1 ] = reg & 0xff ;
msgs [ 0 ] . addr = client - > addr ;
msgs [ 0 ] . len = sizeof ( send_buf ) ;
msgs [ 0 ] . buf = send_buf ;
msgs [ 0 ] . flags = 0 ;
msgs [ 1 ] . addr = client - > addr ;
msgs [ 1 ] . len = size ;
msgs [ 1 ] . buf = ( u8 * ) & recv_buf ;
msgs [ 1 ] . flags = I2C_M_RD ;
ret = i2c_transfer ( client - > adapter , msgs , ARRAY_SIZE ( msgs ) ) ;
if ( ret ! = ARRAY_SIZE ( msgs ) ) {
dev_err ( dev , " Failed to read register, ret = %d \n " , ret ) ;
return ret < 0 ? ret : - EIO ;
}
* value = le32_to_cpu ( recv_buf ) ;
return 0 ;
}
/* get suggested pre_div valuce from mclk frequency */
static unsigned int get_div_from_mclk ( unsigned int mclk )
{
unsigned int div = 8 ;
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( mclk_pre_div ) ; i + + ) {
if ( mclk < = mclk_pre_div [ i ] . mclk ) {
div = mclk_pre_div [ i ] . div ;
break ;
}
}
return div ;
}
static int cx2072x_config_pll ( struct cx2072x_priv * cx2072x )
{
struct device * dev = cx2072x - > dev ;
unsigned int pre_div ;
unsigned int pre_div_val ;
unsigned int pll_input ;
unsigned int pll_output ;
unsigned int int_div ;
unsigned int frac_div ;
u64 frac_num ;
unsigned int frac ;
unsigned int sample_rate = cx2072x - > sample_rate ;
int pt_sample_per_sync = 2 ;
int pt_clock_per_sample = 96 ;
switch ( sample_rate ) {
case 48000 :
case 32000 :
case 24000 :
case 16000 :
break ;
case 96000 :
pt_sample_per_sync = 1 ;
pt_clock_per_sample = 48 ;
break ;
case 192000 :
pt_sample_per_sync = 0 ;
pt_clock_per_sample = 24 ;
break ;
default :
dev_err ( dev , " Unsupported sample rate %d \n " , sample_rate ) ;
return - EINVAL ;
}
/* Configure PLL settings */
pre_div = get_div_from_mclk ( cx2072x - > mclk_rate ) ;
pll_input = cx2072x - > mclk_rate / pre_div ;
pll_output = sample_rate * 3072 ;
int_div = pll_output / pll_input ;
frac_div = pll_output - ( int_div * pll_input ) ;
if ( frac_div ) {
frac_div * = 1000 ;
frac_div / = pll_input ;
2019-05-25 01:25:51 +03:00
frac_num = ( u64 ) ( 4000 + frac_div ) * ( ( 1 < < 20 ) - 4 ) ;
2019-05-21 09:26:52 +03:00
do_div ( frac_num , 7 ) ;
frac = ( ( u32 ) frac_num + 499 ) / 1000 ;
}
pre_div_val = ( pre_div - 1 ) * 2 ;
regmap_write ( cx2072x - > regmap , CX2072X_ANALOG_TEST4 ,
0x40 | ( pre_div_val < < 8 ) ) ;
if ( frac_div = = 0 ) {
/* Int mode */
regmap_write ( cx2072x - > regmap , CX2072X_ANALOG_TEST7 , 0x100 ) ;
} else {
/* frac mode */
regmap_write ( cx2072x - > regmap , CX2072X_ANALOG_TEST6 ,
frac & 0xfff ) ;
regmap_write ( cx2072x - > regmap , CX2072X_ANALOG_TEST7 ,
( u8 ) ( frac > > 12 ) ) ;
}
int_div - - ;
regmap_write ( cx2072x - > regmap , CX2072X_ANALOG_TEST8 , int_div ) ;
/* configure PLL tracking */
if ( frac_div = = 0 ) {
/* disable PLL tracking */
regmap_write ( cx2072x - > regmap , CX2072X_DIGITAL_TEST16 , 0x00 ) ;
} else {
/* configure and enable PLL tracking */
regmap_write ( cx2072x - > regmap , CX2072X_DIGITAL_TEST16 ,
( pt_sample_per_sync < < 4 ) & 0xf0 ) ;
regmap_write ( cx2072x - > regmap , CX2072X_DIGITAL_TEST17 ,
pt_clock_per_sample ) ;
regmap_write ( cx2072x - > regmap , CX2072X_DIGITAL_TEST18 ,
pt_clock_per_sample * 3 / 2 ) ;
regmap_write ( cx2072x - > regmap , CX2072X_DIGITAL_TEST19 , 0x01 ) ;
regmap_write ( cx2072x - > regmap , CX2072X_DIGITAL_TEST20 , 0x02 ) ;
regmap_update_bits ( cx2072x - > regmap , CX2072X_DIGITAL_TEST16 ,
0x01 , 0x01 ) ;
}
return 0 ;
}
static int cx2072x_config_i2spcm ( struct cx2072x_priv * cx2072x )
{
struct device * dev = cx2072x - > dev ;
unsigned int bclk_rate = 0 ;
int is_i2s = 0 ;
int has_one_bit_delay = 0 ;
int is_frame_inv = 0 ;
int is_bclk_inv = 0 ;
2019-05-25 00:44:19 +03:00
int pulse_len ;
2019-05-21 09:26:52 +03:00
int frame_len = cx2072x - > frame_size ;
int sample_size = cx2072x - > sample_size ;
int i2s_right_slot ;
int i2s_right_pause_interval = 0 ;
int i2s_right_pause_pos ;
int is_big_endian = 1 ;
u64 div ;
unsigned int mod ;
union cx2072x_reg_i2spcm_ctrl_reg1 reg1 ;
union cx2072x_reg_i2spcm_ctrl_reg2 reg2 ;
union cx2072x_reg_i2spcm_ctrl_reg3 reg3 ;
union cx2072x_reg_i2spcm_ctrl_reg4 reg4 ;
union cx2072x_reg_i2spcm_ctrl_reg5 reg5 ;
union cx2072x_reg_i2spcm_ctrl_reg6 reg6 ;
union cx2072x_reg_digital_bios_test2 regdbt2 ;
const unsigned int fmt = cx2072x - > dai_fmt ;
if ( frame_len < = 0 ) {
dev_err ( dev , " Incorrect frame len %d \n " , frame_len ) ;
return - EINVAL ;
}
if ( sample_size < = 0 ) {
dev_err ( dev , " Incorrect sample size %d \n " , sample_size ) ;
return - EINVAL ;
}
dev_dbg ( dev , " config_i2spcm set_dai_fmt- %08x \n " , fmt ) ;
regdbt2 . ulval = 0xac ;
/* set master/slave */
switch ( fmt & SND_SOC_DAIFMT_MASTER_MASK ) {
case SND_SOC_DAIFMT_CBM_CFM :
reg2 . r . tx_master = 1 ;
reg3 . r . rx_master = 1 ;
dev_dbg ( dev , " Sets Master mode \n " ) ;
break ;
case SND_SOC_DAIFMT_CBS_CFS :
reg2 . r . tx_master = 0 ;
reg3 . r . rx_master = 0 ;
dev_dbg ( dev , " Sets Slave mode \n " ) ;
break ;
default :
dev_err ( dev , " Unsupported DAI master mode \n " ) ;
return - EINVAL ;
}
/* set format */
switch ( fmt & SND_SOC_DAIFMT_FORMAT_MASK ) {
case SND_SOC_DAIFMT_I2S :
is_i2s = 1 ;
has_one_bit_delay = 1 ;
pulse_len = frame_len / 2 ;
break ;
case SND_SOC_DAIFMT_RIGHT_J :
is_i2s = 1 ;
pulse_len = frame_len / 2 ;
break ;
case SND_SOC_DAIFMT_LEFT_J :
is_i2s = 1 ;
pulse_len = frame_len / 2 ;
break ;
default :
dev_err ( dev , " Unsupported DAI format \n " ) ;
return - EINVAL ;
}
/* clock inversion */
switch ( fmt & SND_SOC_DAIFMT_INV_MASK ) {
case SND_SOC_DAIFMT_NB_NF :
is_frame_inv = is_i2s ;
is_bclk_inv = is_i2s ;
break ;
case SND_SOC_DAIFMT_IB_IF :
is_frame_inv = ! is_i2s ;
is_bclk_inv = ! is_i2s ;
break ;
case SND_SOC_DAIFMT_IB_NF :
is_frame_inv = is_i2s ;
is_bclk_inv = ! is_i2s ;
break ;
case SND_SOC_DAIFMT_NB_IF :
is_frame_inv = ! is_i2s ;
is_bclk_inv = is_i2s ;
break ;
default :
dev_err ( dev , " Unsupported DAI clock inversion \n " ) ;
return - EINVAL ;
}
reg1 . r . rx_data_one_line = 1 ;
reg1 . r . tx_data_one_line = 1 ;
if ( is_i2s ) {
i2s_right_slot = ( frame_len / 2 ) / BITS_PER_SLOT ;
i2s_right_pause_interval = ( frame_len / 2 ) % BITS_PER_SLOT ;
i2s_right_pause_pos = i2s_right_slot * BITS_PER_SLOT ;
}
reg1 . r . rx_ws_pol = is_frame_inv ;
reg1 . r . rx_ws_wid = pulse_len - 1 ;
reg1 . r . rx_frm_len = frame_len / BITS_PER_SLOT - 1 ;
reg1 . r . rx_sa_size = ( sample_size / BITS_PER_SLOT ) - 1 ;
reg1 . r . tx_ws_pol = reg1 . r . rx_ws_pol ;
reg1 . r . tx_ws_wid = pulse_len - 1 ;
reg1 . r . tx_frm_len = reg1 . r . rx_frm_len ;
reg1 . r . tx_sa_size = reg1 . r . rx_sa_size ;
reg2 . r . tx_endian_sel = ! is_big_endian ;
reg2 . r . tx_dstart_dly = has_one_bit_delay ;
if ( cx2072x - > en_aec_ref )
reg2 . r . tx_dstart_dly = 0 ;
reg3 . r . rx_endian_sel = ! is_big_endian ;
reg3 . r . rx_dstart_dly = has_one_bit_delay ;
reg4 . ulval = 0 ;
if ( is_i2s ) {
reg2 . r . tx_slot_1 = 0 ;
reg2 . r . tx_slot_2 = i2s_right_slot ;
reg3 . r . rx_slot_1 = 0 ;
if ( cx2072x - > en_aec_ref )
reg3 . r . rx_slot_2 = 0 ;
else
reg3 . r . rx_slot_2 = i2s_right_slot ;
reg6 . r . rx_pause_start_pos = i2s_right_pause_pos ;
reg6 . r . rx_pause_cycles = i2s_right_pause_interval ;
reg6 . r . tx_pause_start_pos = i2s_right_pause_pos ;
reg6 . r . tx_pause_cycles = i2s_right_pause_interval ;
} else {
dev_err ( dev , " TDM mode is not implemented yet \n " ) ;
return - EINVAL ;
}
regdbt2 . r . i2s_bclk_invert = is_bclk_inv ;
/* Configures the BCLK output */
bclk_rate = cx2072x - > sample_rate * frame_len ;
reg5 . r . i2s_pcm_clk_div_chan_en = 0 ;
/* Disables bclk output before setting new value */
regmap_write ( cx2072x - > regmap , CX2072X_I2SPCM_CONTROL5 , 0 ) ;
if ( reg2 . r . tx_master ) {
/* Configures BCLK rate */
div = PLL_OUT_HZ_48 ;
mod = do_div ( div , bclk_rate ) ;
if ( mod ) {
dev_err ( dev , " Unsupported BCLK %dHz \n " , bclk_rate ) ;
return - EINVAL ;
}
dev_dbg ( dev , " enables BCLK %dHz output \n " , bclk_rate ) ;
reg5 . r . i2s_pcm_clk_div = ( u32 ) div - 1 ;
reg5 . r . i2s_pcm_clk_div_chan_en = 1 ;
}
regmap_write ( cx2072x - > regmap , CX2072X_I2SPCM_CONTROL1 , reg1 . ulval ) ;
regmap_update_bits ( cx2072x - > regmap , CX2072X_I2SPCM_CONTROL2 , 0xffffffc0 ,
reg2 . ulval ) ;
regmap_update_bits ( cx2072x - > regmap , CX2072X_I2SPCM_CONTROL3 , 0xffffffc0 ,
reg3 . ulval ) ;
regmap_write ( cx2072x - > regmap , CX2072X_I2SPCM_CONTROL4 , reg4 . ulval ) ;
regmap_write ( cx2072x - > regmap , CX2072X_I2SPCM_CONTROL6 , reg6 . ulval ) ;
regmap_write ( cx2072x - > regmap , CX2072X_I2SPCM_CONTROL5 , reg5 . ulval ) ;
regmap_write ( cx2072x - > regmap , CX2072X_DIGITAL_BIOS_TEST2 ,
regdbt2 . ulval ) ;
return 0 ;
}
static int afg_power_ev ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * kcontrol , int event )
{
struct snd_soc_component * codec = snd_soc_dapm_to_component ( w - > dapm ) ;
struct cx2072x_priv * cx2072x = snd_soc_component_get_drvdata ( codec ) ;
switch ( event ) {
case SND_SOC_DAPM_POST_PMU :
regmap_update_bits ( cx2072x - > regmap , CX2072X_DIGITAL_BIOS_TEST0 ,
0x00 , 0x10 ) ;
break ;
case SND_SOC_DAPM_PRE_PMD :
regmap_update_bits ( cx2072x - > regmap , CX2072X_DIGITAL_BIOS_TEST0 ,
0x10 , 0x10 ) ;
break ;
}
return 0 ;
}
static const struct snd_kcontrol_new cx2072x_snd_controls [ ] = {
SOC_DOUBLE_R_TLV ( " PortD Boost Volume " , CX2072X_PORTD_GAIN_LEFT ,
CX2072X_PORTD_GAIN_RIGHT , 0 , 3 , 0 , boost_tlv ) ,
SOC_DOUBLE_R_TLV ( " PortC Boost Volume " , CX2072X_PORTC_GAIN_LEFT ,
CX2072X_PORTC_GAIN_RIGHT , 0 , 3 , 0 , boost_tlv ) ,
SOC_DOUBLE_R_TLV ( " PortB Boost Volume " , CX2072X_PORTB_GAIN_LEFT ,
CX2072X_PORTB_GAIN_RIGHT , 0 , 3 , 0 , boost_tlv ) ,
SOC_DOUBLE_R_TLV ( " PortD ADC1 Volume " , CX2072X_ADC1_AMP_GAIN_LEFT_1 ,
CX2072X_ADC1_AMP_GAIN_RIGHT_1 , 0 , 0x4a , 0 , adc_tlv ) ,
SOC_DOUBLE_R_TLV ( " PortC ADC1 Volume " , CX2072X_ADC1_AMP_GAIN_LEFT_2 ,
CX2072X_ADC1_AMP_GAIN_RIGHT_2 , 0 , 0x4a , 0 , adc_tlv ) ,
SOC_DOUBLE_R_TLV ( " PortB ADC1 Volume " , CX2072X_ADC1_AMP_GAIN_LEFT_0 ,
CX2072X_ADC1_AMP_GAIN_RIGHT_0 , 0 , 0x4a , 0 , adc_tlv ) ,
SOC_DOUBLE_R_TLV ( " DAC1 Volume " , CX2072X_DAC1_AMP_GAIN_LEFT ,
CX2072X_DAC1_AMP_GAIN_RIGHT , 0 , 0x4a , 0 , dac_tlv ) ,
SOC_DOUBLE_R ( " DAC1 Switch " , CX2072X_DAC1_AMP_GAIN_LEFT ,
CX2072X_DAC1_AMP_GAIN_RIGHT , 7 , 1 , 0 ) ,
SOC_DOUBLE_R_TLV ( " DAC2 Volume " , CX2072X_DAC2_AMP_GAIN_LEFT ,
CX2072X_DAC2_AMP_GAIN_RIGHT , 0 , 0x4a , 0 , dac_tlv ) ,
SOC_SINGLE_TLV ( " HPF Freq " , CX2072X_CODEC_TEST9 , 0 , 0x3f , 0 , hpf_tlv ) ,
SOC_DOUBLE ( " HPF Switch " , CX2072X_CODEC_TEST9 , 8 , 9 , 1 , 1 ) ,
SOC_SINGLE ( " PortA HP Amp Switch " , CX2072X_PORTA_PIN_CTRL , 7 , 1 , 0 ) ,
} ;
static int cx2072x_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params ,
struct snd_soc_dai * dai )
{
struct snd_soc_component * codec = dai - > component ;
struct cx2072x_priv * cx2072x = snd_soc_component_get_drvdata ( codec ) ;
struct device * dev = codec - > dev ;
const unsigned int sample_rate = params_rate ( params ) ;
int sample_size , frame_size ;
/* Data sizes if not using TDM */
sample_size = params_width ( params ) ;
if ( sample_size < 0 )
return sample_size ;
frame_size = snd_soc_params_to_frame_size ( params ) ;
if ( frame_size < 0 )
return frame_size ;
if ( cx2072x - > mclk_rate = = 0 ) {
2019-05-25 23:32:44 +03:00
dev_err ( dev , " Master clock rate is not configured \n " ) ;
2019-05-21 09:26:52 +03:00
return - EINVAL ;
}
if ( cx2072x - > bclk_ratio )
frame_size = cx2072x - > bclk_ratio ;
switch ( sample_rate ) {
case 48000 :
case 32000 :
case 24000 :
case 16000 :
case 96000 :
case 192000 :
break ;
default :
dev_err ( dev , " Unsupported sample rate %d \n " , sample_rate ) ;
return - EINVAL ;
}
dev_dbg ( dev , " Sample size %d bits, frame = %d bits, rate = %d Hz \n " ,
sample_size , frame_size , sample_rate ) ;
cx2072x - > frame_size = frame_size ;
cx2072x - > sample_size = sample_size ;
cx2072x - > sample_rate = sample_rate ;
if ( dai - > id = = CX2072X_DAI_DSP ) {
cx2072x - > en_aec_ref = true ;
dev_dbg ( cx2072x - > dev , " enables aec reference \n " ) ;
regmap_write ( cx2072x - > regmap ,
CX2072X_ADC1_CONNECTION_SELECT_CONTROL , 3 ) ;
}
if ( cx2072x - > pll_changed ) {
cx2072x_config_pll ( cx2072x ) ;
cx2072x - > pll_changed = false ;
}
if ( cx2072x - > i2spcm_changed ) {
cx2072x_config_i2spcm ( cx2072x ) ;
cx2072x - > i2spcm_changed = false ;
}
return 0 ;
}
static int cx2072x_set_dai_bclk_ratio ( struct snd_soc_dai * dai ,
unsigned int ratio )
{
struct snd_soc_component * codec = dai - > component ;
struct cx2072x_priv * cx2072x = snd_soc_component_get_drvdata ( codec ) ;
cx2072x - > bclk_ratio = ratio ;
return 0 ;
}
static int cx2072x_set_dai_sysclk ( struct snd_soc_dai * dai , int clk_id ,
unsigned int freq , int dir )
{
struct snd_soc_component * codec = dai - > component ;
struct cx2072x_priv * cx2072x = snd_soc_component_get_drvdata ( codec ) ;
if ( clk_set_rate ( cx2072x - > mclk , freq ) ) {
dev_err ( codec - > dev , " set clk rate failed \n " ) ;
return - EINVAL ;
}
cx2072x - > mclk_rate = freq ;
return 0 ;
}
static int cx2072x_set_dai_fmt ( struct snd_soc_dai * dai , unsigned int fmt )
{
struct snd_soc_component * codec = dai - > component ;
struct cx2072x_priv * cx2072x = snd_soc_component_get_drvdata ( codec ) ;
struct device * dev = codec - > dev ;
dev_dbg ( dev , " set_dai_fmt- %08x \n " , fmt ) ;
/* set master/slave */
switch ( fmt & SND_SOC_DAIFMT_MASTER_MASK ) {
case SND_SOC_DAIFMT_CBM_CFM :
case SND_SOC_DAIFMT_CBS_CFS :
break ;
default :
dev_err ( dev , " Unsupported DAI master mode \n " ) ;
return - EINVAL ;
}
/* set format */
switch ( fmt & SND_SOC_DAIFMT_FORMAT_MASK ) {
case SND_SOC_DAIFMT_I2S :
case SND_SOC_DAIFMT_RIGHT_J :
case SND_SOC_DAIFMT_LEFT_J :
break ;
default :
dev_err ( dev , " Unsupported DAI format \n " ) ;
return - EINVAL ;
}
/* clock inversion */
switch ( fmt & SND_SOC_DAIFMT_INV_MASK ) {
case SND_SOC_DAIFMT_NB_NF :
case SND_SOC_DAIFMT_IB_IF :
case SND_SOC_DAIFMT_IB_NF :
case SND_SOC_DAIFMT_NB_IF :
break ;
default :
dev_err ( dev , " Unsupported DAI clock inversion \n " ) ;
return - EINVAL ;
}
cx2072x - > dai_fmt = fmt ;
return 0 ;
}
static const struct snd_kcontrol_new portaouten_ctl =
SOC_DAPM_SINGLE ( " Switch " , CX2072X_PORTA_PIN_CTRL , 6 , 1 , 0 ) ;
static const struct snd_kcontrol_new porteouten_ctl =
SOC_DAPM_SINGLE ( " Switch " , CX2072X_PORTE_PIN_CTRL , 6 , 1 , 0 ) ;
static const struct snd_kcontrol_new portgouten_ctl =
SOC_DAPM_SINGLE ( " Switch " , CX2072X_PORTG_PIN_CTRL , 6 , 1 , 0 ) ;
static const struct snd_kcontrol_new portmouten_ctl =
SOC_DAPM_SINGLE ( " Switch " , CX2072X_PORTM_PIN_CTRL , 6 , 1 , 0 ) ;
static const struct snd_kcontrol_new portbinen_ctl =
SOC_DAPM_SINGLE ( " Switch " , CX2072X_PORTB_PIN_CTRL , 5 , 1 , 0 ) ;
static const struct snd_kcontrol_new portcinen_ctl =
SOC_DAPM_SINGLE ( " Switch " , CX2072X_PORTC_PIN_CTRL , 5 , 1 , 0 ) ;
static const struct snd_kcontrol_new portdinen_ctl =
SOC_DAPM_SINGLE ( " Switch " , CX2072X_PORTD_PIN_CTRL , 5 , 1 , 0 ) ;
static const struct snd_kcontrol_new porteinen_ctl =
SOC_DAPM_SINGLE ( " Switch " , CX2072X_PORTE_PIN_CTRL , 5 , 1 , 0 ) ;
static const struct snd_kcontrol_new i2sadc1l_ctl =
SOC_DAPM_SINGLE ( " Switch " , CX2072X_I2SPCM_CONTROL2 , 0 , 1 , 0 ) ;
static const struct snd_kcontrol_new i2sadc1r_ctl =
SOC_DAPM_SINGLE ( " Switch " , CX2072X_I2SPCM_CONTROL2 , 1 , 1 , 0 ) ;
static const struct snd_kcontrol_new i2sadc2l_ctl =
SOC_DAPM_SINGLE ( " Switch " , CX2072X_I2SPCM_CONTROL2 , 2 , 1 , 0 ) ;
static const struct snd_kcontrol_new i2sadc2r_ctl =
SOC_DAPM_SINGLE ( " Switch " , CX2072X_I2SPCM_CONTROL2 , 3 , 1 , 0 ) ;
static const struct snd_kcontrol_new i2sdac1l_ctl =
SOC_DAPM_SINGLE ( " Switch " , CX2072X_I2SPCM_CONTROL3 , 0 , 1 , 0 ) ;
static const struct snd_kcontrol_new i2sdac1r_ctl =
SOC_DAPM_SINGLE ( " Switch " , CX2072X_I2SPCM_CONTROL3 , 1 , 1 , 0 ) ;
static const struct snd_kcontrol_new i2sdac2l_ctl =
SOC_DAPM_SINGLE ( " Switch " , CX2072X_I2SPCM_CONTROL3 , 2 , 1 , 0 ) ;
static const struct snd_kcontrol_new i2sdac2r_ctl =
SOC_DAPM_SINGLE ( " Switch " , CX2072X_I2SPCM_CONTROL3 , 3 , 1 , 0 ) ;
static const char * const dac_enum_text [ ] = {
" DAC1 Switch " , " DAC2 Switch " ,
} ;
static const struct soc_enum porta_dac_enum =
SOC_ENUM_SINGLE ( CX2072X_PORTA_CONNECTION_SELECT_CTRL , 0 , 2 , dac_enum_text ) ;
static const struct snd_kcontrol_new porta_mux =
SOC_DAPM_ENUM ( " PortA Mux " , porta_dac_enum ) ;
static const struct soc_enum portg_dac_enum =
SOC_ENUM_SINGLE ( CX2072X_PORTG_CONNECTION_SELECT_CTRL , 0 , 2 , dac_enum_text ) ;
static const struct snd_kcontrol_new portg_mux =
SOC_DAPM_ENUM ( " PortG Mux " , portg_dac_enum ) ;
static const struct soc_enum porte_dac_enum =
SOC_ENUM_SINGLE ( CX2072X_PORTE_CONNECTION_SELECT_CTRL , 0 , 2 , dac_enum_text ) ;
static const struct snd_kcontrol_new porte_mux =
SOC_DAPM_ENUM ( " PortE Mux " , porte_dac_enum ) ;
static const struct soc_enum portm_dac_enum =
SOC_ENUM_SINGLE ( CX2072X_PORTM_CONNECTION_SELECT_CTRL , 0 , 2 , dac_enum_text ) ;
static const struct snd_kcontrol_new portm_mux =
SOC_DAPM_ENUM ( " PortM Mux " , portm_dac_enum ) ;
static const char * const adc1in_sel_text [ ] = {
" PortB Switch " , " PortD Switch " , " PortC Switch " , " Widget15 Switch " ,
" PortE Switch " , " PortF Switch " , " PortH Switch "
} ;
static const struct soc_enum adc1in_sel_enum =
SOC_ENUM_SINGLE ( CX2072X_ADC1_CONNECTION_SELECT_CONTROL , 0 , 7 , adc1in_sel_text ) ;
static const struct snd_kcontrol_new adc1_mux =
SOC_DAPM_ENUM ( " ADC1 Mux " , adc1in_sel_enum ) ;
static const char * const adc2in_sel_text [ ] = {
" PortC Switch " , " Widget15 Switch " , " PortH Switch "
} ;
static const struct soc_enum adc2in_sel_enum =
SOC_ENUM_SINGLE ( CX2072X_ADC2_CONNECTION_SELECT_CONTROL , 0 , 3 , adc2in_sel_text ) ;
static const struct snd_kcontrol_new adc2_mux =
SOC_DAPM_ENUM ( " ADC2 Mux " , adc2in_sel_enum ) ;
static const struct snd_kcontrol_new wid15_mix [ ] = {
SOC_DAPM_SINGLE ( " DAC1L Switch " , CX2072X_MIXER_GAIN_LEFT_0 , 7 , 1 , 1 ) ,
SOC_DAPM_SINGLE ( " DAC1R Switch " , CX2072X_MIXER_GAIN_RIGHT_0 , 7 , 1 , 1 ) ,
SOC_DAPM_SINGLE ( " DAC2L Switch " , CX2072X_MIXER_GAIN_LEFT_1 , 7 , 1 , 1 ) ,
SOC_DAPM_SINGLE ( " DAC2R Switch " , CX2072X_MIXER_GAIN_RIGHT_1 , 7 , 1 , 1 ) ,
} ;
# define CX2072X_DAPM_SUPPLY_S(wname, wsubseq, wreg, wshift, wmask, won_val, \
woff_val , wevent , wflags ) \
{ . id = snd_soc_dapm_supply , . name = wname , . kcontrol_news = NULL , \
. num_kcontrols = 0 , . reg = wreg , . shift = wshift , . mask = wmask , \
. on_val = won_val , . off_val = woff_val , \
. subseq = wsubseq , . event = wevent , . event_flags = wflags }
# define CX2072X_DAPM_SWITCH(wname, wreg, wshift, wmask, won_val, woff_val, \
wevent , wflags ) \
{ . id = snd_soc_dapm_switch , . name = wname , . kcontrol_news = NULL , \
. num_kcontrols = 0 , . reg = wreg , . shift = wshift , . mask = wmask , \
. on_val = won_val , . off_val = woff_val , \
. event = wevent , . event_flags = wflags }
# define CX2072X_DAPM_SWITCH(wname, wreg, wshift, wmask, won_val, woff_val, \
wevent , wflags ) \
{ . id = snd_soc_dapm_switch , . name = wname , . kcontrol_news = NULL , \
. num_kcontrols = 0 , . reg = wreg , . shift = wshift , . mask = wmask , \
. on_val = won_val , . off_val = woff_val , \
. event = wevent , . event_flags = wflags }
# define CX2072X_DAPM_REG_E(wid, wname, wreg, wshift, wmask, won_val, woff_val, \
wevent , wflags ) \
{ . id = wid , . name = wname , . kcontrol_news = NULL , . num_kcontrols = 0 , \
. reg = wreg , . shift = wshift , . mask = wmask , \
. on_val = won_val , . off_val = woff_val , \
. event = wevent , . event_flags = wflags }
static const struct snd_soc_dapm_widget cx2072x_dapm_widgets [ ] = {
/*Playback*/
SND_SOC_DAPM_AIF_IN ( " In AIF " , " Playback " , 0 , SND_SOC_NOPM , 0 , 0 ) ,
SND_SOC_DAPM_SWITCH ( " I2S DAC1L " , SND_SOC_NOPM , 0 , 0 , & i2sdac1l_ctl ) ,
SND_SOC_DAPM_SWITCH ( " I2S DAC1R " , SND_SOC_NOPM , 0 , 0 , & i2sdac1r_ctl ) ,
SND_SOC_DAPM_SWITCH ( " I2S DAC2L " , SND_SOC_NOPM , 0 , 0 , & i2sdac2l_ctl ) ,
SND_SOC_DAPM_SWITCH ( " I2S DAC2R " , SND_SOC_NOPM , 0 , 0 , & i2sdac2r_ctl ) ,
SND_SOC_DAPM_REG ( snd_soc_dapm_dac , " DAC1 " , CX2072X_DAC1_POWER_STATE ,
0 , 0xfff , 0x00 , 0x03 ) ,
SND_SOC_DAPM_REG ( snd_soc_dapm_dac , " DAC2 " , CX2072X_DAC2_POWER_STATE ,
0 , 0xfff , 0x00 , 0x03 ) ,
SND_SOC_DAPM_MUX ( " PortA Mux " , SND_SOC_NOPM , 0 , 0 , & porta_mux ) ,
SND_SOC_DAPM_MUX ( " PortG Mux " , SND_SOC_NOPM , 0 , 0 , & portg_mux ) ,
SND_SOC_DAPM_MUX ( " PortE Mux " , SND_SOC_NOPM , 0 , 0 , & porte_mux ) ,
SND_SOC_DAPM_MUX ( " PortM Mux " , SND_SOC_NOPM , 0 , 0 , & portm_mux ) ,
SND_SOC_DAPM_REG ( snd_soc_dapm_supply , " PortA Power " ,
CX2072X_PORTA_POWER_STATE , 0 , 0xfff , 0x00 , 0x03 ) ,
SND_SOC_DAPM_REG ( snd_soc_dapm_supply , " PortM Power " ,
CX2072X_PORTM_POWER_STATE , 0 , 0xfff , 0x00 , 0x03 ) ,
SND_SOC_DAPM_REG ( snd_soc_dapm_supply , " PortG Power " ,
CX2072X_PORTG_POWER_STATE , 0 , 0xfff , 0x00 , 0x03 ) ,
CX2072X_DAPM_SUPPLY_S ( " AFG Power " , 0 , CX2072X_AFG_POWER_STATE ,
0 , 0xfff , 0x00 , 0x03 , afg_power_ev ,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD ) ,
SND_SOC_DAPM_SWITCH ( " PortA Out En " , SND_SOC_NOPM , 0 , 0 ,
& portaouten_ctl ) ,
SND_SOC_DAPM_SWITCH ( " PortE Out En " , SND_SOC_NOPM , 0 , 0 ,
& porteouten_ctl ) ,
SND_SOC_DAPM_SWITCH ( " PortG Out En " , SND_SOC_NOPM , 0 , 0 ,
& portgouten_ctl ) ,
SND_SOC_DAPM_SWITCH ( " PortM Out En " , SND_SOC_NOPM , 0 , 0 ,
& portmouten_ctl ) ,
SND_SOC_DAPM_OUTPUT ( " PORTA " ) ,
SND_SOC_DAPM_OUTPUT ( " PORTG " ) ,
SND_SOC_DAPM_OUTPUT ( " PORTE " ) ,
SND_SOC_DAPM_OUTPUT ( " PORTM " ) ,
SND_SOC_DAPM_OUTPUT ( " AEC REF " ) ,
/*Capture*/
SND_SOC_DAPM_AIF_OUT ( " Out AIF " , " Capture " , 0 , SND_SOC_NOPM , 0 , 0 ) ,
SND_SOC_DAPM_SWITCH ( " I2S ADC1L " , SND_SOC_NOPM , 0 , 0 , & i2sadc1l_ctl ) ,
SND_SOC_DAPM_SWITCH ( " I2S ADC1R " , SND_SOC_NOPM , 0 , 0 , & i2sadc1r_ctl ) ,
SND_SOC_DAPM_SWITCH ( " I2S ADC2L " , SND_SOC_NOPM , 0 , 0 , & i2sadc2l_ctl ) ,
SND_SOC_DAPM_SWITCH ( " I2S ADC2R " , SND_SOC_NOPM , 0 , 0 , & i2sadc2r_ctl ) ,
SND_SOC_DAPM_REG ( snd_soc_dapm_adc , " ADC1 " , CX2072X_ADC1_POWER_STATE ,
0 , 0xff , 0x00 , 0x03 ) ,
SND_SOC_DAPM_REG ( snd_soc_dapm_adc , " ADC2 " , CX2072X_ADC2_POWER_STATE ,
0 , 0xff , 0x00 , 0x03 ) ,
SND_SOC_DAPM_MUX ( " ADC1 Mux " , SND_SOC_NOPM , 0 , 0 , & adc1_mux ) ,
SND_SOC_DAPM_MUX ( " ADC2 Mux " , SND_SOC_NOPM , 0 , 0 , & adc2_mux ) ,
SND_SOC_DAPM_REG ( snd_soc_dapm_supply , " PortB Power " ,
CX2072X_PORTB_POWER_STATE , 0 , 0xfff , 0x00 , 0x03 ) ,
SND_SOC_DAPM_REG ( snd_soc_dapm_supply , " PortC Power " ,
CX2072X_PORTC_POWER_STATE , 0 , 0xfff , 0x00 , 0x03 ) ,
SND_SOC_DAPM_REG ( snd_soc_dapm_supply , " PortD Power " ,
CX2072X_PORTD_POWER_STATE , 0 , 0xfff , 0x00 , 0x03 ) ,
SND_SOC_DAPM_REG ( snd_soc_dapm_supply , " PortE Power " ,
CX2072X_PORTE_POWER_STATE , 0 , 0xfff , 0x00 , 0x03 ) ,
SND_SOC_DAPM_REG ( snd_soc_dapm_supply , " Widget15 Power " ,
CX2072X_MIXER_POWER_STATE , 0 , 0xfff , 0x00 , 0x03 ) ,
SND_SOC_DAPM_MIXER ( " Widget15 Mixer " , SND_SOC_NOPM , 0 , 0 ,
wid15_mix , ARRAY_SIZE ( wid15_mix ) ) ,
SND_SOC_DAPM_SWITCH ( " PortB In En " , SND_SOC_NOPM , 0 , 0 , & portbinen_ctl ) ,
SND_SOC_DAPM_SWITCH ( " PortC In En " , SND_SOC_NOPM , 0 , 0 , & portcinen_ctl ) ,
SND_SOC_DAPM_SWITCH ( " PortD In En " , SND_SOC_NOPM , 0 , 0 , & portdinen_ctl ) ,
SND_SOC_DAPM_SWITCH ( " PortE In En " , SND_SOC_NOPM , 0 , 0 , & porteinen_ctl ) ,
SND_SOC_DAPM_MICBIAS ( " Headset Bias " , CX2072X_ANALOG_TEST11 , 1 , 0 ) ,
SND_SOC_DAPM_MICBIAS ( " PortB Mic Bias " , CX2072X_PORTB_PIN_CTRL , 2 , 0 ) ,
SND_SOC_DAPM_MICBIAS ( " PortD Mic Bias " , CX2072X_PORTD_PIN_CTRL , 2 , 0 ) ,
SND_SOC_DAPM_MICBIAS ( " PortE Mic Bias " , CX2072X_PORTE_PIN_CTRL , 2 , 0 ) ,
SND_SOC_DAPM_INPUT ( " PORTB " ) ,
SND_SOC_DAPM_INPUT ( " PORTC " ) ,
SND_SOC_DAPM_INPUT ( " PORTD " ) ,
SND_SOC_DAPM_INPUT ( " PORTEIN " ) ,
} ;
static const struct snd_soc_dapm_route cx2072x_intercon [ ] = {
/* Playback */
{ " In AIF " , NULL , " AFG Power " } ,
{ " I2S DAC1L " , " Switch " , " In AIF " } ,
{ " I2S DAC1R " , " Switch " , " In AIF " } ,
{ " I2S DAC2L " , " Switch " , " In AIF " } ,
{ " I2S DAC2R " , " Switch " , " In AIF " } ,
{ " DAC1 " , NULL , " I2S DAC1L " } ,
{ " DAC1 " , NULL , " I2S DAC1R " } ,
{ " DAC2 " , NULL , " I2S DAC2L " } ,
{ " DAC2 " , NULL , " I2S DAC2R " } ,
{ " PortA Mux " , " DAC1 Switch " , " DAC1 " } ,
{ " PortA Mux " , " DAC2 Switch " , " DAC2 " } ,
{ " PortG Mux " , " DAC1 Switch " , " DAC1 " } ,
{ " PortG Mux " , " DAC2 Switch " , " DAC2 " } ,
{ " PortE Mux " , " DAC1 Switch " , " DAC1 " } ,
{ " PortE Mux " , " DAC2 Switch " , " DAC2 " } ,
{ " PortM Mux " , " DAC1 Switch " , " DAC1 " } ,
{ " PortM Mux " , " DAC2 Switch " , " DAC2 " } ,
{ " Widget15 Mixer " , " DAC1L Switch " , " DAC1 " } ,
{ " Widget15 Mixer " , " DAC1R Switch " , " DAC2 " } ,
{ " Widget15 Mixer " , " DAC2L Switch " , " DAC1 " } ,
{ " Widget15 Mixer " , " DAC2R Switch " , " DAC2 " } ,
{ " Widget15 Mixer " , NULL , " Widget15 Power " } ,
{ " PortA Out En " , " Switch " , " PortA Mux " } ,
{ " PortG Out En " , " Switch " , " PortG Mux " } ,
{ " PortE Out En " , " Switch " , " PortE Mux " } ,
{ " PortM Out En " , " Switch " , " PortM Mux " } ,
{ " PortA Mux " , NULL , " PortA Power " } ,
{ " PortG Mux " , NULL , " PortG Power " } ,
{ " PortE Mux " , NULL , " PortE Power " } ,
{ " PortM Mux " , NULL , " PortM Power " } ,
{ " PortA Out En " , NULL , " PortA Power " } ,
{ " PortG Out En " , NULL , " PortG Power " } ,
{ " PortE Out En " , NULL , " PortE Power " } ,
{ " PortM Out En " , NULL , " PortM Power " } ,
{ " PORTA " , NULL , " PortA Out En " } ,
{ " PORTG " , NULL , " PortG Out En " } ,
{ " PORTE " , NULL , " PortE Out En " } ,
{ " PORTM " , NULL , " PortM Out En " } ,
/* Capture */
{ " PORTD " , NULL , " Headset Bias " } ,
{ " PortB In En " , " Switch " , " PORTB " } ,
{ " PortC In En " , " Switch " , " PORTC " } ,
{ " PortD In En " , " Switch " , " PORTD " } ,
{ " PortE In En " , " Switch " , " PORTEIN " } ,
{ " ADC1 Mux " , " PortB Switch " , " PortB In En " } ,
{ " ADC1 Mux " , " PortC Switch " , " PortC In En " } ,
{ " ADC1 Mux " , " PortD Switch " , " PortD In En " } ,
{ " ADC1 Mux " , " PortE Switch " , " PortE In En " } ,
{ " ADC1 Mux " , " Widget15 Switch " , " Widget15 Mixer " } ,
{ " ADC2 Mux " , " PortC Switch " , " PortC In En " } ,
{ " ADC2 Mux " , " Widget15 Switch " , " Widget15 Mixer " } ,
{ " ADC1 " , NULL , " ADC1 Mux " } ,
{ " ADC2 " , NULL , " ADC2 Mux " } ,
{ " I2S ADC1L " , " Switch " , " ADC1 " } ,
{ " I2S ADC1R " , " Switch " , " ADC1 " } ,
{ " I2S ADC2L " , " Switch " , " ADC2 " } ,
{ " I2S ADC2R " , " Switch " , " ADC2 " } ,
{ " Out AIF " , NULL , " I2S ADC1L " } ,
{ " Out AIF " , NULL , " I2S ADC1R " } ,
{ " Out AIF " , NULL , " I2S ADC2L " } ,
{ " Out AIF " , NULL , " I2S ADC2R " } ,
{ " Out AIF " , NULL , " AFG Power " } ,
{ " AEC REF " , NULL , " Out AIF " } ,
{ " PortB In En " , NULL , " PortB Power " } ,
{ " PortC In En " , NULL , " PortC Power " } ,
{ " PortD In En " , NULL , " PortD Power " } ,
{ " PortE In En " , NULL , " PortE Power " } ,
} ;
static int cx2072x_set_bias_level ( struct snd_soc_component * codec ,
enum snd_soc_bias_level level )
{
struct cx2072x_priv * cx2072x = snd_soc_component_get_drvdata ( codec ) ;
const enum snd_soc_bias_level old_level =
snd_soc_component_get_bias_level ( codec ) ;
if ( level = = SND_SOC_BIAS_STANDBY & & old_level = = SND_SOC_BIAS_OFF )
regmap_write ( cx2072x - > regmap , CX2072X_AFG_POWER_STATE , 0 ) ;
else if ( level = = SND_SOC_BIAS_OFF & & old_level ! = SND_SOC_BIAS_OFF )
regmap_write ( cx2072x - > regmap , CX2072X_AFG_POWER_STATE , 3 ) ;
return 0 ;
}
/*
* FIXME : the whole jack detection code below is pretty platform - specific ;
* it has lots of implicit assumptions about the pins , etc .
* However , since we have no other code and reference , take this hard - coded
* setup for now . Once when we have different platform implementations ,
* this needs to be rewritten in a more generic form , or moving into the
* platform data .
*/
static void cx2072x_enable_jack_detect ( struct snd_soc_component * codec )
{
struct cx2072x_priv * cx2072x = snd_soc_component_get_drvdata ( codec ) ;
struct snd_soc_dapm_context * dapm = snd_soc_component_get_dapm ( codec ) ;
/* No-sticky input type */
regmap_write ( cx2072x - > regmap , CX2072X_GPIO_STICKY_MASK , 0x1f ) ;
/* Use GPOI0 as interrupt pin */
regmap_write ( cx2072x - > regmap , CX2072X_UM_INTERRUPT_CRTL_E , 0x12 < < 24 ) ;
/* Enables unsolitited message on PortA */
regmap_write ( cx2072x - > regmap , CX2072X_PORTA_UNSOLICITED_RESPONSE , 0x80 ) ;
/* support both nokia and apple headset set. Monitor time = 275 ms */
regmap_write ( cx2072x - > regmap , CX2072X_DIGITAL_TEST15 , 0x73 ) ;
/* Disable TIP detection */
regmap_write ( cx2072x - > regmap , CX2072X_ANALOG_TEST12 , 0x300 ) ;
/* Switch MusicD3Live pin to GPIO */
regmap_write ( cx2072x - > regmap , CX2072X_DIGITAL_TEST1 , 0 ) ;
snd_soc_dapm_mutex_lock ( dapm ) ;
snd_soc_dapm_force_enable_pin_unlocked ( dapm , " PORTD " ) ;
snd_soc_dapm_force_enable_pin_unlocked ( dapm , " Headset Bias " ) ;
snd_soc_dapm_force_enable_pin_unlocked ( dapm , " PortD Mic Bias " ) ;
snd_soc_dapm_mutex_unlock ( dapm ) ;
}
static void cx2072x_disable_jack_detect ( struct snd_soc_component * codec )
{
struct cx2072x_priv * cx2072x = snd_soc_component_get_drvdata ( codec ) ;
regmap_write ( cx2072x - > regmap , CX2072X_UM_INTERRUPT_CRTL_E , 0 ) ;
regmap_write ( cx2072x - > regmap , CX2072X_PORTA_UNSOLICITED_RESPONSE , 0 ) ;
}
static int cx2072x_jack_status_check ( void * data )
{
struct snd_soc_component * codec = data ;
struct cx2072x_priv * cx2072x = snd_soc_component_get_drvdata ( codec ) ;
unsigned int jack ;
unsigned int type = 0 ;
int state = 0 ;
mutex_lock ( & cx2072x - > lock ) ;
regmap_read ( cx2072x - > regmap , CX2072X_PORTA_PIN_SENSE , & jack ) ;
jack = jack > > 24 ;
regmap_read ( cx2072x - > regmap , CX2072X_DIGITAL_TEST11 , & type ) ;
if ( jack = = 0x80 ) {
type = type > > 8 ;
if ( type & 0x8 ) {
/* Apple headset */
state | = SND_JACK_HEADSET ;
if ( type & 0x2 )
state | = SND_JACK_BTN_0 ;
} else {
2021-03-12 21:22:29 +03:00
/*
* Nokia headset ( type & 0x4 ) and
* regular Headphone
*/
2019-05-21 09:26:52 +03:00
state | = SND_JACK_HEADPHONE ;
}
}
/* clear interrupt */
regmap_write ( cx2072x - > regmap , CX2072X_UM_INTERRUPT_CRTL_E , 0x12 < < 24 ) ;
mutex_unlock ( & cx2072x - > lock ) ;
dev_dbg ( codec - > dev , " CX2072X_HSDETECT type=0x%X,Jack state = %x \n " ,
type , state ) ;
return state ;
}
static const struct snd_soc_jack_gpio cx2072x_jack_gpio = {
. name = " headset " ,
. report = SND_JACK_HEADSET | SND_JACK_BTN_0 ,
. debounce_time = 150 ,
. wake = true ,
. jack_status_check = cx2072x_jack_status_check ,
} ;
static int cx2072x_set_jack ( struct snd_soc_component * codec ,
struct snd_soc_jack * jack , void * data )
{
struct cx2072x_priv * cx2072x = snd_soc_component_get_drvdata ( codec ) ;
int err ;
if ( ! jack ) {
cx2072x_disable_jack_detect ( codec ) ;
return 0 ;
}
if ( ! cx2072x - > jack_gpio . gpiod_dev ) {
cx2072x - > jack_gpio = cx2072x_jack_gpio ;
cx2072x - > jack_gpio . gpiod_dev = codec - > dev ;
cx2072x - > jack_gpio . data = codec ;
err = snd_soc_jack_add_gpios ( jack , 1 , & cx2072x - > jack_gpio ) ;
if ( err ) {
cx2072x - > jack_gpio . gpiod_dev = NULL ;
return err ;
}
}
cx2072x_enable_jack_detect ( codec ) ;
return 0 ;
}
static int cx2072x_probe ( struct snd_soc_component * codec )
{
struct cx2072x_priv * cx2072x = snd_soc_component_get_drvdata ( codec ) ;
cx2072x - > codec = codec ;
/*
* FIXME : below is , again , a very platform - specific init sequence ,
* but we keep the code here just for simplicity . It seems that all
* existing hardware implementations require this , so there is no very
* much reason to move this out of the codec driver to the platform
* data .
* But of course it ' s no " right " thing ; if you are a good boy , don ' t
* read and follow the code like this !
*/
pm_runtime_get_sync ( codec - > dev ) ;
regmap_write ( cx2072x - > regmap , CX2072X_AFG_POWER_STATE , 0 ) ;
regmap_multi_reg_write ( cx2072x - > regmap , cx2072x_reg_init ,
ARRAY_SIZE ( cx2072x_reg_init ) ) ;
2019-10-24 18:16:03 +03:00
/* configure PortC as input device */
2019-05-21 09:26:52 +03:00
regmap_update_bits ( cx2072x - > regmap , CX2072X_PORTC_PIN_CTRL ,
0x20 , 0x20 ) ;
regmap_update_bits ( cx2072x - > regmap , CX2072X_DIGITAL_BIOS_TEST2 ,
0x84 , 0xff ) ;
regmap_write ( cx2072x - > regmap , CX2072X_AFG_POWER_STATE , 3 ) ;
pm_runtime_put ( codec - > dev ) ;
return 0 ;
}
static const struct snd_soc_component_driver soc_codec_driver_cx2072x = {
. probe = cx2072x_probe ,
. set_bias_level = cx2072x_set_bias_level ,
. set_jack = cx2072x_set_jack ,
. controls = cx2072x_snd_controls ,
. num_controls = ARRAY_SIZE ( cx2072x_snd_controls ) ,
. dapm_widgets = cx2072x_dapm_widgets ,
. num_dapm_widgets = ARRAY_SIZE ( cx2072x_dapm_widgets ) ,
. dapm_routes = cx2072x_intercon ,
. num_dapm_routes = ARRAY_SIZE ( cx2072x_intercon ) ,
2022-05-04 20:08:42 +03:00
. endianness = 1 ,
2019-05-21 09:26:52 +03:00
} ;
/*
* DAI ops
*/
2021-04-08 09:26:56 +03:00
static const struct snd_soc_dai_ops cx2072x_dai_ops = {
2019-05-21 09:26:52 +03:00
. set_sysclk = cx2072x_set_dai_sysclk ,
. set_fmt = cx2072x_set_dai_fmt ,
. hw_params = cx2072x_hw_params ,
. set_bclk_ratio = cx2072x_set_dai_bclk_ratio ,
} ;
static int cx2072x_dsp_dai_probe ( struct snd_soc_dai * dai )
{
struct cx2072x_priv * cx2072x =
snd_soc_component_get_drvdata ( dai - > component ) ;
cx2072x - > en_aec_ref = true ;
return 0 ;
}
# define CX2072X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
static struct snd_soc_dai_driver soc_codec_cx2072x_dai [ ] = {
{ /* playback and capture */
. name = " cx2072x-hifi " ,
. id = CX2072X_DAI_HIFI ,
. playback = {
. stream_name = " Playback " ,
. channels_min = 1 ,
. channels_max = 2 ,
. rates = CX2072X_RATES_DSP ,
. formats = CX2072X_FORMATS ,
} ,
. capture = {
. stream_name = " Capture " ,
. channels_min = 1 ,
. channels_max = 2 ,
. rates = CX2072X_RATES_DSP ,
. formats = CX2072X_FORMATS ,
} ,
. ops = & cx2072x_dai_ops ,
2021-01-15 07:56:09 +03:00
. symmetric_rate = 1 ,
2019-05-21 09:26:52 +03:00
} ,
{ /* plabayck only, return echo reference to Conexant DSP chip */
. name = " cx2072x-dsp " ,
. id = CX2072X_DAI_DSP ,
. probe = cx2072x_dsp_dai_probe ,
. playback = {
2020-12-08 16:51:54 +03:00
. stream_name = " DSP Playback " ,
2019-05-21 09:26:52 +03:00
. channels_min = 2 ,
. channels_max = 2 ,
. rates = CX2072X_RATES_DSP ,
. formats = CX2072X_FORMATS ,
} ,
. ops = & cx2072x_dai_ops ,
} ,
{ /* plabayck only, return echo reference through I2S TX */
. name = " cx2072x-aec " ,
. id = 3 ,
. capture = {
2020-12-08 16:51:54 +03:00
. stream_name = " AEC Capture " ,
2019-05-21 09:26:52 +03:00
. channels_min = 2 ,
. channels_max = 2 ,
. rates = CX2072X_RATES_DSP ,
. formats = CX2072X_FORMATS ,
} ,
} ,
} ;
static const struct regmap_config cx2072x_regmap = {
. reg_bits = 16 ,
. val_bits = 32 ,
. max_register = CX2072X_REG_MAX ,
. reg_defaults = cx2072x_reg_defaults ,
. num_reg_defaults = ARRAY_SIZE ( cx2072x_reg_defaults ) ,
. cache_type = REGCACHE_RBTREE ,
. readable_reg = cx2072x_readable_register ,
. volatile_reg = cx2072x_volatile_register ,
/* Needs custom read/write functions for various register lengths */
. reg_read = cx2072x_reg_read ,
. reg_write = cx2072x_reg_write ,
} ;
static int __maybe_unused cx2072x_runtime_suspend ( struct device * dev )
{
struct cx2072x_priv * cx2072x = dev_get_drvdata ( dev ) ;
clk_disable_unprepare ( cx2072x - > mclk ) ;
return 0 ;
}
2019-06-17 14:06:15 +03:00
static int __maybe_unused cx2072x_runtime_resume ( struct device * dev )
2019-05-21 09:26:52 +03:00
{
struct cx2072x_priv * cx2072x = dev_get_drvdata ( dev ) ;
return clk_prepare_enable ( cx2072x - > mclk ) ;
}
2022-04-05 19:58:24 +03:00
static int cx2072x_i2c_probe ( struct i2c_client * i2c )
2019-05-21 09:26:52 +03:00
{
struct cx2072x_priv * cx2072x ;
unsigned int ven_id , rev_id ;
int ret ;
cx2072x = devm_kzalloc ( & i2c - > dev , sizeof ( struct cx2072x_priv ) ,
GFP_KERNEL ) ;
if ( ! cx2072x )
return - ENOMEM ;
cx2072x - > regmap = devm_regmap_init ( & i2c - > dev , NULL , i2c ,
& cx2072x_regmap ) ;
if ( IS_ERR ( cx2072x - > regmap ) )
return PTR_ERR ( cx2072x - > regmap ) ;
mutex_init ( & cx2072x - > lock ) ;
i2c_set_clientdata ( i2c , cx2072x ) ;
cx2072x - > dev = & i2c - > dev ;
cx2072x - > pll_changed = true ;
cx2072x - > i2spcm_changed = true ;
cx2072x - > bclk_ratio = 0 ;
cx2072x - > mclk = devm_clk_get ( cx2072x - > dev , " mclk " ) ;
if ( IS_ERR ( cx2072x - > mclk ) ) {
dev_err ( cx2072x - > dev , " Failed to get MCLK \n " ) ;
return PTR_ERR ( cx2072x - > mclk ) ;
}
regmap_read ( cx2072x - > regmap , CX2072X_VENDOR_ID , & ven_id ) ;
regmap_read ( cx2072x - > regmap , CX2072X_REVISION_ID , & rev_id ) ;
dev_info ( cx2072x - > dev , " codec version: %08x,%08x \n " , ven_id , rev_id ) ;
ret = devm_snd_soc_register_component ( cx2072x - > dev ,
& soc_codec_driver_cx2072x ,
soc_codec_cx2072x_dai ,
ARRAY_SIZE ( soc_codec_cx2072x_dai ) ) ;
if ( ret < 0 )
return ret ;
pm_runtime_use_autosuspend ( cx2072x - > dev ) ;
pm_runtime_enable ( cx2072x - > dev ) ;
return 0 ;
}
static int cx2072x_i2c_remove ( struct i2c_client * i2c )
{
pm_runtime_disable ( & i2c - > dev ) ;
return 0 ;
}
static const struct i2c_device_id cx2072x_i2c_id [ ] = {
{ " cx20721 " , 0 } ,
{ " cx20723 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , cx2072x_i2c_id ) ;
# ifdef CONFIG_ACPI
static struct acpi_device_id cx2072x_acpi_match [ ] = {
{ " 14F10720 " , 0 } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( acpi , cx2072x_acpi_match ) ;
# endif
static const struct dev_pm_ops cx2072x_runtime_pm = {
SET_RUNTIME_PM_OPS ( cx2072x_runtime_suspend , cx2072x_runtime_resume ,
NULL )
SET_SYSTEM_SLEEP_PM_OPS ( pm_runtime_force_suspend ,
pm_runtime_force_resume )
} ;
static struct i2c_driver cx2072x_i2c_driver = {
. driver = {
. name = " cx2072x " ,
. acpi_match_table = ACPI_PTR ( cx2072x_acpi_match ) ,
. pm = & cx2072x_runtime_pm ,
} ,
2022-04-05 19:58:24 +03:00
. probe_new = cx2072x_i2c_probe ,
2019-05-21 09:26:52 +03:00
. remove = cx2072x_i2c_remove ,
. id_table = cx2072x_i2c_id ,
} ;
module_i2c_driver ( cx2072x_i2c_driver ) ;
MODULE_DESCRIPTION ( " ASoC cx2072x Codec Driver " ) ;
MODULE_AUTHOR ( " Simon Ho <simon.ho@conexant.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;