blob: faad0e9a3013f4af41143ad3464f44cd0df7a79a [file] [log] [blame]
/*
* Copyright (c) 2014 Samsung Electronics Co. Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/jack.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include <sound/samsung/abox.h>
#include <linux/i2c.h>
#include <linux/regulator/consumer.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/switch.h>
#include <linux/input.h>
#include <linux/completion.h>
#include <linux/mfd/samsung/s2mpu08-private.h>
#include <soc/samsung/acpm_mfd.h>
#include <sound/cod3035x.h>
#include "cod3035x.h"
#define COD3035X_SAMPLE_RATE_48KHZ 48000
#define COD3035X_SAMPLE_RATE_192KHZ 192000
#define COD3035X_MAX_IRQ_CHK_BITS 7
#define COD3035X_START_IRQ_CHK_BIT 2
#define COD3035X_MJ_DET_INVALID (-1)
#define ACPM_ADDR_PMIC 0x01
#define ACPM_ADDR_CODECA 0x07
#ifdef CONFIG_SND_SOC_SAMSUNG_VERBOSE_DEBUG
#ifdef dev_dbg
#undef dev_dbg
#endif
#ifdef dev_info
#undef dev_info
#endif
#if 1 /* if: print option */
#define dev_dbg dev_err
#define dev_info dev_err
#else /* else: print option */
static void no_dev_dbg(void *v, char *s, ...)
{
}
#define dev_dbg no_dev_dbg
#define dev_info no_dev_dbg
#endif /* endif: print option */
#endif
#define COD3035X_WATER_DET_THRESHOLD_MAX 3280
#define COD3035X_WATER_DET_THRESHOLD_MIN 120
#define COD3035X_GDET_FINISH_CHK_MORE_NO 3
#define COD3035X_GDET_FINISH_CHK_MIN 30
#define COD3035X_MIC_DET_DELAY 300
#define COD3035X_FAKE_JACK_INSERT_ADC 15
#define COD3035X_AUX_CABLE_DETECT_ADC 80
#define COD3035X_JACKOUT_DBNC_MASK 0x0F
#define COD3035X_JACKOUT_DBNC_DEFAULT 0x01
#define COD3035X_JACKIN_DBNC_MASK 0xF0
#define COD3035X_JACKIN_DBNC_DEFAULT 0xA0
#ifdef CONFIG_SND_SOC_COD30XX_EXT_ANT
#define COD3035X_3POLE_FAKE_MIN 90
#define COD3035X_3POLE_FAKE_MAX 100
#define COD3035X_4POLE_FAKE_MIN 350
#define COD3035X_4POLE_FAKE_MAX 380
#define COD3035X_ANT_DET_DELAY 150
#endif
/* defined for impedance calculate */
#define COD3035X_IMP_C1 99
#define COD3035X_IMP_C2 33
#define COD3035X_IMP_C3 418
#define COD3035X_IMP_LOW 11
#define COD3035X_IMP_HIGH 200
#define COD3035X_PARAM_SHIFT 5
#define COD3035X_PARAM_1_WIDTH 3
#define COD3035X_PARAM_2_WIDTH 2
#define COD3035X_PARAM_1_MASK MASK(COD3035X_PARAM_1_WIDTH, COD3035X_PARAM_SHIFT)
#define COD3035X_PARAM_2_MASK MASK(COD3035X_PARAM_2_WIDTH, COD3035X_PARAM_SHIFT)
#define COD3035X_P3_BIT_CHECK BIT(9)
#define STREAM_PLAYBACK 0
#define STREAM_CAPTURE 1
static int g_avc_mode;
/* Forward Declarations */
static void cod3035x_reset_io_selector_bits(struct snd_soc_codec *codec);
static void cod3035x_configure_mic_bias(struct snd_soc_codec *codec);
static int cod3035x_disable(struct device *dev);
static int cod3035x_enable(struct device *dev);
static inline void cod3035x_usleep(unsigned int u_sec)
{
usleep_range(u_sec, u_sec + 10);
}
int cod3035a_read_reg(u8 reg, u8 *dest)
{
int ret;
ret = exynos_acpm_read_reg(ACPM_ADDR_CODECA, reg, dest);
if (ret) {
pr_err("[%s] acpm ipc fail!\n", __func__);
return ret;
}
return 0;
}
EXPORT_SYMBOL_GPL(cod3035a_read_reg);
int cod3035a_write_reg(u8 reg, u8 value)
{
int ret;
ret = exynos_acpm_write_reg(ACPM_ADDR_CODECA, reg, value);
if (ret) {
pr_err("[%s] acpm ipc fail!\n", __func__);
return ret;
}
return ret;
}
EXPORT_SYMBOL_GPL(cod3035a_write_reg);
/**
* Helper functions to read ADC value for button detection
*/
#define COD3035X_ADC_SAMPLE_SIZE 5
static void cod3035x_adc_start(struct cod3035x_priv *cod3035x)
{
cod3035x->jack_adc = iio_channel_get_all(cod3035x->dev);
}
static void cod3035x_adc_stop(struct cod3035x_priv *cod3035x)
{
iio_channel_release(cod3035x->jack_adc);
}
static int cod3035x_gdet_adc_get_value(struct cod3035x_priv *cod3035x)
{
int adc_data = -1;
int adc_max = 0;
int adc_min = 0xFFFF;
int adc_total = 0;
int adc_retry_cnt = 0;
int i;
struct iio_channel *jack_adc = cod3035x->jack_adc;
for (i = 0; i < COD3035X_ADC_SAMPLE_SIZE; i++) {
iio_read_channel_raw(&jack_adc[1], &adc_data);
/* if adc_data is negative, ignore */
while (adc_data < 0) {
adc_retry_cnt++;
if (adc_retry_cnt > 10)
return adc_data;
iio_read_channel_raw(&jack_adc[1], &adc_data);
}
/* Update min/max values */
if (adc_data > adc_max)
adc_max = adc_data;
if (adc_data < adc_min)
adc_min = adc_data;
adc_total += adc_data;
}
return (adc_total - adc_max - adc_min) / (COD3035X_ADC_SAMPLE_SIZE - 2);
}
static int cod3035x_adc_get_value(struct cod3035x_priv *cod3035x)
{
int adc_data = -1;
int adc_max = 0;
int adc_min = 0xFFFF;
int adc_total = 0;
int adc_retry_cnt = 0;
int i;
struct iio_channel *jack_adc = cod3035x->jack_adc;
for (i = 0; i < COD3035X_ADC_SAMPLE_SIZE; i++) {
iio_read_channel_raw(&jack_adc[0], &adc_data);
/* if adc_data is negative, ignore */
while (adc_data < 0) {
adc_retry_cnt++;
if (adc_retry_cnt > 10)
return adc_data;
iio_read_channel_raw(&jack_adc[0], &adc_data);
}
/* Update min/max values */
if (adc_data > adc_max)
adc_max = adc_data;
if (adc_data < adc_min)
adc_min = adc_data;
adc_total += adc_data;
}
return (adc_total - adc_max - adc_min) / (COD3035X_ADC_SAMPLE_SIZE - 2);
}
/**
* Return value:
* true: if the register value cannot be cached, hence we have to read from the
* hardware directly
* false: if the register value can be read from cache
*/
static bool cod3035x_volatile_register(struct device *dev, unsigned int reg)
{
/**
* For all the registers for which we want to restore the value during
* regcache_sync operation, we need to return true here. For registers
* whose value need not be cached and restored should return false here.
*
* For the time being, let us cache the value of all registers other
* than the IRQ pending and IRQ status registers.
*/
switch (reg) {
case COD3035X_01_IRQ1PEND ... COD3035X_04_IRQ4PEND:
case COD3035X_09_STATUS1 ... COD3035X_0D_STATUS5:
case COD3035X_60_IRQ_SENSOR ... COD3035X_63_OFFSET_DA:
case COD3035X_80_PDB_ACC1 ... COD3035X_83_CTR_MCB:
case COD3035X_85_CTR_POP2 ... COD3035X_8F_CTR_DLY2:
case COD3035X_90_CTR_DLY3 ... COD3035X_9F_IMP_CNT4:
case COD3035X_A0_IMP_CNT5 ... COD3035X_AF_TEST_ADC5:
return true;
default:
return false;
}
}
/**
* Return value:
* true: if the register value can be read
* flase: if the register cannot be read
*/
static bool cod3035x_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case COD3035X_01_IRQ1PEND ... COD3035X_0D_STATUS5:
case COD3035X_10_PD_REF ... COD3035X_1D_SV_DA:
case COD3035X_20_VOL_AD1 ... COD3035X_28_DSM_ADS:
case COD3035X_30_VOL_HPL ... COD3035X_3E_OVP_2:
case COD3035X_40_DIGITAL_POWER ... COD3035X_4F_DMIC4:
case COD3035X_50_DAC1 ... COD3035X_5F_CRO3:
case COD3035X_60_IRQ_SENSOR ... COD3035X_6C_MIC_ON:
case COD3035X_70_CLK1_COD ... COD3035X_7C_LPF_AD:
case COD3035X_80_PDB_ACC1 ... COD3035X_8F_CTR_DLY2:
case COD3035X_90_CTR_DLY3 ... COD3035X_9F_IMP_CNT4:
case COD3035X_A0_IMP_CNT5 ... COD3035X_AF_TEST_ADC5:
case COD3035X_B0_AUTO_HP1 ... COD3035X_BE_ODSEL2:
case COD3035X_D0_CTRL_IREF1 ... COD3035X_DC_CTRL_EPS:
case COD3035X_E1_PRESET_AVC ... COD3035X_EF_I_GAIN_FILTER1:
case COD3035X_F2_PRESET_AVC ... COD3035X_F6_PRESET_AVC:
return true;
default:
return false;
}
}
static bool cod3035x_writeable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
/* Reg-0x09 to Reg-0x0D are read-only status registers */
case COD3035X_01_IRQ1PEND ... COD3035X_08_IRQ4M:
case COD3035X_10_PD_REF ... COD3035X_1D_SV_DA:
case COD3035X_20_VOL_AD1 ... COD3035X_28_DSM_ADS:
case COD3035X_30_VOL_HPL ... COD3035X_3E_OVP_2:
case COD3035X_40_DIGITAL_POWER ... COD3035X_4F_DMIC4:
case COD3035X_50_DAC1 ... COD3035X_5F_CRO3:
/* Reg-0x60 is read-only */
case COD3035X_61_OFFSET_AD1 ... COD3035X_6C_MIC_ON:
case COD3035X_70_CLK1_COD ... COD3035X_7C_LPF_AD:
case COD3035X_80_PDB_ACC1 ... COD3035X_8F_CTR_DLY2:
case COD3035X_90_CTR_DLY3 ... COD3035X_9F_IMP_CNT4:
case COD3035X_A0_IMP_CNT5 ... COD3035X_AF_TEST_ADC5:
case COD3035X_B0_AUTO_HP1 ... COD3035X_BE_ODSEL2:
case COD3035X_D0_CTRL_IREF1 ... COD3035X_DC_CTRL_EPS:
case COD3035X_E1_PRESET_AVC ... COD3035X_EF_I_GAIN_FILTER1:
case COD3035X_F2_PRESET_AVC ... COD3035X_F6_PRESET_AVC:
return true;
default:
return false;
}
}
const struct regmap_config cod3035x_regmap = {
.reg_bits = 8,
.val_bits = 8,
/* "speedy" string should be described in the name field
* this will be used for the speedy inteface,
* when read/write operations are used in the regmap driver.
* APM functions will be called instead of the I2C
* refer to the "drivers/base/regmap/regmap-i2c.c
*/
.name = "speedy, COD3035X",
.max_register = COD3035X_MAX_REGISTER,
.readable_reg = cod3035x_readable_register,
.writeable_reg = cod3035x_writeable_register,
.volatile_reg = cod3035x_volatile_register,
.use_single_rw = true,
.cache_type = REGCACHE_RBTREE,
};
/**
* TLV_DB_SCALE_ITEM
*
* (TLV: Threshold Limit Value)
*
* For various properties, the dB values don't change linearly with respect to
* the digital value of related bit-field. At most, they are quasi-linear,
* that means they are linear for various ranges of digital values. Following
* table define such ranges of various properties.
*
* TLV_DB_RANGE_HEAD(num)
* num defines the number of linear ranges of dB values.
*
* s0, e0, TLV_DB_SCALE_ITEM(min, step, mute),
* s0: digital start value of this range (inclusive)
* e0: digital end valeu of this range (inclusive)
* min: dB value corresponding to s0
* step: the delta of dB value in this range
* mute: ?
*
* Example:
* TLV_DB_RANGE_HEAD(3),
* 0, 1, TLV_DB_SCALE_ITEM(-2000, 2000, 0),
* 2, 4, TLV_DB_SCALE_ITEM(1000, 1000, 0),
* 5, 6, TLV_DB_SCALE_ITEM(3800, 8000, 0),
*
* The above code has 3 linear ranges with following digital-dB mapping.
* (0...6) -> (-2000dB, 0dB, 1000dB, 2000dB, 3000dB, 3800dB, 4600dB),
*
* DECLARE_TLV_DB_SCALE
*
* This macro is used in case where there is a linear mapping between
* the digital value and dB value.
*
* DECLARE_TLV_DB_SCALE(name, min, step, mute)
*
* name: name of this dB scale
* min: minimum dB value corresponding to digital 0
* step: the delta of dB value
* mute: ?
*
* NOTE: The information is mostly for user-space consumption, to be viewed
* alongwith amixer.
*/
/**
* cod3035x_ctvol_bst_tlv
*
* (0...2) -> (0dB, 1200dB, 2000dB)
*/
static const unsigned int cod3035x_ctvol_bst_tlv[] = {
TLV_DB_RANGE_HEAD(2),
0, 1, TLV_DB_SCALE_ITEM(0, 1200, 0),
2, 2, TLV_DB_SCALE_ITEM(2000, 0, 0),
};
/**
* cod3035x_avc_ctvol_hp_tlv
*
* Range: 0dB to +4dB, step 1dB
*
* CTVOL_HP, reg(0x57), shift(0), width(3), invert(1), max(5)
*/
static const DECLARE_TLV_DB_SCALE(cod3035x_avc_ctvol_hp_tlv, 0, 100, 0);
/**
* cod3019_ctvol_ep_tlv
*
* Range: 0dB to +12dB, step 1dB
*
* CTVOL_EP, reg(0x32), shift(4), width(4), invert(0), max(12)
*/
static const DECLARE_TLV_DB_SCALE(cod3035x_ctvol_ep_tlv, 0, 100, 0);
/**
* cod3035x_dvol_adc_tlv
*
* Map as per data-sheet:
* (0x00 to 0x86) -> (+12dB to -55dB, step 0.5dB)
* (0x87 to 0x91) -> (-56dB to -66dB, step 1dB)
* (0x92 to 0x94) -> (-68dB to -72dB, step 2dB)
* (0x95 to 0x96) -> (-78dB to -84dB, step 6dB)
*
* When the map is in descending order, we need to set the invert bit
* and arrange the map in ascending order. The offsets are calculated as
* (max - offset).
*
* offset_in_table = max - offset_actual;
*
* DVOL_ADL, reg(0x47), shift(0), width(8), invert(1), max(0x96)
* DVOL_ADR, reg(0x48), shift(0), width(8), invert(1), max(0x96)
* DVOL_ADC, reg(0x4C), shift(0), width(8), invert(1), max(0x96)
*/
static const unsigned int cod3035x_dvol_adc_tlv[] = {
TLV_DB_RANGE_HEAD(4),
0x00, 0x01, TLV_DB_SCALE_ITEM(-8400, 600, 0),
0x02, 0x04, TLV_DB_SCALE_ITEM(-7200, 200, 0),
0x05, 0x09, TLV_DB_SCALE_ITEM(-6600, 100, 0),
0x10, 0x96, TLV_DB_SCALE_ITEM(-5500, 50, 0),
};
/**
* cod3035x_dvol_dac_tlv
*
* Map as per data-sheet:
* 0x00 ~ 0xE0 : +42dB to -70dB, step 0.5dB
* 0xE1 ~ 0xE5 : -72dB to -80dB, step 2dB
* 0xE6 : -82.4dB
* 0xE7 ~ 0xE9 : -84.3dB to -96.3dB, step 6dB
*
* When the map is in descending order, we need to set the invert bit
* and arrange the map in ascending order. The offsets are calculated as
* (max - offset).
*
* offset_in_table = max - offset_actual;
*
* DAC_DAL, reg(0x51), shift(0), width(8), invert(1), max(0xE9)
* DAC_DAR, reg(0x52), shift(0), width(8), invert(1), max(0xE9)
*/
static const unsigned int cod3035x_dvol_dac_tlv[] = {
TLV_DB_RANGE_HEAD(4),
0x00, 0x03, TLV_DB_SCALE_ITEM(-9630, 600, 0),
0x04, 0x04, TLV_DB_SCALE_ITEM(-8240, 0, 0),
0x05, 0x09, TLV_DB_SCALE_ITEM(-8000, 200, 0),
0x0A, 0xE9, TLV_DB_SCALE_ITEM(-7000, 50, 0),
};
static const unsigned int cod3035x_hp_avol_bypass_mode_tlv[] = {
TLV_DB_RANGE_HEAD(1),
0x00, 0x3F, TLV_DB_SCALE_ITEM(-5900, 100, 0),
};
/**
* cod3035x_dnc_min_gain_tlv
*
* Range: -6dB to 0dB, step 1dB
*
* DNC_MINGAIN , reg(0x55), shift(5), width(3)
*/
static const unsigned int cod3035x_dnc_min_gain_tlv[] = {
TLV_DB_RANGE_HEAD(1),
0x00, 0x06, TLV_DB_SCALE_ITEM(-600, 0, 0),
};
/**
* cod3035x_dnc_max_gain_tlv
*
* Range: 0dB to 24dB, step 1dB
*
* DNC_MAXGAIN , reg(0x55), shift(0), width(5)
*/
static const unsigned int cod3035x_dnc_max_gain_tlv[] = {
TLV_DB_RANGE_HEAD(1),
0x06, 0x1e, TLV_DB_SCALE_ITEM(0, 2400, 0),
};
/**
* cod3035x_dnc_lvl_tlv
*
* Range: -10.5dB to 0dB, step 1.5dB
*
* DNCLVL_R/L, reg(0x55), shift(0/4), width(3), invert(0), max(7)
*/
static const DECLARE_TLV_DB_SCALE(cod3035x_dnc_lvl_tlv, -1050, 0, 0);
/**
* DMIC GAIN
*
* Selection digital mic gain through conversion code level.
*/
static const char *cod3035x_dmic_code_level_text[] = {
"0", "1", "2", "3",
"4", "5", "6", "7"
};
static const struct soc_enum cod3035x_dmic2_gain_code_level_enum =
SOC_ENUM_SINGLE(COD3035X_4A_DMIC2, DMIC_GAIN1_SHIFT,
ARRAY_SIZE(cod3035x_dmic_code_level_text),
cod3035x_dmic_code_level_text);
static const struct soc_enum cod3035x_dmic4_gain_code_level_enum =
SOC_ENUM_SINGLE(COD3035X_4F_DMIC4, DMIC4_GAIN_SHIFT,
ARRAY_SIZE(cod3035x_dmic_code_level_text),
cod3035x_dmic_code_level_text);
/**
* HP amp reference current
*
* Selection HP amp reference current through conversion code level.
*/
static const char *cod3035x_hp_code_level_text[] = {
"0", "1", "2", "3",
"4", "5", "6", "7"
};
static const struct soc_enum cod3035x_in_mu_ctmi_hpa_code_level_enum =
SOC_ENUM_SINGLE(COD3035X_BB_AUTO_HP12, IN_MU_CTMI_HPA_SHIFT,
ARRAY_SIZE(cod3035x_hp_code_level_text),
cod3035x_hp_code_level_text);
static const struct soc_enum cod3035x_out_mu_ctmi_hpa_code_level_enum =
SOC_ENUM_SINGLE(COD3035X_BB_AUTO_HP12, OUT_MU_CTMI_HPA_SHIFT,
ARRAY_SIZE(cod3035x_hp_code_level_text),
cod3035x_hp_code_level_text);
/**
* mono_mix_mode
*
* Selecting the Mode of Mono Mixer (inside DAC block)
*/
static const char *cod3035x_mono_mix_mode_text[] = {
"Disable", "R", "L", "LR-Invert",
"(L+R)/2", "L+R"
};
static const struct soc_enum cod3035x_mono_mix_mode_enum =
SOC_ENUM_SINGLE(COD3035X_50_DAC1, DAC1_MONOMIX_SHIFT,
ARRAY_SIZE(cod3035x_mono_mix_mode_text),
cod3035x_mono_mix_mode_text);
static int dac_soft_mute_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
unsigned long dac_control;
bool soft_mute_flag;
dac_control = snd_soc_read(codec, COD3035X_50_DAC1);
soft_mute_flag = dac_control & DAC1_SOFT_MUTE_MASK;
ucontrol->value.integer.value[0] = soft_mute_flag;
return 0;
}
static int dac_soft_mute_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
int value = ucontrol->value.integer.value[0];
if (!value)
/* enable soft mute */
snd_soc_update_bits(codec, COD3035X_50_DAC1,
DAC1_SOFT_MUTE_MASK, DAC1_SOFT_MUTE_MASK);
else
/* diable soft mute */
snd_soc_update_bits(codec, COD3035X_50_DAC1,
DAC1_SOFT_MUTE_MASK, 0x0);
dev_info(codec->dev, "%s: soft mute : %s\n", __func__,
(!value) ? "on":"off");
return 0;
}
static int avc_bypass_mode_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
unsigned long avc_control;
bool bypass_mode_flag;
avc_control = snd_soc_read(codec, COD3035X_54_AVC1);
bypass_mode_flag = avc_control & AVC_BYPS_MASK;
ucontrol->value.integer.value[0] = bypass_mode_flag;
return 0;
}
static int avc_bypass_mode_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
int value = ucontrol->value.integer.value[0];
switch (value) {
case LEGACY_MODE_0:
/* diable avc bypass mode */
snd_soc_update_bits(codec, COD3035X_54_AVC1,
AVC_BYPS_MASK, 0x0);
break;
case LEGACY_MODE_1:
/* enable avc bypass mode */
snd_soc_update_bits(codec, COD3035X_54_AVC1,
AVC_BYPS_MASK, AVC_BYPS_MASK);
break;
case DNC_MODE:
snd_soc_update_bits(codec, COD3035X_5C_AVC9,
AVCDNC_SEL_MASK, AVCDNC_SEL_MASK);
cod3035a_write_reg(COD3035A_40_DNC0, 0xC0);
snd_soc_write(codec, COD3035X_54_AVC1, 0x03);
break;
case AVC_MODE:
snd_soc_update_bits(codec, COD3035X_5C_AVC9,
AVCDNC_SEL_MASK, 0x0);
cod3035a_write_reg(COD3035A_40_DNC0, 0x40);
snd_soc_write(codec, COD3035X_54_AVC1, 0x03);
break;
default:
break;
}
g_avc_mode = value;
dev_info(codec->dev, "%s: avc bypass mode : %s, avc_mode: %d.\n",
__func__, (value) ? "on":"off", g_avc_mode);
return 0;
}
static int avc_mute_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
unsigned long avc_control;
bool mute_flag;
avc_control = snd_soc_read(codec, COD3035X_54_AVC1);
mute_flag = avc_control & AVC_MU_EN_MASK;
ucontrol->value.integer.value[0] = mute_flag;
return 0;
}
static int avc_mute_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
int value = ucontrol->value.integer.value[0];
if (value)
snd_soc_update_bits(codec, COD3035X_54_AVC1,
AVC_MU_EN_MASK, AVC_MU_EN_MASK);
else
snd_soc_update_bits(codec, COD3035X_54_AVC1,
AVC_MU_EN_MASK, 0x0);
dev_info(codec->dev, "%s: avc mute enable : %s\n",
__func__, (value) ? "on":"off");
return 0;
}
static int hp_avol_mode_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
return 0;
}
static int hp_avol_mode_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
int value = ucontrol->value.integer.value[0];
dev_info(codec->dev, "%s: hp analog volume : %d\n",
__func__, value);
if (value >= 0) {
snd_soc_update_bits(codec, COD3035X_E3_PRESET_AVC,
HP_PN_MASK, 0x0);
snd_soc_update_bits(codec, COD3035X_E5_PRESET_AVC,
HP_PN_MASK, 0x0);
snd_soc_update_bits(codec, COD3035X_E3_PRESET_AVC,
HP_AVOL_MASK, value);
snd_soc_update_bits(codec, COD3035X_E5_PRESET_AVC,
HP_AVOL_MASK, value);
} else {
snd_soc_update_bits(codec, COD3035X_E3_PRESET_AVC,
HP_PN_MASK, HP_PN_MASK);
snd_soc_update_bits(codec, COD3035X_E5_PRESET_AVC,
HP_PN_MASK, HP_PN_MASK);
snd_soc_update_bits(codec, COD3035X_E3_PRESET_AVC,
HP_AVOL_MASK, -value);
snd_soc_update_bits(codec, COD3035X_E5_PRESET_AVC,
HP_AVOL_MASK, -value);
}
return 0;
}
static int ovp_value_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
unsigned long ovp_control;
ovp_control = snd_soc_read(codec, COD3035X_3D_OVP_1);
ucontrol->value.integer.value[0] = ovp_control;
return 0;
}
static int ovp_value_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
int value = ucontrol->value.integer.value[0];
snd_soc_write(codec, 0x3D, value);
dev_info(codec->dev, "%s: ovp_tuning_value: 0x%x.\n",
__func__, value);
return 0;
}
static int det_thr_value_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
unsigned long det_control;
det_control = snd_soc_read(codec, COD3035X_84_CTR_POP1);
ucontrol->value.integer.value[0] = det_control;
return 0;
}
static int det_thr_value_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
int value = ucontrol->value.integer.value[0];
struct cod3035x_priv *cod3035x = snd_soc_codec_get_drvdata(codec);
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, false);
snd_soc_write(codec, COD3035X_84_CTR_POP1, value);
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, true);
dev_info(codec->dev, "%s: detect_threshold: 0x%x.\n",
__func__, value);
return 0;
}
static int mcb2_chop_mode_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
unsigned long mcb2_control;
bool chop_mode_flag;
mcb2_control = snd_soc_read(codec, COD3035X_76_CHOP_AD);
chop_mode_flag = mcb2_control & EN_MCB2_CHOP_MASK;
ucontrol->value.integer.value[0] = chop_mode_flag;
return 0;
}
static int mcb2_chop_mode_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
int value = ucontrol->value.integer.value[0];
if (value)
/* enable mcb2 chop mode */
snd_soc_update_bits(codec, COD3035X_76_CHOP_AD,
EN_MCB2_CHOP_MASK, EN_MCB2_CHOP_MASK);
else
/* diable mcb2 chop mode */
snd_soc_update_bits(codec, COD3035X_76_CHOP_AD,
EN_MCB2_CHOP_MASK, 0x0);
dev_info(codec->dev, "%s: mcb2 chop mode : %s\n",
__func__, (value) ? "on":"off");
return 0;
}
/**
* struct snd_kcontrol_new cod3035x_snd_control
*
* Every distinct bit-fields within the CODEC SFR range may be considered
* as a control elements. Such control elements are defined here.
*
* Depending on the access mode of these registers, different macros are
* used to define these control elements.
*
* SOC_ENUM: 1-to-1 mapping between bit-field value and provided text
* SOC_SINGLE: Single register, value is a number
* SOC_SINGLE_TLV: Single register, value corresponds to a TLV scale
* SOC_SINGLE_TLV_EXT: Above + custom get/set operation for this value
* SOC_SINGLE_RANGE_TLV: Register value is an offset from minimum value
* SOC_DOUBLE: Two bit-fields are updated in a single register
* SOC_DOUBLE_R: Two bit-fields in 2 different registers are updated
*/
/**
* All the data goes into cod3035x_snd_controls.
* All path inter-connections goes into cod3035x_dapm_routes
*/
static const struct snd_kcontrol_new cod3035x_snd_controls[] = {
SOC_SINGLE_TLV("MIC1 Boost Volume", COD3035X_20_VOL_AD1,
VOLAD1_CTVOL_BST1_SHIFT,
(BIT(VOLAD1_CTVOL_BST1_WIDTH) - 1), 0,
cod3035x_ctvol_bst_tlv),
SOC_SINGLE_TLV("MIC2 Boost Volume", COD3035X_21_VOL_AD2,
VOLAD2_CTVOL_BST2_SHIFT,
(BIT(VOLAD2_CTVOL_BST2_WIDTH) - 1), 0,
cod3035x_ctvol_bst_tlv),
SOC_SINGLE_TLV("MIC3 Boost Volume", COD3035X_22_VOL_AD3,
VOLAD3_CTVOL_BST3_SHIFT,
(BIT(VOLAD3_CTVOL_BST3_WIDTH) - 1), 0,
cod3035x_ctvol_bst_tlv),
SOC_SINGLE_TLV("Headphone Volume", COD3035X_57_AVC4,
AVC_CTVOL_HP_SHIFT,
(BIT(AVC_CTVOL_HP_WIDTH) - 1), 0,
cod3035x_avc_ctvol_hp_tlv),
SOC_SINGLE_TLV("Earphone Volume", COD3035X_32_VOL_EP,
CTVOL_EP_SHIFT,
(BIT(CTVOL_EP_WIDTH) - 1), 0,
cod3035x_ctvol_ep_tlv),
SOC_SINGLE_TLV("ADC Left Gain", COD3035X_47_AVOLL1,
AD_DA_DVOL_SHIFT,
AD_DVOL_MAXNUM, 1, cod3035x_dvol_adc_tlv),
SOC_SINGLE_TLV("ADC Right Gain", COD3035X_48_AVOLR1,
AD_DA_DVOL_SHIFT,
AD_DVOL_MAXNUM, 1, cod3035x_dvol_adc_tlv),
SOC_SINGLE_TLV("ADC Center Gain", COD3035X_4C_AVOLL2,
AD_DA_DVOL_SHIFT,
AD_DVOL_MAXNUM, 1, cod3035x_dvol_adc_tlv),
SOC_DOUBLE_R_TLV("DAC Gain", COD3035X_51_DVOLL, COD3035X_52_DVOLR,
AD_DA_DVOL_SHIFT,
DA_DVOL_MAXNUM, 1, cod3035x_dvol_dac_tlv),
SOC_DOUBLE_R_TLV("HP Analog Volume AVCBypass",
COD3035X_30_VOL_HPL, COD3035X_31_VOL_HPR,
CTVOL_HP_AVCBYPASS_SHIFT,
CTVOL_HP_AVCBYPASS_MAX_NUM,
1,
cod3035x_hp_avol_bypass_mode_tlv),
SOC_ENUM("MonoMix Mode", cod3035x_mono_mix_mode_enum),
SOC_SINGLE_EXT("DAC Soft Mute", SND_SOC_NOPM, 0, 100, 0,
dac_soft_mute_get, dac_soft_mute_put),
SOC_SINGLE_EXT("AVC Bypass Mode", SND_SOC_NOPM, 0, 100, 0,
avc_bypass_mode_get, avc_bypass_mode_put),
SOC_SINGLE_EXT("AVC Mute Enable", SND_SOC_NOPM, 0, 100, 0,
avc_mute_get, avc_mute_put),
SOC_SINGLE_EXT("OVP Tuning Value", SND_SOC_NOPM, 0, 100, 0,
ovp_value_get, ovp_value_put),
SOC_SINGLE_EXT("Detect Threshold", SND_SOC_NOPM, 0, 100, 0,
det_thr_value_get, det_thr_value_put),
SOC_SINGLE_EXT("MCB2 Chop Mode", SND_SOC_NOPM, 0, 100, 0,
mcb2_chop_mode_get, mcb2_chop_mode_put),
SOC_SINGLE_EXT("HP Analog Volume", SND_SOC_NOPM, 0, 100, 0,
hp_avol_mode_get, hp_avol_mode_put),
SOC_ENUM("DMIC1 Volume", cod3035x_dmic2_gain_code_level_enum),
SOC_ENUM("DMIC2 Volume", cod3035x_dmic4_gain_code_level_enum),
SOC_ENUM("In Mute HP Current",
cod3035x_in_mu_ctmi_hpa_code_level_enum),
SOC_ENUM("Out Mute HP Current",
cod3035x_out_mu_ctmi_hpa_code_level_enum),
};
static int dac_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
dev_dbg(codec->dev, "%s called, event = %d\n", __func__, event);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
/* DAC digital power On */
snd_soc_update_bits(codec, COD3035X_40_DIGITAL_POWER,
PDB_DACDIG_MASK, PDB_DACDIG_MASK);
/* DAC digital Reset On/Off */
snd_soc_update_bits(codec, COD3035X_40_DIGITAL_POWER,
RSTB_DAT_DA_MASK, 0x0);
snd_soc_update_bits(codec, COD3035X_40_DIGITAL_POWER,
RSTB_DAT_DA_MASK, RSTB_DAT_DA_MASK);
break;
case SND_SOC_DAPM_PRE_PMD:
/* DAC digital Reset Off */
snd_soc_update_bits(codec, COD3035X_40_DIGITAL_POWER,
RSTB_DAT_DA_MASK, 0x0);
/* DAC digital power Off */
snd_soc_update_bits(codec, COD3035X_40_DIGITAL_POWER,
PDB_DACDIG_MASK, 0x0);
break;
default:
break;
}
return 0;
}
static void cod3035x_adc_digital_mute(struct snd_soc_codec *codec, bool on)
{
dev_dbg(codec->dev, "%s called, %s\n", __func__,
on ? "Mute" : "Unmute");
if (on)
snd_soc_update_bits(codec, COD3035X_46_ADC1,
ADC1_MUTE_AD_EN_MASK, ADC1_MUTE_AD_EN_MASK);
else
snd_soc_update_bits(codec, COD3035X_46_ADC1,
ADC1_MUTE_AD_EN_MASK, 0);
}
static int cod3035x_capture_init_manual_mode(struct snd_soc_codec *codec)
{
dev_dbg(codec->dev, "%s called\n", __func__);
snd_soc_write(codec, COD3035X_BC_ODSEL0, 0x03);
/* VMID On */
snd_soc_update_bits(codec, COD3035X_10_PD_REF,
PDB_VMID_MASK, PDB_VMID_MASK);
/* VMID Fast Charging On */
snd_soc_update_bits(codec, COD3035X_19_CTRL_REF,
CTMF_VMID_MASK, (CTMF_VMID_1K_OM << CTMF_VMID_SHIFT));
/* VREFP_AD Pre Charging ON */
snd_soc_update_bits(codec, COD3035X_11_PD_AD1,
EN_DSML_PREQ_MASK|EN_DSMR_PREQ_MASK,
EN_DSML_PREQ_MASK|EN_DSMR_PREQ_MASK);
snd_soc_update_bits(codec, COD3035X_13_PD_AD3,
EN_DSMC_PREQ_MASK, EN_DSMC_PREQ_MASK);
/* IGEN On */
snd_soc_update_bits(codec, COD3035X_10_PD_REF,
PDB_IGEN_MASK, PDB_IGEN_MASK);
return 0;
}
static int cod3035x_capture_init(struct snd_soc_codec *codec)
{
struct cod3035x_priv *cod3035x = snd_soc_codec_get_drvdata(codec);
dev_dbg(codec->dev, "%s called\n", __func__);
mutex_lock(&cod3035x->adc_mute_lock);
snd_soc_write(codec, COD3035X_44_IF1_FORMAT4, 0xFF);
/* Enable ADC digital mute before configuring ADC */
cod3035x_adc_digital_mute(codec, true);
mutex_unlock(&cod3035x->adc_mute_lock);
/* DAC digital power On */
snd_soc_update_bits(codec, COD3035X_40_DIGITAL_POWER,
PDB_DACDIG_MASK, PDB_DACDIG_MASK);
/* Recording Digital Power on */
snd_soc_update_bits(codec, COD3035X_40_DIGITAL_POWER,
PDB_ADCDIG_MASK, PDB_ADCDIG_MASK);
/* Recording Digital Reset on/off */
snd_soc_update_bits(codec, COD3035X_40_DIGITAL_POWER,
RSTB_DAT_AD_MASK, 0);
snd_soc_update_bits(codec, COD3035X_40_DIGITAL_POWER,
RSTB_DAT_AD_MASK, RSTB_DAT_AD_MASK);
/* Power up ADC channel 2 */
snd_soc_update_bits(codec, COD3035X_4B_ADC2,
ADC2_PDB_ADCDIG2_MASK, ADC2_PDB_ADCDIG2_MASK);
cod3035x_capture_init_manual_mode(codec);
return 0;
}
static int cod3035x_dmic_capture_init(struct snd_soc_codec *codec)
{
struct cod3035x_priv *cod3035x = snd_soc_codec_get_drvdata(codec);
dev_dbg(codec->dev, "%s called\n", __func__);
mutex_lock(&cod3035x->adc_mute_lock);
snd_soc_write(codec, COD3035X_44_IF1_FORMAT4, 0xFF);
/* enable ADC digital mute before configuring ADC */
cod3035x_adc_digital_mute(codec, true);
mutex_unlock(&cod3035x->adc_mute_lock);
/* Recording Digital Power on */
snd_soc_update_bits(codec, COD3035X_40_DIGITAL_POWER,
PDB_ADCDIG_MASK, PDB_ADCDIG_MASK);
/* Recording Digital Reset on/off */
snd_soc_update_bits(codec, COD3035X_40_DIGITAL_POWER,
RSTB_DAT_AD_MASK, 0x0);
snd_soc_update_bits(codec, COD3035X_40_DIGITAL_POWER,
RSTB_DAT_AD_MASK, RSTB_DAT_AD_MASK);
/* Power up ADC channel 2 */
snd_soc_update_bits(codec, COD3035X_4B_ADC2,
ADC2_PDB_ADCDIG2_MASK, ADC2_PDB_ADCDIG2_MASK);
return 0;
}
static void cod3035x_capture_deinit_manual_mode(struct snd_soc_codec *codec)
{
/* VMID OFF */
snd_soc_update_bits(codec, COD3035X_10_PD_REF, PDB_VMID_MASK, 0);
snd_soc_write(codec, COD3035X_BC_ODSEL0, 0x00);
}
static int cod3035x_capture_deinit(struct snd_soc_codec *codec)
{
struct cod3035x_priv *cod3035x = snd_soc_codec_get_drvdata(codec);
dev_dbg(codec->dev, "%s called\n", __func__);
cod3035x_capture_deinit_manual_mode(codec);
/* Power down ADC channel 2 */
snd_soc_update_bits(codec, COD3035X_4B_ADC2, ADC2_PDB_ADCDIG2_MASK, 0);
/* Recording Digital Reset on */
snd_soc_update_bits(codec, COD3035X_40_DIGITAL_POWER,
RSTB_DAT_AD_MASK, 0);
/* Recording Digital Power off */
snd_soc_update_bits(codec, COD3035X_40_DIGITAL_POWER,
PDB_ADCDIG_MASK, 0);
mutex_lock(&cod3035x->adc_mute_lock);
snd_soc_write(codec, COD3035X_44_IF1_FORMAT4, 0xFF);
/* disable ADC digital mute after configuring ADC */
cod3035x_adc_digital_mute(codec, false);
mutex_unlock(&cod3035x->adc_mute_lock);
return 0;
}
static int cod3035x_dmic_capture_deinit(struct snd_soc_codec *codec)
{
struct cod3035x_priv *cod3035x = snd_soc_codec_get_drvdata(codec);
/* Power down ADC channel 2 */
snd_soc_update_bits(codec, COD3035X_4B_ADC2, ADC2_PDB_ADCDIG2_MASK, 0);
/* Recording Digital Reset on */
snd_soc_update_bits(codec, COD3035X_40_DIGITAL_POWER,
RSTB_DAT_AD_MASK, 0x0);
/* Recording Digital Power off */
snd_soc_update_bits(codec, COD3035X_40_DIGITAL_POWER,
PDB_ADCDIG_MASK, 0x0);
mutex_lock(&cod3035x->adc_mute_lock);
snd_soc_write(codec, COD3035X_44_IF1_FORMAT4, 0xFF);
/* disable ADC digital mute after configuring ADC */
cod3035x_adc_digital_mute(codec, false);
mutex_unlock(&cod3035x->adc_mute_lock);
return 0;
}
static int adc_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
int event)
{
int dac_on;
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct cod3035x_priv *cod3035x = snd_soc_codec_get_drvdata(codec);
dev_dbg(codec->dev, "%s called, event = %d\n", __func__, event);
dac_on = snd_soc_read(codec, COD3035X_40_DIGITAL_POWER);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
break;
case SND_SOC_DAPM_POST_PMU:
/* disable ADC digital mute after configuring ADC */
queue_work(cod3035x->adc_mute_wq, &cod3035x->adc_mute_work);
break;
case SND_SOC_DAPM_PRE_PMD:
mutex_lock(&cod3035x->adc_mute_lock);
/* enable ADC digital mute before configuring ADC */
cod3035x_adc_digital_mute(codec, true);
mutex_unlock(&cod3035x->adc_mute_lock);
break;
default:
break;
}
return 0;
}
static int dadc_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct cod3035x_priv *cod3035x = snd_soc_codec_get_drvdata(codec);
dev_dbg(codec->dev, "%s called, event = %d\n", __func__, event);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
break;
case SND_SOC_DAPM_POST_PMU:
/* disable ADC digital mute after configuring ADC */
queue_work(cod3035x->adc_mute_wq, &cod3035x->adc_mute_work);
break;
case SND_SOC_DAPM_PRE_PMD:
mutex_lock(&cod3035x->adc_mute_lock);
/* enable ADC digital mute before configuring ADC */
cod3035x_adc_digital_mute(codec, true);
mutex_unlock(&cod3035x->adc_mute_lock);
break;
default:
break;
}
return 0;
}
static void remove_tdma_noise(struct snd_soc_codec *codec)
{
struct cod3035x_priv *cod3035x = snd_soc_codec_get_drvdata(codec);
unsigned int mic_on;
dev_dbg(codec->dev, "%s: adc mute for remove tdma noise after jack out.\n",
__func__);
mic_on = snd_soc_read(codec, COD3035X_6C_MIC_ON);
if (mic_on & EN_MIC3_MASK) {
dev_dbg(codec->dev, "%s: MIC3 is active, Boost power down.\n", __func__);
mutex_lock(&cod3035x->adc_mute_lock);
snd_soc_write(codec, COD3035X_44_IF1_FORMAT4, 0xFF);
/* disable ADC digital mute after configuring ADC */
cod3035x_adc_digital_mute(codec, false);
/* MIC3 OFF */
snd_soc_update_bits(codec, COD3035X_12_PD_AD2, PDB_MIC_BST3_MASK, 0);
mutex_unlock(&cod3035x->adc_mute_lock);
}
}
int cod3035x_mic_bias_ev(struct snd_soc_codec *codec, int mic_bias, int event)
{
int is_other_mic_on, mask;
dev_dbg(codec->dev, "%s Called, Mic bias = %d, Event = %d\n",
__func__, mic_bias, event);
is_other_mic_on = snd_soc_read(codec, COD3035X_10_PD_REF);
if (mic_bias == COD3035X_MICBIAS1) {
is_other_mic_on &= PDB_MCB2_CODEC_MASK;
mask = is_other_mic_on ? PDB_MCB1_MASK :
PDB_MCB1_MASK | PDB_MCB_LDO_CODEC_MASK;
} else if (mic_bias == COD3035X_MICBIAS2) {
is_other_mic_on &= PDB_MCB1_MASK;
mask = is_other_mic_on ? PDB_MCB2_CODEC_MASK :
PDB_MCB2_CODEC_MASK | PDB_MCB_LDO_CODEC_MASK;
} else {
dev_err(codec->dev, "%s Called , Invalid MIC ID\n", __func__);
return -1;
}
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
if (mic_bias == COD3035X_MICBIAS2)
snd_soc_update_bits(codec, COD3035X_19_CTRL_REF,
CTRM_MCB2_MASK, CTRM_MCB2_MASK);
else
snd_soc_update_bits(codec,
COD3035X_10_PD_REF, mask, mask);
break;
case SND_SOC_DAPM_POST_PMD:
if (mic_bias == COD3035X_MICBIAS2)
snd_soc_update_bits(codec, COD3035X_19_CTRL_REF,
CTRM_MCB2_MASK, 0);
else
snd_soc_update_bits(codec, COD3035X_10_PD_REF, mask, 0);
break;
}
return 0;
}
/**
* Mute mic if it is active
*
* Returns -1 if error, else 0
*/
static int cod3035x_mute_mic(struct snd_soc_codec *codec, bool on)
{
struct cod3035x_priv *cod3035x = snd_soc_codec_get_drvdata(codec);
dev_dbg(codec->dev, "%s called, %s\n", __func__,
on ? "Mute" : "Unmute");
if (on) {
mutex_lock(&cod3035x->adc_mute_lock);
cod3035x_adc_digital_mute(codec, true);
mutex_unlock(&cod3035x->adc_mute_lock);
} else {
mutex_lock(&cod3035x->adc_mute_lock);
cod3035x_adc_digital_mute(codec, false);
mutex_unlock(&cod3035x->adc_mute_lock);
}
return 0;
}
static void cod3035x_save_dac_value(struct snd_soc_codec *codec)
{
unsigned char lvol = 0x0, rvol = 0x0;
struct cod3035x_priv *cod3035x = snd_soc_codec_get_drvdata(codec);
dev_dbg(codec->dev, "%s called \n", __func__);
lvol = snd_soc_read(codec, COD3035X_51_DVOLL);
rvol = snd_soc_read(codec, COD3035X_52_DVOLR);
if (lvol == 0xff && rvol == 0xff) {
/* checking the DAC is already muted */
dev_dbg(codec->dev, "DAC is already muted.\n");
}
else {
/* save the dac gains */
cod3035x->lvol = lvol;
cod3035x->rvol = rvol;
}
}
static void cod3035x_dac_mute(struct snd_soc_codec *codec, bool on)
{
struct cod3035x_priv *cod3035x = snd_soc_codec_get_drvdata(codec);
dev_dbg(codec->dev, "%s called, %s\n", __func__,
on ? "Mute" : "Unmute");
if (on) {
cod3035x_save_dac_value(codec);
snd_soc_write(codec, COD3035X_51_DVOLL, 0xff);
snd_soc_write(codec, COD3035X_52_DVOLR, 0xff);
dev_dbg(codec->dev, "Mute: lvol = 0xff, rvol = 0xff.\n");
}
else {
snd_soc_write(codec, COD3035X_51_DVOLL, cod3035x->lvol);
snd_soc_write(codec, COD3035X_52_DVOLR, cod3035x->rvol);
dev_dbg(codec->dev, "Unmute: lvol = 0x%x, rvol = 0x%x.\n",
cod3035x->lvol, cod3035x->rvol);
}
}
/* process the button events based on the need */
void cod3035x_process_button_ev(struct snd_soc_codec *codec, int code, int on)
{
bool key_press = on ? true : false;
cod3035x_mute_mic(codec, key_press);
}
static unsigned int cod3035x_get_mic_status(struct snd_soc_codec *codec)
{
struct cod3035x_priv *cod3035x = snd_soc_codec_get_drvdata(codec);
dev_dbg(codec->dev, "%s, mic_status: 0x%x\n",
__func__, cod3035x->mic_status);
return cod3035x->mic_status;
}
static void cod3035x_set_off_mic_status(struct snd_soc_codec *codec,
unsigned int mic)
{
struct cod3035x_priv *cod3035x = snd_soc_codec_get_drvdata(codec);
cod3035x->mic_status &= ~mic;
dev_dbg(codec->dev, "%s, mic_status: 0x%x\n",
__func__, cod3035x->mic_status);
}
static void cod3035x_set_on_mic_status(struct snd_soc_codec *codec,
unsigned int mic)
{
struct cod3035x_priv *cod3035x = snd_soc_codec_get_drvdata(codec);
cod3035x->mic_status |= mic;
dev_dbg(codec->dev, "%s, mic_status: 0x%x\n",
__func__, cod3035x->mic_status);
}
static int cod3035_power_on_mic1(struct snd_soc_codec *codec)
{
struct cod3035x_priv *cod3035x = snd_soc_codec_get_drvdata(codec);
unsigned int mix_val1, mix_val2 = 0;
dev_dbg(codec->dev, "%s called\n", __func__);
mix_val1 = snd_soc_read(codec, COD3035X_25_MIX_AD1);
mix_val1 &= EN_MIX_MIC1L_MASK | EN_MIX_MIC1R_MASK;
mix_val2 = snd_soc_read(codec, COD3035X_26_MIX_ADC);
mix_val2 &= EN_MIX_MIC1C_MASK;
/* Reset the mixer-switches before powering on */
snd_soc_update_bits(codec, COD3035X_25_MIX_AD1,
EN_MIX_MIC1L_MASK | EN_MIX_MIC1R_MASK, 0x0);
snd_soc_update_bits(codec, COD3035X_26_MIX_ADC,
EN_MIX_MIC1C_MASK, 0x0);
/* ADC Current reference ON */
snd_soc_update_bits(codec, COD3035X_10_PD_REF,
PDB_IGEN_AD_MASK, PDB_IGEN_AD_MASK);
/* MIC1 On */
snd_soc_update_bits(codec, COD3035X_12_PD_AD2,
PDB_MIC_BST1_MASK|PDB_MIC_PGA1_MASK,
PDB_MIC_BST1_MASK|PDB_MIC_PGA1_MASK);
/* Mixer on */
snd_soc_update_bits(codec, COD3035X_11_PD_AD1,
PDB_MIXL_MASK|PDB_MIXR_MASK,
PDB_MIXL_MASK|PDB_MIXR_MASK);
snd_soc_update_bits(codec, COD3035X_13_PD_AD3,
PDB_MIXC_MASK, PDB_MIXC_MASK);
/* DSM on */
snd_soc_update_bits(codec, COD3035X_11_PD_AD1,
PDB_DSML_MASK|PDB_DSMR_MASK,
PDB_DSML_MASK|PDB_DSMR_MASK);
snd_soc_update_bits(codec, COD3035X_13_PD_AD3,
PDB_DSMC_MASK, PDB_DSMC_MASK);
/* Reset Micbias1 on */
snd_soc_update_bits(codec, COD3035X_13_PD_AD3, RESETB_BST1_MASK, 0);
/* Mixer path selection */
snd_soc_update_bits(codec, COD3035X_25_MIX_AD1,
EN_MIX_MIC1L_MASK | EN_MIX_MIC1R_MASK, mix_val1);
snd_soc_update_bits(codec, COD3035X_26_MIX_ADC,
EN_MIX_MIC1C_MASK, mix_val2);
msleep(50);
/* EN_BST_DIODE, RESETB_BST1 OFF */
snd_soc_update_bits(codec, COD3035X_13_PD_AD3, EN_BST_DIODE_MASK, 0);
snd_soc_update_bits(codec, COD3035X_13_PD_AD3,
RESETB_BST1_MASK, RESETB_BST1_MASK);
if (cod3035x->capture_aifrate == COD3035X_SAMPLE_RATE_192KHZ)
snd_soc_write(codec, COD3035X_49_DMIC1, 0x01);
/* VREFP_AD Pre Charging OFF */
snd_soc_update_bits(codec, COD3035X_11_PD_AD1,
EN_DSML_PREQ_MASK|EN_DSMR_PREQ_MASK, 0);
snd_soc_update_bits(codec, COD3035X_13_PD_AD3,
EN_DSMC_PREQ_MASK, 0);
/* Fast VMID Charging OFF */
snd_soc_update_bits(codec, COD3035X_19_CTRL_REF,
CTMF_VMID_MASK, (CTMF_VMID_60K_OM << CTMF_VMID_SHIFT));
/* DSM RESET OFF */
snd_soc_update_bits(codec, COD3035X_11_PD_AD1,
RESETB_DSML_MASK|RESETB_DSMR_MASK,
RESETB_DSML_MASK|RESETB_DSMR_MASK);
snd_soc_update_bits(codec, COD3035X_13_PD_AD3,
RESETB_DSMC_MASK, RESETB_DSMC_MASK);
cod3035x_set_on_mic_status(codec, MIC1_FLAG);
return 0;
}
static int cod3035_power_on_dmic1(struct snd_soc_codec *codec)
{
struct cod3035x_priv *cod3035x = snd_soc_codec_get_drvdata(codec);
unsigned int dmic;
dev_dbg(codec->dev, "%s called\n", __func__);
dmic = snd_soc_read(codec, COD3035X_49_DMIC1);
if (!dmic) {
dev_dbg(codec->dev, "%s, need to set dmic mux setting. lmux: %d, rmux: %d\n",
__func__, cod3035x->dmic1_lmux, cod3035x->dmic1_rmux);
snd_soc_update_bits(codec, COD3035X_49_DMIC1,
SEL_DMIC_L_MASK | SEL_DMIC_R_MASK,
cod3035x->dmic1_lmux << SEL_DMIC_L_SHIFT |
cod3035x->dmic1_rmux << SEL_DMIC_R_SHIFT);
}
snd_soc_update_bits(codec, COD3035X_49_DMIC1,
EN_DMIC_MASK, EN_DMIC_MASK);
snd_soc_update_bits(codec, COD3035X_4A_DMIC2,
DMIC_OSR_MASK, OSR64 << DMIC_OSR_SHIFT);
return 0;
}
static int cod3035_power_off_mic1(struct snd_soc_codec *codec)
{
int other_mic;
dev_dbg(codec->dev, "%s called\n", __func__);
other_mic = snd_soc_read(codec, COD3035X_6C_MIC_ON);
other_mic &= (EN_MIC2_MASK | EN_MIC3_MASK | EN_LN_MASK);
cod3035x_set_off_mic_status(codec, MIC1_FLAG);
if (!other_mic) {
/* DSM RESET ON */
snd_soc_update_bits(codec, COD3035X_11_PD_AD1,
RESETB_DSML_MASK|RESETB_DSMR_MASK, 0);
snd_soc_update_bits(codec,
COD3035X_13_PD_AD3, RESETB_DSMC_MASK, 0);
}
if (!other_mic) {
/* DSM OFF */
snd_soc_update_bits(codec, COD3035X_11_PD_AD1,
PDB_DSML_MASK|PDB_DSMR_MASK, 0);
snd_soc_update_bits(codec,
COD3035X_13_PD_AD3, PDB_DSMC_MASK, 0);
}
if (!other_mic) {
/* MIXER OFF */
snd_soc_update_bits(codec, COD3035X_11_PD_AD1,
PDB_MIXL_MASK|PDB_MIXR_MASK, 0);
snd_soc_update_bits(codec,
COD3035X_13_PD_AD3, PDB_MIXC_MASK, 0);
}
/* MIC1 OFF */
snd_soc_update_bits(codec, COD3035X_12_PD_AD2,
PDB_MIC_BST1_MASK|PDB_MIC_PGA1_MASK, 0);
if (!cod3035x_get_mic_status(codec))
snd_soc_update_bits(codec, COD3035X_10_PD_REF,
PDB_IGEN_AD_MASK, 0);
return 0;
}
static int cod3035_power_off_dmic1(struct snd_soc_codec *codec)
{
struct cod3035x_priv *cod3035x = snd_soc_codec_get_drvdata(codec);
int other_mic;
unsigned int dmic;
dev_dbg(codec->dev, "%s called\n", __func__);
other_mic = snd_soc_read(codec, COD3035X_6C_MIC_ON);
other_mic &= EN_DMIC2_MASK;
if (!other_mic) {
/* Save dmic mux setting */
dmic = snd_soc_read(codec, COD3035X_49_DMIC1);
cod3035x->dmic1_lmux = dmic & SEL_DMIC_L_MASK;
cod3035x->dmic1_rmux = dmic & SEL_DMIC_R_MASK;
snd_soc_update_bits(codec, COD3035X_4E_DMIC3,
DMIC_CLK_ZTE_MASK, DMIC_CLK_ZTE_MASK);
snd_soc_write(codec, COD3035X_49_DMIC1, 0x00);
snd_soc_update_bits(codec, COD3035X_4A_DMIC2,
DMIC_GAIN1_MASK | DMIC_OSR_MASK,
LEVEL1 << DMIC_GAIN1_SHIFT | OSR64 << DMIC_OSR_SHIFT);
snd_soc_update_bits(codec, COD3035X_4E_DMIC3,
DMIC_CLK_ZTE_MASK, 0);
}
return 0;
}
static int cod3035_power_on_mic2(struct snd_soc_codec *codec)
{
struct cod3035x_priv *cod3035x = snd_soc_codec_get_drvdata(codec);
unsigned int mix_val1, mix_val2 = 0;
dev_dbg(codec->dev, "%s called\n", __func__);
mix_val1 = snd_soc_read(codec, COD3035X_25_MIX_AD1);
mix_val1 &= EN_MIX_MIC2L_MASK | EN_MIX_MIC2R_MASK;
mix_val2 = snd_soc_read(codec, COD3035X_26_MIX_ADC);
mix_val2 &= EN_MIX_MIC2C_MASK;
/* Reset the mixer-switches before powering on */
snd_soc_update_bits(codec,
COD3035X_25_MIX_AD1,
EN_MIX_MIC2L_MASK | EN_MIX_MIC2R_MASK, 0x0);
snd_soc_update_bits(codec, COD3035X_26_MIX_ADC,
EN_MIX_MIC2C_MASK, 0x0);
/* ADC Current reference ON */
snd_soc_update_bits(codec, COD3035X_10_PD_REF,
PDB_IGEN_AD_MASK, PDB_IGEN_AD_MASK);
/* MIC2 On */
snd_soc_update_bits(codec, COD3035X_12_PD_AD2,
PDB_MIC_BST2_MASK|PDB_MIC_PGA2_MASK,
PDB_MIC_BST2_MASK|PDB_MIC_PGA2_MASK);
/* Mixer on */
snd_soc_update_bits(codec, COD3035X_11_PD_AD1,
PDB_MIXL_MASK|PDB_MIXR_MASK,
PDB_MIXL_MASK|PDB_MIXR_MASK);
snd_soc_update_bits(codec, COD3035X_13_PD_AD3,
PDB_MIXC_MASK, PDB_MIXC_MASK);
/* DSM on */
snd_soc_update_bits(codec, COD3035X_11_PD_AD1,
PDB_DSML_MASK|PDB_DSMR_MASK,
PDB_DSML_MASK|PDB_DSMR_MASK);
snd_soc_update_bits(codec, COD3035X_13_PD_AD3,
PDB_DSMC_MASK, PDB_DSMC_MASK);
/* Reset Micbias2 on */
snd_soc_update_bits(codec, COD3035X_13_PD_AD3, RESETB_BST2_MASK, 0);
/* Mixer path selection */
snd_soc_update_bits(codec, COD3035X_25_MIX_AD1,
EN_MIX_MIC2L_MASK | EN_MIX_MIC2R_MASK, mix_val1);
snd_soc_update_bits(codec, COD3035X_26_MIX_ADC,
EN_MIX_MIC2C_MASK, mix_val2);
msleep(50);
/* EN_BST_DIODE, RESETB_BST2 OFF */
snd_soc_update_bits(codec, COD3035X_13_PD_AD3, EN_BST_DIODE_MASK, 0);
snd_soc_update_bits(codec, COD3035X_13_PD_AD3,
RESETB_BST2_MASK, RESETB_BST2_MASK);
if (cod3035x->capture_aifrate == COD3035X_SAMPLE_RATE_192KHZ)
snd_soc_write(codec, COD3035X_49_DMIC1, 0x10);
/* VREFP_AD Pre Charging OFF */
snd_soc_update_bits(codec, COD3035X_11_PD_AD1,
EN_DSML_PREQ_MASK|EN_DSMR_PREQ_MASK, 0);
snd_soc_update_bits(codec, COD3035X_13_PD_AD3,
EN_DSMC_PREQ_MASK, 0);
/* Fast VMID Charging OFF */
snd_soc_update_bits(codec, COD3035X_19_CTRL_REF,
CTMF_VMID_MASK, (CTMF_VMID_60K_OM << CTMF_VMID_SHIFT));
/* DSM RESET OFF */
snd_soc_update_bits(codec, COD3035X_11_PD_AD1,
RESETB_DSML_MASK|RESETB_DSMR_MASK,
RESETB_DSML_MASK|RESETB_DSMR_MASK);
snd_soc_update_bits(codec, COD3035X_13_PD_AD3,
RESETB_DSMC_MASK, RESETB_DSMC_MASK);
cod3035x_set_on_mic_status(codec, MIC2_FLAG);
return 0;
}
static int cod3035_power_on_dmic2(struct snd_soc_codec *codec)
{
struct cod3035x_priv *cod3035x = snd_soc_codec_get_drvdata(codec);
unsigned int dmic;
dev_dbg(codec->dev, "%s called\n", __func__);
dmic = snd_soc_read(codec, COD3035X_49_DMIC1);
if (!dmic) {
dev_dbg(codec->dev, "%s, need to set dmic mux setting. lmux: %d, rmux: %d\n",
__func__, cod3035x->dmic1_lmux, cod3035x->dmic1_rmux);
snd_soc_update_bits(codec, COD3035X_49_DMIC1,
SEL_DMIC_L_MASK | SEL_DMIC_R_MASK,
cod3035x->dmic1_lmux << SEL_DMIC_L_SHIFT |
cod3035x->dmic1_rmux << SEL_DMIC_R_SHIFT);
}
snd_soc_update_bits(codec, COD3035X_49_DMIC1,
EN_DMIC_MASK, EN_DMIC_MASK);
snd_soc_update_bits(codec, COD3035X_4A_DMIC2,
DMIC_OSR_MASK, OSR64 << DMIC_OSR_SHIFT);
return 0;
}
static int cod3035_power_off_mic2(struct snd_soc_codec *codec)
{
int other_mic;
dev_dbg(codec->dev, "%s called\n", __func__);
other_mic = snd_soc_read(codec, COD3035X_6C_MIC_ON);
other_mic &= (EN_MIC1_MASK | EN_MIC3_MASK | EN_LN_MASK);
cod3035x_set_off_mic_status(codec, MIC2_FLAG);
if (!other_mic) {
/* DSM RESET ON */
snd_soc_update_bits(codec, COD3035X_11_PD_AD1,
RESETB_DSML_MASK|RESETB_DSMR_MASK, 0);
snd_soc_update_bits(codec,
COD3035X_13_PD_AD3,
RESETB_DSMC_MASK, 0);
}
if (!other_mic) {
/* DSM OFF */
snd_soc_update_bits(codec, COD3035X_11_PD_AD1,
PDB_DSML_MASK|PDB_DSMR_MASK, 0);
snd_soc_update_bits(codec,
COD3035X_13_PD_AD3, PDB_DSMC_MASK, 0);
}
if (!other_mic) {
/* MIXER OFF */
snd_soc_update_bits(codec, COD3035X_11_PD_AD1,
PDB_MIXL_MASK|PDB_MIXR_MASK, 0);
snd_soc_update_bits(codec,
COD3035X_13_PD_AD3, PDB_MIXC_MASK, 0);
}
/* MIC2 OFF */
snd_soc_update_bits(codec, COD3035X_12_PD_AD2,
PDB_MIC_BST2_MASK|PDB_MIC_PGA2_MASK, 0);
if (!cod3035x_get_mic_status(codec))
snd_soc_update_bits(codec, COD3035X_10_PD_REF,
PDB_IGEN_AD_MASK, 0);
return 0;
}
static int cod3035_power_off_dmic2(struct snd_soc_codec *codec)
{
struct cod3035x_priv *cod3035x = snd_soc_codec_get_drvdata(codec);
int other_mic;
unsigned int dmic;
dev_dbg(codec->dev, "%s called\n", __func__);
other_mic = snd_soc_read(codec, COD3035X_6C_MIC_ON);
other_mic &= EN_DMIC1_MASK;
if (!other_mic) {
/* Save dmic mux setting */
dmic = snd_soc_read(codec, COD3035X_49_DMIC1);
cod3035x->dmic1_lmux = dmic & SEL_DMIC_L_MASK;
cod3035x->dmic1_rmux = dmic & SEL_DMIC_R_MASK;
snd_soc_update_bits(codec, COD3035X_4E_DMIC3,
DMIC_CLK_ZTE_MASK, DMIC_CLK_ZTE_MASK);
snd_soc_write(codec, COD3035X_49_DMIC1, 0x00);
snd_soc_update_bits(codec, COD3035X_4A_DMIC2,
DMIC_GAIN1_MASK | DMIC_OSR_MASK,
LEVEL1 << DMIC_GAIN1_SHIFT | OSR64 << DMIC_OSR_SHIFT);
snd_soc_update_bits(codec, COD3035X_4E_DMIC3,
DMIC_CLK_ZTE_MASK, 0);
}
return 0;
}
static int cod3035_power_on_mic3(struct snd_soc_codec *codec)
{
unsigned int mix_val1, mix_val2 = 0;
dev_dbg(codec->dev, "%s called\n", __func__);
mix_val1 = snd_soc_read(codec, COD3035X_25_MIX_AD1);
mix_val1 &= EN_MIX_MIC3L_MASK | EN_MIX_MIC3R_MASK;
mix_val2 = snd_soc_read(codec, COD3035X_26_MIX_ADC);
mix_val2 &= EN_MIX_MIC3C_MASK;
/* Reset the mixer-switches before powering on */
snd_soc_update_bits(codec, COD3035X_25_MIX_AD1,
EN_MIX_MIC3L_MASK | EN_MIX_MIC3R_MASK, 0x0);
snd_soc_update_bits(codec, COD3035X_26_MIX_ADC,
EN_MIX_MIC3C_MASK, 0x0);
/* ADC Current reference ON */
snd_soc_update_bits(codec, COD3035X_10_PD_REF,
PDB_IGEN_AD_MASK, PDB_IGEN_AD_MASK);
/* MIC3 On */
snd_soc_update_bits(codec, COD3035X_12_PD_AD2,
PDB_MIC_BST3_MASK|PDB_MIC_PGA3_MASK,
PDB_MIC_BST3_MASK|PDB_MIC_PGA3_MASK);
/* Mixer on */
snd_soc_update_bits(codec, COD3035X_11_PD_AD1,
PDB_MIXL_MASK|PDB_MIXR_MASK,
PDB_MIXL_MASK|PDB_MIXR_MASK);
snd_soc_update_bits(codec, COD3035X_13_PD_AD3,
PDB_MIXC_MASK, PDB_MIXC_MASK);
/* DSM on */
snd_soc_update_bits(codec, COD3035X_11_PD_AD1,
PDB_DSML_MASK|PDB_DSMR_MASK,
PDB_DSML_MASK|PDB_DSMR_MASK);
snd_soc_update_bits(codec, COD3035X_13_PD_AD3,
PDB_DSMC_MASK, PDB_DSMC_MASK);
/* Reset Micbias3 on */
snd_soc_update_bits(codec,
COD3035X_13_PD_AD3, RESETB_BST3_MASK, 0);
/* Mixer path selection */
snd_soc_update_bits(codec, COD3035X_25_MIX_AD1,
EN_MIX_MIC3L_MASK | EN_MIX_MIC3R_MASK, mix_val1);
snd_soc_update_bits(codec, COD3035X_26_MIX_ADC,
EN_MIX_MIC3C_MASK, mix_val2);
msleep(50);
/* EN_BST_DIODE, RESETB_BST2 OFF */
snd_soc_update_bits(codec,
COD3035X_13_PD_AD3, EN_BST_DIODE_MASK, 0);
snd_soc_update_bits(codec, COD3035X_13_PD_AD3,
RESETB_BST3_MASK, RESETB_BST3_MASK);
/* VREFP_AD Pre Charging OFF */
snd_soc_update_bits(codec, COD3035X_11_PD_AD1,
EN_DSML_PREQ_MASK|EN_DSMR_PREQ_MASK, 0);
snd_soc_update_bits(codec, COD3035X_13_PD_AD3,
EN_DSMC_PREQ_MASK, 0);
/* Fast VMID Charging OFF */
snd_soc_update_bits(codec, COD3035X_19_CTRL_REF,
CTMF_VMID_MASK,
(CTMF_VMID_60K_OM << CTMF_VMID_SHIFT));
/* DSM RESET OFF */
snd_soc_update_bits(codec, COD3035X_11_PD_AD1,
RESETB_DSML_MASK|RESETB_DSMR_MASK,
RESETB_DSML_MASK|RESETB_DSMR_MASK);
snd_soc_update_bits(codec, COD3035X_13_PD_AD3,
RESETB_DSMC_MASK, RESETB_DSMC_MASK);
cod3035x_set_on_mic_status(codec, MIC3_FLAG);
return 0;
}
static int cod3035_power_off_mic3(struct snd_soc_codec *codec)
{
int other_mic;
dev_dbg(codec->dev, "%s called\n", __func__);
other_mic = snd_soc_read(codec, COD3035X_6C_MIC_ON);
other_mic &= (EN_MIC1_MASK | EN_MIC2_MASK | EN_LN_MASK);
cod3035x_set_off_mic_status(codec, MIC3_FLAG);
if (!other_mic) {
/* DSM RESET ON */
snd_soc_update_bits(codec, COD3035X_11_PD_AD1,
RESETB_DSML_MASK|RESETB_DSMR_MASK, 0);
snd_soc_update_bits(codec,
COD3035X_13_PD_AD3, RESETB_DSMC_MASK, 0);
}
if (!other_mic) {
/* DSM OFF */
snd_soc_update_bits(codec, COD3035X_11_PD_AD1,
PDB_DSML_MASK|PDB_DSMR_MASK, 0);
snd_soc_update_bits(codec,
COD3035X_13_PD_AD3, PDB_DSMC_MASK, 0);
}
if (!other_mic) {
/* MIXER OFF */
snd_soc_update_bits(codec, COD3035X_11_PD_AD1,
PDB_MIXL_MASK|PDB_MIXR_MASK, 0);
snd_soc_update_bits(codec,
COD3035X_13_PD_AD3, PDB_MIXC_MASK, 0);
}
/* MIC3 OFF */
snd_soc_update_bits(codec, COD3035X_12_PD_AD2,
PDB_MIC_BST3_MASK|PDB_MIC_PGA3_MASK, 0);
if (!cod3035x_get_mic_status(codec))
snd_soc_update_bits(codec, COD3035X_10_PD_REF,
PDB_IGEN_AD_MASK, 0);
return 0;
}
static int cod3035_power_on_linein(struct snd_soc_codec *codec)
{
unsigned int mix_val1, mix_val2, mix_val3 = 0;
dev_dbg(codec->dev, "%s called\n", __func__);
mix_val1 = snd_soc_read(codec, COD3035X_25_MIX_AD1);
mix_val1 &= EN_MIX_LNLL_MASK | EN_MIX_LNRR_MASK;
mix_val2 = snd_soc_read(codec, COD3035X_26_MIX_ADC);
mix_val2 &= EN_MIX_LNLC_MASK | EN_MIX_LNRC_MASK;
mix_val3 = snd_soc_read(codec, COD3035X_27_MIX_AD2);
mix_val3 &= EN_MIX_LNLR_MASK | EN_MIX_LNRL_MASK;
/* Reset the mixer-switches before powering on */
snd_soc_update_bits(codec, COD3035X_25_MIX_AD1,
EN_MIX_LNLL_MASK | EN_MIX_LNRR_MASK, 0x0);
snd_soc_update_bits(codec, COD3035X_26_MIX_ADC,
EN_MIX_LNLC_MASK | EN_MIX_LNRC_MASK, 0x0);
snd_soc_update_bits(codec, COD3035X_27_MIX_AD2,
EN_MIX_LNLR_MASK | EN_MIX_LNRL_MASK, 0x0);
/* RESETB_LN ON */
snd_soc_update_bits(codec, COD3035X_28_DSM_ADS,
RESETB_LN_MASK, RESETB_LN_MASK);
/* IGEN_AD ON */
snd_soc_update_bits(codec, COD3035X_10_PD_REF,
PDB_IGEN_AD_MASK, PDB_IGEN_AD_MASK);
/* Mixer on */
snd_soc_update_bits(codec, COD3035X_11_PD_AD1,
PDB_MIXL_MASK|PDB_MIXR_MASK,
PDB_MIXL_MASK|PDB_MIXR_MASK);
/* DSM on */
snd_soc_update_bits(codec, COD3035X_11_PD_AD1,
PDB_DSML_MASK|PDB_DSMR_MASK,
PDB_DSML_MASK|PDB_DSMR_MASK);
/* Mixer path selection */
snd_soc_update_bits(codec, COD3035X_25_MIX_AD1,
EN_MIX_LNLL_MASK | EN_MIX_LNRR_MASK, mix_val1);
snd_soc_update_bits(codec, COD3035X_26_MIX_ADC,
EN_MIX_LNLC_MASK | EN_MIX_LNRC_MASK, mix_val2);
snd_soc_update_bits(codec, COD3035X_27_MIX_AD2,
EN_MIX_LNLR_MASK | EN_MIX_LNRL_MASK, mix_val3);
msleep(50);
/* RESETB_LN OFF */
snd_soc_update_bits(codec, COD3035X_28_DSM_ADS,
RESETB_LN_MASK, 0);
/* VREFP_AD Pre Charging OFF */
snd_soc_update_bits(codec, COD3035X_11_PD_AD1,
EN_DSML_PREQ_MASK|EN_DSMR_PREQ_MASK, 0);
/* Fast VMID Charging OFF */
snd_soc_update_bits(codec, COD3035X_19_CTRL_REF,
CTMF_VMID_MASK,
(CTMF_VMID_60K_OM << CTMF_VMID_SHIFT));
/* DSM RESET OFF */
snd_soc_update_bits(codec, COD3035X_11_PD_AD1,
RESETB_DSML_MASK|RESETB_DSMR_MASK,
RESETB_DSML_MASK|RESETB_DSMR_MASK);
cod3035x_set_on_mic_status(codec, LN_FLAG);
return 0;
}
static int cod3035_power_off_linein(struct snd_soc_codec *codec)
{
int other_mic;
dev_dbg(codec->dev, "%s called\n", __func__);
other_mic = snd_soc_read(codec, COD3035X_6C_MIC_ON);
other_mic &= (EN_MIC1_MASK | EN_MIC2_MASK | EN_MIC3_MASK);
cod3035x_set_off_mic_status(codec, LN_FLAG);
if (!other_mic) {
/* DSM RESET ON */
snd_soc_update_bits(codec, COD3035X_11_PD_AD1,
RESETB_DSML_MASK|RESETB_DSMR_MASK, 0);
}
if (!other_mic) {
/* DSM OFF */
snd_soc_update_bits(codec, COD3035X_11_PD_AD1,
PDB_DSML_MASK|PDB_DSMR_MASK, 0);
}
if (!other_mic) {
/* MIXER OFF */
snd_soc_update_bits(codec, COD3035X_11_PD_AD1,
PDB_MIXL_MASK|PDB_MIXR_MASK, 0);
snd_soc_update_bits(codec,
COD3035X_13_PD_AD3, PDB_MIXC_MASK, 0);
}
/* LINE OFF */
snd_soc_update_bits(codec, COD3035X_12_PD_AD2,
PDB_LNL_MASK | PDB_LNR_MASK, 0);
if (!cod3035x_get_mic_status(codec))
snd_soc_update_bits(codec, COD3035X_10_PD_REF,
PDB_IGEN_AD_MASK, 0);
return 0;
}
static int vmid_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
dev_dbg(codec->dev, "%s called\n", __func__);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
cod3035x_capture_init(codec);
break;
case SND_SOC_DAPM_POST_PMU:
break;
case SND_SOC_DAPM_PRE_PMD:
cod3035x_capture_deinit(codec);
break;
default:
break;
}
return 0;
}
static int dvmid_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
dev_dbg(codec->dev, "%s called\n", __func__);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
cod3035x_dmic_capture_init(codec);
break;
case SND_SOC_DAPM_POST_PMU:
break;
case SND_SOC_DAPM_PRE_PMD:
cod3035x_dmic_capture_deinit(codec);
break;
default:
break;
}
return 0;
}
static int cod3035x_hp_playback_init(struct snd_soc_codec *codec)
{
dev_dbg(codec->dev, "%s called\n", __func__);
/* 0x45: |0x10, DAC Input Data Selection */
snd_soc_update_bits(codec, COD3035X_45_IF1_FORMAT5, 0x10, 0x10);
snd_soc_write(codec, COD3035X_73_DSM1_DA, 0x20);
snd_soc_write(codec, COD3035X_74_DSM2_DA, 0x89);
cod3035x_usleep(100);
return 0;
}
/* 0, 1: LEGACY mode */
static int post_hpdrv_legacy(struct snd_soc_codec *codec,
int avc_val, int hp_avol)
{
dev_dbg(codec->dev, "%s called\n", __func__);
if (avc_val & AVC_BYPS_MASK) {
snd_soc_update_bits(codec, COD3035X_54_AVC1,
DAC_VOL_BYPS_MASK | AVC_BYPS_MASK,
DAC_VOL_BYPS_MASK | AVC_BYPS_MASK);
snd_soc_write(codec, COD3035X_B9_AUTO_HP10, hp_avol);
}
snd_soc_update_bits(codec, COD3035X_37_MIX_DA1,
EN_HP_MIXL_DCTL_MASK | EN_HP_MIXR_DCTR_MASK,
EN_HP_MIXL_DCTL_MASK | EN_HP_MIXR_DCTR_MASK);
snd_soc_update_bits(codec, COD3035X_18_PWAUTO_DA,
APW_HP_MASK, APW_HP_MASK);
msleep(135);
cod3035x_dac_mute(codec, false);
snd_soc_write(codec, COD3035X_BA_AUTO_HP11, 0x05);
snd_soc_update_bits(codec, COD3035X_3E_OVP_2,
LOCK_UP_TIME_SLOT_MASK | OVP_APON_MASK,
LOCK_UP_TIME_1_5 << LOCK_UP_TIME_SLOT_SHIFT | OVP_APON_MASK);
if (avc_val & AVC_BYPS_MASK)
snd_soc_write(codec, COD3035X_BE_ODSEL2, 0x60);
return 0;
}
/* 2: NORMAL_mode */
static int post_hpdrv_normal(struct snd_soc_codec *codec)
{
dev_dbg(codec->dev, "%s called\n", __func__);
//snd_soc_write(codec, COD3035X_54_AVC1, 0x03);
snd_soc_write(codec, 0x33, 0x00);
snd_soc_write(codec, 0xBB, 0x33);
snd_soc_update_bits(codec, COD3035X_37_MIX_DA1,
EN_HP_MIXL_DCTL_MASK | EN_HP_MIXR_DCTR_MASK,
EN_HP_MIXL_DCTL_MASK | EN_HP_MIXR_DCTR_MASK);
snd_soc_update_bits(codec, COD3035X_18_PWAUTO_DA,
APW_HP_MASK, APW_HP_MASK);
msleep(135);
cod3035x_dac_mute(codec, false);
snd_soc_update_bits(codec, COD3035X_3E_OVP_2,
LOCK_UP_TIME_SLOT_MASK | OVP_APON_MASK,
LOCK_UP_TIME_1_5 << LOCK_UP_TIME_SLOT_SHIFT | OVP_APON_MASK);
return 0;
}
/* 3: UHQA_mode */
static int post_hpdrv_uhqa(struct snd_soc_codec *codec)
{
dev_dbg(codec->dev, "%s called\n", __func__);
//snd_soc_write(codec, COD3035X_54_AVC1, 0x03);
snd_soc_write(codec, 0x33, 0x00);
snd_soc_update_bits(codec, COD3035X_37_MIX_DA1,
EN_HP_MIXL_DCTL_MASK | EN_HP_MIXR_DCTR_MASK,
EN_HP_MIXL_DCTL_MASK | EN_HP_MIXR_DCTR_MASK);
snd_soc_write(codec, COD3035X_BB_AUTO_HP12, 0x66);
snd_soc_write(codec, COD3035X_B8_AUTO_HP9, 0x55);
snd_soc_update_bits(codec, COD3035X_18_PWAUTO_DA,
APW_HP_MASK, APW_HP_MASK);
msleep(135);
cod3035x_dac_mute(codec, false);
snd_soc_write(codec, COD3035X_3E_OVP_2, 0x12);
return 0;
}
/* EAR Power Off */
static int normal_ear_power_off(struct snd_soc_codec *codec, int ep_on)
{
dev_dbg(codec->dev, "%s called\n", __func__);
/* 0x18 <-- 0x00: ~0x02, HP Path Auto Power Off */
if (ep_on) {
snd_soc_update_bits(codec, COD3035X_18_PWAUTO_DA,
APW_HP_MASK, 0);
} else {
snd_soc_update_bits(codec, COD3035X_18_PWAUTO_DA,
PW_AUTO_DA_MASK | APW_HP_MASK, 0);
}
cod3035x_usleep(3000);
snd_soc_update_bits(codec, COD3035X_3E_OVP_2,
OVP_APON_MASK, 0);
snd_soc_update_bits(codec, COD3035X_37_MIX_DA1,
EN_HP_MIXL_DCTL_MASK | EN_HP_MIXR_DCTR_MASK, 0);
snd_soc_write(codec, COD3035X_F6_ACV_DECAY1, 0xC7);
return 0;
}
static int uhqa_ear_power_off(struct snd_soc_codec *codec, int ep_on)
{
dev_dbg(codec->dev, "%s called\n", __func__);
/* 0x18 <-- 0x00: ~0x02, HP Path Auto Power Off */
if (ep_on) {
snd_soc_update_bits(codec, COD3035X_18_PWAUTO_DA,
APW_HP_MASK, 0);
} else {
snd_soc_update_bits(codec, COD3035X_18_PWAUTO_DA,
PW_AUTO_DA_MASK | APW_HP_MASK, 0);
}
cod3035x_usleep(3000);
snd_soc_write(codec, COD3035X_D6_CTRL_IREF5, 0x59);
snd_soc_write(codec, COD3035X_DB_CTRL_HPS, 0x5E);
snd_soc_update_bits(codec, COD3035X_3E_OVP_2,
OVP_APON_MASK, 0);
snd_soc_update_bits(codec, COD3035X_37_MIX_DA1,
EN_HP_MIXL_DCTL_MASK | EN_HP_MIXR_DCTR_MASK, 0);
return 0;
}
static int legacy_ear_power_off(struct snd_soc_codec *codec, int ep_on)
{
dev_dbg(codec->dev, "%s called\n", __func__);
snd_soc_write(codec, COD3035X_BA_AUTO_HP11, 0x02);
/* 0x18 <-- 0x00: ~0x02, HP Path Auto Power Off */
if (ep_on) {
snd_soc_update_bits(codec, COD3035X_18_PWAUTO_DA,
APW_HP_MASK, 0);
} else {
snd_soc_update_bits(codec, COD3035X_18_PWAUTO_DA,
PW_AUTO_DA_MASK | APW_HP_MASK, 0);
}
cod3035x_usleep(3000);
snd_soc_update_bits(codec, COD3035X_3E_OVP_2,
OVP_APON_MASK, 0);
snd_soc_update_bits(codec, COD3035X_37_MIX_DA1,
EN_HP_MIXL_DCTL_MASK | EN_HP_MIXR_DCTR_MASK, 0);
return 0;
}
static int hpdrv_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
int hp_on, ep_on;
int chop_val;
int avc_val;
int hp_avol;
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct cod3035x_priv *cod3035x = snd_soc_codec_get_drvdata(codec);
avc_val = snd_soc_read(codec, COD3035X_54_AVC1);
hp_avol = snd_soc_read(codec, COD3035X_30_VOL_HPL);
chop_val = snd_soc_read(codec, COD3035X_77_CHOP_DA);
hp_on = chop_val & EN_HP_CHOP_MASK;
ep_on = chop_val & EN_EP_CHOP_MASK;
if (!hp_on) {
dev_dbg(codec->dev, "%s called but headphone not enabled\n",
__func__);
return 0;
}
dev_dbg(codec->dev, "%s called, event = %d, avc_mode: %d\n",
__func__, event, g_avc_mode);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
cod3035x_dac_mute(codec, true);
cod3035x_hp_playback_init(codec);
break;
case SND_SOC_DAPM_POST_PMU:
if (cod3035x->is_lassenA) {
if (cod3035x->playback_aifrate == COD3035X_SAMPLE_RATE_192KHZ)
post_hpdrv_uhqa(codec);
else
post_hpdrv_normal(codec);
} else {
post_hpdrv_legacy(codec, avc_val, hp_avol);
}
break;
case SND_SOC_DAPM_PRE_PMD:
cod3035x_dac_mute(codec, true);
msleep(100);
if (cod3035x->is_lassenA) {
if (cod3035x->playback_aifrate == COD3035X_SAMPLE_RATE_192KHZ)
uhqa_ear_power_off(codec, ep_on);
else
normal_ear_power_off(codec, ep_on);
} else {
legacy_ear_power_off(codec, ep_on);
}
break;
default:
break;
}
return 0;
}
/* special feature to damp down the DC offset on the receiver
*
* dampdown_dc_offset :
* make the DC offset convergence to 0
*/
void dampdown_dc_offset(struct snd_soc_codec *codec)
{
int damping_value = 0, index = 0, msb = 0, write_value = 0;
/* damping down the DC offset
* 0xD7 : DC offset
* 0xDA : temporary storage
*/
damping_value = snd_soc_read(codec, 0xD7);
snd_soc_write(codec, 0xDA, damping_value);
msb = damping_value & 0x80;
damping_value = damping_value & 0x7F;
for (index = damping_value; index > 0; index-=2) {
write_value = index | msb;
snd_soc_write(codec, 0xD7, write_value);
}
write_value = msb | 0x0;
snd_soc_write(codec, 0xD7, write_value);
}
/* dampdown_dc_offset :
* make the DC offset convergence to the previous saved value in 0xDA
*/
void dampup_dc_offset(struct snd_soc_codec *codec)
{
int damping_value = 0, index = 0, msb = 0, write_value = 0;
/* damping back the DC offset
* 0xD7 : DC offset
* 0xDA : temporary storage
*/
damping_value = snd_soc_read(codec, 0xDA);
msb = damping_value & 0x80;
damping_value = damping_value & 0x7F;
for (index=0; damping_value > index; index +=2 ) {
write_value = index | msb;
snd_soc_write(codec, 0xD7, write_value);
}
write_value = damping_value | msb;
snd_soc_write(codec, 0xD7, write_value);
}
static int epdrv_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
int hp_on, ep_on;
int chop_val;
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct cod3035x_priv *cod3035x = snd_soc_codec_get_drvdata(codec);
chop_val = snd_soc_read(codec, COD3035X_77_CHOP_DA);
hp_on = chop_val & EN_HP_CHOP_MASK;
ep_on = chop_val & EN_EP_CHOP_MASK;
if (!ep_on) {
dev_dbg(codec->dev, "%s called but ear-piece not enabled\n",
__func__);
return 0;
}
dev_dbg(codec->dev, "%s called, event = %d\n", __func__, event);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
snd_soc_write(codec, COD3035X_73_DSM1_DA, 0x40);
snd_soc_write(codec, COD3035X_74_DSM2_DA, 0x88);
snd_soc_update_bits(codec, COD3035X_54_AVC1,
AVC_BYPS_MASK, AVC_BYPS_MASK);
snd_soc_update_bits(codec, COD3035X_18_PWAUTO_DA,
APW_EP_MASK | PW_AUTO_DA_MASK,
APW_EP_MASK | PW_AUTO_DA_MASK);
snd_soc_update_bits(codec, COD3035X_38_MIX_DA2,
EN_EP_MIX_DCTL_MASK, EN_EP_MIX_DCTL_MASK);
msleep(136);
/* damping down the DC offset
* 0xD7 : DC offset
* 0xDA : temporary storage
*/
if (cod3035x->model_feature_flag & MODEL_FLAG_EP_DC_OFFSET_SWEEP)
dampdown_dc_offset(codec);
snd_soc_update_bits(codec, COD3035X_78_CTRL_CP,
CTMF_CP_CLK_MASK, CTMF_CP_CLK_97_5KHZ << CTMF_CP_CLK_SHIFT);
break;
case SND_SOC_DAPM_PRE_PMD:
snd_soc_update_bits(codec, COD3035X_54_AVC1,
AVC_BYPS_MASK, 0x0);
snd_soc_update_bits(codec, COD3035X_78_CTRL_CP,
CTMF_CP_CLK_MASK, CTMF_CP_CLK_780KHZ << CTMF_CP_CLK_SHIFT);
snd_soc_write(codec, COD3035X_32_VOL_EP, 0x06);
/* damping back the DC offset
* 0xD7 : DC offset
* 0xDA : temporary storage
*/
if (cod3035x->model_feature_flag & MODEL_FLAG_EP_DC_OFFSET_SWEEP)
dampup_dc_offset(codec);
if (hp_on) {
snd_soc_update_bits(codec, COD3035X_18_PWAUTO_DA,
APW_EP_MASK, 0x0);
} else {
snd_soc_update_bits(codec, COD3035X_18_PWAUTO_DA,
PW_AUTO_DA_MASK | APW_EP_MASK, 0x0);
}
cod3035x_usleep(1000);
snd_soc_update_bits(codec, COD3035X_38_MIX_DA2,
EN_EP_MIX_DCTL_MASK, 0x0);
break;
default:
break;
}
return 0;
}
static int mic1_pga_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
int mic_on;
dev_dbg(codec->dev, "%s called, event = %d\n", __func__, event);
mic_on = snd_soc_read(codec, COD3035X_6C_MIC_ON);
if (!(mic_on & EN_MIC1_MASK)) {
dev_dbg(codec->dev, "%s: MIC1 is not enabled, returning.\n",
__func__);
return 0;
}
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
cod3035_power_on_mic1(codec);
break;
case SND_SOC_DAPM_PRE_PMD:
cod3035_power_off_mic1(codec);
break;
default:
break;
}
return 0;
}
static int dmic1_pga_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
int mic_on;
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
dev_dbg(codec->dev, "%s called, event = %d\n", __func__, event);
mic_on = snd_soc_read(codec, COD3035X_6C_MIC_ON);
if (!(mic_on & EN_DMIC1_MASK)) {
dev_dbg(codec->dev, "%s: DMIC1 is not enabled, returning.\n",
__func__);
return 0;
}
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
cod3035_power_on_dmic1(codec);
break;
case SND_SOC_DAPM_PRE_PMD:
cod3035_power_off_dmic1(codec);
break;
default:
break;
}
return 0;
}
static int mic2_pga_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
int mic_on;
dev_dbg(codec->dev, "%s called, event = %d\n", __func__, event);
mic_on = snd_soc_read(codec, COD3035X_6C_MIC_ON);
if (!(mic_on & EN_MIC2_MASK)) {
dev_dbg(codec->dev, "%s: MIC2 is not enabled, returning.\n",
__func__);
return 0;
}
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
cod3035_power_on_mic2(codec);
break;
case SND_SOC_DAPM_PRE_PMD:
cod3035_power_off_mic2(codec);
break;
default:
break;
}
return 0;
}
static int dmic2_pga_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
int mic_on;
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
dev_dbg(codec->dev, "%s called, event = %d\n", __func__, event);
mic_on = snd_soc_read(codec, COD3035X_6C_MIC_ON);
if (!(mic_on & EN_DMIC2_MASK)) {
dev_dbg(codec->dev, "%s: DMIC2 is not enabled, returning.\n",
__func__);
return 0;
}
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
cod3035_power_on_dmic2(codec);
break;
case SND_SOC_DAPM_PRE_PMD:
cod3035_power_off_dmic2(codec);
break;
default:
break;
}
return 0;
}
static int mic3_pga_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
int mic_on;
dev_dbg(codec->dev, "%s called, event = %d\n", __func__, event);
mic_on = snd_soc_read(codec, COD3035X_6C_MIC_ON);
if (!(mic_on & EN_MIC3_MASK)) {
dev_dbg(codec->dev, "%s: MIC3 is not enabled, returning.\n",
__func__);
return 0;
}
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
cod3035_power_on_mic3(codec);
break;
case SND_SOC_DAPM_PRE_PMD:
cod3035_power_off_mic3(codec);
break;
default:
break;
}
return 0;
}
static int linein_pga_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
int linein_on;
dev_dbg(codec->dev, "%s called, event = %d\n", __func__, event);
linein_on = snd_soc_read(codec, COD3035X_6C_MIC_ON);
if (!(linein_on & EN_LN_MASK)) {
dev_dbg(codec->dev, "%s: LINE IN is not enabled, returning.\n",
__func__);
return 0;
}
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
cod3035_power_on_linein(codec);
break;
case SND_SOC_DAPM_PRE_PMD:
cod3035_power_off_linein(codec);
break;
default:
break;
}
return 0;
}
/*DMIC1 DMIC1 L [6:4] */
static const char * const cod3035x_dmicl_src1[] = {
"AMICL DMIC1L", "AMICR DMIC1L", "AMICC DMIC1L", "Zero DMIC1L",
"DMIC1L DMIC1L", "DMIC1R DMIC1L", "DMIC2L DMIC1L", "DMIC2R DMIC1L"
};
static SOC_ENUM_SINGLE_DECL(cod3035x_dmicl_enum1, COD3035X_49_DMIC1,
SEL_DMIC_L_SHIFT, cod3035x_dmicl_src1);
static const struct snd_kcontrol_new cod3035x_dmicl_mux1 =
SOC_DAPM_ENUM("DMICL Mux1", cod3035x_dmicl_enum1);
/*DMIC1 DMIC1 R [2:0] */
static const char * const cod3035x_dmicr_src1[] = {
"AMICR DMIC1R", "AMICL DMIC1R", "AMICC DMIC1R", "Zero DMIC1R",
"DMIC1L DMIC1R", "DMIC1R DMIC1R", "DMIC2L DMIC1R", "DMIC2R DMIC1R"
};
static SOC_ENUM_SINGLE_DECL(cod3035x_dmicr_enum1, COD3035X_49_DMIC1,
SEL_DMIC_R_SHIFT, cod3035x_dmicr_src1);
static const struct snd_kcontrol_new cod3035x_dmicr_mux1 =
SOC_DAPM_ENUM("DMICR Mux1", cod3035x_dmicr_enum1);
/*DMIC3 DMIC3 L [6:4] */
static const char * const cod3035x_dmicl_src2[] = {
"AMICL DMIC2L", "AMICR DMIC2L", "AMICC DMIC2L", "Zero DMIC2L",
"DMIC1L DMIC2L", "DMIC1R DMIC2L", "DMIC2L DMIC2L", "DMIC2R DMIC2L"
};
static SOC_ENUM_SINGLE_DECL(cod3035x_dmicl_enum2, COD3035X_4E_DMIC3,
SEL_DMIC_L_SHIFT, cod3035x_dmicl_src2);
static const struct snd_kcontrol_new cod3035x_dmicl_mux2 =
SOC_DAPM_ENUM("DMICL Mux2", cod3035x_dmicl_enum2);
/*DMIC3 DMIC3 R [2:0] */
static const char * const cod3035x_dmicr_src2[] = {
"AMICR DMIC2R", "AMICL DMIC2R", "AMICC DMIC2R", "Zero DMIC2R",
"DMIC1L DMIC2R", "DMIC1R DMIC2R", "DMIC2L DMIC2R", "DMIC2R DMIC2R"
};
static SOC_ENUM_SINGLE_DECL(cod3035x_dmicr_enum2, COD3035X_4E_DMIC3,
SEL_DMIC_R_SHIFT, cod3035x_dmicr_src2);
static const struct snd_kcontrol_new cod3035x_dmicr_mux2 =
SOC_DAPM_ENUM("DMICR Mux2", cod3035x_dmicr_enum2);
/*SEL_ADC0 [1:0] */
static const char * const cod3035x_adc_dat_src0[] = {
"ADC1", "ADC2", "ADC3", "Off"
};
static SOC_ENUM_SINGLE_DECL(cod3035x_adc_dat_enum0, COD3035X_44_IF1_FORMAT4,
SEL_ADC0_SHIFT, cod3035x_adc_dat_src0);
static const struct snd_kcontrol_new cod3035x_adc_dat_mux0 =
SOC_DAPM_ENUM("ADC DAT Mux0", cod3035x_adc_dat_enum0);
/*SEL_ADC1 [3:2] */
static const char * const cod3035x_adc_dat_src1[] = {
"ADC1", "ADC2", "ADC3", "Off"
};
static SOC_ENUM_SINGLE_DECL(cod3035x_adc_dat_enum1,
COD3035X_44_IF1_FORMAT4,
SEL_ADC1_SHIFT, cod3035x_adc_dat_src1);
static const struct snd_kcontrol_new cod3035x_adc_dat_mux1 =
SOC_DAPM_ENUM("ADC DAT Mux1", cod3035x_adc_dat_enum1);
static const struct snd_kcontrol_new adcl_mix[] = {
SOC_DAPM_SINGLE("MIC1L Switch", COD3035X_25_MIX_AD1,
EN_MIX_MIC1L_SHIFT, 1, 0),
SOC_DAPM_SINGLE("MIC2L Switch", COD3035X_25_MIX_AD1,
EN_MIX_MIC2L_SHIFT, 1, 0),
SOC_DAPM_SINGLE("MIC3L Switch", COD3035X_25_MIX_AD1,
EN_MIX_MIC3L_SHIFT, 1, 0),
SOC_DAPM_SINGLE("LINELL Switch", COD3035X_25_MIX_AD1,
EN_MIX_LNLL_SHIFT, 1, 0),
SOC_DAPM_SINGLE("LINERL Switch", COD3035X_27_MIX_AD2,
EN_MIX_LNRL_SHIFT, 1, 0),
};
static const struct snd_kcontrol_new adcr_mix[] = {
SOC_DAPM_SINGLE("MIC1R Switch", COD3035X_25_MIX_AD1,
EN_MIX_MIC1R_SHIFT, 1, 0),
SOC_DAPM_SINGLE("MIC2R Switch", COD3035X_25_MIX_AD1,
EN_MIX_MIC2R_SHIFT, 1, 0),
SOC_DAPM_SINGLE("MIC3R Switch", COD3035X_25_MIX_AD1,
EN_MIX_MIC3R_SHIFT, 1, 0),
SOC_DAPM_SINGLE("LINELR Switch", COD3035X_27_MIX_AD2,
EN_MIX_LNLR_SHIFT, 1, 0),
SOC_DAPM_SINGLE("LINERR Switch", COD3035X_25_MIX_AD1,
EN_MIX_LNRR_SHIFT, 1, 0),
};
static const struct snd_kcontrol_new adcc_mix[] = {
SOC_DAPM_SINGLE("MIC1C Switch", COD3035X_26_MIX_ADC,
EN_MIX_MIC1C_SHIFT, 1, 0),
SOC_DAPM_SINGLE("MIC2C Switch", COD3035X_26_MIX_ADC,
EN_MIX_MIC2C_SHIFT, 1, 0),
SOC_DAPM_SINGLE("MIC3C Switch", COD3035X_26_MIX_ADC,
EN_MIX_MIC3C_SHIFT, 1, 0),
SOC_DAPM_SINGLE("LINELC Switch", COD3035X_26_MIX_ADC,
EN_MIX_LNLC_SHIFT, 1, 0),
SOC_DAPM_SINGLE("LINERC Switch", COD3035X_26_MIX_ADC,
EN_MIX_LNRC_SHIFT, 1, 0),
};
static const struct snd_kcontrol_new hp_on[] = {
SOC_DAPM_SINGLE("HP On", COD3035X_77_CHOP_DA, EN_HP_CHOP_SHIFT, 1, 0),
};
static const struct snd_kcontrol_new ep_on[] = {
SOC_DAPM_SINGLE("EP On", COD3035X_77_CHOP_DA, EN_EP_CHOP_SHIFT, 1, 0),
};
static const struct snd_kcontrol_new mic1_on[] = {
SOC_DAPM_SINGLE("MIC1 On", COD3035X_6C_MIC_ON,
EN_MIC1_SHIFT, 1, 0),
};
static const struct snd_kcontrol_new mic2_on[] = {
SOC_DAPM_SINGLE("MIC2 On", COD3035X_6C_MIC_ON,
EN_MIC2_SHIFT, 1, 0),
};
static const struct snd_kcontrol_new mic3_on[] = {
SOC_DAPM_SINGLE("MIC3 On", COD3035X_6C_MIC_ON,
EN_MIC3_SHIFT, 1, 0),
};
static const char * const cod3035x_fm_texts[] = {
"None",
"FM On",
};
static SOC_ENUM_SINGLE_VIRT_DECL(cod3035x_fm_enum, cod3035x_fm_texts);
static const struct snd_kcontrol_new cod3035x_fm_mux[] = {
SOC_DAPM_ENUM("FM Link", cod3035x_fm_enum),
};
static const struct snd_kcontrol_new linein_on[] = {
SOC_DAPM_SINGLE("LINEIN On", COD3035X_6C_MIC_ON,
EN_LN_SHIFT, 1, 0),
};
static const struct snd_kcontrol_new dmic1_on[] = {
SOC_DAPM_SINGLE("DMIC1 On", COD3035X_6C_MIC_ON,
EN_DMIC1_SHIFT, 1, 0),
};
static const struct snd_kcontrol_new dmic2_on[] = {
SOC_DAPM_SINGLE("DMIC2 On", COD3035X_6C_MIC_ON,
EN_DMIC2_SHIFT, 1, 0),
};
static const struct snd_soc_dapm_widget cod3035x_dapm_widgets[] = {
SND_SOC_DAPM_SWITCH("HP", SND_SOC_NOPM, 0, 0, hp_on),
SND_SOC_DAPM_SWITCH("EP", SND_SOC_NOPM, 0, 0, ep_on),
SND_SOC_DAPM_SWITCH("MIC1", SND_SOC_NOPM, 0, 0, mic1_on),
SND_SOC_DAPM_SWITCH("MIC2", SND_SOC_NOPM, 0, 0, mic2_on),
SND_SOC_DAPM_SWITCH("MIC3", SND_SOC_NOPM, 0, 0, mic3_on),
SND_SOC_DAPM_SWITCH("LINEIN", SND_SOC_NOPM, 0, 0, linein_on),
SND_SOC_DAPM_SWITCH("DMIC1", SND_SOC_NOPM, 0, 0, dmic1_on),
SND_SOC_DAPM_SWITCH("DMIC2", SND_SOC_NOPM, 0, 0, dmic2_on),
SND_SOC_DAPM_SUPPLY("VMID", SND_SOC_NOPM, 0, 0, vmid_ev,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_SUPPLY("DVMID", SND_SOC_NOPM, 0, 0, dvmid_ev,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_OUT_DRV_E("EPDRV", SND_SOC_NOPM, 0, 0, NULL, 0,
epdrv_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_OUT_DRV_E("HPDRV", SND_SOC_NOPM, 0, 0, NULL, 0,
hpdrv_ev, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_MUX("FM Link", SND_SOC_NOPM, 0, 0,
cod3035x_fm_mux),
SND_SOC_DAPM_PGA_E("MIC1_PGA", SND_SOC_NOPM, 0, 0,
NULL, 0, mic1_pga_ev,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_PGA_E("MIC2_PGA", SND_SOC_NOPM, 0, 0,
NULL, 0, mic2_pga_ev,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_PGA_E("MIC3_PGA", SND_SOC_NOPM, 0, 0,
NULL, 0, mic3_pga_ev,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_PGA_E("LINEIN_PGA", SND_SOC_NOPM, 0, 0,
NULL, 0, linein_pga_ev,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_PGA_E("DMIC1_PGA", SND_SOC_NOPM, 0, 0,
NULL, 0, dmic1_pga_ev,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_PGA_E("DMIC2_PGA", SND_SOC_NOPM, 0, 0,
NULL, 0, dmic2_pga_ev,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_MIXER("ADCL Mixer", SND_SOC_NOPM, 0, 0, adcl_mix,
ARRAY_SIZE(adcl_mix)),
SND_SOC_DAPM_MIXER("ADCC Mixer", SND_SOC_NOPM, 0, 0, adcc_mix,
ARRAY_SIZE(adcc_mix)),
SND_SOC_DAPM_MIXER("ADCR Mixer", SND_SOC_NOPM, 0, 0, adcr_mix,
ARRAY_SIZE(adcr_mix)),
SND_SOC_DAPM_MUX("DMICL Mux1",
SND_SOC_NOPM, 0, 0, &cod3035x_dmicl_mux1),
SND_SOC_DAPM_MUX("DMICR Mux1",
SND_SOC_NOPM, 0, 0, &cod3035x_dmicr_mux1),
SND_SOC_DAPM_MUX("DMICL Mux2",
SND_SOC_NOPM, 0, 0, &cod3035x_dmicl_mux2),
SND_SOC_DAPM_MUX("DMICR Mux2",
SND_SOC_NOPM, 0, 0, &cod3035x_dmicr_mux2),
SND_SOC_DAPM_MUX("ADC DAT Mux0",
SND_SOC_NOPM, 0, 0, &cod3035x_adc_dat_mux0),
SND_SOC_DAPM_MUX("ADC DAT Mux1",
SND_SOC_NOPM, 0, 0, &cod3035x_adc_dat_mux1),
SND_SOC_DAPM_DAC_E("DAC", "AIF Playback", SND_SOC_NOPM, 0, 0,
dac_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_DAC_E("DAC", "AIF2 Playback", SND_SOC_NOPM, 0, 0,
dac_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_ADC_E("ADC", "AIF Capture", SND_SOC_NOPM, 0, 0,
adc_ev, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_ADC_E("ADC", "AIF2 Capture", SND_SOC_NOPM, 0, 0,
adc_ev, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_ADC_E("DADC", "AIF Capture", SND_SOC_NOPM, 0, 0,
dadc_ev, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_ADC_E("DADC", "AIF2 Capture", SND_SOC_NOPM, 0, 0,
dadc_ev, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_OUTPUT("HPOUTLN"),
SND_SOC_DAPM_OUTPUT("EPOUTN"),
SND_SOC_DAPM_OUTPUT("AIF4OUT"),
SND_SOC_DAPM_INPUT("IN1L"),
SND_SOC_DAPM_INPUT("IN2L"),
SND_SOC_DAPM_INPUT("IN3L"),
SND_SOC_DAPM_INPUT("IN4L"),
SND_SOC_DAPM_INPUT("AIF4IN"),
};
static const struct snd_soc_dapm_route cod3035x_dapm_routes[] = {
/* Sink, Control, Source */
{"EPDRV", NULL, "DAC"},
{"EP", "EP On", "EPDRV"},
{"EPOUTN", NULL, "EP"},
{"HPDRV", NULL, "DAC"},
{"HP", "HP On", "HPDRV"},
{"HPOUTLN", NULL, "HP"},
{"DAC", NULL, "AIF Playback"},
{"DAC", NULL, "AIF2 Playback"},
{"MIC1_PGA", NULL, "IN1L"},
{"MIC1_PGA", NULL, "VMID"},
{"MIC1", "MIC1 On", "MIC1_PGA"},
{"ADCL Mixer", "MIC1L Switch", "MIC1"},
{"ADCC Mixer", "MIC1C Switch", "MIC1"},
{"ADCR Mixer", "MIC1R Switch", "MIC1"},
{"DMIC1_PGA", NULL, "IN1L"},
{"DMIC1_PGA", NULL, "DVMID"},
{"DMIC1", "DMIC1 On", "DMIC1_PGA"},
{"DMICL Mux1", "DMIC1L DMIC1L", "DMIC1"},
{"DMICL Mux1", "DMIC1R DMIC1L", "DMIC1"},
{"DMICR Mux1", "DMIC1L DMIC1R", "DMIC1"},
{"DMICR Mux1", "DMIC1R DMIC1R", "DMIC1"},
{"DMICL Mux1", "DMIC2L DMIC1L", "DMIC1"},
{"DMICL Mux1", "DMIC2R DMIC1L", "DMIC1"},
{"DMICR Mux1", "DMIC2L DMIC1R", "DMIC1"},
{"DMICR Mux1", "DMIC2R DMIC1R", "DMIC1"},
{"MIC2_PGA", NULL, "IN2L"},
{"MIC2_PGA", NULL, "VMID"},
{"MIC2", "MIC2 On", "MIC2_PGA"},
{"ADCL Mixer", "MIC2L Switch", "MIC2"},
{"ADCC Mixer", "MIC2C Switch", "MIC2"},
{"ADCR Mixer", "MIC2R Switch", "MIC2"},
{"DMIC2_PGA", NULL, "IN2L"},
{"DMIC2_PGA", NULL, "DVMID"},
{"DMIC2", "DMIC2 On", "DMIC2_PGA"},
{"DMICL Mux1", "DMIC1L DMIC1L", "DMIC2"},
{"DMICL Mux1", "DMIC1R DMIC1L", "DMIC2"},
{"DMICR Mux1", "DMIC1L DMIC1R", "DMIC2"},
{"DMICR Mux1", "DMIC1R DMIC1R", "DMIC2"},
{"DMICL Mux1", "DMIC2L DMIC1L", "DMIC2"},
{"DMICL Mux1", "DMIC2R DMIC1L", "DMIC2"},
{"DMICR Mux1", "DMIC2L DMIC1R", "DMIC2"},
{"DMICR Mux1", "DMIC2R DMIC1R", "DMIC2"},
{"MIC3_PGA", NULL, "IN3L"},
{"MIC3_PGA", NULL, "VMID"},
{"MIC3", "MIC3 On", "MIC3_PGA"},
{"ADCL Mixer", "MIC3L Switch", "MIC3"},
{"ADCC Mixer", "MIC3C Switch", "MIC3"},
{"ADCR Mixer", "MIC3R Switch", "MIC3"},
{"LINEIN_PGA", NULL, "IN4L"},
{"LINEIN_PGA", NULL, "VMID"},
{"LINEIN", "LINEIN On", "LINEIN_PGA"},
{"ADCL Mixer", "LINELL Switch", "LINEIN"},
{"ADCL Mixer", "LINERL Switch", "LINEIN"},
{"ADCC Mixer", "LINELC Switch", "LINEIN"},
{"ADCC Mixer", "LINERC Switch", "LINEIN"},
{"ADCR Mixer", "LINELR Switch", "LINEIN"},
{"ADCR Mixer", "LINERR Switch", "LINEIN"},
{"ADC", NULL, "ADCL Mixer"},
{"ADC", NULL, "ADCC Mixer"},
{"ADC", NULL, "ADCR Mixer"},
{"DADC", NULL, "DMICL Mux1"},
{"DADC", NULL, "DMICR Mux1"},
{"DADC", NULL, "DMICL Mux2"},
{"DADC", NULL, "DMICR Mux2"},
{"AIF Capture", NULL, "ADC"},
{"AIF2 Capture", NULL, "ADC"},
{"AIF Capture", NULL, "DADC"},
{"AIF2 Capture", NULL, "DADC"},
{"FM Link", "FM On", "ADC"},
{"DAC", NULL, "FM Link"},
};
static int cod3035x_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct snd_soc_codec *codec = dai->codec;
dev_dbg(codec->dev, "%s called. fmt: %d\n", __func__, fmt);
/* I2S Mode */
snd_soc_write(codec, COD3035X_41_IF1_FORMAT1, 0x00);
/* length set to 16 bits */
snd_soc_write(codec, COD3035X_42_IF1_FORMAT2, 0x10);
/* BCLK : 32fs */
snd_soc_write(codec, COD3035X_43_IF1_FORMAT3, 0x20);
return 0;
}
static int cod3035x_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
dev_dbg(codec->dev, "(%s) %s completed\n",
substream->stream ? "C" : "P", __func__);
return 0;
}
static void cod3035x_dai_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
dev_dbg(codec->dev, "(%s) %s completed\n",
substream->stream ? "C" : "P", __func__);
}
static void cod3035x_adc_mute_work(struct work_struct *work)
{
struct cod3035x_priv *cod3035x =
container_of(work, struct cod3035x_priv, adc_mute_work);
struct snd_soc_codec *codec = cod3035x->codec;
mutex_lock(&cod3035x->adc_mute_lock);
msleep(200);
dev_dbg(codec->dev, "%s called\n", __func__);
cod3035x_adc_digital_mute(codec, false);
cod3035x_usleep(2000);
if (cod3035x->uhqa_rec_mode)
snd_soc_write(codec, COD3035X_44_IF1_FORMAT4, 0xEA);
else
snd_soc_write(codec, COD3035X_44_IF1_FORMAT4, 0xE4);
mutex_unlock(&cod3035x->adc_mute_lock);
}
static void cod3035x_legacy_uhqa(struct snd_soc_codec *codec)
{
dev_dbg(codec->dev, "%s called. UHQA Mode\n", __func__);
snd_soc_write(codec, COD3035X_F2_PRESET_AVC, 0x03);
snd_soc_write(codec, COD3035X_F3_PRESET_AVC, 0x1F);
snd_soc_write(codec, COD3035X_F4_PRESET_AVC, 0x5F);
snd_soc_write(codec, COD3035X_5A_AVC7, 0xFF);
snd_soc_write(codec, COD3035X_F5_PRESET_AVC, 0x10);
snd_soc_write(codec, COD3035X_F6_PRESET_AVC, 0x0F);
snd_soc_update_bits(codec, COD3035X_50_DAC1,
DAC1_INGN_MODE_MASK, DAC1_UHQA_MODE);
snd_soc_update_bits(codec, COD3035X_53_UHQA,
UHQA_MODE_MASK, UHQA_ENABLE);
snd_soc_write(codec, COD3035X_7A_MAN_GN1, 0x2F);
snd_soc_write(codec, COD3035X_7B_MAN_GN2, 0xE6);
snd_soc_update_bits(codec, COD3035X_D6_CTRL_IREF5,
CTMF_DCT_CAP_MASK,
CTMF_DTC_CAP_167KHZ << CTMF_DCT_CAP_SHIFT);
snd_soc_write(codec, COD3035X_B8_AUTO_HP9, 0x55);
snd_soc_write(codec, COD3035X_BB_AUTO_HP12, 0x66);
}
static void cod3035x_legacy_normal(struct snd_soc_codec *codec)
{
dev_dbg(codec->dev, "%s called. NOT UHQA Mode\n", __func__);
snd_soc_write(codec, COD3035X_F2_PRESET_AVC, 0x03);
snd_soc_write(codec, COD3035X_F3_PRESET_AVC, 0x1F);
snd_soc_write(codec, COD3035X_F4_PRESET_AVC, 0x55);
snd_soc_write(codec, COD3035X_5A_AVC7, 0x44);
snd_soc_write(codec, COD3035X_F5_PRESET_AVC, 0x1A);
snd_soc_write(codec, COD3035X_F6_PRESET_AVC, 0xC6);
snd_soc_update_bits(codec, COD3035X_50_DAC1,
DAC1_INGN_MODE_MASK, DAC1_NORMAL_MODE);
snd_soc_update_bits(codec, COD3035X_53_UHQA,
UHQA_MODE_MASK, UHQA_DISABLE);
snd_soc_write(codec, COD3035X_7A_MAN_GN1, 0x27);
snd_soc_write(codec, COD3035X_7B_MAN_GN2, 0xE7);
snd_soc_update_bits(codec, COD3035X_D6_CTRL_IREF5,
CTMF_DCT_CAP_MASK,
CTMF_DTC_CAP_87KHZ << CTMF_DCT_CAP_SHIFT);
snd_soc_write(codec, COD3035X_B8_AUTO_HP9, 0x11);
snd_soc_write(codec, COD3035X_BB_AUTO_HP12, 0x33);
}
static void cod3035x_lassenA_uhqa(struct snd_soc_codec *codec)
{
dev_dbg(codec->dev, "%s called. LassenA, UHQA Mode\n", __func__);
snd_soc_write(codec, COD3035X_F2_PRESET_AVC, 0x03);
snd_soc_write(codec, COD3035X_F3_PRESET_AVC, 0x1F);
snd_soc_write(codec, COD3035X_F5_PRESET_AVC, 0x10);
snd_soc_write(codec, COD3035X_F6_PRESET_AVC, 0x0F);
snd_soc_update_bits(codec, COD3035X_50_DAC1,
DAC1_INGN_MODE_MASK, DAC1_INGN_MODE_MASK);
snd_soc_update_bits(codec, COD3035X_53_UHQA,
UHQA_MODE_MASK, UHQA_ENABLE);
snd_soc_write(codec, COD3035X_58_AVC5, 0x81);
snd_soc_write(codec, COD3035X_7A_MAN_GN1, 0x2F);
snd_soc_write(codec, COD3035X_7B_MAN_GN2, 0xE6);
#if 1
cod3035x_usleep(100);
cod3035a_write_reg(COD3035A_50_OFF_DLY1, 0x0F);
cod3035a_write_reg(COD3035A_51_OFF_DLY2, 0xB0);
cod3035x_usleep(100);
// snd_soc_write(codec, COD3035X_54_AVC1, 0x8F);
cod3035x_usleep(100);
cod3035a_write_reg(COD3035A_41_DNC1, 0x01);
cod3035a_write_reg(COD3035A_42_DNC2, 0xDA);
cod3035a_write_reg(COD3035A_43_DNC3, 0x32);
cod3035a_write_reg(COD3035A_44_DNC4, 0x32);
cod3035a_write_reg(COD3035A_4A_DNC10, 0xFE);
cod3035a_write_reg(COD3035A_4B_DNC11, 0x87);
cod3035a_write_reg(COD3035A_4C_DNC12, 0x10);
cod3035a_write_reg(COD3035A_4D_DNC13, 0x86);
cod3035a_write_reg(COD3035A_4E_DNC14, 0x00);
cod3035x_usleep(100);
#endif
snd_soc_update_bits(codec, COD3035X_D6_CTRL_IREF5,
CTMF_DCT_CAP_MASK, CTMF_DTC_CAP_167KHZ << CTMF_DCT_CAP_SHIFT);
snd_soc_write(codec, COD3035X_BB_AUTO_HP12, 0x66);
snd_soc_write(codec, COD3035X_B8_AUTO_HP9, 0x55);
snd_soc_write(codec, COD3035X_7D_AVC_PARA29, 0x8B);
cod3035x_usleep(1000);
}
static void cod3035x_lassenA_normal(struct snd_soc_codec *codec)
{
dev_dbg(codec->dev, "%s called. NOT UHQA Mode\n", __func__);
snd_soc_write(codec, COD3035X_F2_PRESET_AVC, 0x03);
snd_soc_write(codec, COD3035X_F3_PRESET_AVC, 0x1F);
snd_soc_write(codec, COD3035X_F5_PRESET_AVC, 0x1A);
snd_soc_write(codec, COD3035X_F6_PRESET_AVC, 0xC7);
snd_soc_write(codec, COD3035X_58_AVC5, 0x81);
snd_soc_update_bits(codec, COD3035X_50_DAC1,
DAC1_INGN_MODE_MASK, 0x0);
snd_soc_update_bits(codec, COD3035X_53_UHQA,
UHQA_MODE_MASK, 0x0);
snd_soc_write(codec, COD3035X_7A_MAN_GN1, 0x2F);
snd_soc_write(codec, COD3035X_7B_MAN_GN2, 0x80);
#if 1
cod3035x_usleep(100);
cod3035a_write_reg(COD3035A_50_OFF_DLY1, 0x1A);
cod3035a_write_reg(COD3035A_51_OFF_DLY2, 0x68);
cod3035x_usleep(100);
// snd_soc_write(codec, COD3035X_54_AVC1, 0x8F);
cod3035x_usleep(100);
cod3035a_write_reg(COD3035A_41_DNC1, 0x01);
cod3035a_write_reg(COD3035A_42_DNC2, 0xDA);
cod3035a_write_reg(COD3035A_43_DNC3, 0x32);
cod3035a_write_reg(COD3035A_44_DNC4, 0x32);
cod3035a_write_reg(COD3035A_4A_DNC10, 0xFE);
cod3035a_write_reg(COD3035A_4B_DNC11, 0x85);
cod3035a_write_reg(COD3035A_4C_DNC12, 0x00);
cod3035a_write_reg(COD3035A_4D_DNC13, 0x83);
cod3035a_write_reg(COD3035A_4E_DNC14, 0x10);
cod3035x_usleep(100);
#endif
// snd_soc_write(codec, COD3035X_54_AVC1, 0x03);
snd_soc_write(codec, COD3035X_BB_AUTO_HP12, 0x33);
snd_soc_write(codec, COD3035X_7D_AVC_PARA29, 0x8B);
cod3035x_usleep(1000);
}
void playback_hw_params(struct snd_soc_codec *codec,
struct cod3035x_priv *cod3035x,
unsigned int cur_aifrate)
{
dev_dbg(codec->dev, "%s called. priv_aif: %d, cur_aif: %d\n",
__func__, cod3035x->playback_aifrate, cur_aifrate);
/* Set Normal mode */
if (cur_aifrate == COD3035X_SAMPLE_RATE_48KHZ) {
if (cod3035x->is_lassenA)
/* lassenA & normal mode */
cod3035x_lassenA_normal(codec);
else
/* legacy & normal mode */
cod3035x_legacy_normal(codec);
}
/* Set UHQA mode */
if (cur_aifrate == COD3035X_SAMPLE_RATE_192KHZ) {
if (cod3035x->is_lassenA)
/* lassenA & UHQA mode */
cod3035x_lassenA_uhqa(codec);
else
/* legacy & UHQA */
cod3035x_legacy_uhqa(codec);
}
cod3035x->playback_aifrate = cur_aifrate;
}
void capture_hw_params(struct snd_soc_codec *codec,
struct cod3035x_priv *cod3035x,
unsigned int cur_aifrate)
{
dev_dbg(codec->dev, "%s called. priv_aif: %d, cur_aif: %d\n",
__func__, cod3035x->capture_aifrate, cur_aifrate);
if (cod3035x->capture_aifrate != cur_aifrate) {
/* Set Normal Rec mode */
if (cur_aifrate == COD3035X_SAMPLE_RATE_48KHZ) {
cod3035x->uhqa_rec_mode = false;
snd_soc_update_bits(codec, COD3035X_53_UHQA,
UHQA_MODE_MASK, UHQA_DISABLE);
snd_soc_update_bits(codec, COD3035X_4A_DMIC2,
DMIC_ST1_MASK | DMIC_ST0_MASK,
(DMIC_ST1_DISABLE << DMIC_ST1_SHIFT) |\
(DMIC_ST0_DISABLE << DMIC_ST0_SHIFT));
}
/* Set UHQA Rec mode */
if (cur_aifrate == COD3035X_SAMPLE_RATE_192KHZ) {
cod3035x->uhqa_rec_mode = true;
snd_soc_update_bits(codec, COD3035X_53_UHQA,
UHQA_MODE_MASK, UHQA_ENABLE);
snd_soc_update_bits(codec, COD3035X_4A_DMIC2,
DMIC_ST1_MASK | DMIC_ST0_MASK,
(DMIC_ST1_ENABLE << DMIC_ST1_SHIFT) |\
(DMIC_ST0_ENABLE << DMIC_ST0_SHIFT));
}
cod3035x->capture_aifrate = cur_aifrate;
}
}
static int cod3035x_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct cod3035x_priv *cod3035x = snd_soc_codec_get_drvdata(codec);
unsigned int cur_aifrate, width, channels;
/* 192 KHz support */
cur_aifrate = params_rate(params);
width = params_width(params);
channels = params_channels(params);
dev_dbg(codec->dev, "%s called. width: %d, channels: %d, lassenA: %d\n",
__func__, width, channels, cod3035x->is_lassenA);
if (width == 16) {
snd_soc_write(codec, COD3035X_41_IF1_FORMAT1, 0x00);
snd_soc_update_bits(codec, COD3035X_42_IF1_FORMAT2,
I2S1_DL_MASK, I2S1_DL_16BIT);
snd_soc_update_bits(codec, COD3035X_43_IF1_FORMAT3,
I2S1_XFS_MASK, I2S1_XFS_32FS);
} else if (width == 24) {
snd_soc_write(codec, COD3035X_41_IF1_FORMAT1, 0x20);
snd_soc_update_bits(codec, COD3035X_42_IF1_FORMAT2,
I2S1_DL_MASK, I2S1_DL_24BIT);
snd_soc_update_bits(codec, COD3035X_43_IF1_FORMAT3,
I2S1_XFS_MASK, I2S1_XFS_64FS);
}
if (substream->stream == STREAM_PLAYBACK)
playback_hw_params(codec, cod3035x, cur_aifrate);
if (substream->stream == STREAM_CAPTURE)
capture_hw_params(codec, cod3035x, cur_aifrate);
return 0;
}
static void cod3035x_set_adc_gpio(struct cod3035x_priv *cod3035x, int val)
{
if (gpio_is_valid(cod3035x->adc_pin))
gpio_direction_output(cod3035x->adc_pin, val);
dev_dbg(cod3035x->dev, "%s : adc gpio value: %d\n",
__func__, gpio_get_value(cod3035x->adc_pin));
}
/**
* To measure more accurate gdet adc value, call this function.
*
* Return value:
* 0: gdet adc check error
* 1: jack detection
* 2: water detection
* 3: idel status
*/
static int cod3035x_finish_chk_more(struct cod3035x_priv *cod3035x)
{
struct cod3035x_jack_det *jackdet = &cod3035x->jack_det;
int finish_chk = 0;
int i;
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, false);
snd_soc_write(cod3035x->codec, COD3035X_85_CTR_POP2, 0xC0);
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, true);
cod3035x_usleep(100);
for (i = 0; i < COD3035X_GDET_FINISH_CHK_MORE_NO; i++) {
/* read adc for water detection */
if (cod3035x->use_det_gdet_adc_mode == 2)
cod3035x_set_adc_gpio(cod3035x, 1);
mdelay(50);
if (cod3035x->use_det_gdet_adc_mode == 1)
jackdet->gdet_adc_val =
cod3035x_gdet_adc_get_value(cod3035x);
else if (cod3035x->use_det_gdet_adc_mode == 2)
jackdet->gdet_adc_val =
cod3035x_adc_get_value(cod3035x);
if (jackdet->gdet_adc_val < cod3035x->water_threshold_adc_min)
finish_chk = 1;
else if (jackdet->gdet_adc_val >= cod3035x->water_threshold_adc_min &&
jackdet->gdet_adc_val < cod3035x->water_threshold_adc_max)
finish_chk = 2;
else if (jackdet->gdet_adc_val >= cod3035x->water_threshold_adc_max)
finish_chk = 3;
dev_dbg(cod3035x->dev, "%s called. gdet_adc: %d, finish_chk: %d\n",
__func__, jackdet->gdet_adc_val, finish_chk);
}
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, false);
snd_soc_write(cod3035x->codec, COD3035X_85_CTR_POP2, 0x50);
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, true);
return finish_chk;
}
bool isIncompInsert(struct cod3035x_priv *cod3035x,
struct cod3035x_jack_det *jackdet)
{
int adc = jackdet->gdet_adc_val;
if (adc >= cod3035x->fake_jack_adc &&
adc < cod3035x->water_threshold_adc_min)
return true;
#ifdef CONFIG_SND_SOC_COD30XX_EXT_ANT
if (cod3035x->dtv_detect) {
if ((adc > COD3035X_3POLE_FAKE_MIN && adc < COD3035X_3POLE_FAKE_MAX) ||
(adc > COD3035X_4POLE_FAKE_MIN && adc < COD3035X_4POLE_FAKE_MAX))
return true;
}
#endif
return false;
}
static void cod3035x_gdet_adc_work(struct work_struct *work)
{
struct cod3035x_priv *cod3035x =
container_of(work, struct cod3035x_priv, gdet_adc_work);
struct snd_soc_codec *codec = cod3035x->codec;
struct cod3035x_jack_det *jackdet = &cod3035x->jack_det;
cancel_work_sync(&cod3035x->jack_det_work);
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, false);
/* For defence jack detect lock, PDB_JD power on */
snd_soc_update_bits(codec, COD3035X_80_PDB_ACC1, PDB_JD_MASK, PDB_JD_MASK);
snd_soc_write(codec, COD3035X_85_CTR_POP2, 0xC0);
/* read adc for water detection */
if (cod3035x->use_det_gdet_adc_mode == 1)
jackdet->gdet_adc_val = cod3035x_gdet_adc_get_value(cod3035x);
else if (cod3035x->use_det_gdet_adc_mode == 2) {
cod3035x_set_adc_gpio(cod3035x, 1);
jackdet->gdet_adc_val = cod3035x_adc_get_value(cod3035x);
}
dev_dbg(cod3035x->dev, "%s called. gdet adc: %d\n", __func__,
jackdet->gdet_adc_val);
snd_soc_write(codec, COD3035X_85_CTR_POP2, 0x50);
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, true);
if ((jackdet->gdet_adc_val < cod3035x->fake_jack_adc) ||
((jackdet->gdet_adc_val >= cod3035x->aux_cable_detect_adc &&
jackdet->gdet_adc_val < cod3035x->water_threshold_adc_min) &&
(cod3035x->model_feature_flag & MODEL_FLAG_5PIN_AUX_DET))) {
/* Jack detection process */
dev_dbg(cod3035x->dev, "%s Jack detected.\n", __func__);
jackdet->jack_det = true;
if (cod3035x->model_feature_flag & MODEL_FLAG_5PIN_BTN_DELAY) {
cod3035x->btn_delay_masking = true;
dev_dbg(cod3035x->dev, "%s called, btn irq masking on\n", __func__);
cancel_delayed_work(&cod3035x->btn_delay_work);
queue_delayed_work(cod3035x->btn_delay_wq,
&cod3035x->btn_delay_work, msecs_to_jiffies(3000));
}
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, false);
/* IRQ masking off */
snd_soc_write(cod3035x->codec, COD3035X_05_IRQ1M, 0x00);
snd_soc_write(cod3035x->codec, COD3035X_06_IRQ2M, 0x00);
/* Jack in */
snd_soc_write(cod3035x->codec, COD3035X_93_CTR_DLY6, 0x20);
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, true);
cancel_work_sync(&cod3035x->jack_det_work);
queue_work(cod3035x->jack_det_wq, &cod3035x->jack_det_work);
} else if (isIncompInsert(cod3035x, jackdet)) {
/* fake jack inserted, check gdet adc after 1 sec */
msleep(1000);
queue_work(cod3035x->gdet_adc_wq, &cod3035x->gdet_adc_work);
} else if (jackdet->gdet_adc_val >= cod3035x->water_threshold_adc_min &&
jackdet->gdet_adc_val < cod3035x->water_threshold_adc_max) {
/* Water detection process */
dev_dbg(cod3035x->dev, "%s Water detected.\n", __func__);
jackdet->water_det = true;
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, false);
/* IRQ masking on */
snd_soc_write(codec, COD3035X_05_IRQ1M, 0x01);
snd_soc_write(codec, COD3035X_06_IRQ2M, 0x01);
/* Water polling */
snd_soc_update_bits(codec, COD3035X_80_PDB_ACC1,
PDB_JD_CLK_EN_MASK, 0x0);
snd_soc_write(cod3035x->codec, COD3035X_93_CTR_DLY6, 0x40);
snd_soc_update_bits(codec, COD3035X_80_PDB_ACC1,
PDB_JD_CLK_EN_MASK, PDB_JD_CLK_EN_MASK);
/* Set gdet pop resistance */
snd_soc_write(codec, COD3035X_85_CTR_POP2, 0xC0);
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, true);
} else {
/* Water finish check process */
if (cod3035x_finish_chk_more(cod3035x) == 3) {
dev_dbg(cod3035x->dev, "%s Water finished. water: %d, jack:%d\n",
__func__, jackdet->water_det, jackdet->jack_det);
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, false);
/* IRQ masking off */
snd_soc_write(cod3035x->codec, COD3035X_05_IRQ1M, 0x00);
snd_soc_write(cod3035x->codec, COD3035X_06_IRQ2M, 0x00);
/* Water out */
snd_soc_write(codec, COD3035X_93_CTR_DLY6, 0x10);
if (jackdet->jack_det == true) {
snd_soc_write(codec, COD3035X_99_TEST2, 0x40);
snd_soc_write(codec, COD3035X_99_TEST2, 0x00);
}
snd_soc_write(codec, COD3035X_93_CTR_DLY6, 0x00);
jackdet->water_det = false;
jackdet->jack_det = false;
queue_work(cod3035x->jack_det_wq, &cod3035x->jack_det_work);
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, true);
} else
queue_work(cod3035x->gdet_adc_wq, &cod3035x->gdet_adc_work);
}
}
static void cod3035x_jack_imp_cal(struct cod3035x_priv *cod3035x,
int param1, int param2, int param3, int param4, int param5)
{
struct snd_soc_codec *codec = cod3035x->codec;
unsigned long imp1, imp2;
unsigned long p1, p2, p3, dout;
long temp1, temp2, temp3, temp4;
int impedance;
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, false);
imp1 = snd_soc_read(codec, COD3035X_AB_TEST_ADC1);
imp2 = snd_soc_read(codec, COD3035X_AE_TEST_ADC4);
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, true);
dev_dbg(cod3035x->dev,
"%s called. 0x7B: %02x, 0x7C:%02x, 0x7E:%02x, 0x7F:%02x, 0x80:%02x\n",
__func__, param1, param2, param3, param4, param5);
dev_dbg(cod3035x->dev,
"%s called. imp1: %02lx, imp2:%02lx\n", __func__, imp1, imp2);
p1 = param3;
p2 = param1 & COD3035X_PARAM_1_MASK;
p2 = p2 << 3;
p2 |= param4;
p3 = param2 & COD3035X_PARAM_2_MASK;
p3 = p3 << 3;
p3 |= param5;
/* 2's complement check */
if (p3 & COD3035X_P3_BIT_CHECK)
p3 = p3 - 1024;
/* p3 parameter correciton */
p3 = p3 - 15;
dout = imp2 & GPADC_IMP_DATA_SMP_MASK;
dout = dout << 4;
dout |= imp1;
dev_dbg(cod3035x->dev, "%s called. p1:%ld, p2:%ld, p3:%ld, dout:%ld\n",
__func__, p1, p2, p3, dout);
/* calculate impedance check */
temp1 = p1 * (COD3035X_IMP_C3 * p1 + 1024);
temp2 = (COD3035X_IMP_C2 * dout) - (COD3035X_IMP_C2 * p3) -
(COD3035X_IMP_C1 * p2) - (COD3035X_IMP_C2 * p2);
temp3 = p2 * (COD3035X_IMP_C1 * p1 + COD3035X_IMP_C2 * p1 +
COD3035X_IMP_C3 * p1);
temp4 = (dout - p3) * (COD3035X_IMP_C3 * p1 + COD3035X_IMP_C2 * p1);
impedance = ((temp1 * temp2) / (temp3 - temp4)) / 128;
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, false);
impedance = impedance - cod3035x->jack_imp_gap;
/* Set register according to impedance */
if (impedance < 0) {
dev_dbg(cod3035x->dev, "%s called. jack impedance is high: %d\n",
__func__, impedance);
snd_soc_write(codec, COD3035X_A6_IMP_CNT11, 0x72);
} else if (impedance < COD3035X_IMP_LOW) {
dev_dbg(cod3035x->dev, "%s called. jack impedance is low: %d\n",
__func__, impedance);
snd_soc_write(codec, COD3035X_A6_IMP_CNT11, 0x52);
} else if (impedance < COD3035X_IMP_HIGH) {
dev_dbg(cod3035x->dev, "%s called. jack impedance is mid: %d\n",
__func__, impedance);
snd_soc_write(codec, COD3035X_A6_IMP_CNT11, 0x62);
} else {
dev_dbg(cod3035x->dev, "%s called. jack impedance is high: %d\n",
__func__, impedance);
snd_soc_write(codec, COD3035X_A6_IMP_CNT11, 0x72);
}
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, true);
}
#ifdef CONFIG_SND_SOC_COD30XX_EXT_ANT
void read_irq_status(struct cod3035x_priv *cod3035x,
unsigned int *irq1, unsigned int *irq2,
unsigned int *status1, unsigned int *status3)
{
unsigned int irq3;
struct snd_soc_codec *codec = cod3035x->codec;
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, false);
*irq1 = snd_soc_read(codec, COD3035X_01_IRQ1PEND);
*irq2 = snd_soc_read(codec, COD3035X_02_IRQ2PEND);
*status1 = snd_soc_read(codec, COD3035X_09_STATUS1);
*status3 = snd_soc_read(codec, COD3035X_0B_STATUS3);
irq3 = snd_soc_read(codec, COD3035X_02_IRQ2PEND);
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, true);
dev_dbg(codec->dev,
"%s, IRQ1: 0x%02x, IRQ2: 0x%02x, STATUS1: 0x%02x, STATUS3: 0x%02x\n",
__func__, *irq1, *irq2, *status1, *status3);
dev_dbg(codec->dev, "%s, IRQ3: 0x%02x\n", __func__, irq3);
}
void cod3035x_jack_reset(struct cod3035x_priv *cod3035x)
{
struct snd_soc_codec *codec = cod3035x->codec;
dev_dbg(codec->dev, "%s called, Jack Power RESET.\n", __func__);
mutex_lock(&cod3035x->reset_lock);
/* jack power reset */
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, false);
snd_soc_update_bits(codec, COD3035X_80_PDB_ACC1,
PDB_JD_MASK, 0);
cod3035x_usleep(100);
snd_soc_update_bits(codec, COD3035X_80_PDB_ACC1,
PDB_JD_MASK, PDB_JD_MASK);
cod3035x_usleep(100);
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, true);
mutex_unlock(&cod3035x->reset_lock);
}
void set_ant_jack_flag(struct cod3035x_priv *cod3035x,
struct cod3035x_jack_det* jackdet, int measured_adc)
{
struct snd_soc_codec *codec = cod3035x->codec;
dev_dbg(codec->dev, "%s called, ant_irq: %d, ant_det: %d, mic_det: %d, adc: %d\n",
__func__, jackdet->ant_irq, jackdet->ant_det, jackdet->mic_det, measured_adc);
dev_dbg(codec->dev, "%s, ant_adc_range: %d, ant_det_gpio: %d\n",
__func__, cod3035x->ant_adc_range, gpio_get_value(cod3035x->ant_det_gpio));
if (jackdet->ignore_ext_ant) {
dev_info(codec->dev, "%s: ignore_ext_ant\n", __func__);
return;
}
/* case 9 ~ 10 */
if (cod3035x->ant_adc_range < measured_adc) {
jackdet->ant_det = true;
jackdet->mic_det = false;
} else {
jackdet->ant_det = false;
}
/* case 11 ~ 14 */
if (jackdet->ant_irq == true) {
if (gpio_get_value(cod3035x->ant_det_gpio) == true) {
/* 12 and 14 cases, 3/4 pole only removed */
jackdet->ant_det = true;
jackdet->mic_det = false;
dev_dbg(codec->dev,
"%s, ant_det_gpio true, ant_det true, mic_det false.\n", __func__);
} else {
/* 11 and 13 cases, antenna and jack has been inserted
* this will be handled by jack inserting operation.
*/
jackdet->ant_det = false;
dev_dbg(codec->dev,
"%s, 3/4 Pole jack inserted after ANT only state.\n", __func__);
}
jackdet->ant_irq = false;
}
dev_info(codec->dev, "%s ant_det[%d] jack_det[%d] mic_det[%d]\n",
__func__, jackdet->ant_det, jackdet->jack_det, jackdet->mic_det);
}
int cod3035x_get_jack_status(struct cod3035x_jack_det *jackdet)
{
if (jackdet->ant_det == true)
/* 11 ~ 14 cases: Antenna In state,
* otherwise, antenna detection will be ignored,
* judged with M-det
*/
return JACK_ANT;
else if (jackdet->ant_det == false
&& jackdet->jack_det == false
&& jackdet->mic_det == false)
return JACK_OUT;
else if (jackdet->ant_det == false
&& jackdet->jack_det == true
&& jackdet->mic_det == false)
return JACK_3POLE;
else if (jackdet->ant_det == false
&& jackdet->jack_det == true
&& jackdet->mic_det == true)
return JACK_4POLE;
return JACK_OUT;
}
void send_status_to_audioframework(struct switch_dev *sdev, int jack_det_status)
{
switch (jack_det_status) {
case JACK_3POLE:
case JACK_ANT_3POLE:
switch_set_state(sdev, 2); /* 3 Pole */
break;
case JACK_4POLE:
case JACK_ANT_4POLE:
switch_set_state(sdev, 1); /* 4 Pole */
break;
case JACK_OUT:
switch_set_state(sdev, 0);
break;
case JACK_ANT:
switch_set_state(sdev, 256);
break;
default : /* jack out */
break;
}
}
void set_micbias_manual_mode(struct snd_soc_codec* codec)
{
/* mic bias manual mode */
snd_soc_update_bits(codec, COD3035X_81_PDB_ACC2,
T_PDB_MCB_LDO_MASK|T_PDB_MCB2_MASK,
T_PDB_MCB_LDO_MASK|T_PDB_MCB2_MASK);
snd_soc_update_bits(codec, COD3035X_80_PDB_ACC1,
DET_PDB_MCB_LDO_MASK|PDB_MCB2_MASK,
DET_PDB_MCB_LDO_MASK|PDB_MCB2_MASK);
}
void set_micbias_auto_mode(struct snd_soc_codec* codec)
{
/* mic bias auto mode */
snd_soc_update_bits(codec, COD3035X_80_PDB_ACC1,
DET_PDB_MCB_LDO_MASK|PDB_MCB2_MASK,
0x0);
snd_soc_update_bits(codec, COD3035X_81_PDB_ACC2,
T_PDB_MCB_LDO_MASK|T_PDB_MCB2_MASK,
0x0);
}
int cod3035x_set_codec_jackstatus(struct snd_soc_codec* codec,
struct cod3035x_jack_det *jackdet)
{
struct cod3035x_priv *cod3035x = snd_soc_codec_get_drvdata(codec);
unsigned int jack_state;
if (jackdet->jack_det) {
jack_state = snd_soc_read(cod3035x->codec, 0x0B) & 0x000000F0;
if (jack_state != 0x40)
return false;
}
if (jackdet->ant_det) {
set_micbias_auto_mode(codec);
if (!(cod3035x->model_feature_flag & MODEL_FLAG_5PIN_ANT)) {
snd_soc_write(codec, COD3035X_98_TEST1, 0x80);
}
snd_soc_write(codec, COD3035X_93_CTR_DLY6, 0x00);
snd_soc_write(codec, COD3035X_94_JACK_CTR, 0x00);
dev_dbg(codec->dev, "%s Ext Antenna inserted\n", __func__);
} else if (jackdet->jack_det && jackdet->mic_det) {
set_micbias_auto_mode(codec);
snd_soc_write(codec, COD3035X_98_TEST1, 0x00);
snd_soc_write(codec, COD3035X_93_CTR_DLY6, 0x00);
snd_soc_write(codec, COD3035X_94_JACK_CTR, 0x30);
dev_dbg(codec->dev, "%s 4 Pole Jack-In\n", __func__);
/* earjack mic reset */
snd_soc_update_bits(codec, COD3035X_13_PD_AD3,
RESETB_BST3_MASK, 0);
msleep(40);
snd_soc_update_bits(codec, COD3035X_13_PD_AD3,
RESETB_BST3_MASK, RESETB_BST3_MASK);
} else if (jackdet->jack_det) {
set_micbias_manual_mode(codec);
snd_soc_write(codec, COD3035X_98_TEST1, 0x00);
snd_soc_write(codec, COD3035X_93_CTR_DLY6, 0x00);
snd_soc_write(codec, COD3035X_94_JACK_CTR, 0x20);
dev_dbg(codec->dev, "%s 3 Pole Jack-In\n", __func__);
} else {
jack_state = snd_soc_read(cod3035x->codec, 0x0C) & 0x00000060;
if (jack_state != 0x00)
return false;
set_micbias_auto_mode(codec);
snd_soc_write(codec, COD3035X_98_TEST1, 0x00);
snd_soc_write(codec, COD3035X_93_CTR_DLY6, 0x00);
snd_soc_write(codec, COD3035X_94_JACK_CTR, 0x00);
dev_dbg(codec->dev, "%s JACK OUT\n", __func__);
}
return true;
}
#endif
static void cod3035x_btn_delay_work(struct work_struct *work)
{
struct cod3035x_priv *cod3035x =
container_of(work, struct cod3035x_priv, btn_delay_work.work);
dev_dbg(cod3035x->dev, "%s called, btn irq masking off\n", __func__);
cod3035x->btn_delay_masking = false;
}
static void cod3035x_jack_det_work(struct work_struct *work)
{
struct cod3035x_priv *cod3035x =
container_of(work, struct cod3035x_priv, jack_det_work);
struct cod3035x_jack_det *jackdet = &cod3035x->jack_det;
struct snd_soc_codec *codec = cod3035x->codec;
int jack_det_status = 0, adc = 0;
if (!cod3035x->dtv_detect) {
dev_dbg(cod3035x->dev, "%s(%d) jackdet: %d\n",
__func__, __LINE__, jackdet->jack_det);
mutex_lock(&cod3035x->jackdet_lock);
if (jackdet->jack_det == true) {
/* set delay for read correct adc value */
msleep(cod3035x->mic_det_delay);
/* check jack det status */
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, false);
jack_det_status = snd_soc_read(codec, COD3035X_09_STATUS1);
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, true);
jack_det_status = jack_det_status & STATUS1_JACK_DET_MASK;
if (jack_det_status == false &&
!(cod3035x->model_feature_flag & MODEL_FLAG_5PIN_JACK)) {
dev_dbg(cod3035x->dev, "%s fake jack inserted.\n", __func__);
queue_work(cod3035x->gdet_adc_wq, &cod3035x->gdet_adc_work);
mutex_unlock(&cod3035x->jackdet_lock);
return;
}
/* read adc for mic detect */
adc = cod3035x_adc_get_value(cod3035x);
dev_dbg(cod3035x->dev, "%s mic det adc: %d\n", __func__, adc);
if (adc > cod3035x->mic_adc_range)
jackdet->mic_det = true;
else
jackdet->mic_det = false;
dev_dbg(cod3035x->dev, "%s Mic det: %d\n",
__func__, jackdet->mic_det);
jackdet->adc_val = adc;
} else {
dev_dbg(cod3035x->dev, "%s JACK OUT\n", __func__);
/* jack/mic out */
jackdet->mic_det = false;
jackdet->adc_val = -EINVAL;
}
/* Send the jack detect event to the audio framework */
if (jackdet->jack_det && jackdet->mic_det)
switch_set_state(&cod3035x->sdev, 1); /* 4 Pole */
else if (jackdet->jack_det)
switch_set_state(&cod3035x->sdev, 2); /* 3 Pole */
else
switch_set_state(&cod3035x->sdev, 0);
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, false);
if (jackdet->jack_det && jackdet->mic_det) {
snd_soc_write(codec, COD3035X_93_CTR_DLY6, 0x00);
snd_soc_write(codec, COD3035X_94_JACK_CTR, 0x30);
dev_dbg(cod3035x->dev, "%s 4 Pole Jack-In\n", __func__);
/* earjack mic reset */
msleep(40);
snd_soc_update_bits(codec, COD3035X_13_PD_AD3,
RESETB_BST3_MASK, 0);
msleep(40);
snd_soc_update_bits(codec, COD3035X_13_PD_AD3,
RESETB_BST3_MASK, RESETB_BST3_MASK);
} else if (jackdet->jack_det) {
snd_soc_write(codec, COD3035X_93_CTR_DLY6, 0x00);
snd_soc_write(codec, COD3035X_94_JACK_CTR, 0x20);
dev_dbg(cod3035x->dev, "%s 3 Pole Jack-In\n", __func__);
} else {
snd_soc_write(codec, COD3035X_93_CTR_DLY6, 0x00);
snd_soc_write(codec, COD3035X_94_JACK_CTR, 0x00);
dev_dbg(cod3035x->dev, "%s JACK OUT\n", __func__);
}
/* LDET LTH has been added for mass products which is using 5 pin jack house
* This feature can't be enabled with the Ext Antenna feature.
*/
if (cod3035x->model_feature_flag & MODEL_FLAG_LDET_VTH_ENABLE) {
if (jackdet->jack_det) {
dev_dbg(cod3035x->dev, "%s jackin_ldet_vth: %d\n",
__func__, cod3035x->jackin_ldet_vth);
snd_soc_update_bits(codec, COD3035X_84_CTR_POP1,
CTRV_LDET_VTH_MASK, cod3035x->jackin_ldet_vth);
} else {
snd_soc_update_bits(codec, COD3035X_84_CTR_POP1,
CTRV_LDET_VTH_MASK, CTRV_LDET_VTH_0900);
}
}
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, true);
dev_dbg(cod3035x->codec->dev, "Jack %s, Mic %s\n",
jackdet->jack_det ? "inserted" : "removed",
jackdet->mic_det ? "inserted" : "removed");
}
#ifdef CONFIG_SND_SOC_COD30XX_EXT_ANT
else {
unsigned int irq1, irq2, status1, status3;
dev_dbg(cod3035x->dev, "%s(%d) jackdet: %d\n",
__func__, __LINE__, jackdet->jack_det);
mutex_lock(&cod3035x->jackdet_lock);
/* check the jack detect status */
read_irq_status(cod3035x, &irq1, &irq2, &status1, &status3);
if (jackdet->jack_det == true) {
/* set delay for read correct adc value */
msleep(cod3035x->mic_det_delay);
if ((irq1 != 0xffffffff) && (irq2 != 0xffffffff)) {
jack_det_status = status1 & STATUS1_JACK_DET_MASK;
jackdet->jack_det = jack_det_status ? true:false;
}
if (jack_det_status == false) {
dev_dbg(cod3035x->dev, "%s fake jack inserted.\n", __func__);
queue_work(cod3035x->gdet_adc_wq, &cod3035x->gdet_adc_work);
mutex_unlock(&cod3035x->jackdet_lock);
return;
}
/* read adc for mic detect */
adc = cod3035x_adc_get_value(cod3035x);
dev_dbg(cod3035x->dev, "%s mic det adc: %d\n", __func__, adc);
if (adc > cod3035x->mic_adc_range)
jackdet->mic_det = true;
else
jackdet->mic_det = false;
dev_dbg(cod3035x->dev, "%s Mic det: %d\n",
__func__, jackdet->mic_det);
jackdet->adc_val = adc;
} else {
dev_dbg(cod3035x->dev, "%s JACK OUT\n", __func__);
/* jack/mic out */
jackdet->ant_irq = false;
jackdet->ant_det = false;
jackdet->mic_det = false;
jackdet->adc_val = -EINVAL;
}
set_ant_jack_flag(cod3035x, jackdet, adc);
jack_det_status = cod3035x_get_jack_status(jackdet);
dev_dbg(cod3035x->dev, "%s ant[%d], jack[%d], mic[%d]\n",
__func__, jackdet->ant_det, jackdet->jack_det, jackdet->mic_det);
/* codec setting as an inserted status */
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, false);
if(!cod3035x_set_codec_jackstatus(codec, jackdet)) {
dev_dbg(cod3035x->dev,
"%s jack status does not match with machine state\n", __func__);
cod3035x_jack_reset(cod3035x);
mutex_unlock(&cod3035x->jackdet_lock);
return;
}
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, true);
if (jackdet->prev_jack_det_status != JACK_ANT) {
dev_dbg(cod3035x->dev, "%s prev_jack_det_status(%d)\n", __func__,
jackdet->prev_jack_det_status);
jackdet->prev_jack_det_status = jack_det_status;
send_status_to_audioframework(&(cod3035x->sdev), jack_det_status);
} else {
dev_dbg(cod3035x->dev, "%s prev_jack_det_status(%d)\n", __func__,
jackdet->prev_jack_det_status);
if (jackdet->prev_jack_det_status == jack_det_status) {
dev_dbg(cod3035x->dev, "%s current jack_det_status(%d) == prev_jack_det_status(%d)\n",
__func__, jackdet->prev_jack_det_status, jack_det_status);
mutex_unlock(&cod3035x->jackdet_lock);
return;
} else {
dev_dbg(cod3035x->dev, "%s current jack_det_status(%d) != prev_jack_det_status(%d)\n",
__func__, jackdet->prev_jack_det_status, jack_det_status);
jackdet->prev_jack_det_status = jack_det_status;
queue_delayed_work(cod3035x->jack_report_wq, &cod3035x->jack_report_work,
COD3035X_ANT_DET_DELAY);
}
}
dev_dbg(cod3035x->dev, "%s water status: [%d]\n", __func__, jackdet->water_det);
dev_dbg(cod3035x->codec->dev, "Ant %s, Jack %s, Mic %s\n",
jackdet->ant_det ? "inserted" : "removed",
jackdet->jack_det ? "inserted" : "removed",
jackdet->mic_det ? "inserted" : "removed");
}
#endif
mutex_unlock(&cod3035x->jackdet_lock);
}
#define ADC_TRACE_NUM 5
#define ADC_TRACE_NUM2 2
#define ADC_READ_DELAY_US 500
#define ADC_READ_DELAY_MS 1
#define ADC_DEVI_THRESHOLD 18000
#define BUTTON_PRESS 1
#define BUTTON_RELEASE 0
static int get_adc_avg(int *adc_values)
{
int i;
int adc_sum = 0;
for (i = 0; i < ADC_TRACE_NUM; i++)
adc_sum += adc_values[i];
adc_sum = adc_sum / ADC_TRACE_NUM;
return adc_sum;
}
static int get_adc_devi(int avg, int *adc_values)
{
int i;
int devi = 0, diff;
for (i = 0; i < ADC_TRACE_NUM; i++) {
diff = adc_values[i]-avg;
devi += (diff*diff);
}
return devi;
}
static void cod3035x_buttons_work(struct work_struct *work)
{
struct cod3035x_priv *cod3035x =
container_of(work, struct cod3035x_priv, buttons_work);
struct cod3035x_jack_det *jd = &cod3035x->jack_det;
struct jack_buttons_zone *btn_zones = cod3035x->jack_buttons_zones;
int num_buttons_zones = ARRAY_SIZE(cod3035x->jack_buttons_zones);
int adc_values[ADC_TRACE_NUM];
int current_button_state;
int adc;
int i, avg, devi;
int adc_final_values[ADC_TRACE_NUM2];
int j;
int adc_final = 0;
int adc_max = 0;
if (!jd->jack_det) {
dev_err(cod3035x->dev, "Skip button events for jack_out\n");
if (jd->privious_button_state == BUTTON_PRESS) {
jd->button_det = false;
input_report_key(cod3035x->input, jd->button_code, 0);
input_sync(cod3035x->input);
cod3035x_process_button_ev(cod3035x->codec, jd->button_code, 0);
dev_dbg(cod3035x->dev, ":key %d released when jack_out\n", jd->button_code);
}
return;
}
if (!jd->mic_det) {
dev_err(cod3035x->dev, "Skip button events for 3-pole jack\n");
return;
}
/* set delay for read correct adc value */
mdelay(10);
for (j = 0; j < ADC_TRACE_NUM2; j++) {
/* read GPADC for button */
for (i = 0; i < ADC_TRACE_NUM; i++) {
adc = cod3035x_adc_get_value(cod3035x);
adc_values[i] = adc;
udelay(ADC_READ_DELAY_US);
}
/*
* check avg/devi value is proper
* if not read adc after 5 ms
*/
avg = get_adc_avg(adc_values);
devi = get_adc_devi(avg, adc_values);
dev_dbg(cod3035x->dev,
":button adc avg: %d, devi: %d\n", avg, devi);
if (devi > ADC_DEVI_THRESHOLD) {
queue_work(cod3035x->buttons_wq,
&cod3035x->buttons_work);
for (i = 0; i < ADC_TRACE_NUM;) {
dev_err(cod3035x->dev,
":retry button_work : %d %d %d %d %d\n",
adc_values[i + 0],
adc_values[i + 1],
adc_values[i + 2],
adc_values[i + 3],
adc_values[i + 4]);
i += 5;
}
return;
}
adc_final_values[j] = avg;
if (avg > adc_max)
adc_max = avg;
mdelay(ADC_READ_DELAY_MS);
}
adc_final = adc_max;
/* check button press/release */
if (adc_final > cod3035x->btn_release_value) {
dev_dbg(cod3035x->dev,
"Button Released! adc_fanal: %d, btn_value: %d\n",
adc_final,
cod3035x->btn_release_value);
current_button_state = BUTTON_RELEASE;
} else {
dev_dbg(cod3035x->dev,
"Button Pressed! adc_final: %d, btn_value: %d\n",
adc_final,
cod3035x->btn_release_value);
current_button_state = BUTTON_PRESS;
}
/* check jack status */
if (current_button_state == BUTTON_PRESS &&
(!jd->jack_det || jd->privious_button_state == current_button_state)){
if (!jd->jack_det)
dev_dbg(cod3035x->dev, "skip button press event, jack was not detected\n");
if (jd->privious_button_state == current_button_state)
dev_dbg(cod3035x->dev, "skip button press event, status are same\n");
jd->button_det = false;
jd->privious_button_state = BUTTON_RELEASE;
input_report_key(cod3035x->input, jd->button_code, 0);
input_sync(cod3035x->input);
cod3035x_process_button_ev(cod3035x->codec, jd->button_code, 0);
dev_dbg(cod3035x->dev, ":key %d released when jack_out\n", jd->button_code);
return;
}
if (jd->privious_button_state == current_button_state) {
dev_dbg(cod3035x->dev, "status are same\n");
return;
}
jd->privious_button_state = current_button_state;
adc = adc_final;
jd->adc_val = adc_final;
for (i = 0; i < 4; i++)
dev_dbg(cod3035x->dev,
"[DEBUG]: adc(%d), buttons: code(%d), low(%d), high(%d)\n",
adc,
cod3035x->jack_buttons_zones[i].code,
cod3035x->jack_buttons_zones[i].adc_low,
cod3035x->jack_buttons_zones[i].adc_high);
/* determine which button press or release */
if (current_button_state == BUTTON_PRESS) {
for (i = 0; i < num_buttons_zones; i++)
if (adc >= btn_zones[i].adc_low &&
adc <= btn_zones[i].adc_high) {
jd->button_code = btn_zones[i].code;
input_report_key(cod3035x->input,
jd->button_code,
1);
input_sync(cod3035x->input);
jd->button_det = true;
cod3035x_process_button_ev(cod3035x->codec,
jd->button_code,
1);
dev_dbg(cod3035x->dev, ":key %d is pressed, adc %d\n",
btn_zones[i].code, adc);
return;
}
dev_dbg(cod3035x->dev, ":key skipped. ADC %d\n", adc);
} else {
snd_soc_update_bits(cod3035x->codec, COD3035X_12_PD_AD2,
PDB_MIC_BST3_MASK|PDB_MIC_PGA3_MASK, 0);
snd_soc_update_bits(cod3035x->codec, COD3035X_13_PD_AD3,
RESETB_BST3_MASK, 0);
msleep(40);
snd_soc_update_bits(cod3035x->codec, COD3035X_12_PD_AD2,
PDB_MIC_BST3_MASK|PDB_MIC_PGA3_MASK,
PDB_MIC_BST3_MASK|PDB_MIC_PGA3_MASK);
snd_soc_update_bits(cod3035x->codec, COD3035X_13_PD_AD3,
RESETB_BST3_MASK, RESETB_BST3_MASK);
jd->button_det = false;
input_report_key(cod3035x->input, jd->button_code, 0);
input_sync(cod3035x->input);
cod3035x_process_button_ev(cod3035x->codec,
jd->button_code,
0);
dev_dbg(cod3035x->dev, ":key %d released\n", jd->button_code);
}
return;
}
#if defined(CONFIG_SND_SOC_COD30XX_EXT_ANT)
static irqreturn_t cod3035x_ant_thread_isr(int irq, void *data)
{
struct cod3035x_priv *cod3035x = data;
struct cod3035x_jack_det *jd = &cod3035x->jack_det;
bool det_status_change = false;
int curr_data;
int pre_data;
int loopcnt;
int check_loop_cnt = 16; /* 500ms h/w req */
unsigned int jack_state;
int ant_case=0;
mutex_lock(&cod3035x->key_lock);
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, false);
pr_info("1.%s jack_det[%d], mic_det[%d], ant_det[%d]\n",
__func__, jd->jack_det, jd->mic_det, jd->ant_det);
/*
* Sequence for EAR ANT DETECT IRQ
* case 1[3 pole jack insert]:
* case 2[3 pole jack remove]:
* case 3[4 pole jack insert]:
* case 4[4 pole jack remove]:
* case 5[EXT ANT + 3 pole jack insert]:
* case 6[EXT ANT + 3 pole jack remove]:
* case 7[EXT ANT + 4 pole jack insert]:
* case 8[EXT ANT + 4 pole jack remove]:
=> EAR_ANT_DET pin don't case, ADC(mic_adc_range) check in cod3035_jack_det_work()
* case 9[EXT ANT insert]:
* case 10[EXT ANT remove]:
=> EAR_ANT_DET pin don't case, ADC(ant_adc_range) check in cod3035_jack_det_work()
* case 11[EXT ANT inserted + 3 pole jack insert]: High -> Low
* case 12[EXT ANT inserted + 3 pole jack remove]: Low -> High
* case 13[EXT ANT inserted + 4 pole jack insert]: High -> Low
* case 14[EXT ANT inserted + 4 pole jack remove]: Low -> High
=> use cod3035x_ant_thread_isr()
*/
curr_data = gpio_get_value(cod3035x->ant_det_gpio);
pre_data = curr_data;
loopcnt = 0;
dev_dbg(cod3035x->dev, "%s pre_data: %d", __func__, pre_data);
while (true) {
curr_data = gpio_get_value(cod3035x->ant_det_gpio);
/*
* If current gpio value was checked differently with pre gpio state,
* skip processing for antenna interrupt.
*/
if (pre_data != curr_data) {
dev_dbg(cod3035x->dev, "%s skip ant irq: pre_data != curr_data\n", __func__);
mutex_unlock(&cod3035x->key_lock);
goto skip;
} else {
loopcnt++;
}
if (loopcnt >= check_loop_cnt)
break;
msleep(20);
}
det_status_change = true;
jd->ant_irq = true;
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, false);
jack_state = snd_soc_read(cod3035x->codec, 0x0B) & 0x000000F0;
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, true);
if (jack_state == 0x50 || jack_state == 0x60)
ant_case = 2;
else if (jack_state == 0x40)
ant_case = 1;
else
ant_case = 0;
pr_info("2.%s jack_det[%d], mic_det[%d], ant_det[%d], ant_case[%d]\n",
__func__, jd->jack_det, jd->mic_det, jd->ant_det, ant_case);
mutex_unlock(&cod3035x->key_lock);
if (det_status_change && ant_case == 2 && curr_data) {
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, false);
cod3035x_jack_reset(cod3035x);
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, true);
} else if (det_status_change && ant_case == 1) {
cancel_work_sync(&cod3035x->jack_det_work);
queue_work(cod3035x->jack_det_wq, &cod3035x->jack_det_work);
}
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, false);
snd_soc_update_bits(cod3035x->codec, COD3035X_80_PDB_ACC1,
PDB_JD_MASK, PDB_JD_MASK);
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, true);
jd->ant_irq = false;
skip:
return IRQ_HANDLED;
}
static void cod3035x_jack_report_work(struct work_struct *work)
{
struct cod3035x_priv *cod3035x =
container_of(work, struct cod3035x_priv, jack_report_work.work);
struct cod3035x_jack_det *jackdet = &cod3035x->jack_det;
//struct snd_soc_codec *codec = cod3035x->codec;
int jack_det_status = 0, adc = 0;
dev_info(cod3035x->dev, "%s : start\n", __func__);
mutex_lock(&cod3035x->jackreport_lock);
/* read adc for mic detect */
adc = cod3035x_adc_get_value(cod3035x);
set_ant_jack_flag(cod3035x, jackdet, adc);
jack_det_status = cod3035x_get_jack_status(jackdet);
dev_info(cod3035x->dev, "%s ant[%d], jack[%d], mic[%d]\n",
__func__, jackdet->ant_det, jackdet->jack_det, jackdet->mic_det);
if (jackdet->prev_jack_det_status == jack_det_status) {
dev_info(cod3035x->dev, "%s current jack_det_status(%d) == prev_jack_det_status(%d)\n",
__func__, jackdet->prev_jack_det_status, jack_det_status);
send_status_to_audioframework(&(cod3035x->sdev), jack_det_status);
}
mutex_unlock(&cod3035x->jackreport_lock);
}
#endif
int cod3035x_jack_mic_register(struct snd_soc_codec *codec)
{
struct cod3035x_priv *cod3035x = snd_soc_codec_get_drvdata(codec);
int i, ret;
cod3035x->sdev.name = "h2w";
ret = switch_dev_register(&cod3035x->sdev);
if (ret < 0)
dev_err(codec->dev, "Switch registration failed\n");
cod3035x->input = devm_input_allocate_device(codec->dev);
if (!cod3035x->input) {
dev_err(codec->dev, "Failed to allocate input device\n");
return -ENOMEM;
}
/* Not handling Headset events for now.Headset event handling
* registered as Input device, causing some conflict with Keyboard Input
* device.So, temporarily not handling Headset event, it will be enabled
* after proper fix.
*/
cod3035x->input->name = "Codec3035 Headset Events";
cod3035x->input->phys = dev_name(codec->dev);
cod3035x->input->id.bustype = BUS_I2C;
cod3035x->input->evbit[0] = BIT_MASK(EV_KEY);
for (i = 0; i < 4; i++)
set_bit(cod3035x->jack_buttons_zones[i].code, cod3035x->input->keybit);
cod3035x->input->dev.parent = codec->dev;
input_set_drvdata(cod3035x->input, codec);
ret = input_register_device(cod3035x->input);
if (ret != 0) {
cod3035x->input = NULL;
dev_err(codec->dev, "Failed to register 3035 input device\n");
}
#ifdef CONFIG_PM
pm_runtime_get_sync(codec->dev);
#else
cod3035x_enable(codec->dev);
#endif
#ifdef CONFIG_PM
pm_runtime_put_sync(codec->dev);
#else
cod3035x_disable(codec->dev);
#endif
return 0;
}
EXPORT_SYMBOL_GPL(cod3035x_jack_mic_register);
static const struct snd_soc_dai_ops cod3035x_dai_ops = {
.set_fmt = cod3035x_dai_set_fmt,
.startup = cod3035x_dai_startup,
.shutdown = cod3035x_dai_shutdown,
.hw_params = cod3035x_dai_hw_params,
};
#define COD3035X_RATES SNDRV_PCM_RATE_8000_192000
#define COD3035X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_driver cod3035x_dai[] = {
{
.name = "cod3035x-aif",
.id = 1,
.playback = {
.stream_name = "AIF Playback",
.channels_min = 1,
.channels_max = 8,
.rates = COD3035X_RATES,
.formats = COD3035X_FORMATS,
},
.capture = {
.stream_name = "AIF Capture",
.channels_min = 1,
.channels_max = 8,
.rates = COD3035X_RATES,
.formats = COD3035X_FORMATS,
},
.ops = &cod3035x_dai_ops,
.symmetric_rates = 1,
},
{
.name = "cod3035x-aif2",
.id = 2,
.playback = {
.stream_name = "AIF2 Playback",
.channels_min = 1,
.channels_max = 2,
.rates = COD3035X_RATES,
.formats = COD3035X_FORMATS,
},
.capture = {
.stream_name = "AIF2 Capture",
.channels_min = 1,
.channels_max = 2,
.rates = COD3035X_RATES,
.formats = COD3035X_FORMATS,
},
.ops = &cod3035x_dai_ops,
.symmetric_rates = 1,
},
};
static int cod3035x_regulators_enable(struct snd_soc_codec *codec)
{
int ret;
struct cod3035x_priv *cod3035x = snd_soc_codec_get_drvdata(codec);
cod3035x->regulator_count++;
ret = regulator_enable(cod3035x->vdd);
ret = regulator_enable(cod3035x->vdd2);
dev_dbg(codec->dev, "%s regulator: %d\n",
__func__, cod3035x->regulator_count);
return ret;
}
static void cod3035x_regulators_disable(struct snd_soc_codec *codec)
{
struct cod3035x_priv *cod3035x = snd_soc_codec_get_drvdata(codec);
cod3035x->regulator_count--;
regulator_disable(cod3035x->vdd);
regulator_disable(cod3035x->vdd2);
dev_dbg(codec->dev, "%s regulator: %d\n",
__func__, cod3035x->regulator_count);
}
static void cod3035x_reset_io_selector_bits(struct snd_soc_codec *codec)
{
/* Reset output selector bits */
snd_soc_update_bits(codec, COD3035X_77_CHOP_DA,
EN_HP_CHOP_MASK | EN_EP_CHOP_MASK, 0x0);
}
/*
* Configure the mic1 and mic2 bias voltages with default value or the value
* received from the device tree.
* Also configure the internal LDO voltage.
*/
static void cod3035x_configure_mic_bias(struct snd_soc_codec *codec)
{
struct cod3035x_priv *cod3035x = snd_soc_codec_get_drvdata(codec);
/* Configure Mic1 Bias Voltage */
snd_soc_update_bits(codec, COD3035X_19_CTRL_REF,
CTRV_MCB1_MASK,
(cod3035x->mic_bias1_voltage << CTRV_MCB1_SHIFT));
/* Configure Mic2 Bias Voltage */
snd_soc_update_bits(codec, COD3035X_83_CTR_MCB,
CTRV_MCB2_MASK,
(cod3035x->mic_bias2_voltage << CTRV_MCB2_SHIFT));
/* Configure Mic Bias LDO Voltage */
snd_soc_update_bits(codec, COD3035X_83_CTR_MCB,
CTRV_MCB_LDO_MASK,
(cod3035x->mic_bias_ldo_voltage << CTRV_MCB_LDO_SHIFT));
}
/**
* cod3035x_codec_initialize : To be called if f/w update fails
*
* In case the firmware is not present or corrupt, we should still be able to
* run the codec with decent parameters. This values are updated as per the
* latest stable firmware.
*
* The values provided in this function are hard-coded register values, and we
* need not update these values as per bit-fields.
*/
static void cod3035x_codec_initialize(void *context)
{
struct snd_soc_codec *codec = (struct snd_soc_codec *)context;
struct cod3035x_priv *cod3035x = snd_soc_codec_get_drvdata(codec);
dev_dbg(codec->dev, "%s called, setting defaults\n", __func__);
#ifdef CONFIG_PM
pm_runtime_get_sync(codec->dev);
#else
cod3035x_enable(codec->dev);
#endif
cod3035x->mic_status = 0;
snd_soc_write(codec, COD3035X_05_IRQ1M, 0x00);
snd_soc_write(codec, COD3035X_06_IRQ2M, 0x00);
snd_soc_write(codec, COD3035X_07_IRQ3M, 0x53);
snd_soc_write(codec, COD3035X_08_IRQ4M, 0x53);
/*
* Control of threshold level
* CTRV_GDET_VTH: 0.982V -> 1.2V
* CTRV_LDET_VTH: 1.241V -> 1.636V
* CTRV_SURGE_REFP: 3.2V -> 2.4V
* CTRV_SURGE_REFN: -2.6V
* If want this control, set 'dis_ovp_det_mode' in device tree file.
*/
if (cod3035x->dis_ovp_det_mode) {
snd_soc_write(codec, COD3035X_3D_OVP_1, 0xE6);
snd_soc_write(codec, COD3035X_84_CTR_POP1, 0xFC);
} else {
snd_soc_write(codec, COD3035X_3D_OVP_1, 0xD7);
snd_soc_write(codec, COD3035X_84_CTR_POP1, 0xBE);
}
snd_soc_write(codec, COD3035X_A6_IMP_CNT11, 0x42);
snd_soc_write(codec, COD3035X_93_CTR_DLY6, 0x00);
snd_soc_write(codec, COD3035X_94_JACK_CTR, 0x00);
#ifdef CONFIG_SND_SOC_COD30XX_EXT_ANT
if (cod3035x->dtv_detect) {
snd_soc_write(codec, COD3035X_8E_CTR_DLY1, 0x00);
snd_soc_write(codec, COD3035X_8F_CTR_DLY2, 0x00);
} else {
snd_soc_write(codec, COD3035X_8E_CTR_DLY1, 0xC0);
snd_soc_write(codec, COD3035X_8F_CTR_DLY2, 0xC0);
}
#else
snd_soc_write(codec, COD3035X_8E_CTR_DLY1, 0xC0);
snd_soc_write(codec, COD3035X_8F_CTR_DLY2, 0xC0);
#endif
/* Jack debounce time setting */
snd_soc_write(codec, COD3035X_8D_CTR_DBNC3,
cod3035x->jackin_dbnc_time | cod3035x->jackout_dbnc_time);
snd_soc_write(codec, COD3035X_9A_TEST3, 0x01);
snd_soc_write(codec, COD3035X_44_IF1_FORMAT4, 0xFF);
/* HP amplifier reference current to 8uA*/
snd_soc_write(codec, COD3035X_B0_AUTO_HP1, 0xA0);
snd_soc_write(codec, COD3035X_B4_AUTO_HP5, 0x01);
snd_soc_write(codec, COD3035X_B5_AUTO_HP6, 0xFC);
snd_soc_write(codec, COD3035X_B6_AUTO_HP7, 0xFF);
snd_soc_write(codec, COD3035X_B7_AUTO_HP8, 0xB7);
snd_soc_write(codec, COD3035X_B8_AUTO_HP9, 0x55);
snd_soc_write(codec, COD3035X_BA_AUTO_HP11, 0x02);
snd_soc_write(codec, COD3035X_BB_AUTO_HP12, 0x66);
snd_soc_write(codec, COD3035X_BE_ODSEL2, 0x60);
/* Disable AVC mute function */
snd_soc_write(codec, COD3035X_54_AVC1, 0x8F);
snd_soc_write(codec, COD3035X_55_AVC2, 0x78);
/* Disable DRC function */
snd_soc_write(codec, COD3035X_57_AVC4, 0x00);
/* Preset register for AVC */
snd_soc_write(codec, COD3035X_E1_PRESET_AVC, 0xF1);
snd_soc_write(codec, COD3035X_E3_PRESET_AVC, 0x00);
snd_soc_write(codec, COD3035X_E5_PRESET_AVC, 0x00);
snd_soc_write(codec, COD3035X_E7_PRESET_AVC, 0x00);
snd_soc_write(codec, COD3035X_E8_PRESET_AVC, 0x00);
snd_soc_write(codec, COD3035X_E9_PRESET_AVC, 0x00);
snd_soc_write(codec, COD3035X_EA_PRESET_AVC, 0x01);
snd_soc_write(codec, COD3035X_F2_PRESET_AVC, 0x03);
snd_soc_write(codec, COD3035X_F3_PRESET_AVC, 0x1F);
snd_soc_write(codec, COD3035X_F4_PRESET_AVC, 0x55);
snd_soc_write(codec, COD3035X_5A_AVC7, 0x44);
snd_soc_write(codec, COD3035X_F5_PRESET_AVC, 0x1A);
snd_soc_write(codec, COD3035X_F6_PRESET_AVC, 0xC6);
snd_soc_write(codec, COD3035X_1B_SDM_STR, 0x01);
snd_soc_write(codec, COD3035X_80_PDB_ACC1, 0x02);
msleep(100);
snd_soc_write(codec, COD3035X_80_PDB_ACC1, 0x03);
snd_soc_update_bits(codec, COD3035X_81_PDB_ACC2,
T_PDB_IMP_MASK, T_PDB_IMP_MASK);
/* MIC Boost LPF On */
snd_soc_write(codec, COD3035X_7C_LPF_AD, 0x1F);
/* Default value, enabling HPF and setting freq at 100Hz */
snd_soc_write(codec, COD3035X_46_ADC1, 0x0c);
snd_soc_write(codec, COD3035X_4B_ADC2, 0x0c);
snd_soc_update_bits(codec, COD3035X_D0_CTRL_IREF1,
CTMI_VCM_MASK | CTMI_MIX_MASK,
(CTMI_VCM_4U << CTMI_VCM_SHIFT) | CTMI_MIX_2U);
snd_soc_update_bits(codec, COD3035X_D1_CTRL_IREF2,
CTMI_INT1_MASK, CTMI_INT1_4U);
snd_soc_update_bits(codec, COD3035X_D2_CTRL_IREF3,
CTMI_MIC2_MASK | CTMI_MIC1_MASK,
(CTMI_MIC2_2U << CTMI_MIC2_SHIFT) | CTMI_MIC1_2U);
snd_soc_update_bits(codec, COD3035X_D3_CTRL_IREF4,
CTMI_MIC_BUFF_MASK | CTMI_MIC3_MASK,
(CTMI_MIC_BUFF_2U << CTMI_MIC_BUFF_SHIFT) | CTMI_MIC3_2U);
snd_soc_write(codec, COD3035X_47_AVOLL1, 0x18);
snd_soc_write(codec, COD3035X_48_AVOLR1, 0x18);
/* Boost 20 dB, Gain 0 dB for MIC1/MIC2/MIC3 */
snd_soc_write(codec, COD3035X_20_VOL_AD1, 0x54);
snd_soc_write(codec, COD3035X_21_VOL_AD2, 0x54);
snd_soc_write(codec, COD3035X_22_VOL_AD3, 0x54);
snd_soc_write(codec, COD3035X_22_VOL_AD3, 0x54);
/* default HP Volume setting */
snd_soc_write(codec, COD3035X_30_VOL_HPL, 0x0A);
snd_soc_write(codec, COD3035X_31_VOL_HPR, 0x0A);
snd_soc_write(codec, COD3035X_51_DVOLL, 0x54);
snd_soc_write(codec, COD3035X_52_DVOLR, 0x54);
snd_soc_write(codec, COD3035X_10_PD_REF, 0x10);
/* Change receiver REF current */
switch (cod3035x->rcv_drv_current) {
case 0:
snd_soc_update_bits(codec, COD3035X_DC_CTRL_EPS,
CTMI_EP_A_MASK, (CTMI_EP_A_1_UA << CTMI_EP_A_SHIFT));
break;
case 1:
snd_soc_update_bits(codec, COD3035X_DC_CTRL_EPS,
CTMI_EP_A_MASK, (CTMI_EP_A_2_UA << CTMI_EP_A_SHIFT));
break;
case 2:
snd_soc_update_bits(codec, COD3035X_DC_CTRL_EPS,
CTMI_EP_A_MASK, (CTMI_EP_A_3_UA << CTMI_EP_A_SHIFT));
break;
case 3:
snd_soc_update_bits(codec, COD3035X_DC_CTRL_EPS,
CTMI_EP_A_MASK, (CTMI_EP_A_4_UA << CTMI_EP_A_SHIFT));
break;
default:
snd_soc_update_bits(codec, COD3035X_DC_CTRL_EPS,
CTMI_EP_A_MASK, (CTMI_EP_A_4_UA << CTMI_EP_A_SHIFT));
break;
}
/* Configure mic bias voltage */
cod3035x_configure_mic_bias(codec);
/* All boot time hardware access is done. Put the device to sleep. */
#ifdef CONFIG_PM
pm_runtime_put_sync(codec->dev);
#else
cod3035x_disable(codec->dev);
#endif
}
/**
* cod3035x_post_fw_update_success: To be called after f/w update
*
* The firmware may be enabling some of the path and power registers which are
* used during path enablement. We need to keep the values of these registers
* consistent so that the functionality of the codec driver doesn't change
* because of the firmware.
*/
static void cod3035x_regmap_sync(struct device *dev)
{
struct cod3035x_priv *cod3035x = dev_get_drvdata(dev);
unsigned char reg[COD3035X_MAX_REGISTER] = {0,};
int i;
/* Read from Cache */
for (i = 0; i < COD3035X_REGCACHE_SYNC_END_REG; i++)
if (cod3035x_readable_register(dev, i) &&
(!cod3035x_volatile_register(dev, i)))
reg[i] = (unsigned char)
snd_soc_read(cod3035x->codec, i);
regcache_cache_bypass(cod3035x->regmap, true);
snd_soc_write(cod3035x->codec, COD3035X_40_DIGITAL_POWER,
reg[COD3035X_40_DIGITAL_POWER]);
/* Update HW */
for (i = 0; i < COD3035X_REGCACHE_SYNC_END_REG ; i++)
if (cod3035x_writeable_register(dev, i) &&
(!cod3035x_volatile_register(dev, i)))
snd_soc_write(cod3035x->codec, i, reg[i]);
regcache_cache_bypass(cod3035x->regmap, false);
}
static void cod3035x_reg_restore(struct snd_soc_codec *codec)
{
struct cod3035x_priv *cod3035x = snd_soc_codec_get_drvdata(codec);
snd_soc_update_bits(codec, COD3035X_80_PDB_ACC1,
PDB_JD_CLK_EN_MASK, PDB_JD_CLK_EN_MASK);
/* Give 15ms delay before storing the otp values */
usleep_range(15000, 15000 + 1000);
if (!cod3035x->is_probe_done) {
cod3035x_regmap_sync(codec->dev);
cod3035x_reset_io_selector_bits(codec);
} else {
cod3035x_regmap_sync(codec->dev);
}
}
static void cod3035x_i2c_parse_dt(struct cod3035x_priv *cod3035x)
{
/* todo .. Need to add DT parsing for 3035 */
struct device *dev = cod3035x->dev;
struct device_node *np = dev->of_node;
unsigned int bias_v_conf;
int mic_range, mic_delay, btn_rel_val;
int water_finish_chk_min, water_threshold_min, water_threshold_max;
int gdet_mode, ret;
struct of_phandle_args args;
int jack_imp_tuning;
int fake_jack_insert;
int aux_cable_detect;
int jackout_dbnc, jackin_dbnc, jackin_ldet;
int i = 0;
unsigned int rcv_drv_current;
unsigned int feature_flag;
u8 lassenA_rev = 0;
#ifdef CONFIG_SND_SOC_COD30XX_EXT_ANT
int ant_range;
#endif
cod3035x->int_gpio = of_get_gpio(np, 0);
if (cod3035x->int_gpio < 0)
dev_err(dev, "(*)Error in getting Codec-3035 Interrupt gpio\n");
/* Check dtv_check gpio in DT */
cod3035x->dtv_detect = false;
cod3035x->dtv_check_gpio = of_get_named_gpio(np, "dtv-check-gpio", 0);
if (cod3035x->dtv_check_gpio < 0) {
if (of_property_read_bool(np, "legacy-dtv-support")) {
cod3035x->dtv_detect = true;
pr_info("%s : legacy-dtv-support property detected.\n", __func__);
} else {
cod3035x->dtv_detect = false;
pr_warn("%s : not support dtv-check-gpio\n", __func__);
}
} else {
pr_info("%s : dtv-check-gpio = %d\n", __func__,
cod3035x->dtv_check_gpio);
if (gpio_is_valid(cod3035x->dtv_check_gpio)) {
if (gpio_get_value(cod3035x->dtv_check_gpio))
cod3035x->dtv_detect = true;
else
cod3035x->dtv_detect = false;
pr_info("%s : dtv_detect %d, dtv-check-gpio %d\n",
__func__, cod3035x->dtv_detect,
gpio_get_value(cod3035x->dtv_check_gpio));
} else {
cod3035x->dtv_detect = false;
pr_err("%s : invalid dtv-check-gpio\n", __func__);
}
pr_info("%s : dtv-check-gpio check done. dtv_detect %s\n",
__func__, cod3035x->dtv_detect ? "True" : "False");
}
#ifdef CONFIG_SND_SOC_COD30XX_EXT_ANT
if (cod3035x->dtv_detect) {
cod3035x->ant_det_gpio = of_get_named_gpio(np, "ant-det-gpio", 0);
if (cod3035x->ant_det_gpio < 0) {
pr_err("%s : can not find the earjack-antdet-gpio in the dt\n", __func__);
} else
pr_info("%s : earjack-ant-det-gpio =%d\n", __func__, cod3035x->ant_det_gpio);
ret = of_property_read_u32(dev->of_node, "ant-adc-range", &ant_range);
if (!ret)
cod3035x->ant_adc_range = ant_range;
else
cod3035x->ant_adc_range = 3400;
}
#endif
/* Default Bias Voltages */
cod3035x->mic_bias1_voltage = MIC_BIAS1_VO_3_0V;
cod3035x->mic_bias2_voltage = MIC_BIAS2_VO_3_0V;
cod3035x->mic_bias_ldo_voltage = MIC_BIAS_LDO_VO_3_3V;
ret = of_property_read_u32(dev->of_node,
"mic-bias1-voltage", &bias_v_conf);
if ((!ret) && ((bias_v_conf >= MIC_BIAS1_VO_2_8V) &&
(bias_v_conf <= MIC_BIAS1_VO_3_0V)))
cod3035x->mic_bias1_voltage = bias_v_conf;
else
dev_dbg(dev, "Property 'mic-bias1-voltage' %s",
ret ? "not found" : "invalid value (use:1-3)");
ret = of_property_read_u32(dev->of_node,
"mic-bias2-voltage", &bias_v_conf);
if ((!ret) && ((bias_v_conf >= MIC_BIAS2_VO_2_8V) &&
(bias_v_conf <= MIC_BIAS2_VO_3_0V)))
cod3035x->mic_bias2_voltage = bias_v_conf;
else
dev_dbg(dev, "Property 'mic-bias2-voltage' %s",
ret ? "not found" : "invalid value (use:1-3)");
ret = of_property_read_u32(dev->of_node, "mic-adc-range", &mic_range);
if (!ret)
cod3035x->mic_adc_range = mic_range;
else
cod3035x->mic_adc_range = 1120;
/* in order to implement various requirments by MCD
* 0x01 : The epdrv makes 0xD7 register sweep.
* This can fix the high freq. noise issue.
*/
ret = of_property_read_u32(dev->of_node, "use-feature-flag", &feature_flag);
if (!ret)
cod3035x->model_feature_flag = feature_flag;
else
cod3035x->model_feature_flag = 0;
ret = of_property_read_u32(dev->of_node, "mic-det-delay", &mic_delay);
if (!ret)
cod3035x->mic_det_delay = mic_delay;
else
cod3035x->mic_det_delay = COD3035X_MIC_DET_DELAY;
/* Receiver Driving current */
ret = of_property_read_u32(dev->of_node, "rcv-drv-current", &rcv_drv_current);
if (!ret)
cod3035x->rcv_drv_current = rcv_drv_current;
else
cod3035x->rcv_drv_current = 3; /* LassenX default value */
ret = of_property_read_u32(dev->of_node,
"btn-release-value", &btn_rel_val);
if (!ret)
cod3035x->btn_release_value = btn_rel_val;
else
cod3035x->btn_release_value = 1100;
ret = of_property_read_u32(dev->of_node, "water-threshold-min",
&water_threshold_min);
if (!ret)
cod3035x->water_threshold_adc_min = water_threshold_min;
else
cod3035x->water_threshold_adc_min = COD3035X_WATER_DET_THRESHOLD_MIN;
ret = of_property_read_u32(dev->of_node, "water-finish-chk-min",
&water_finish_chk_min);
if (!ret)
cod3035x->water_finish_chk_adc_min = water_finish_chk_min;
else
cod3035x->water_finish_chk_adc_min = COD3035X_GDET_FINISH_CHK_MIN;
ret = of_property_read_u32(dev->of_node, "water-threshold-max",
&water_threshold_max);
if (!ret)
cod3035x->water_threshold_adc_max = water_threshold_max;
else
cod3035x->water_threshold_adc_max = COD3035X_WATER_DET_THRESHOLD_MAX;
ret = of_property_read_u32(dev->of_node, "jack-imp-tuning", &jack_imp_tuning);
if (!ret)
cod3035x->jack_imp_gap = jack_imp_tuning;
else
cod3035x->jack_imp_gap = 0;
ret = of_property_read_u32(dev->of_node, "fake-jack-adc", &fake_jack_insert);
if (!ret)
cod3035x->fake_jack_adc = fake_jack_insert;
else
cod3035x->fake_jack_adc = COD3035X_FAKE_JACK_INSERT_ADC;
ret = of_property_read_u32(dev->of_node, "aux-cable-detect-adc", &aux_cable_detect);
if (!ret)
cod3035x->aux_cable_detect_adc = aux_cable_detect;
else
cod3035x->aux_cable_detect_adc = COD3035X_AUX_CABLE_DETECT_ADC;
/* Jackout debounce time setting */
ret = of_property_read_u32(dev->of_node, "jackout-dbnc-time", &jackout_dbnc);
if (!ret)
cod3035x->jackout_dbnc_time = COD3035X_JACKOUT_DBNC_MASK & jackout_dbnc;
else
cod3035x->jackout_dbnc_time = COD3035X_JACKOUT_DBNC_DEFAULT;
/* Jackin debounce time setting */
ret = of_property_read_u32(dev->of_node, "jackin-dbnc-time", &jackin_dbnc);
if (!ret)
cod3035x->jackin_dbnc_time = COD3035X_JACKIN_DBNC_MASK & jackin_dbnc;
else
cod3035x->jackin_dbnc_time = COD3035X_JACKIN_DBNC_DEFAULT;
/* Jack in LDET VTH setting */
ret = of_property_read_u32(dev->of_node, "jackin-ldet-vth", &jackin_ldet);
if (!ret)
cod3035x->jackin_ldet_vth = CTRV_LDET_VTH_MASK & jackin_ldet;
else
cod3035x->jackin_ldet_vth = CTRV_LDET_VTH_1241;
ret = of_property_read_u32(dev->of_node,
"mic-bias-ldo-voltage", &bias_v_conf);
if ((!ret) && ((bias_v_conf >= MIC_BIAS_LDO_VO_2_8V) &&
(bias_v_conf <= MIC_BIAS_LDO_VO_3_3V)))
cod3035x->mic_bias_ldo_voltage = bias_v_conf;
else
dev_dbg(dev, "Property 'mic-bias-ldo-voltage' %s",
ret ? "not found" : "invalid value (use:1-3)");
dev_dbg(dev, "Bias voltage values: bias1 = %d, bias2= %d, ldo = %d\n",
cod3035x->mic_bias1_voltage,
cod3035x->mic_bias2_voltage,
cod3035x->mic_bias_ldo_voltage);
if (of_find_property(dev->of_node,
"update-firmware", NULL))
cod3035x->update_fw = true;
else
cod3035x->update_fw = false;
if (of_find_property(dev->of_node, "use-btn-adc-mode", NULL) != NULL)
cod3035x->use_btn_adc_mode = true;
if (of_find_property(dev->of_node, "dis-ovp-det-mode", NULL) != NULL)
cod3035x->dis_ovp_det_mode = true;
/*
* For use lassen A revision chipset,
* set 'use-lassenA' device tree option.
*/
if (of_find_property(dev->of_node, "use-lassenA", NULL) != NULL)
cod3035x->is_lassenA = 1;
else
cod3035x->is_lassenA = 0;
/*
* Check pmic indicator register bit (0x159 [7])
* to use the driver of lassen A02 revision.
*/
exynos_acpm_read_reg(ACPM_ADDR_PMIC, COD3035X_LASSENA02_ADDR, &lassenA_rev);
if (lassenA_rev & COD3035X_LASSENA02_MASK)
cod3035x->is_lassenA = 2;
dev_dbg(dev, "%s : cod3035x->is_lassenA revision: %d\n",
__func__, cod3035x->is_lassenA);
if (of_find_property(dev->of_node, "use-det-gdet-adc-mode", NULL) != NULL) {
cod3035x->use_det_gdet_adc_mode = true;
ret = of_property_read_u32(dev->of_node, "use-det-gdet-adc-mode", &gdet_mode);
if (!ret)
cod3035x->use_det_gdet_adc_mode = gdet_mode;
else
cod3035x->use_det_gdet_adc_mode = false;
dev_dbg(dev, "%s : cod3035x->use_det_gdet_adc_mode: %d\n",
__func__, cod3035x->use_det_gdet_adc_mode);
}
dev_err(dev, "Using %s for button detection\n",
cod3035x->use_btn_adc_mode ? "GPADC" : "internal h/w");
if (cod3035x->use_btn_adc_mode) {
/* Parsing but-zones, a maximum of 4 buttons are supported */
for (i = 0; i < 4; i++) {
if (of_parse_phandle_with_args(dev->of_node,
"but-zones-list",
"#list-but-cells", i, &args))
break;
cod3035x->jack_buttons_zones[i].code = args.args[0];
cod3035x->jack_buttons_zones[i].adc_low = args.args[1];
cod3035x->jack_buttons_zones[i].adc_high = args.args[2];
}
/* initialize button status */
cod3035x->jack_det.privious_button_state = BUTTON_RELEASE;
for (i = 0; i < 4; i++)
dev_err(dev, "[DEBUG]: buttons: code(%d), low(%d), high(%d)\n",
cod3035x->jack_buttons_zones[i].code,
cod3035x->jack_buttons_zones[i].adc_low,
cod3035x->jack_buttons_zones[i].adc_high);
}
}
struct codec_notifier_struct {
struct cod3035x_priv *cod3035x;
};
static struct codec_notifier_struct codec_notifier_t;
static int cod3035x_notifier_handler(struct notifier_block *nb,
unsigned long insert,
void *data)
{
struct codec_notifier_struct *codec_notifier_data = data;
struct cod3035x_priv *cod3035x = codec_notifier_data->cod3035x;
struct snd_soc_codec *codec = cod3035x->codec;
struct cod3035x_jack_det *jackdet = &cod3035x->jack_det;
unsigned int stat1, pend1, pend2, pend3, pend4;
unsigned int param1, param2, param3, param4, param5;
unsigned int temp1, temp2;
mutex_lock(&cod3035x->key_lock);
wake_lock_timeout(&cod3035x->codec_wake_lock, 5 * HZ);
pend1 = cod3035x->irq_val[0];
pend2 = cod3035x->irq_val[1];
pend3 = cod3035x->irq_val[2];
pend4 = cod3035x->irq_val[3];
stat1 = cod3035x->irq_val[4];
param1 = cod3035x->irq_val[5];
param2 = cod3035x->irq_val[6];
param3 = cod3035x->irq_val[7];
param4 = cod3035x->irq_val[8];
param5 = cod3035x->irq_val[9];
dev_dbg(cod3035x->dev,
"[DEBUG] %s , line %d 01: %02x, 02:%02x, 03:%02x, 04:%02x, stat1:%02x\n",
__func__, __LINE__, pend1, pend2, pend3, pend4, stat1);
/* surge out */
if (pend4 & IRQ4_OVPR_MONI_F || pend4 & IRQ4_OVPL_MONI_F) {
dev_dbg(cod3035x->dev, "[DEBUG] %s IRQ4_OVPLR_MONI_F, line %d\n",
__func__, __LINE__);
jackdet->surge_det = false;
mutex_unlock(&cod3035x->key_lock);
goto out;
}
if (jackdet->surge_det) {
dev_dbg(cod3035x->dev, "[DEBUG] %s jackdet->surge_det, line %d\n",
__func__, __LINE__);
mutex_unlock(&cod3035x->key_lock);
goto out;
}
/* surge in */
if (pend3 & IRQ3_OVPR_MONI_R || pend3 & IRQ3_OVPL_MONI_R) {
dev_dbg(cod3035x->dev, "[DEBUG] %s surge in, line %d\n",
__func__, __LINE__);
jackdet->surge_det = true;
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, false);
/* Jack in */
snd_soc_write(codec, COD3035X_93_CTR_DLY6, 0x20);
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, true);
mutex_unlock(&cod3035x->key_lock);
goto out;
}
/* AP Check state*/
if ((pend1 & IRQ1_ST_APCHECK_R) || (pend4 & IRQ4_ST_MOIST_F)) {
dev_dbg(cod3035x->dev, "[DEBUG] %s AP Check state, line %d\n",
__func__, __LINE__);
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, false);
temp1 = snd_soc_read(codec, 0xAF);
temp2 = snd_soc_read(codec, 0xAD);
dev_dbg(cod3035x->dev, "[DEBUG] %s 0xAF:%02x, 0xAD:%02x, line %d\n",
__func__, temp1, temp2, __LINE__);
snd_soc_write(cod3035x->codec, COD3035X_93_CTR_DLY6, 0x00);
snd_soc_write(cod3035x->codec, COD3035X_99_TEST2, 0x00);
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, true);
queue_work(cod3035x->gdet_adc_wq, &cod3035x->gdet_adc_work);
mutex_unlock(&cod3035x->key_lock);
goto out;
}
/* Jack interrupt in water */
if (pend1 & IRQ1_ST_WTJACKIN_R) {
dev_dbg(cod3035x->dev, "[DEBUG] %s Jack interrupt in water, line %d\n",
__func__, __LINE__);
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, false);
temp1 = snd_soc_read(codec, 0xAF);
temp2 = snd_soc_read(codec, 0xAD);
dev_dbg(cod3035x->dev, "[DEBUG] %s 0xAF:%02x, 0xAD:%02x, line %d\n",
__func__, temp1, temp2, __LINE__);
snd_soc_update_bits(codec, COD3035X_80_PDB_ACC1,
PDB_JD_CLK_EN_MASK, 0x0);
snd_soc_write(cod3035x->codec, COD3035X_93_CTR_DLY6, 0x00);
snd_soc_update_bits(codec, COD3035X_80_PDB_ACC1,
PDB_JD_CLK_EN_MASK, PDB_JD_CLK_EN_MASK);
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, true);
mutex_unlock(&cod3035x->key_lock);
goto out;
}
/* Water out interrupt */
if (pend1 & IRQ1_ST_WTJACKOUT_R) {
dev_dbg(cod3035x->dev, "[DEBUG] %s Water out interrupt, line %d\n",
__func__, __LINE__);
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, false);
temp1 = snd_soc_read(codec, 0xAF);
temp2 = snd_soc_read(codec, 0xAD);
dev_dbg(cod3035x->dev, "[DEBUG] %s 0xAF:%02x, 0xAD:%02x, line %d\n",
__func__, temp1, temp2, __LINE__);
snd_soc_update_bits(codec, COD3035X_80_PDB_ACC1,
PDB_JD_CLK_EN_MASK, 0x0);
snd_soc_write(cod3035x->codec, COD3035X_93_CTR_DLY6, 0x00);
snd_soc_update_bits(codec, COD3035X_80_PDB_ACC1,
PDB_JD_CLK_EN_MASK, PDB_JD_CLK_EN_MASK);
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, true);
mutex_unlock(&cod3035x->key_lock);
goto out;
}
/* Jack out interrupt */
if (pend1 & IRQ1_ST_JACKOUT_R) {
dev_dbg(cod3035x->dev, "[DEBUG] %s Jack out interrupt, line %d\n",
__func__, __LINE__);
jackdet->jack_det = false;
if ((cod3035x->model_feature_flag & MODEL_FLAG_5PIN_BTN_DELAY) &&
cod3035x->btn_delay_masking)
cod3035x->btn_delay_masking = false;
if (cod3035x->model_feature_flag & MODEL_FLAG_JACKOUT_TDMA_NOISE)
remove_tdma_noise(codec);
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, false);
/* IRQ masking off */
snd_soc_write(codec, COD3035X_05_IRQ1M, 0x00);
snd_soc_write(codec, COD3035X_06_IRQ2M, 0x00);
/* HP OVP OFF */
snd_soc_update_bits(codec, COD3035X_3E_OVP_2,
OVP_APON_MASK, 0);
/* Jack out */
snd_soc_write(codec, COD3035X_93_CTR_DLY6, 0x00);
/* Impedance value reset */
snd_soc_write(codec, COD3035X_A6_IMP_CNT11, 0x42);
/* For defence jack detect lock, PDB_JD power on */
#ifdef CONFIG_SND_SOC_COD30XX_EXT_ANT
if (cod3035x->dtv_detect) {
cod3035x_usleep(100);
snd_soc_update_bits(codec, COD3035X_80_PDB_ACC1, PDB_JD_MASK, PDB_JD_MASK);
}
#endif
if (cod3035x->is_suspend)
regcache_cache_only(cod3035x->regmap, true);
cancel_work_sync(&cod3035x->jack_det_work);
queue_work(cod3035x->jack_det_wq, &cod3035x->jack_det_work);
if (cod3035x->use_btn_adc_mode)
queue_work(cod3035x->buttons_wq, &cod3035x->buttons_work);
else
pr_err("[DEBUG] %s , line %d\n", __func__, __LINE__);
mutex_unlock(&cod3035x->key_lock);
goto out;
}
/* Impedance check interrupt */
if (pend3 & IRQ3_IMP_CHECK_DONE_R) {
cod3035x_jack_imp_cal(cod3035x, param1, param2, param3, param4, param5);
mutex_unlock(&cod3035x->key_lock);
goto out;
}
/* Button interrupt */
if ((pend1 & IRQ1_BTN_DET_R) || (pend2 & IRQ2_BTN_DET_F)) {
dev_dbg(cod3035x->dev, "[DEBUG] %s Button interrupt, line %d\n",
__func__, __LINE__);
if ((cod3035x->model_feature_flag & MODEL_FLAG_5PIN_BTN_DELAY) &&
cod3035x->btn_delay_masking) {
dev_dbg(cod3035x->dev, "%s, btn irq masking on, btn irq skip\n",
__func__);
mutex_unlock(&cod3035x->key_lock);
goto out;
}
if (cod3035x->use_btn_adc_mode) {
/* start button work */
queue_work(cod3035x->buttons_wq, &cod3035x->buttons_work);
} else {
pr_err("[DEBUG] %s , line %d\n", __func__, __LINE__);
/* need to implement button detection */
}
}
mutex_unlock(&cod3035x->key_lock);
out:
return IRQ_HANDLED;
}
static BLOCKING_NOTIFIER_HEAD(cod3035x_notifier);
int cod3035x_register_notifier(struct notifier_block *n,
struct cod3035x_priv *cod3035x)
{
int ret;
codec_notifier_t.cod3035x = cod3035x;
ret = blocking_notifier_chain_register(&cod3035x_notifier, n);
if (ret < 0)
pr_err("[DEBUG] %s(%d)\n", __func__, __LINE__);
return ret;
}
/* Notifier registration.
* MFD driver get interrupts from PMIC and MFD filters the interrups.
* if the interrup belongs to codec,
* then it notify the interrupt to the codec.
* The notifier is the way to communicate btw them
*
* The notifier contains
* >> the irq1, irq2, irq3, irq4 and status registers information of codec.
*/
void cod3035x_call_notifier(int irq1, int irq2, int irq3, int irq4, int status1,
int param1, int param2, int param3, int param4, int param5)
{
struct cod3035x_priv *cod3035x = codec_notifier_t.cod3035x;
dev_dbg(cod3035x->dev,
"[DEBUG] %s(%d) 0x1: %02x 0x2: %02x 0x3: %02x 0x4: %02x\n",
__func__, __LINE__, irq1, irq2, irq3, irq4);
cod3035x->irq_val[0] = irq1;
cod3035x->irq_val[1] = irq2;
cod3035x->irq_val[2] = irq3;
cod3035x->irq_val[3] = irq4;
cod3035x->irq_val[4] = status1;
cod3035x->irq_val[5] = param1;
cod3035x->irq_val[6] = param2;
cod3035x->irq_val[7] = param3;
cod3035x->irq_val[8] = param4;
cod3035x->irq_val[9] = param5;
blocking_notifier_call_chain(&cod3035x_notifier, 0, &codec_notifier_t);
}
EXPORT_SYMBOL(cod3035x_call_notifier);
struct notifier_block codec_notifier;
/* To support PBA function test */
#include "../../sound/soc/samsung/jack_cod3035.c"
static int cod3035x_codec_probe(struct snd_soc_codec *codec)
{
struct cod3035x_priv *cod3035x = snd_soc_codec_get_drvdata(codec);
#ifdef CONFIG_SND_SOC_COD30XX_EXT_ANT
int ret;
#endif
dev_dbg(codec->dev, "3035x CODEC_PROBE: (*) %s\n", __func__);
cod3035x->codec = codec;
cod3035x->vdd = devm_regulator_get(codec->dev, "vdd_ldo36");
if (IS_ERR(cod3035x->vdd)) {
dev_warn(codec->dev, "failed to get regulator vdd\n");
return PTR_ERR(cod3035x->vdd);
}
cod3035x->vdd2 = devm_regulator_get(codec->dev, "vdd_ldo37");
if (IS_ERR(cod3035x->vdd2)) {
dev_warn(codec->dev, "failed to get regulator vdd2\n");
return PTR_ERR(cod3035x->vdd2);
}
#ifdef CONFIG_PM
pm_runtime_get_sync(codec->dev);
#else
cod3035x_enable(codec->dev);
#endif
cod3035x->is_probe_done = true;
/* Initialize work queue for button handling */
INIT_WORK(&cod3035x->buttons_work, cod3035x_buttons_work);
cod3035x->buttons_wq = create_singlethread_workqueue("buttons_wq");
if (cod3035x->buttons_wq == NULL) {
dev_err(codec->dev, "Failed to create buttons_wq\n");
return -ENOMEM;
}
/* Initiallize work queue for jack detect handling */
INIT_WORK(&cod3035x->jack_det_work, cod3035x_jack_det_work);
cod3035x->jack_det_wq = create_singlethread_workqueue("jack_det_wq");
if (cod3035x->jack_det_wq == NULL) {
dev_err(codec->dev, "Failed to create jack_det_wq\n");
return -ENOMEM;
}
/* Initialize work queue for water detect handling */
INIT_WORK(&cod3035x->gdet_adc_work, cod3035x_gdet_adc_work);
cod3035x->gdet_adc_wq = create_singlethread_workqueue("gdet_adc_wq");
if (cod3035x->gdet_adc_wq == NULL) {
dev_err(codec->dev, "Failed to create gdet_adc_wq\n");
return -ENOMEM;
}
INIT_WORK(&cod3035x->adc_mute_work, cod3035x_adc_mute_work);
cod3035x->adc_mute_wq = create_singlethread_workqueue("adc_mute_wq");
if (cod3035x->adc_mute_wq == NULL) {
dev_err(codec->dev, "Failed to create adc_mute_wq\n");
return -ENOMEM;
}
INIT_DELAYED_WORK(&cod3035x->btn_delay_work, cod3035x_btn_delay_work);
cod3035x->btn_delay_wq = create_singlethread_workqueue("btn_delay_wq");
if (cod3035x->btn_delay_wq == NULL) {
dev_err(codec->dev, "Failed to create btn_delay_wq\n");
return -ENOMEM;
}
cod3035x_adc_start(cod3035x);
/* cod3035x->aifrate = COD3035X_SAMPLE_RATE_48KHZ; */
cod3035x->playback_aifrate = 0;
cod3035x->capture_aifrate = 0;
cod3035x->dmic1_lmux = 0;
cod3035x->dmic1_rmux = 0;
cod3035x_i2c_parse_dt(cod3035x);
#if defined(CONFIG_SND_SOC_COD30XX_EXT_ANT)
if (cod3035x->dtv_detect) {
INIT_DELAYED_WORK(&cod3035x->jack_report_work, cod3035x_jack_report_work);
cod3035x->jack_report_wq = create_singlethread_workqueue("jack_report_wq");
if (cod3035x->jack_report_wq == NULL) {
dev_err(codec->dev, "Failed to create jack_report_wq\n");
return -ENOMEM;
}
}
#endif
cod3035x->jack_det.adc_val = -EINVAL;
cod3035x->jack_det.gdet_adc_val = -EINVAL;
mutex_init(&cod3035x->jackdet_lock);
mutex_init(&cod3035x->reset_lock);
mutex_init(&cod3035x->key_lock);
mutex_init(&cod3035x->adc_mute_lock);
#ifdef CONFIG_SND_SOC_COD30XX_EXT_ANT
if (cod3035x->dtv_detect)
mutex_init(&cod3035x->jackreport_lock);
#endif
wake_lock_init(&cod3035x->codec_wake_lock, WAKE_LOCK_SUSPEND, "codec_wl");
/*
* interrupt pin should be shared with pmic.
* so codec driver use notifier because of knowing
* the interrupt status from mfd.
*/
codec_notifier.notifier_call = cod3035x_notifier_handler,
cod3035x_register_notifier(&codec_notifier, cod3035x);
set_codec_notifier_flag();
msleep(20);
cod3035x_codec_initialize(codec);
/* additional codec initialize for lassen A */
if (cod3035x->is_lassenA) {
cod3035x_lassenA_normal(codec);
snd_soc_write(codec, COD3035X_57_AVC4, 0x80);
snd_soc_write(codec, COD3035X_BE_ODSEL2, 0x00);
snd_soc_write(codec, COD3035X_5A_AVC7, 0x05);
snd_soc_write(codec, COD3035X_55_AVC2, 0x6E);
snd_soc_write(codec, COD3035X_F4_ACV_10, 0xD3);
snd_soc_write(codec, COD3035X_E9_PRESET_AVC, 0x1F);
snd_soc_write(codec, COD3035X_EA_PRESET_AVC, 0x1F);
snd_soc_write(codec, COD3035X_E1_PRESET_AVC, 0x22);
}
/* it should be modify to move machine driver */
cod3035x_jack_mic_register(codec);
#ifdef CONFIG_SND_SOC_COD30XX_EXT_ANT
if (cod3035x->dtv_detect) {
cod3035x->jack_det.prev_jack_det_status = -EINVAL;
if (cod3035x->ant_det_gpio > 0) {
dev_err(codec->dev, "[DEBUG]%s : ant_det_gpio %d\n",
__func__, (int)cod3035x->ant_det_gpio);
ret = gpio_request(cod3035x->ant_det_gpio, "cod3035x_ant_detect");
if (ret < 0)
dev_err(codec->dev, "%s : Request for %d GPIO failed\n",
__func__, (int)cod3035x->ant_det_gpio);
ret = gpio_direction_input(cod3035x->ant_det_gpio);
if (ret < 0)
dev_err(codec->dev,
"Setting 3026 interrupt GPIO direction to input: failed\n");
/* If not set int_gpio, do lock init */
if (cod3035x->int_gpio <= 0) {
mutex_init(&cod3035x->jackdet_lock);
mutex_init(&cod3035x->jackreport_lock);
mutex_init(&cod3035x->key_lock);
}
ret = request_threaded_irq(
gpio_to_irq(cod3035x->ant_det_gpio),
NULL, cod3035x_ant_thread_isr,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"sec_ant_detect", cod3035x);
if (ret < 0)
dev_err(codec->dev,
"Error %d in requesting 3026 interrupt line:%d\n",
ret, cod3035x->ant_det_gpio);
ret = irq_set_irq_wake(gpio_to_irq(cod3035x->ant_det_gpio), 1);
if (ret < 0)
dev_err(codec->dev, "cannot set 3026 irq_set_irq_wake\n");
#ifdef CONFIG_SEC_FACTORY
cod3035x->jack_det.ignore_ext_ant = 1;
disable_irq(gpio_to_irq(cod3035x->ant_det_gpio));
cancel_work_sync(&cod3035x->jack_det_work);
queue_work(cod3035x->jack_det_wq, &cod3035x->jack_det_work);
#endif
}
}
#endif
snd_soc_dapm_ignore_suspend(snd_soc_codec_get_dapm(codec), "HPOUTLN");
snd_soc_dapm_ignore_suspend(snd_soc_codec_get_dapm(codec), "EPOUTN");
snd_soc_dapm_ignore_suspend(snd_soc_codec_get_dapm(codec), "IN1L");
snd_soc_dapm_ignore_suspend(snd_soc_codec_get_dapm(codec), "IN2L");
snd_soc_dapm_ignore_suspend(snd_soc_codec_get_dapm(codec), "IN3L");
snd_soc_dapm_ignore_suspend(snd_soc_codec_get_dapm(codec), "IN4L");
snd_soc_dapm_ignore_suspend(snd_soc_codec_get_dapm(codec), "AIF Playback");
snd_soc_dapm_ignore_suspend(snd_soc_codec_get_dapm(codec), "AIF Capture");
snd_soc_dapm_ignore_suspend(snd_soc_codec_get_dapm(codec), "AIF2 Playback");
snd_soc_dapm_ignore_suspend(snd_soc_codec_get_dapm(codec), "AIF2 Capture");
snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec));
/* To support PBA function test */
create_jack_devices(cod3035x);
#ifdef CONFIG_PM
pm_runtime_put_sync(codec->dev);
#else
cod3035x_disable(codec->dev);
#endif
return 0;
}
static int cod3035x_codec_remove(struct snd_soc_codec *codec)
{
struct cod3035x_priv *cod3035x = snd_soc_codec_get_drvdata(codec);
dev_dbg(codec->dev, "(*) %s called\n", __func__);
cancel_delayed_work_sync(&cod3035x->key_work);
if (cod3035x->int_gpio) {
free_irq(gpio_to_irq(cod3035x->int_gpio), cod3035x);
gpio_free(cod3035x->int_gpio);
}
if ((cod3035x->use_det_gdet_adc_mode == 2) && cod3035x->adc_pin) {
devm_gpio_free(cod3035x->codec->dev, cod3035x->adc_pin);
}
cod3035x_regulators_disable(codec);
destroy_workqueue(cod3035x->buttons_wq);
destroy_workqueue(cod3035x->jack_det_wq);
destroy_workqueue(cod3035x->gdet_adc_wq);
#if defined(CONFIG_SND_SOC_COD30XX_EXT_ANT)
if (cod3035x->dtv_detect)
destroy_workqueue(cod3035x->jack_report_wq);
#endif
wake_lock_destroy(&cod3035x->codec_wake_lock);
cod3035x_adc_stop(cod3035x);
return 0;
}
static struct snd_soc_codec_driver soc_codec_dev_cod3035x = {
.probe = cod3035x_codec_probe,
.remove = cod3035x_codec_remove,
.controls = cod3035x_snd_controls,
.num_controls = ARRAY_SIZE(cod3035x_snd_controls),
.dapm_widgets = cod3035x_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(cod3035x_dapm_widgets),
.dapm_routes = cod3035x_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(cod3035x_dapm_routes),
.ignore_pmdown_time = true,
.idle_bias_off = true,
};
static int cod3035x_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct cod3035x_priv *cod3035x;
struct pinctrl *pinctrl;
int ret;
cod3035x = kzalloc(sizeof(struct cod3035x_priv), GFP_KERNEL);
if (cod3035x == NULL)
return -ENOMEM;
cod3035x->dev = &i2c->dev;
cod3035x->i2c_addr = i2c->addr;
cod3035x->use_external_jd = false;
cod3035x->is_probe_done = false;
cod3035x->use_btn_adc_mode = false;
cod3035x->regulator_count = 0;
cod3035x->dis_ovp_det_mode = false;
cod3035x->btn_delay_masking = false;
cod3035x->regmap = devm_regmap_init_i2c(i2c, &cod3035x_regmap);
if (IS_ERR(cod3035x->regmap)) {
dev_err(&i2c->dev, "Failed to allocate regmap: %li\n",
PTR_ERR(cod3035x->regmap));
ret = -ENOMEM;
goto err;
}
regcache_mark_dirty(cod3035x->regmap);
pinctrl = devm_pinctrl_get(&i2c->dev);
if (IS_ERR(pinctrl)) {
dev_warn(&i2c->dev, "did not get pins for codec: %li\n",
PTR_ERR(pinctrl));
} else {
cod3035x->pinctrl = pinctrl;
dev_err(&i2c->dev, "cod3035x_i2c_probe pinctrl\n");
}
i2c_set_clientdata(i2c, cod3035x);
ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_cod3035x,
cod3035x_dai, ARRAY_SIZE(cod3035x_dai));
if (ret < 0) {
dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
goto err;
}
#ifdef CONFIG_PM
pm_runtime_enable(cod3035x->dev);
#endif
return ret;
err:
kfree(cod3035x);
return ret;
}
static int cod3035x_i2c_remove(struct i2c_client *i2c)
{
struct cod3035x_priv *cod3035x = dev_get_drvdata(&i2c->dev);
snd_soc_unregister_codec(&i2c->dev);
kfree(cod3035x);
return 0;
}
static int cod3035x_enable(struct device *dev)
{
struct cod3035x_priv *cod3035x = dev_get_drvdata(dev);
dev_dbg(dev, "(*) %s\n", __func__);
abox_enable_mclk(true);
cod3035x_regulators_enable(cod3035x->codec);
cod3035x->is_suspend = false;
/* Disable cache_only feature and sync the cache with h/w */
regcache_cache_only(cod3035x->regmap, false);
cod3035x_reg_restore(cod3035x->codec);
return 0;
}
static int cod3035x_disable(struct device *dev)
{
struct cod3035x_priv *cod3035x = dev_get_drvdata(dev);
dev_dbg(dev, "(*) %s\n", __func__);
cod3035x->is_suspend = true;
/* As device is going to suspend-state, limit the writes to cache */
regcache_cache_only(cod3035x->regmap, true);
cod3035x_regulators_disable(cod3035x->codec);
abox_enable_mclk(false);
return 0;
}
static int cod3035x_sys_suspend(struct device *dev)
{
#ifndef CONFIG_PM
if (abox_is_on()) {
dev_dbg(dev, "(*)Don't suspend codec3035x, cp functioning\n");
return 0;
}
dev_dbg(dev, "(*) %s\n", __func__);
cod3035x_disable(dev);
#endif
return 0;
}
static int cod3035x_sys_resume(struct device *dev)
{
#ifndef CONFIG_PM
struct cod3035x_priv *cod3035x = dev_get_drvdata(dev);
if (!cod3035x->is_suspend) {
dev_dbg(dev, "(*)codec3035x not resuming, cp functioning\n");
return 0;
}
dev_dbg(dev, "(*) %s\n", __func__);
cod3035x_enable(dev);
#endif
return 0;
}
#ifdef CONFIG_PM
static int cod3035x_runtime_resume(struct device *dev)
{
dev_dbg(dev, "(*) %s\n", __func__);
cod3035x_enable(dev);
return 0;
}
static int cod3035x_runtime_suspend(struct device *dev)
{
dev_dbg(dev, "(*) %s\n", __func__);
cod3035x_disable(dev);
return 0;
}
#endif
static const struct dev_pm_ops cod3035x_pm = {
SET_SYSTEM_SLEEP_PM_OPS(
cod3035x_sys_suspend,
cod3035x_sys_resume
)
#ifdef CONFIG_PM
SET_RUNTIME_PM_OPS(
cod3035x_runtime_suspend,
cod3035x_runtime_resume,
NULL
)
#endif
};
static const struct i2c_device_id cod3035x_i2c_id[] = {
{ "cod3035x", 3035 },
{ }
};
MODULE_DEVICE_TABLE(i2c, cod3035x_i2c_id);
const struct of_device_id cod3035x_of_match[] = {
{ .compatible = "codec,cod3035x", },
{},
};
static struct i2c_driver cod3035x_i2c_driver = {
.driver = {
.name = "cod3035x",
.owner = THIS_MODULE,
.pm = &cod3035x_pm,
.of_match_table = of_match_ptr(cod3035x_of_match),
},
.probe = cod3035x_i2c_probe,
.remove = cod3035x_i2c_remove,
.id_table = cod3035x_i2c_id,
};
module_i2c_driver(cod3035x_i2c_driver);
MODULE_DESCRIPTION("ASoC COD3035X driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:COD3035X-codec");