diff options
Diffstat (limited to 'sound/soc/sprd/codec/sprd/v3/sprd-codec.c')
| -rwxr-xr-x | sound/soc/sprd/codec/sprd/v3/sprd-codec.c | 3858 |
1 files changed, 3858 insertions, 0 deletions
diff --git a/sound/soc/sprd/codec/sprd/v3/sprd-codec.c b/sound/soc/sprd/codec/sprd/v3/sprd-codec.c new file mode 100755 index 00000000..8d778aaf --- /dev/null +++ b/sound/soc/sprd/codec/sprd/v3/sprd-codec.c @@ -0,0 +1,3858 @@ +/* + * sound/soc/sprd/codec/sprd/v3/sprd-codec.c + * + * SPRD-CODEC -- SpreadTrum Tiger intergrated codec. + * + * Copyright (C) 2013 SpreadTrum Ltd. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include "sprd-asoc-debug.h" +#define pr_fmt(fmt) pr_sprd_fmt("CDCV3") fmt + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/sysfs.h> +#include <linux/stat.h> +#include <linux/atomic.h> +#include <linux/regulator/consumer.h> +#include <linux/completion.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/power_supply.h> +#include <linux/io.h> +#include <linux/of.h> + +#include <sound/core.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/tlv.h> +#include <sound/sprd-audio-hook.h> + +#include <soc/sprd/sprd-audio.h> +#include <soc/sprd/globalregs.h> +#include "sprd-codec.h" +#include "sprd-asoc-common.h" +#include <soc/sprd/arch_misc.h> +#include <linux/of_irq.h> + +#ifdef CONFIG_SND_SOC_VBC_SRC_SAMPLE_RATE +#if (CONFIG_SND_SOC_VBC_SRC_SAMPLE_RATE != 32000) && (CONFIG_SND_SOC_CODEC_SRC_SAMPLE_RATE != 48000) +#error "We hope the CODEC sample rate is 32KHz or 48KHz!" +#endif +#define SRC_SUPPORT_RATE (SNDRV_PCM_RATE_44100) +#else /* !CONFIG_SND_SOC_VBC_SRC_SAMPLE_RATE */ +#define SRC_SUPPORT_RATE (0) +#endif + +int hp_register_notifier(struct notifier_block *nb); +int hp_unregister_notifier(struct notifier_block *nb); +int get_hp_plug_state(void); + +#define SOC_REG(r) ((unsigned short)(r)) +#define FUN_REG(f) ((unsigned short)(-((f) + 1))) +#define ID_FUN(id, lr) ((unsigned short)(((id) << 1) | (lr))) + +#define SPRD_CODEC_AP_BASE_HI (SPRD_CODEC_AP_BASE & 0xFFFF0000) +#define SPRD_CODEC_DP_BASE_HI (SPRD_CODEC_DP_BASE & 0xFFFF0000) + +enum { + SPRD_CODEC_HP_PA_ORDER = 100, + SPRD_CODEC_HP_SWITCH_ORDER = 101, + SPRD_CODEC_ANA_MIXER_ORDER = 102, /*must be later with switch order*/ + SPRD_CODEC_HP_PA_POST_ORDER = 103, + SPRD_CODEC_HP_POP_ORDER = 104, + SPRD_CODEC_PA_ORDER = 104, /*last power up, later with ANA_MIXER order*/ +}; + +enum { + SPRD_CODEC_PLAYBACK, + SPRD_CODEC_CAPTRUE, + SPRD_CODEC_CAPTRUE1, + SPRD_CODEC_CHAN_MAX, +}; + +static const char *sprd_codec_chan_name[SPRD_CODEC_CHAN_MAX] = { + "DAC", + "ADC", + "ADC1", +}; + +static inline const char *sprd_codec_chan_get_name(int chan_id) +{ + return sprd_codec_chan_name[chan_id]; +} + +enum { + SPRD_CODEC_PGA_START = 0, + SPRD_CODEC_PGA_SPKL = SPRD_CODEC_PGA_START, + SPRD_CODEC_PGA_SPKR, + SPRD_CODEC_PGA_HPL, + SPRD_CODEC_PGA_HPR, + SPRD_CODEC_PGA_EAR, + SPRD_CODEC_PGA_ADCL, + SPRD_CODEC_PGA_ADCR, + SPRD_CODEC_PGA_DACL, + SPRD_CODEC_PGA_DACR, + SPRD_CODEC_PGA_MIC, + SPRD_CODEC_PGA_AUXMIC, + SPRD_CODEC_PGA_HEADMIC, + SPRD_CODEC_PGA_AIL, + SPRD_CODEC_PGA_AIR, + + SPRD_CODEC_PGA_END +}; + +#define GET_PGA_ID(x) ((x)-SPRD_CODEC_PGA_START) +#define SPRD_CODEC_PGA_MAX (SPRD_CODEC_PGA_END - SPRD_CODEC_PGA_START) +static const char *sprd_codec_pga_debug_str[SPRD_CODEC_PGA_MAX] = { + "SPKL", + "SPKR", + "HPL", + "HPR", + "EAR", + "ADCL", + "ADCR", + "DACL", + "DACR", + "MIC", + "AUXMIC", + "HEADMIC", + "AIL", + "AIR", +}; + +typedef int (*sprd_codec_pga_set) (struct snd_soc_codec * codec, int pgaval); + +struct sprd_codec_pga { + sprd_codec_pga_set set; + int min; +}; + +struct sprd_codec_pga_op { + int pgaval; + sprd_codec_pga_set set; +}; + +enum { + SPRD_CODEC_LEFT = 0, + SPRD_CODEC_RIGHT = 1, +}; + +enum { + SPRD_CODEC_MIXER_START = SPRD_CODEC_PGA_END + 20, + SPRD_CODEC_AIL = SPRD_CODEC_MIXER_START, + SPRD_CODEC_AIR, + SPRD_CODEC_MAIN_MIC, + SPRD_CODEC_AUX_MIC, + SPRD_CODEC_HP_MIC, + SPRD_CODEC_ADC_MIXER_MAX, + SPRD_CODEC_HP_DACL = SPRD_CODEC_ADC_MIXER_MAX, + SPRD_CODEC_HP_DACR, + SPRD_CODEC_HP_DAC_MIXER_MAX, + SPRD_CODEC_HP_ADCL = SPRD_CODEC_HP_DAC_MIXER_MAX, + SPRD_CODEC_HP_ADCR, + SPRD_CODEC_HP_MIXER_MAX, + SPRD_CODEC_SPK_DACL = SPRD_CODEC_HP_MIXER_MAX, + SPRD_CODEC_SPK_DACR, + SPRD_CODEC_SPK_DAC_MIXER_MAX, + SPRD_CODEC_SPK_ADCL = SPRD_CODEC_SPK_DAC_MIXER_MAX, + SPRD_CODEC_SPK_ADCR, + SPRD_CODEC_SPK_MIXER_MAX, + SPRD_CODEC_EAR_DACL = SPRD_CODEC_SPK_MIXER_MAX, + SPRD_CODEC_EAR_MIXER_MAX, + + SPRD_CODEC_MIXER_END +}; + +#define GET_MIXER_ID(x) ((unsigned short)(x)-ID_FUN(SPRD_CODEC_MIXER_START, SPRD_CODEC_LEFT)) +#define SPRD_CODEC_MIXER_MAX (ID_FUN(SPRD_CODEC_MIXER_END, SPRD_CODEC_LEFT) -ID_FUN(SPRD_CODEC_MIXER_START, SPRD_CODEC_LEFT)) +static const char *sprd_codec_mixer_debug_str[SPRD_CODEC_MIXER_MAX] = { + "AIL->ADCL", + "AIL->ADCR", + "AIR->ADCL", + "AIR->ADCR", + "MAIN MIC->ADCL", + "MAIN MIC->ADCR", + "AUX MIC->ADCL", + "AUX MIC->ADCR", + "HP MIC->ADCL", + "HP MIC->ADCR", + "DACL->HPL", + "DACL->HPR", + "DACR->HPL", + "DACR->HPR", + "ADCL->HPL", + "ADCL->HPR", + "ADCR->HPL", + "ADCR->HPR", + "DACL->SPKL", + "DACL->SPKR", + "DACR->SPKL", + "DACR->SPKR", + "ADCL->SPKL", + "ADCL->SPKR", + "ADCR->SPKL", + "ADCR->SPKR", + "DACL->EAR", + "DACR->EAR(bug)" +}; + +#define IS_SPRD_CODEC_MIXER_RANG(reg) (((reg) >= ID_FUN(SPRD_CODEC_MIXER_START, SPRD_CODEC_LEFT)) && ((reg) <= ID_FUN(SPRD_CODEC_MIXER_END, SPRD_CODEC_LEFT))) +#define IS_SPRD_CODEC_PGA_RANG(reg) (((reg) >= SPRD_CODEC_PGA_START) && ((reg) <= SPRD_CODEC_PGA_END)) +#define IS_SPRD_CODEC_MIC_BIAS_RANG(reg) (((reg) >= SPRD_CODEC_MIC_BIAS_START) && ((reg) <= SPRD_CODEC_MIC_BIAS_END)) + +typedef int (*sprd_codec_mixer_set) (struct snd_soc_codec * codec, int on); +struct sprd_codec_mixer { + int on; + sprd_codec_mixer_set set; +}; + +uint32_t sprd_get_vbat_voltage(void) + __attribute__ ((weak, alias("__sprd_get_vbat_voltage"))); +static uint32_t __sprd_get_vbat_voltage(void) +{ + pr_err("ERR: Can't get vbat!\n"); + return 3800; +} + +struct sprd_codec_ldo_v_map { + int ldo_v_level; + int volt; +}; + +const static struct sprd_codec_ldo_v_map ldo_v_map[] = { + /*{ , 3400}, */ + {LDO_V_29, 3600}, + {LDO_V_31, 3700}, + {LDO_V_32, 3800}, + {LDO_V_33, 3900}, + {LDO_V_34, 4000}, + {LDO_V_35, 4100}, + {LDO_V_36, 4200}, + {LDO_V_38, 4300}, +}; + +const static struct sprd_codec_ldo_v_map ldo_vcom_v_map[] = { + /*{ , 3400}, */ + {LDO_V_29, 3800}, + {LDO_V_31, 3900}, + {LDO_V_32, 4000}, + {LDO_V_33, 4100}, + {LDO_V_34, 4200}, + {LDO_V_35, 4300}, + {LDO_V_36, 4400}, + {LDO_V_38, 4500}, +}; + +struct sprd_codec_ldo_cfg { + const struct sprd_codec_ldo_v_map *v_map; + int v_map_size; +}; + +struct sprd_codec_inter_pa { + /* FIXME little endian */ + u32 LDO_V_sel:4; + u32 DTRI_F_sel:4; + u32 is_DEMI_mode:1; + u32 is_classD_mode:1; + u32 is_LDO_mode:1; + u32 is_auto_LDO_mode:1; + u32 RESV:20; +}; + +struct sprd_codec_pa_setting { + union { + struct sprd_codec_inter_pa setting; + u32 value; + }; + int set; +}; + +struct sprd_codec_inter_hp_pa { + /* FIXME little endian */ + u32 class_g_osc:2; + u32 class_g_mode:2; + u32 class_g_low_power:2; + u32 class_g_cgcal:2; + u32 class_g_unmute_delay_100ms:3; + u32 class_g_cgcal_delay_10ms:3; + u32 class_g_close_delay_10ms:2; + u32 class_g_open_delay_10ms:4; + u32 class_g_vdd_delay_10ms:6; + u32 class_g_chp_delay_30ms:4; + u32 class_g_all_close_delay_100ms:2; + //u32 RESV:0; +}; +struct sprd_codec_inter_hp_pa_delay { + u32 class_g_delay1:4; + u32 class_g_delay2:4; +}; + +struct sprd_codec_hp_pa_setting { + union { + struct sprd_codec_inter_hp_pa setting; + u32 value; + }; + union{ + struct sprd_codec_inter_hp_pa_delay delay_setting; + u32 delay_value; + }; + int set; +}; + +enum { + SPRD_CODEC_MIC_BIAS_START = + ID_FUN(SPRD_CODEC_MIXER_END, SPRD_CODEC_LEFT) + 100, + SPRD_CODEC_MIC_BIAS = SPRD_CODEC_MIC_BIAS_START, + SPRD_CODEC_AUXMIC_BIAS, + SPRD_CODEC_HEADMIC_BIAS, + + SPRD_CODEC_MIC_BIAS_END +}; + +#define GET_MIC_BIAS_ID(x) ((x)-SPRD_CODEC_MIC_BIAS_START) +#define SPRD_CODEC_MIC_BIAS_MAX (SPRD_CODEC_MIC_BIAS_END -SPRD_CODEC_MIC_BIAS_START) +static const char *mic_bias_name[SPRD_CODEC_MIC_BIAS_MAX] = { + "Mic Bias", + "AuxMic Bias", + "HeadMic Bias", +}; + +enum { + SPRD_CODEC_HP_PA_VER_1, + SPRD_CODEC_HP_PA_VER_2, + SPRD_CODEC_HP_PA_VER_MAX, +}; + +static unsigned long sprd_codec_dp_base = 0; +/* codec private data */ +struct sprd_codec_priv { + struct snd_soc_codec *codec; + int da_sample_val; + int ad_sample_val; + int ad1_sample_val; + struct sprd_codec_mixer mixer[SPRD_CODEC_MIXER_MAX]; + struct sprd_codec_pga_op pga[SPRD_CODEC_PGA_MAX]; + int mic_bias[SPRD_CODEC_MIC_BIAS_MAX]; + + int ap_irq; + struct completion completion_hp_pop; + + int dp_irq; + struct completion completion_dac_mute; + struct power_supply audio_ldo; + + struct sprd_codec_pa_setting inter_pa; + struct mutex inter_pa_mutex; + + int hp_ver; + struct sprd_codec_hp_pa_setting inter_hp_pa; + int class_g_delay1; + int class_g_delay2; + struct mutex inter_hp_pa_mutex; + struct mutex inter_hp_pa_delay_mutex; + + struct regulator *main_mic; + struct regulator *aux_mic; + struct regulator *head_mic; + atomic_t ldo_refcount; + struct delayed_work ovp_delayed_work; + + struct sprd_codec_ldo_cfg s_vcom; + struct sprd_codec_ldo_cfg s_vpa; + +#define SPRD_CODEC_PA_SW_AOL (BIT(0)) +#define SPRD_CODEC_PA_SW_EAR (BIT(1)) +#define SPRD_CODEC_PA_SW_FUN (SPRD_CODEC_PA_SW_AOL | SPRD_CODEC_PA_SW_EAR) + int sprd_codec_fun; + spinlock_t sprd_codec_fun_lock; + spinlock_t sprd_codec_pa_sw_lock; + + struct notifier_block nb; +}; + +static int sprd_codec_power_get(struct device *dev, struct regulator **regu, + const char *id) +{ + if (!*regu) { + *regu = regulator_get(dev, id); + if (IS_ERR(*regu)) { + pr_err("ERR:Failed to request %ld: %s\n", + PTR_ERR(*regu), id); + *regu = 0; + return PTR_ERR(*regu); + } + } + return 0; +} + +static int sprd_codec_power_put(struct regulator **regu) +{ + if (*regu) { + regulator_set_mode(*regu, REGULATOR_MODE_NORMAL); + regulator_disable(*regu); + regulator_put(*regu); + *regu = 0; + } + return 0; +} + +static int sprd_codec_mic_bias_set(struct regulator **regu, int on) +{ + int ret = 0; + if (*regu) { + if (on) { + ret = regulator_enable(*regu); + if (ret) { + sprd_codec_power_put(regu); + return ret; + } + } else { + ret = regulator_disable(*regu); + } + } + return ret; +} + +#ifdef CONFIG_SND_SOC_SPRD_AUDIO_DEBUG +static const char *get_event_name(int event) +{ + const char *ev_name; + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ev_name = "PRE_PMU"; + break; + case SND_SOC_DAPM_POST_PMU: + ev_name = "POST_PMU"; + break; + case SND_SOC_DAPM_PRE_PMD: + ev_name = "PRE_PMD"; + break; + case SND_SOC_DAPM_POST_PMD: + ev_name = "POST_PMD"; + break; + default: + BUG(); + return 0; + } + return ev_name; +} +#endif + +static void sprd_codec_set_fun(struct sprd_codec_priv *sprd_codec, int fun) +{ + spin_lock(&sprd_codec->sprd_codec_fun_lock); + sprd_codec->sprd_codec_fun |= fun; + spin_unlock(&sprd_codec->sprd_codec_fun_lock); +} + +static void sprd_codec_clr_fun(struct sprd_codec_priv *sprd_codec, int fun) +{ + spin_lock(&sprd_codec->sprd_codec_fun_lock); + sprd_codec->sprd_codec_fun &= ~fun; + spin_unlock(&sprd_codec->sprd_codec_fun_lock); +} + +static int sprd_codec_test_fun(struct sprd_codec_priv *sprd_codec, int fun) +{ + int ret; + spin_lock(&sprd_codec->sprd_codec_fun_lock); + ret = sprd_codec->sprd_codec_fun & fun; + spin_unlock(&sprd_codec->sprd_codec_fun_lock); + return ret; +} + +static void sprd_codec_wait(u32 wait_time) +{ + if (wait_time) + schedule_timeout_uninterruptible(msecs_to_jiffies(wait_time)); +} + +#if 0 /* sometime we will use this print function */ +static unsigned int sprd_codec_read(struct snd_soc_codec *codec, + unsigned int reg); +static void sprd_codec_print_regs(struct snd_soc_codec *codec) +{ + int reg; + pr_warn("sprd_codec register digital part\n"); + for (reg = SPRD_CODEC_DP_BASE; reg < SPRD_CODEC_DP_END; reg += 0x10) { + pr_warn("0x%04x | 0x%04x 0x%04x 0x%04x 0x%04x\n", + (reg - SPRD_CODEC_DP_BASE) + , sprd_codec_read(codec, reg + 0x00) + , sprd_codec_read(codec, reg + 0x04) + , sprd_codec_read(codec, reg + 0x08) + , sprd_codec_read(codec, reg + 0x0C) + ); + } + pr_warn("sprd_codec register analog part\n"); + for (reg = SPRD_CODEC_AP_BASE; reg < SPRD_CODEC_AP_END; reg += 0x10) { + pr_warn("0x%04x | 0x%04x 0x%04x 0x%04x 0x%04x\n", + (reg - SPRD_CODEC_AP_BASE) + , sprd_codec_read(codec, reg + 0x00) + , sprd_codec_read(codec, reg + 0x04) + , sprd_codec_read(codec, reg + 0x08) + , sprd_codec_read(codec, reg + 0x0C) + ); + } +} +#endif + +static inline int _sprd_codec_ldo_cfg_set(struct sprd_codec_ldo_cfg *p_v_cfg, const struct sprd_codec_ldo_v_map + *v_map, const int v_map_size) +{ + p_v_cfg->v_map = v_map; + p_v_cfg->v_map_size = v_map_size; + return 0; +} + +static inline int _sprd_codec_hold(int index, int volt, + const struct sprd_codec_ldo_v_map *v_map) +{ + int scope = 40; /* unit: mv */ + int ret = ((v_map[index].volt - volt) <= scope); + if (index >= 1) { + return (ret || ((volt - v_map[index - 1].volt) <= scope)); + } + return ret; +} + +static int sprd_codec_auto_ldo_volt(struct snd_soc_codec *codec, + void (*set_level) (struct snd_soc_codec *, + int), + const struct sprd_codec_ldo_v_map *v_map, + const int v_map_size, int init) +{ + int i; + int volt = sprd_get_vbat_voltage(); + sp_asoc_pr_dbg("Get vbat voltage %d\n", volt); + for (i = 0; i < v_map_size; i++) { + if (volt <= ldo_v_map[i].volt) { + sp_asoc_pr_dbg("Hold %d\n", + _sprd_codec_hold(i, volt, v_map)); + if (init || !_sprd_codec_hold(i, volt, v_map)) { + set_level(codec, v_map[i].ldo_v_level); + } + return 0; + } + } + return -EFAULT; +} + +static inline void sprd_codec_vcm_v_sel(struct snd_soc_codec *codec, int v_sel) +{ + int mask; + int val; + sp_asoc_pr_dbg("VCM Set %d\n", v_sel); + mask = VCM_V_MASK << VCM_V; + val = (v_sel << VCM_V) & mask; + snd_soc_update_bits(codec, SOC_REG(PMUR4_PMUR3), mask, val); +} + +static inline void sprd_codec_auxadc_en(struct snd_soc_codec *codec, int on) +{ + int mask; + int val; + sp_asoc_pr_dbg("AUXADC Set %d\n", on); + mask = BIT(AUXADC_EN); + val = on ? mask : 0; + snd_soc_update_bits(codec, SOC_REG(PMUR4_PMUR3), mask, val); +} + +static int sprd_codec_pga_spk_set(struct snd_soc_codec *codec, int pgaval) +{ + int reg, val; + reg = DCGR3; + val = (pgaval & 0xF) << 4; + return snd_soc_update_bits(codec, SOC_REG(reg), 0xF0, val); +} + +static int sprd_codec_pga_spkr_set(struct snd_soc_codec *codec, int pgaval) +{ + int reg, val; + reg = DCGR3; + val = pgaval & 0xF; + return snd_soc_update_bits(codec, SOC_REG(reg), 0x0F, val); +} + +static int sprd_codec_pga_hpl_set(struct snd_soc_codec *codec, int pgaval) +{ + int reg, val; + reg = DCGR2_DCGR1; + val = (pgaval & 0xF) << 4; + return snd_soc_update_bits(codec, SOC_REG(reg), 0xF0, val); +} + +static int sprd_codec_pga_hpr_set(struct snd_soc_codec *codec, int pgaval) +{ + int reg, val; + reg = DCGR2_DCGR1; + val = pgaval & 0xF; + return snd_soc_update_bits(codec, SOC_REG(reg), 0x0F, val); +} + +static int sprd_codec_pga_ear_set(struct snd_soc_codec *codec, int pgaval) +{ + int reg, val; + reg = DCGR2_DCGR1; + val = ((pgaval & 0xF) << 12); + return snd_soc_update_bits(codec, SOC_REG(reg), 0xF000, val); +} + +static int sprd_codec_pga_adcl_set(struct snd_soc_codec *codec, int pgaval) +{ + int reg, val; + reg = ACGR3_ACGR2; + val = pgaval & 0x3F; + return snd_soc_update_bits(codec, SOC_REG(reg), 0x3F, val); +} + +static int sprd_codec_pga_adcr_set(struct snd_soc_codec *codec, int pgaval) +{ + int reg, val; + reg = ACGR3_ACGR2; + val = (pgaval & 0x3F) << 8; + return snd_soc_update_bits(codec, SOC_REG(reg), 0x3F00, val); +} + +static int sprd_codec_pga_dacl_set(struct snd_soc_codec *codec, int pgaval) +{ + int reg, val; + reg = DACGR_DACR; + val = (pgaval & 0x07) << 12; + return snd_soc_update_bits(codec, SOC_REG(reg), 0x7000, val); +} + +static int sprd_codec_pga_dacr_set(struct snd_soc_codec *codec, int pgaval) +{ + int reg, val; + reg = DACGR_DACR; + val = (pgaval & 0x07) << 8; + return snd_soc_update_bits(codec, SOC_REG(reg), 0x0700, val); +} + +static int sprd_codec_pga_mic_set(struct snd_soc_codec *codec, int pgaval) +{ + int reg, val; + reg = ACGR1; + val = (pgaval & 0x03) << 6; + return snd_soc_update_bits(codec, SOC_REG(reg), 0xC0, val); +} + +static int sprd_codec_pga_auxmic_set(struct snd_soc_codec *codec, int pgaval) +{ + int reg, val; + reg = ACGR1; + val = (pgaval & 0x03) << 4; + return snd_soc_update_bits(codec, SOC_REG(reg), 0x30, val); +} + +static int sprd_codec_pga_headmic_set(struct snd_soc_codec *codec, int pgaval) +{ + int reg, val; + reg = ACGR1; + val = (pgaval & 0x03) << 2; + return snd_soc_update_bits(codec, SOC_REG(reg), 0x0C, val); +} + +static int sprd_codec_pga_ailr_set(struct snd_soc_codec *codec, int pgaval) +{ + int reg, val; + reg = ACGR1; + val = pgaval & 0x03; + return snd_soc_update_bits(codec, SOC_REG(reg), 0x03, val); +} + +static struct sprd_codec_pga sprd_codec_pga_cfg[SPRD_CODEC_PGA_MAX] = { + {sprd_codec_pga_spk_set, 0}, + {sprd_codec_pga_spkr_set, 0}, + {sprd_codec_pga_hpl_set, 0}, + {sprd_codec_pga_hpr_set, 0}, + {sprd_codec_pga_ear_set, 0}, + + {sprd_codec_pga_adcl_set, 0}, + {sprd_codec_pga_adcr_set, 0}, + + {sprd_codec_pga_dacl_set, 0}, + {sprd_codec_pga_dacr_set, 0}, + {sprd_codec_pga_mic_set, 0}, + {sprd_codec_pga_auxmic_set, 0}, + {sprd_codec_pga_headmic_set, 0}, + {sprd_codec_pga_ailr_set, 0}, + {sprd_codec_pga_ailr_set, 0}, +}; + +/* adc mixer */ + +static int ailadcl_set(struct snd_soc_codec *codec, int on) +{ + sp_asoc_pr_dbg("%s %d\n", __func__, on); + return snd_soc_update_bits(codec, SOC_REG(AAICR2_AAICR1), BIT(AIL_ADCL), + on << AIL_ADCL); +} + +static int ailadcr_set(struct snd_soc_codec *codec, int on) +{ + sp_asoc_pr_dbg("%s %d\n", __func__, on); + return snd_soc_update_bits(codec, SOC_REG(AAICR2_AAICR1), BIT(AIL_ADCR), + on << AIL_ADCR); +} + +static int airadcl_set(struct snd_soc_codec *codec, int on) +{ + sp_asoc_pr_dbg("%s %d\n", __func__, on); + return snd_soc_update_bits(codec, SOC_REG(AAICR2_AAICR1), BIT(AIR_ADCL), + on << AIR_ADCL); +} + +static int airadcr_set(struct snd_soc_codec *codec, int on) +{ + sp_asoc_pr_dbg("%s %d\n", __func__, on); + return snd_soc_update_bits(codec, SOC_REG(AAICR2_AAICR1), BIT(AIR_ADCR), + on << AIR_ADCR); +} + +static int mainmicadcl_set(struct snd_soc_codec *codec, int on) +{ + sp_asoc_pr_dbg("%s %d\n", __func__, on); + return snd_soc_update_bits(codec, SOC_REG(AAICR2_AAICR1), BIT(MIC_ADCL), + on << MIC_ADCL); +} + +static int mainmicadcr_set(struct snd_soc_codec *codec, int on) +{ + sp_asoc_pr_dbg("%s %d\n", __func__, on); + return snd_soc_update_bits(codec, SOC_REG(AAICR2_AAICR1), BIT(MIC_ADCR), + on << MIC_ADCR); +} + +static int auxmicadcl_set(struct snd_soc_codec *codec, int on) +{ + sp_asoc_pr_dbg("%s %d\n", __func__, on); + return snd_soc_update_bits(codec, SOC_REG(AAICR2_AAICR1), + BIT(AUXMIC_ADCL), on << AUXMIC_ADCL); +} + +static int auxmicadcr_set(struct snd_soc_codec *codec, int on) +{ + sp_asoc_pr_dbg("%s %d\n", __func__, on); + return snd_soc_update_bits(codec, SOC_REG(AAICR2_AAICR1), + BIT(AUXMIC_ADCR), on << AUXMIC_ADCR); +} + +static int hpmicadcl_set(struct snd_soc_codec *codec, int on) +{ + sp_asoc_pr_dbg("%s %d\n", __func__, on); + return snd_soc_update_bits(codec, SOC_REG(AAICR2_AAICR1), + BIT(HEADMIC_ADCL), on << HEADMIC_ADCL); +} + +static int hpmicadcr_set(struct snd_soc_codec *codec, int on) +{ + sp_asoc_pr_dbg("%s %d\n", __func__, on); + return snd_soc_update_bits(codec, SOC_REG(AAICR2_AAICR1), + BIT(HEADMIC_ADCR), on << HEADMIC_ADCR); +} + +/* hp mixer */ + +static int daclhpl_set(struct snd_soc_codec *codec, int on) +{ + sp_asoc_pr_dbg("%s %d\n", __func__, on); + return snd_soc_update_bits(codec, SOC_REG(DAOCR3_DAOCR1), + BIT(DACL_P_HPL), on << DACL_P_HPL); +} + +static int daclhpr_set(struct snd_soc_codec *codec, int on) +{ + sp_asoc_pr_dbg("%s %d\n", __func__, on); + return snd_soc_update_bits(codec, SOC_REG(DAOCR3_DAOCR1), + BIT(DACL_N_HPR), on << DACL_N_HPR); +} + +static int dacrhpl_set(struct snd_soc_codec *codec, int on) +{ + sp_asoc_pr_dbg("%s %d\n", __func__, on); + return snd_soc_update_bits(codec, SOC_REG(DAOCR3_DAOCR1), + BIT(DACR_P_HPL), on << DACR_P_HPL); +} + +static int dacrhpr_set(struct snd_soc_codec *codec, int on) +{ + sp_asoc_pr_dbg("%s %d\n", __func__, on); + return snd_soc_update_bits(codec, SOC_REG(DAOCR3_DAOCR1), + BIT(DACR_P_HPR), on << DACR_P_HPR); +} + +static int adclhpl_set(struct snd_soc_codec *codec, int on) +{ + sp_asoc_pr_dbg("%s %d\n", __func__, on); + return snd_soc_update_bits(codec, SOC_REG(DAOCR3_DAOCR1), + BIT(ADCL_P_HPL), on << ADCL_P_HPL); +} + +static int adclhpr_set(struct snd_soc_codec *codec, int on) +{ + sp_asoc_pr_dbg("%s %d\n", __func__, on); + return snd_soc_update_bits(codec, SOC_REG(DAOCR3_DAOCR1), + BIT(ADCL_N_HPR), on << ADCL_N_HPR); +} + +static int adcrhpl_set(struct snd_soc_codec *codec, int on) +{ + sp_asoc_pr_dbg("%s %d\n", __func__, on); + return snd_soc_update_bits(codec, SOC_REG(DAOCR3_DAOCR1), + BIT(ADCR_P_HPL), on << ADCR_P_HPL); +} + +static int adcrhpr_set(struct snd_soc_codec *codec, int on) +{ + sp_asoc_pr_dbg("%s %d\n", __func__, on); + return snd_soc_update_bits(codec, SOC_REG(DAOCR3_DAOCR1), + BIT(ADCR_P_HPR), on << ADCR_P_HPR); +} + +/* spkl mixer */ + +static int daclspkl_set(struct snd_soc_codec *codec, int on) +{ + sp_asoc_pr_dbg("%s %d\n", __func__, on); + return snd_soc_update_bits(codec, SOC_REG(DAOCR2), BIT(DACL_AOL), + on << DACL_AOL); +} + +static int dacrspkl_set(struct snd_soc_codec *codec, int on) +{ + sp_asoc_pr_dbg("%s %d\n", __func__, on); + return snd_soc_update_bits(codec, SOC_REG(DAOCR2), BIT(DACR_AOL), + on << DACR_AOL); +} + +static int adclspkl_set(struct snd_soc_codec *codec, int on) +{ + sp_asoc_pr_dbg("%s %d\n", __func__, on); + return snd_soc_update_bits(codec, SOC_REG(DAOCR2), BIT(ADCL_AOL), + on << ADCL_AOL); +} + +static int adcrspkl_set(struct snd_soc_codec *codec, int on) +{ + sp_asoc_pr_dbg("%s %d\n", __func__, on); + return snd_soc_update_bits(codec, SOC_REG(DAOCR2), BIT(ADCR_AOL), + on << ADCR_AOL); +} + +/* spkr mixer */ + +static int daclspkr_set(struct snd_soc_codec *codec, int on) +{ + sp_asoc_pr_dbg("%s %d\n", __func__, on); + return snd_soc_update_bits(codec, SOC_REG(DAOCR2), BIT(DACL_AOR), + on << DACL_AOR); +} + +static int dacrspkr_set(struct snd_soc_codec *codec, int on) +{ + sp_asoc_pr_dbg("%s %d\n", __func__, on); + return snd_soc_update_bits(codec, SOC_REG(DAOCR2), BIT(DACR_AOR), + on << DACR_AOR); +} + +static int adclspkr_set(struct snd_soc_codec *codec, int on) +{ + sp_asoc_pr_dbg("%s %d\n", __func__, on); + return snd_soc_update_bits(codec, SOC_REG(DAOCR2), BIT(ADCL_AOR), + on << ADCL_AOR); +} + +static int adcrspkr_set(struct snd_soc_codec *codec, int on) +{ + sp_asoc_pr_dbg("%s %d\n", __func__, on); + return snd_soc_update_bits(codec, SOC_REG(DAOCR2), BIT(ADCR_AOR), + on << ADCR_AOR); +} + +/* ear mixer */ + +static int daclear_set(struct snd_soc_codec *codec, int on) +{ + sp_asoc_pr_dbg("%s %d\n", __func__, on); + return snd_soc_update_bits(codec, SOC_REG(DAOCR3_DAOCR1), BIT(DACL_EAR), + on << DACL_EAR); +} + +static sprd_codec_mixer_set mixer_setting[SPRD_CODEC_MIXER_MAX] = { + /* adc mixer */ + ailadcl_set, ailadcr_set, + airadcl_set, airadcr_set, + mainmicadcl_set, mainmicadcr_set, + auxmicadcl_set, auxmicadcr_set, + hpmicadcl_set, hpmicadcr_set, + /* hp mixer */ + daclhpl_set, daclhpr_set, + dacrhpl_set, dacrhpr_set, + adclhpl_set, adclhpr_set, + adcrhpl_set, adcrhpr_set, + /* spk mixer */ + daclspkl_set, daclspkr_set, + dacrspkl_set, dacrspkr_set, + adclspkl_set, adclspkr_set, + adcrspkl_set, adcrspkr_set, + /* ear mixer */ + daclear_set, 0, +}; + +/* DO NOT USE THIS FUNCTION */ +static inline void __sprd_codec_pa_sw_en(struct snd_soc_codec *codec, int on) +{ + int mask; + int val; + mask = BIT(PA_SW_EN); + val = on ? mask : 0; + snd_soc_update_bits(codec, SOC_REG(PMUR2_PMUR1), mask, val); +} + +static inline void sprd_codec_pa_sw_set(struct snd_soc_codec *codec, int fun) +{ + struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); + sp_asoc_pr_dbg("%s fun 0x%08x\n", __func__, fun); + spin_lock(&sprd_codec->sprd_codec_pa_sw_lock); + sprd_codec_set_fun(sprd_codec, fun); + __sprd_codec_pa_sw_en(codec, 1); + spin_unlock(&sprd_codec->sprd_codec_pa_sw_lock); +} + +static inline void sprd_codec_pa_sw_clr(struct snd_soc_codec *codec, int fun) +{ + struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); + sp_asoc_pr_dbg("%s fun 0x%08x\n", __func__, fun); + spin_lock(&sprd_codec->sprd_codec_pa_sw_lock); + sprd_codec_clr_fun(sprd_codec, fun); + if (!sprd_codec_test_fun(sprd_codec, SPRD_CODEC_PA_SW_FUN)) + __sprd_codec_pa_sw_en(codec, 0); + spin_unlock(&sprd_codec->sprd_codec_pa_sw_lock); +} + +/* inter PA */ + +static inline void sprd_codec_pa_d_en(struct snd_soc_codec *codec, int on) +{ + int mask; + int val; + sp_asoc_pr_dbg("%s set %d\n", __func__, on); + mask = BIT(PA_D_EN); + val = on ? mask : 0; + snd_soc_update_bits(codec, SOC_REG(DCR2_DCR1), mask, val); +} + +static inline void sprd_codec_pa_demi_en(struct snd_soc_codec *codec, int on) +{ + int mask; + + sp_asoc_pr_dbg("%s set %d\n", __func__, on); + if (on) + /*when emi is on, need to set this register from xun && junjie.chen*/ + snd_soc_update_bits(codec, SOC_REG(DCR4_DCR3), 0xffff, 0x4); + else + snd_soc_update_bits(codec, SOC_REG(DCR4_DCR3), 0xffff, 0); + + mask = BIT(PA_DEMI_EN); + snd_soc_update_bits(codec, SOC_REG(DCR2_DCR1), mask, on?mask:0); + + /*open ocp from xun && weifeng*/ + mask = BIT(DRV_OCP_AOL_PD) | BIT(DRV_OCP_AOR_PD); + snd_soc_update_bits(codec, SOC_REG(DCR4_DCR3), mask, 0); +} + +static inline void sprd_codec_pa_ldo_en(struct snd_soc_codec *codec, int on) +{ + int mask; + int val; + sp_asoc_pr_dbg("%s set %d\n", __func__, on); + mask = BIT(PA_LDO_EN); + val = on ? mask : 0; + snd_soc_update_bits(codec, SOC_REG(PMUR2_PMUR1), mask, val); + if (on) { + sprd_codec_pa_sw_clr(codec, SPRD_CODEC_PA_SW_AOL); + } +} + +static inline void sprd_codec_pa_ldo_v_sel(struct snd_soc_codec *codec, + int v_sel) +{ + int mask; + int val; + sp_asoc_pr_dbg("%s set %d\n", __func__, v_sel); + mask = PA_LDO_V_MASK << PA_LDO_V; + val = (v_sel << PA_LDO_V) & mask; + snd_soc_update_bits(codec, SOC_REG(PMUR6_PMUR5), mask, val); +} + +static inline void sprd_codec_pa_ovp_v_sel(struct snd_soc_codec *codec, + int v_sel) +{ + int mask; + int val; + sp_asoc_pr_dbg("%s set %d\n", __func__, v_sel); + mask = PA_OVP_V_MASK << PA_OVP_V; + val = (v_sel << PA_OVP_V) & mask; + snd_soc_update_bits(codec, SOC_REG(PMUR8_PMUR7), mask, val); +} + +static inline void sprd_codec_pa_dtri_f_sel(struct snd_soc_codec *codec, + int f_sel) +{ + int mask; + int val; + sp_asoc_pr_dbg("%s set %d\n", __func__, f_sel); + mask = PA_DTRI_F_MASK << PA_DTRI_F; + val = (f_sel << PA_DTRI_F) & mask; + snd_soc_update_bits(codec, SOC_REG(DCR2_DCR1), mask, val); +} + +static inline void sprd_codec_pa_en(struct snd_soc_codec *codec, int on) +{ + struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); + int mask; + int val; + sp_asoc_pr_dbg("%s set %d\n", __func__, on); + spin_lock(&sprd_codec->sprd_codec_pa_sw_lock); + if (on) { + mask = BIT(PA_EN); + val = mask; + } else { + if (!sprd_codec_test_fun(sprd_codec, SPRD_CODEC_PA_SW_FUN)) + mask = BIT(PA_EN) | BIT(PA_SW_EN) | BIT(PA_LDO_EN); + else + mask = BIT(PA_EN) | BIT(PA_LDO_EN); + val = 0; + } + snd_soc_update_bits(codec, SOC_REG(PMUR2_PMUR1), mask, val); + spin_unlock(&sprd_codec->sprd_codec_pa_sw_lock); +} + +static inline void sprd_codec_ovp_ldo_en(struct snd_soc_codec *codec, int on) +{ + int mask; + int val; + sp_asoc_pr_dbg("%s set %d\n", __func__, on); + mask = BIT(OVP_LDO_EN); + val = on ? mask : 0; + snd_soc_update_bits(codec, SOC_REG(PMUR2_PMUR1), mask, val); +} + +static inline void sprd_codec_inter_pa_init(struct sprd_codec_priv *sprd_codec) +{ + sprd_codec->inter_pa.setting.LDO_V_sel = 0x03; + sprd_codec->inter_pa.setting.DTRI_F_sel = 0x01; +} + +static void sprd_codec_ovp_irq_enable(struct snd_soc_codec *codec) +{ + int mask = BIT(OVP_IRQ); + snd_soc_update_bits(codec, SOC_REG(AUDIF_INT_CLR), mask, mask); + snd_soc_update_bits(codec, SOC_REG(AUDIF_INT_EN), mask, mask); +} + +static void sprd_codec_ovp_start(struct sprd_codec_priv *sprd_codec, int ms) +{ + schedule_delayed_work(&sprd_codec->ovp_delayed_work, + msecs_to_jiffies(ms)); +} + +static int sprd_ovp_irq(struct snd_soc_codec *codec, int is_ovp) +{ + int volt = sprd_get_vbat_voltage(); + sp_asoc_pr_dbg("OVP %d curr_volt %d\n", is_ovp, volt); + if (is_ovp) { + sprd_codec_pa_en(codec, 0); + snd_soc_update_bits(codec, SOC_REG(DCR2_DCR1), BIT(AOL_EN), 0); + } else { + snd_soc_update_bits(codec, SOC_REG(DCR2_DCR1), BIT(AOL_EN), + BIT(AOL_EN)); + sprd_codec_pa_en(codec, 1); + } + return 0; +} + +#define to_sprd_codec(x) container_of((x), struct sprd_codec_priv, ovp_delayed_work) +#define to_delay_worker(x) container_of((x), struct delayed_work, work) +static void sprd_codec_ovp_delay_worker(struct work_struct *work) +{ + struct sprd_codec_priv *sprd_codec = + to_sprd_codec(to_delay_worker(work)); + struct snd_soc_codec *codec = sprd_codec->codec; + + if (!(snd_soc_read(codec, IFR2_IFR1) & BIT(OVP_FLAG))) { + } else { + sprd_codec_pa_ldo_v_sel(codec, 0x7); + } +} + +static inline int sprd_codec_pa_ldo_auto(struct snd_soc_codec *codec, int init) +{ + struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); + return sprd_codec_auto_ldo_volt + (codec, sprd_codec_pa_ldo_v_sel, sprd_codec->s_vpa.v_map, + sprd_codec->s_vpa.v_map_size, init); +} + +static inline int sprd_codec_pa_ldo_cfg(struct sprd_codec_priv *sprd_codec, const struct sprd_codec_ldo_v_map + *v_map, const int v_map_size) +{ + return _sprd_codec_ldo_cfg_set(&sprd_codec->s_vpa, v_map, v_map_size); +} + +static int sprd_inter_speaker_pa(struct snd_soc_codec *codec, int on) +{ + struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); + struct sprd_codec_inter_pa *p_setting = &sprd_codec->inter_pa.setting; + sp_asoc_pr_info("inter PA Switch %s\n", STR_ON_OFF(on)); + mutex_lock(&sprd_codec->inter_pa_mutex); + if (on) { + if (sprd_codec->hp_ver != SPRD_CODEC_HP_PA_VER_2) + sprd_codec_pa_ovp_v_sel(codec, PA_OVP_435); + else + sprd_codec_pa_ovp_v_sel(codec, PA_OVP_435); + sprd_codec_pa_ldo_v_sel(codec, 0x7); + sprd_codec_ovp_ldo_en(codec,1); + sprd_codec_ovp_irq_enable(codec); + sprd_codec_pa_d_en(codec, p_setting->is_classD_mode); + sprd_codec_pa_demi_en(codec, p_setting->is_DEMI_mode); + sprd_codec_pa_ldo_en(codec, p_setting->is_LDO_mode); + if (p_setting->is_LDO_mode) { + if (p_setting->is_auto_LDO_mode) { + sprd_codec_pa_ldo_auto(codec, 1); + } else { + sprd_codec_pa_ldo_v_sel(codec, + p_setting->LDO_V_sel); + } + } else { + sprd_codec_pa_sw_set(codec, SPRD_CODEC_PA_SW_AOL); + } + sprd_codec_pa_dtri_f_sel(codec, p_setting->DTRI_F_sel); + sprd_codec_pa_en(codec, 1); + sprd_codec->inter_pa.set = 1; + } else { + sprd_codec->inter_pa.set = 0; + sprd_codec_pa_en(codec, 0); + sprd_codec_wait(2); + sprd_codec_pa_sw_clr(codec, SPRD_CODEC_PA_SW_AOL); + sprd_codec_pa_ldo_en(codec, 0); + } + mutex_unlock(&sprd_codec->inter_pa_mutex); + return 0; +} + +static int spk_pa_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int on = ! !SND_SOC_DAPM_EVENT_ON(event); + sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event)); + return sprd_inter_speaker_pa(w->codec, on); +} + +#define SPRD_CODEC_HP_VER(bit) \ +do { \ + struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); \ + switch(sprd_codec->hp_ver) { \ + case SPRD_CODEC_HP_PA_VER_2: \ + mask = BIT(V2_##bit); \ + break; \ + case SPRD_CODEC_HP_PA_VER_1: \ + default: \ + mask = BIT(bit); \ + break; \ + } \ +} while(0) + +static inline void sprd_codec_hp_classg_en(struct snd_soc_codec *codec, int on) +{ + int mask; + int val; + sp_asoc_pr_dbg("%s set %d\n", __func__, on); + SPRD_CODEC_HP_VER(AUDIO_CLASSG_EN); + val = on ? mask : 0; + snd_soc_update_bits(codec, SOC_REG(DCR8_DCR7), mask, val); +} + +static inline void sprd_codec_hp_pa_lpw(struct snd_soc_codec *codec, int lpw) +{ + struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); + int mask; + int val; + sp_asoc_pr_dbg("%s set %d\n", __func__, lpw); + switch (sprd_codec->hp_ver) { + case SPRD_CODEC_HP_PA_VER_2: + if (sci_get_ana_chip_ver() >= SC2711BA) { + mask = V2_AUDIO_CHP_LPW_MASK_BA << V2_AUDIO_CHP_LPW_BA; + val = (lpw << V2_AUDIO_CHP_LPW_BA) & mask; + } else { + mask = V2_AUDIO_CHP_LPW_MASK << V2_AUDIO_CHP_LPW; + val = (lpw << V2_AUDIO_CHP_LPW) & mask; + } + break; + case SPRD_CODEC_HP_PA_VER_1: + default: + if (sci_get_ana_chip_ver() >= SC2713CA) { + mask = AUDIO_CHP_LPW_MASK_CA << AUDIO_CHP_LPW_CA; + val = (lpw << AUDIO_CHP_LPW_CA) & mask; + } else { + mask = AUDIO_CHP_LPW_MASK << AUDIO_CHP_LPW; + val = (lpw << AUDIO_CHP_LPW) & mask; + } + break; + } + snd_soc_update_bits(codec, SOC_REG(DCR8_DCR7), mask, val); +} + +static inline void sprd_codec_hp_pa_mode(struct snd_soc_codec *codec, int on) +{ + struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); + int mask; + int val; + sp_asoc_pr_dbg("%s set %d\n", __func__, on); + switch (sprd_codec->hp_ver) { + case SPRD_CODEC_HP_PA_VER_2: + if (sci_get_ana_chip_ver() >= SC2711BA) + mask = BIT(V2_AUDIO_CHP_MODE_BA ); + else + mask = BIT(V2_AUDIO_CHP_MODE); + break; + case SPRD_CODEC_HP_PA_VER_1: + default: + if (sci_get_ana_chip_ver() >= SC2713CA) + mask = BIT(AUDIO_CHP_MODE_CA ); + else + mask = BIT(AUDIO_CHP_MODE ); + break; + } + val = on? mask :0; + snd_soc_update_bits(codec, SOC_REG(DCR8_DCR7), mask, val); +} + +static inline void sprd_codec_hp_pa_osc(struct snd_soc_codec *codec, int osc) +{ + struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); + int mask; + int val; + sp_asoc_pr_dbg("%s set %d\n", __func__, osc); + switch (sprd_codec->hp_ver) { + case SPRD_CODEC_HP_PA_VER_2: + mask = V2_AUDIO_CHP_OSC_MASK << V2_AUDIO_CHP_OSC; + val = (osc << V2_AUDIO_CHP_OSC) & mask; + break; + case SPRD_CODEC_HP_PA_VER_1: + default: + mask = AUDIO_CHP_OSC_MASK << AUDIO_CHP_OSC; + val = (osc << AUDIO_CHP_OSC) & mask; + break; + } + snd_soc_update_bits(codec, SOC_REG(DCR8_DCR7), mask, val); +} + +static inline void sprd_codec_hp_pa_en(struct snd_soc_codec *codec, int on) +{ + int mask; + int val; + sp_asoc_pr_dbg("%s set %d\n", __func__, on); + SPRD_CODEC_HP_VER(AUDIO_CHP_EN); + val = on ? mask : 0; + snd_soc_update_bits(codec, SOC_REG(DCR8_DCR7), mask, val); +} + +static inline void sprd_codec_hp_pa_hpl_en(struct snd_soc_codec *codec, int on) +{ + int mask; + int val; + sp_asoc_pr_dbg("%s set %d\n", __func__, on); + SPRD_CODEC_HP_VER(AUDIO_CHP_HPL_EN); + val = on ? mask : 0; + snd_soc_update_bits(codec, SOC_REG(DCR8_DCR7), mask, val); +} + +static inline void sprd_codec_hp_pa_hpr_en(struct snd_soc_codec *codec, int on) +{ + int mask; + int val; + sp_asoc_pr_dbg("%s set %d\n", __func__, on); + SPRD_CODEC_HP_VER(AUDIO_CHP_HPR_EN); + val = on ? mask : 0; + snd_soc_update_bits(codec, SOC_REG(DCR8_DCR7), mask, val); +} + +static inline void sprd_codec_hp_pa_hpl_mute(struct snd_soc_codec *codec, + int on) +{ + int mask; + int val; + sp_asoc_pr_dbg("%s set %d\n", __func__, on); + SPRD_CODEC_HP_VER(AUDIO_CHP_LMUTE); + val = on ? mask : 0; + snd_soc_update_bits(codec, SOC_REG(DCR8_DCR7), mask, val); +} + +static inline void sprd_codec_hp_pa_hpr_mute(struct snd_soc_codec *codec, + int on) +{ + int mask; + int val; + sp_asoc_pr_dbg("%s set %d\n", __func__, on); + SPRD_CODEC_HP_VER(AUDIO_CHP_RMUTE); + val = on ? mask : 0; + snd_soc_update_bits(codec, SOC_REG(DCR8_DCR7), mask, val); +} + +static inline void sprd_codec_hp_pa_cgcal_en(struct snd_soc_codec *codec, + int on) +{ + int mask; + int val; + sp_asoc_pr_dbg("%s set %d\n", __func__, on); + SPRD_CODEC_HP_VER(AUDIO_CGCAL_EN); + val = on ? mask : 0; + snd_soc_update_bits(codec, SOC_REG(DCR8_DCR7), mask, val); +} + +static inline void sprd_codec_inter_hp_pa_init(struct sprd_codec_priv + *sprd_codec) +{ + sprd_codec->inter_hp_pa.setting.class_g_osc = 0x01; + sprd_codec->inter_hp_pa.setting.class_g_cgcal = 1; + sprd_codec->inter_hp_pa.setting.class_g_cgcal_delay_10ms = 3; + sprd_codec->inter_hp_pa.setting.class_g_unmute_delay_100ms = 5; + sprd_codec->inter_hp_pa.setting.class_g_close_delay_10ms = 0; + sprd_codec->inter_hp_pa.setting.class_g_open_delay_10ms = 0; + sprd_codec->inter_hp_pa.setting.class_g_vdd_delay_10ms = 0; + sprd_codec->inter_hp_pa.setting.class_g_chp_delay_30ms = 0; + sprd_codec->inter_hp_pa.setting.class_g_all_close_delay_100ms = 0; +} + + +static void sprd_inter_headphone_pa_pre(struct sprd_codec_priv *sprd_codec,int on) +{ + struct snd_soc_codec *codec = sprd_codec->codec; + static struct regulator *regulator = 0; + struct sprd_codec_inter_hp_pa *p_setting = + &sprd_codec->inter_hp_pa.setting; + + static struct regulator *regulator_reg= 0; + +#ifdef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13 + static struct regulator *regulator_vb= 0; + int classg_vol = 2200000; +#endif + + if (on) { + if (!regulator) { + sp_asoc_pr_dbg("%s set vddclsg on\n", __func__); + /*1.CG_EN */ + //open clk + regulator_reg = regulator_get(0, "VREG"); + if (IS_ERR(regulator_reg)) { + pr_err("ERR:Failed to request %ld: %s\n", + PTR_ERR(regulator_reg), "VREG"); + BUG_ON(1); + } + regulator_set_mode(regulator_reg, REGULATOR_MODE_STANDBY); + if (regulator_enable(regulator_reg) < 0) { + } else { +#ifdef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13 + //open vb + regulator_vb = regulator_get(0, "VB"); + if (IS_ERR(regulator_vb)) { + pr_err("ERR:Failed to request %ld: %s\n", + PTR_ERR(regulator_vb), "VB"); + BUG_ON(1); + } + regulator_set_mode(regulator_vb, REGULATOR_MODE_STANDBY); + if (regulator_enable(regulator_vb) < 0) { + } else { +#endif + sprd_codec_hp_classg_en(codec, 1); +#ifdef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13 + } +#endif + } + //close clk + regulator_set_mode(regulator_reg, + REGULATOR_MODE_NORMAL); + regulator_disable(regulator_reg); + regulator_put(regulator_reg); + regulator_reg = 0; + sprd_codec_wait(p_setting->class_g_open_delay_10ms * 10); + /*2. LDO PD */ + regulator = regulator_get(0, CLASS_G_LDO_ID); + if (IS_ERR(regulator)) { + pr_err("ERR:Failed to request %ld: %s\n", + PTR_ERR(regulator), CLASS_G_LDO_ID); + BUG_ON(1); + } + regulator_set_mode(regulator, REGULATOR_MODE_STANDBY); +#ifdef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13 + if (sprd_codec->hp_ver == SPRD_CODEC_HP_PA_VER_1) + classg_vol = 1200000; + regulator_set_voltage(regulator, classg_vol, classg_vol); +#endif + if (regulator_enable(regulator) < 0) { + regulator_set_mode(regulator, + REGULATOR_MODE_NORMAL); + regulator_disable(regulator); + regulator_put(regulator); + regulator = 0; + } + } + } else{ + if (regulator) { + sp_asoc_pr_dbg("%s set vddclsg off\n", __func__); + /*LDO PD*/ + regulator_set_mode(regulator, REGULATOR_MODE_NORMAL); + regulator_disable(regulator); + regulator_put(regulator); + regulator = 0; + /*CG_EN*/ + //open clk + regulator_reg = regulator_get(0, "VREG"); + if (IS_ERR(regulator_reg)) { + pr_err("ERR:Failed to request %ld: %s\n", + PTR_ERR(regulator_reg), "VREG"); + BUG_ON(1); + } + regulator_set_mode(regulator_reg, REGULATOR_MODE_STANDBY); + if (regulator_enable(regulator_reg) < 0) { + } else { + sprd_codec_hp_classg_en(codec, 0); +#ifdef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13 + if(regulator_vb) { + //close vb + regulator_set_mode(regulator_vb, REGULATOR_MODE_NORMAL); + regulator_disable(regulator_vb); + regulator_put(regulator_vb); + regulator_vb = 0; + } +#endif + } + //close clk + regulator_set_mode(regulator_reg, + REGULATOR_MODE_NORMAL); + regulator_disable(regulator_reg); + regulator_put(regulator_reg); + regulator_reg = 0; + } + } +} +static int hp_notifier_handler (struct notifier_block *nb, + unsigned long action, void *data) +{ + struct sprd_codec_priv *sprd_codec = container_of (nb, struct sprd_codec_priv, nb); + sprd_inter_headphone_pa_pre (sprd_codec, !!action); + return 0; +} + +static int sprd_inter_headphone_pa(struct snd_soc_codec *codec, int on) +{ + struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); + struct sprd_codec_inter_hp_pa *p_setting = + &sprd_codec->inter_hp_pa.setting; + /*static struct regulator *regulator = 0;*/ + sp_asoc_pr_info("inter HP PA Switch %s\n", STR_ON_OFF(on)); + mutex_lock(&sprd_codec->inter_hp_pa_mutex); + if (on) { + /*mute */ + sprd_codec_hp_pa_hpl_mute(codec, 1); + sprd_codec_hp_pa_hpr_mute(codec, 1); + sprd_codec_auxadc_en(codec, 1); + sprd_codec_hp_pa_lpw(codec, p_setting->class_g_low_power); + sprd_codec_hp_pa_mode(codec, p_setting->class_g_mode); + sprd_codec_hp_pa_osc(codec, p_setting->class_g_osc); + sprd_codec_hp_pa_cgcal_en(codec, p_setting->class_g_cgcal); + sprd_codec_wait(sprd_codec->class_g_delay1 * 30); + /*3. CHP EN */ + sprd_codec_hp_pa_en(codec, 1); + sprd_codec_wait(p_setting->class_g_chp_delay_30ms * 30); + /*4. L/R EN */ + sprd_codec_hp_pa_hpl_en(codec, 1); + sprd_codec_hp_pa_hpr_en(codec, 1); + sprd_codec_wait(sprd_codec->class_g_delay2* 30); + if (p_setting->class_g_cgcal) { + sprd_codec_wait(p_setting->class_g_cgcal_delay_10ms *10); + } + } else { + sprd_codec_wait(p_setting->class_g_close_delay_10ms * 10); + sprd_codec_auxadc_en(codec, 0); + /*1. L/R disable */ + sprd_codec_hp_pa_hpl_en(codec, 0); + sprd_codec_hp_pa_hpr_en(codec, 0); + /*2. CHP disable */ + sprd_codec_hp_pa_en(codec, 0); + /*3. unmute */ + sprd_codec_hp_pa_hpl_mute(codec, 0); + sprd_codec_hp_pa_hpr_mute(codec, 0); + sprd_codec_wait(p_setting->class_g_all_close_delay_100ms* 100); + } + mutex_unlock(&sprd_codec->inter_hp_pa_mutex); + return 0; +} + +static int sprd_inter_headphone_pa_post(struct snd_soc_codec *codec, int on) +{ + struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); + struct sprd_codec_inter_hp_pa *p_setting = + &sprd_codec->inter_hp_pa.setting; + mutex_lock(&sprd_codec->inter_hp_pa_mutex); + if (on) { + /*open classG mute delay time */ + sprd_codec_wait(p_setting->class_g_unmute_delay_100ms * 100); + /*unmute */ + sprd_codec_hp_pa_hpl_mute(codec, 0); + sprd_codec_hp_pa_hpr_mute(codec, 0); + + sprd_codec->inter_hp_pa.set = 1; + } else { + sprd_codec->inter_hp_pa.set = 0; + /*mute */ + sprd_codec_hp_pa_hpl_mute(codec, 1); + sprd_codec_hp_pa_hpr_mute(codec, 1); + sprd_codec_wait(10); + } + mutex_unlock(&sprd_codec->inter_hp_pa_mutex); + return 0; +} + +#ifdef CONFIG_SND_SOC_SPRD_AUDIO_USE_INTER_HP_PA +static int hp_pa_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int on = ! !SND_SOC_DAPM_EVENT_ON(event); + sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event)); + return sprd_inter_headphone_pa(w->codec, on); +} + +static int hp_pa_post_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int on = ! !SND_SOC_DAPM_EVENT_ON(event); + sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event)); + return sprd_inter_headphone_pa_post(w->codec, on); +} +#else +static int hp_pa_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + return 0; +} + +static int hp_pa_post_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + return 0; +} +#endif + +static int sprd_codec_set_sample_rate(struct snd_soc_codec *codec, int rate, + int mask, int shift) +{ + switch (rate) { + case 8000: + snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL), mask, + SPRD_CODEC_RATE_8000 << shift); + break; + case 11025: + snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL), mask, + SPRD_CODEC_RATE_11025 << shift); + break; + case 16000: + snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL), mask, + SPRD_CODEC_RATE_16000 << shift); + break; + case 22050: + snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL), mask, + SPRD_CODEC_RATE_22050 << shift); + break; + case 32000: + snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL), mask, + SPRD_CODEC_RATE_32000 << shift); + break; + case 44100: + snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL), mask, + SPRD_CODEC_RATE_44100 << shift); + break; + case 48000: + snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL), mask, + SPRD_CODEC_RATE_48000 << shift); + break; + case 96000: + snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL), mask, + SPRD_CODEC_RATE_96000 << shift); + break; + default: + pr_err("ERR:SPRD-CODEC not support this rate %d\n", rate); + break; + } + sp_asoc_pr_dbg("Set Playback rate 0x%x\n", + snd_soc_read(codec, AUD_DAC_CTL)); + return 0; +} + +static int sprd_codec_set_ad_sample_rate(struct snd_soc_codec *codec, int rate, + int mask, int shift) +{ + int set; +#ifdef CONFIG_SND_SOC_VBC_SRC_SAMPLE_RATE + if (rate == 44100) + rate = CONFIG_SND_SOC_VBC_SRC_SAMPLE_RATE; +#endif + set = rate / 4000; + if (set > 13) { + pr_err("ERR:SPRD-CODEC not support this ad rate %d\n", rate); + } + snd_soc_update_bits(codec, SOC_REG(AUD_ADC_CTL), mask, set << shift); + return 0; +} + +static int sprd_codec_sample_rate_setting(struct sprd_codec_priv *sprd_codec) +{ + sp_asoc_pr_dbg("%s AD %d DA %d AD1 %d\n", __func__, + sprd_codec->ad_sample_val, sprd_codec->da_sample_val, + sprd_codec->ad1_sample_val); + if (sprd_codec->ad_sample_val) { + sprd_codec_set_ad_sample_rate(sprd_codec->codec, + sprd_codec->ad_sample_val, 0x0F, + 0); + } + if (sprd_codec->ad1_sample_val) { + sprd_codec_set_ad_sample_rate(sprd_codec->codec, + sprd_codec->ad1_sample_val, 0xF0, + 4); + } + if (sprd_codec->da_sample_val) { + sprd_codec_set_sample_rate(sprd_codec->codec, + sprd_codec->da_sample_val, 0x0F, 0); + } + return 0; +} + +static inline int sprd_codec_vcom_ldo_auto(struct snd_soc_codec *codec, + int init) +{ + struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); + return sprd_codec_auto_ldo_volt(codec, sprd_codec_vcm_v_sel, + sprd_codec->s_vcom.v_map, + sprd_codec->s_vcom.v_map_size, init); +} + +static inline int sprd_codec_vcom_ldo_cfg(struct sprd_codec_priv *sprd_codec, const struct sprd_codec_ldo_v_map + *v_map, const int v_map_size) +{ + return _sprd_codec_ldo_cfg_set(&sprd_codec->s_vcom, v_map, v_map_size); +} + +static DEFINE_MUTEX(ldo_mutex); +static int sprd_codec_ldo_on(struct sprd_codec_priv *sprd_codec) +{ + struct regulator **regu = &sprd_codec->head_mic; + sp_asoc_pr_dbg("%s\n", __func__); + + mutex_lock(&ldo_mutex); + atomic_inc(&sprd_codec->ldo_refcount); + if (atomic_read(&sprd_codec->ldo_refcount) == 1) { + sp_asoc_pr_dbg("LDO ON!\n"); + if (*regu) { + regulator_set_mode(*regu, REGULATOR_MODE_NORMAL); + } + arch_audio_codec_analog_enable(); + sprd_codec_vcom_ldo_auto(sprd_codec->codec, 1); + } + mutex_unlock(&ldo_mutex); + + return 0; +} + +static int sprd_codec_ldo_off(struct sprd_codec_priv *sprd_codec) +{ + struct regulator **regu = &sprd_codec->head_mic; + sp_asoc_pr_dbg("%s\n", __func__); + + mutex_lock(&ldo_mutex); + if (atomic_dec_and_test(&sprd_codec->ldo_refcount)) { + if (*regu && regulator_is_enabled(*regu)) { + regulator_set_mode(*regu, REGULATOR_MODE_STANDBY); + } + arch_audio_codec_analog_disable(); + sp_asoc_pr_dbg("LDO OFF!\n"); + } + mutex_unlock(&ldo_mutex); + + return 0; +} + +static int sprd_codec_analog_open(struct snd_soc_codec *codec) +{ + int ret = 0; + struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); + + sp_asoc_pr_dbg("%s\n", __func__); + sprd_codec_sample_rate_setting(sprd_codec); + + /* SC7710/SC8830 ask from ASIC to set initial value */ + snd_soc_update_bits(codec, SOC_REG(PMUR4_PMUR3), BIT(SEL_VCMI), + BIT(SEL_VCMI)); + snd_soc_update_bits(codec, SOC_REG(PMUR4_PMUR3), BIT(VCMI_FAST_EN), + BIT(VCMI_FAST_EN)); + snd_soc_write(codec, AUD_SDM_CTL0, 0x400); + + return ret; +} + +static int sprd_codec_digital_open(struct snd_soc_codec *codec) +{ + struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + sp_asoc_pr_dbg("%s\n", __func__); + + /* FIXME : Some Clock SYNC bug will cause MUTE */ + snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL), BIT(DAC_MUTE_EN), 0); + + sprd_codec_sample_rate_setting(sprd_codec); + + return ret; +} + +static void sprd_codec_power_enable(struct snd_soc_codec *codec) +{ + struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); + int ret; + + sp_asoc_pr_dbg("%s\n", __func__); + + ret = sprd_codec_ldo_on(sprd_codec); + if (ret != 0) + pr_err("ERR:SPRD-CODEC open ldo error %d\n", ret); + + sprd_codec_analog_open(codec); +} + +static void sprd_codec_power_disable(struct snd_soc_codec *codec) +{ + struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); + + sp_asoc_pr_dbg("%s\n", __func__); + + sprd_codec_ldo_off(sprd_codec); +} + +static int digital_power_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int ret = 0; + + sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event)); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + arch_audio_codec_digital_reg_enable(); + arch_audio_codec_digital_enable(); + arch_audio_codec_digital_reset(); + sprd_codec_digital_open(w->codec); + break; + case SND_SOC_DAPM_POST_PMD: + /*maybe ADC module use it, so we cann't close it */ + /*arch_audio_codec_digital_disable(); */ + arch_audio_codec_digital_reg_disable(); + break; + default: + BUG(); + ret = -EINVAL; + } + + return ret; +} + +static int analog_power_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + int ret = 0; + + sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event)); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + sprd_codec_power_enable(codec); + break; + case SND_SOC_DAPM_POST_PMD: + sprd_codec_power_disable(codec); + break; + default: + BUG(); + ret = -EINVAL; + } + + return ret; +} + +static int chan_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int chan_id = FUN_REG(w->reg); + int on = ! !SND_SOC_DAPM_EVENT_ON(event); + + sp_asoc_pr_info("%s %s\n", sprd_codec_chan_get_name(chan_id), + STR_ON_OFF(on)); + + return 0; +} + +static int dfm_out_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int on = ! !SND_SOC_DAPM_EVENT_ON(event); + struct snd_soc_codec *codec = w->codec; + struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); + + sp_asoc_pr_info("DFM-OUT %s\n", STR_ON_OFF(on)); + sprd_codec->ad_sample_val = sprd_codec->da_sample_val; + sprd_codec_sample_rate_setting(sprd_codec); + + return 0; +} + +static int _mixer_set_mixer(struct snd_soc_codec *codec, int id, int lr, + int try_on, int need_set) +{ + struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); + int reg = GET_MIXER_ID(ID_FUN(id, lr)); + struct sprd_codec_mixer *mixer = &(sprd_codec->mixer[reg]); + if (try_on) { + mixer->set = mixer_setting[reg]; + /* NOTES: reduce linein pop noise must open ADCL/R ->HP MIXER + AFTER delay 250ms for both ADCL/ADCR switch complete. */ + if (need_set) + return mixer->set(codec, mixer->on); + } else { + mixer_setting[reg] (codec, 0); + mixer->set = 0; + } + return 0; +} + +static inline int _mixer_setting(struct snd_soc_codec *codec, int start, + int end, int lr, int try_on, int need_set) +{ + int id; + for (id = start; id < end; id++) { + _mixer_set_mixer(codec, id, lr, try_on, need_set); + } + return 0; +} + +static inline int _mixer_setting_one(struct snd_soc_codec *codec, int id, + int try_on, int need_set) +{ + int lr = id & 0x1; + id = (id >>1) + SPRD_CODEC_MIXER_START; + return _mixer_setting(codec, id, id + 1, lr, try_on, need_set); +} + +#ifndef CONFIG_SND_SOC_SPRD_CODEC_NO_HP_POP +static void sprd_codec_hp_pop_irq_enable(struct snd_soc_codec *codec) +{ + int mask = BIT(AUDIO_POP_IRQ); + snd_soc_update_bits(codec, SOC_REG(AUDIF_INT_CLR), mask, mask); + snd_soc_update_bits(codec, SOC_REG(AUDIF_INT_EN), mask, mask); +} +#endif + +static irqreturn_t sprd_codec_ap_irq(int irq, void *dev_id) +{ + int mask; + struct sprd_codec_priv *sprd_codec = dev_id; + struct snd_soc_codec *codec = sprd_codec->codec; + mask = snd_soc_read(codec, AUDIF_INT_MASK); + sp_asoc_pr_dbg("HP POP IRQ Mask = 0x%x\n", mask); + if (BIT(AUDIO_POP_IRQ) & mask) { + snd_soc_update_bits(codec, SOC_REG(AUDIF_INT_EN), BIT(AUDIO_POP_IRQ), 0); + complete(&sprd_codec->completion_hp_pop); + } + if (BIT(OVP_IRQ) & mask) { + snd_soc_update_bits(codec, SOC_REG(AUDIF_INT_EN), BIT(OVP_IRQ), 0); + sprd_codec_ovp_start(sprd_codec, 1); + sprd_codec_ovp_irq_enable(codec); + } + return IRQ_HANDLED; +} + +#ifndef CONFIG_SND_SOC_SPRD_CODEC_NO_HP_POP +static inline int is_hp_pop_compelet(struct snd_soc_codec *codec) +{ + int val; + val = snd_soc_read(codec, IFR2_IFR1); + val = (val >> HP_POP_FLG) & HP_POP_FLG_MASK; + sp_asoc_pr_dbg("HP POP= 0x%x\n", val); + return HP_POP_FLG_NEAR_CMP == val; +} + +static inline int hp_pop_wait_for_compelet(struct snd_soc_codec *codec) +{ + int i; + int hp_pop_complete; + struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); + hp_pop_complete = msecs_to_jiffies(SPRD_CODEC_HP_POP_TIMEOUT); + for (i = 0; i < 2; i++) { + sp_asoc_pr_dbg("HP POP %d IRQ Enable\n", i); + sprd_codec_hp_pop_irq_enable(codec); + init_completion(&sprd_codec->completion_hp_pop); + hp_pop_complete = + wait_for_completion_timeout(&sprd_codec->completion_hp_pop, + hp_pop_complete); + sp_asoc_pr_dbg("HP POP %d Completion %d\n", i, hp_pop_complete); + if (!hp_pop_complete) { + if (!is_hp_pop_compelet(codec)) { + pr_err("ERR:HP POP %d timeout not complete\n", + i); + } else { + pr_err("ERR:HP POP %d timeout but complete\n", + i); + } + } else { + /* 01 change to 10 maybe walk to 11 shortly, + so, check it double. */ + sprd_codec_wait(2); + if (is_hp_pop_compelet(codec)) { + return 0; + } + } + } + return 0; +} + +static int hp_pop_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + int mask; + int ret = 0; + + sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event)); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + mask = HP_POP_STEP_MASK << HP_POP_STEP; + snd_soc_update_bits(codec, SOC_REG(PNRCR2_PNRCR1), mask, + HP_POP_STEP_2 << HP_POP_STEP); + mask = HP_POP_CTL_MASK << HP_POP_CTL; + snd_soc_update_bits(codec, SOC_REG(PNRCR2_PNRCR1), mask, + HP_POP_CTL_UP << HP_POP_CTL); + sp_asoc_pr_dbg("U PNRCR1 = 0x%x\n", + snd_soc_read(codec, PNRCR2_PNRCR1)); + break; + case SND_SOC_DAPM_PRE_PMD: + mask = HP_POP_STEP_MASK << HP_POP_STEP; + snd_soc_update_bits(codec, SOC_REG(PNRCR2_PNRCR1), mask, + HP_POP_STEP_1 << HP_POP_STEP); + mask = HP_POP_CTL_MASK << HP_POP_CTL; + snd_soc_update_bits(codec, SOC_REG(PNRCR2_PNRCR1), mask, + HP_POP_CTL_DOWN << HP_POP_CTL); + sp_asoc_pr_dbg("D PNRCR1 = 0x%x\n", + snd_soc_read(codec, PNRCR2_PNRCR1)); + break; + case SND_SOC_DAPM_POST_PMU: + ret = hp_pop_wait_for_compelet(codec); + mask = HP_POP_CTL_MASK << HP_POP_CTL; + snd_soc_update_bits(codec, SOC_REG(PNRCR2_PNRCR1), mask, + HP_POP_CTL_HOLD << HP_POP_CTL); + sp_asoc_pr_dbg("HOLD PNRCR1 = 0x%x\n", + snd_soc_read(codec, PNRCR2_PNRCR1)); + break; + case SND_SOC_DAPM_POST_PMD: + ret = hp_pop_wait_for_compelet(codec); + mask = HP_POP_CTL_MASK << HP_POP_CTL; + snd_soc_update_bits(codec, SOC_REG(PNRCR2_PNRCR1), mask, + HP_POP_CTL_DIS << HP_POP_CTL); + sp_asoc_pr_dbg("DIS PNRCR1 = 0x%x\n", + snd_soc_read(codec, PNRCR2_PNRCR1)); + break; + default: + BUG(); + ret = -EINVAL; + } + + return ret; +} +#else +#ifndef CONFIG_SND_SOC_SPRD_HP_POP_DELAY_TIME +#define CONFIG_SND_SOC_SPRD_HP_POP_DELAY_TIME (0) +#endif +static int hp_pop_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int ret = 0; + sp_asoc_pr_dbg("%s Event is %s wait %dms\n", __func__, + get_event_name(event), + CONFIG_SND_SOC_SPRD_HP_POP_DELAY_TIME); + switch (event) { + case SND_SOC_DAPM_POST_PMU: + sprd_codec_wait(CONFIG_SND_SOC_SPRD_HP_POP_DELAY_TIME); + break; + default: + BUG(); + ret = -EINVAL; + } + return ret; +} +#endif + +static int hp_switch_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; +#ifndef CONFIG_SND_SOC_SPRD_CODEC_NO_HP_POP + int mask; +#endif + int ret = 0; + + sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event)); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: +#if 1 /* enable the diff function in shark from weifeng.ni */ + snd_soc_update_bits(codec, SOC_REG(DCR2_DCR1), BIT(DIFF_EN), + BIT(DIFF_EN)); + /*enable from */ + snd_soc_update_bits(codec, SOC_REG(DCR2_DCR1), BIT(HP_VCMI_EN), + BIT(HP_VCMI_EN)); + +#endif + +#ifndef CONFIG_SND_SOC_SPRD_CODEC_NO_HP_POP + mask = HP_POP_CTL_MASK << HP_POP_CTL; + snd_soc_update_bits(codec, SOC_REG(PNRCR2_PNRCR1), mask, + HP_POP_CTL_DIS << HP_POP_CTL); + sp_asoc_pr_dbg("DIS(en) PNRCR1 = 0x%x\n", + snd_soc_read(codec, PNRCR2_PNRCR1)); +#endif + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, SOC_REG(DCR2_DCR1), BIT(DIFF_EN), 0); + snd_soc_update_bits(codec, SOC_REG(DCR2_DCR1), BIT(HP_VCMI_EN), 0); + +#ifndef CONFIG_SND_SOC_SPRD_CODEC_NO_HP_POP + mask = HP_POP_CTL_MASK << HP_POP_CTL; + snd_soc_update_bits(codec, SOC_REG(PNRCR2_PNRCR1), mask, + HP_POP_CTL_HOLD << HP_POP_CTL); + sp_asoc_pr_dbg("HOLD(en) PNRCR1 = 0x%x\n", + snd_soc_read(codec, PNRCR2_PNRCR1)); +#endif + goto _pre_pmd; + break; + case SND_SOC_DAPM_POST_PMD: + /* do nothing now */ + break; + default: + BUG(); + ret = -EINVAL; + } + + _mixer_setting(codec, SPRD_CODEC_HP_DACL, + SPRD_CODEC_HP_DAC_MIXER_MAX, SPRD_CODEC_LEFT, + snd_soc_read(codec, DCR2_DCR1) & BIT(HPL_EN), 1); + + _mixer_setting(codec, SPRD_CODEC_HP_DACL, + SPRD_CODEC_HP_DAC_MIXER_MAX, SPRD_CODEC_RIGHT, + snd_soc_read(codec, DCR2_DCR1) & BIT(HPR_EN), 1); + + _mixer_setting(codec, SPRD_CODEC_HP_ADCL, + SPRD_CODEC_HP_MIXER_MAX, SPRD_CODEC_LEFT, + snd_soc_read(codec, DCR2_DCR1) & BIT(HPL_EN), 0); + + _mixer_setting(codec, SPRD_CODEC_HP_ADCL, + SPRD_CODEC_HP_MIXER_MAX, SPRD_CODEC_RIGHT, + snd_soc_read(codec, DCR2_DCR1) & BIT(HPR_EN), 0); + +_pre_pmd: + + return ret; +} + +static int spk_switch_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event)); + + if (snd_soc_read(codec, DCR2_DCR1) & BIT(AOL_EN)) { + switch (event) { + case SND_SOC_DAPM_POST_PMU: + sprd_codec_pa_sw_set(codec, SPRD_CODEC_PA_SW_AOL); + break; + case SND_SOC_DAPM_PRE_PMD: + sprd_codec_pa_sw_clr(codec, SPRD_CODEC_PA_SW_AOL); + return 0; + default: + break; + } + } + + _mixer_setting(codec, SPRD_CODEC_SPK_DACL, + SPRD_CODEC_SPK_DAC_MIXER_MAX, SPRD_CODEC_LEFT, + (snd_soc_read(codec, DCR2_DCR1) & BIT(AOL_EN)), 1); + + _mixer_setting(codec, SPRD_CODEC_SPK_DACL, + SPRD_CODEC_SPK_DAC_MIXER_MAX, SPRD_CODEC_RIGHT, + (snd_soc_read(codec, DCR2_DCR1) & BIT(AOR_EN)), 1); + + _mixer_setting(codec, SPRD_CODEC_SPK_ADCL, + SPRD_CODEC_SPK_MIXER_MAX, SPRD_CODEC_LEFT, + (snd_soc_read(codec, DCR2_DCR1) & BIT(AOL_EN)), 0); + + _mixer_setting(codec, SPRD_CODEC_SPK_ADCL, + SPRD_CODEC_SPK_MIXER_MAX, SPRD_CODEC_RIGHT, + (snd_soc_read(codec, DCR2_DCR1) & BIT(AOR_EN)), 0); + + return 0; +} + +#ifdef CONFIG_SND_SOC_SPRD_CODEC_EAR_WITH_IN_SPK +static int ear_switch_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int ret = 0; + sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event)); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + sprd_codec_pa_sw_set(codec, SPRD_CODEC_PA_SW_EAR); + break; + case SND_SOC_DAPM_POST_PMD: + sprd_codec_pa_sw_clr(codec, SPRD_CODEC_PA_SW_EAR); + break; + default: + BUG(); + ret = -EINVAL; + } + + return ret; +} +#endif + +static int adcpgal_set(struct snd_soc_codec *codec, int on) +{ + int mask = ADCPGAL_EN_MASK << ADCPGAL_EN; + return snd_soc_update_bits(codec, SOC_REG(AACR2_AACR1), mask, + on ? mask : 0); +} + +static int adcpgar_set(struct snd_soc_codec *codec, int on) +{ + int mask = ADCPGAR_EN_MASK << ADCPGAR_EN; + return snd_soc_update_bits(codec, SOC_REG(AACR2_AACR1), mask, + on ? mask : 0); +} + +static int adc_switch_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + int is_right = (w->shift == SPRD_CODEC_RIGHT); + int on = (! !SND_SOC_DAPM_EVENT_ON(event)); + sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event)); + + if (is_right) { + adcpgar_set(codec, on); + _mixer_setting(codec, SPRD_CODEC_AIL, SPRD_CODEC_ADC_MIXER_MAX, + SPRD_CODEC_RIGHT, on, 1); + } else { + adcpgal_set(codec, on); + _mixer_setting(codec, SPRD_CODEC_AIL, SPRD_CODEC_ADC_MIXER_MAX, + SPRD_CODEC_LEFT, on, 1); + } + + return 0; +} + +static int pga_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); + int id = GET_PGA_ID(FUN_REG(w->reg)); + struct sprd_codec_pga_op *pga = &(sprd_codec->pga[id]); + int ret = 0; + int min = sprd_codec_pga_cfg[id].min; + + sp_asoc_pr_dbg("%s Set %s(%d) Event is %s\n", __func__, + sprd_codec_pga_debug_str[id], pga->pgaval, + get_event_name(event)); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + pga->set = sprd_codec_pga_cfg[id].set; + ret = pga->set(codec, pga->pgaval); + break; + case SND_SOC_DAPM_PRE_PMD: + pga->set = 0; + ret = sprd_codec_pga_cfg[id].set(codec, min); + break; + default: + BUG(); + ret = -EINVAL; + } + return ret; +} + +static int adcpgar_byp_set(struct snd_soc_codec *codec, int value) +{ + int mask = ADCPGAR_BYP_MASK << ADCPGAR_BYP; + return snd_soc_update_bits(codec, SOC_REG(AACR2_AACR1), mask, + value << ADCPGAR_BYP); +} + +static int adcpgar_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + int ret = 0; + + sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event)); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + adcpgar_byp_set(codec, 2); + sprd_codec_wait(100); + adcpgar_byp_set(codec, 0); + break; + case SND_SOC_DAPM_PRE_PMD: + break; + default: + BUG(); + ret = -EINVAL; + } + return ret; +} + +static int ana_loop_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); + int id = GET_MIXER_ID(FUN_REG(w->reg)); + struct sprd_codec_mixer *mixer; + int ret = 0; + static int s_need_wait = 1; + + sp_asoc_pr_dbg("Entering %s event is %s\n", __func__, + get_event_name(event)); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* NOTES: reduce linein pop noise must open ADCL/R ->HP/SPK MIXER + AFTER delay 250ms for both ADCL/ADCR switch complete. + */ + if (s_need_wait == 1) { + /* NOTES: reduce linein pop noise must delay 250ms + after linein mixer switch on. + actually this function perform after + adc_switch_event function for + both ADCL/ADCR switch complete. + */ + sprd_codec_wait(250); + s_need_wait++; + sp_asoc_pr_dbg("ADC Switch ON delay\n"); + } + mixer = &(sprd_codec->mixer[id]); + if (mixer->set) { + mixer->set(codec, mixer->on); + } + break; + case SND_SOC_DAPM_PRE_PMD: + s_need_wait = 1; + break; + default: + BUG(); + ret = -EINVAL; + } + + return ret; +} + +static int mic_bias_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); + int reg = FUN_REG(w->reg); + int ret = 0; + int on = ! !SND_SOC_DAPM_EVENT_ON(event); + struct regulator **regu; + + sp_asoc_pr_dbg("%s %s Event is %s\n", __func__, + mic_bias_name[GET_MIC_BIAS_ID(reg)], + get_event_name(event)); + + switch (reg) { + case SPRD_CODEC_MIC_BIAS: + regu = &sprd_codec->main_mic; + break; + case SPRD_CODEC_AUXMIC_BIAS: + regu = &sprd_codec->aux_mic; + break; + case SPRD_CODEC_HEADMIC_BIAS: + regu = &sprd_codec->head_mic; + break; + default: + BUG(); + ret = -EINVAL; + } + + ret = sprd_codec_mic_bias_set(regu, on); + + return ret; +} + +static int mixer_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); + int id = GET_MIXER_ID(FUN_REG(w->reg)); + struct sprd_codec_mixer *mixer = &(sprd_codec->mixer[id]); + int ret = 0; + + sp_asoc_pr_dbg("%s event is %s\n", sprd_codec_mixer_debug_str[id], + get_event_name(event)); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + mixer->on = 1; + break; + case SND_SOC_DAPM_PRE_PMD: + mixer->on = 0; + break; + default: + BUG(); + ret = -EINVAL; + } + if (ret >= 0) + _mixer_setting_one(codec, id, mixer->on, 1); + + return ret; +} + +static int mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = wlist->widgets[0]->codec; + struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); + int id = GET_MIXER_ID(FUN_REG(mc->reg)); + ucontrol->value.integer.value[0] = sprd_codec->mixer[id].on; + return 0; +} + +static int mixer_need_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol, bool need_set) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = wlist->widgets[0]->codec; + struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); + int id = GET_MIXER_ID(FUN_REG(mc->reg)); + struct sprd_codec_mixer *mixer = &(sprd_codec->mixer[id]); + int ret = 0; + + if (mixer->on == ucontrol->value.integer.value[0]) + return 0; + + sp_asoc_pr_info("Set %s Switch %s\n", sprd_codec_mixer_debug_str[id], + STR_ON_OFF(ucontrol->value.integer.value[0])); + + /*notice the sequence */ + snd_soc_dapm_put_volsw(kcontrol, ucontrol); + + /*update reg: must be set after snd_soc_dapm_put_enum_double->change = snd_soc_test_bits(widget->codec, e->reg, mask, val); */ + + if (mixer->set && need_set) + ret = mixer->set(codec, mixer->on); + + return ret; +} + +static int mixer_set_mem(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return mixer_need_set(kcontrol, ucontrol, 0); +} + +static int mixer_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return mixer_need_set(kcontrol, ucontrol, 1); +} + +#define SPRD_CODEC_MIXER(xname, xreg)\ + SOC_SINGLE_EXT(xname, FUN_REG(xreg), 0, 1, 0, mixer_get, mixer_set) + +/*Just for LINE IN path, mixer_set not really set mixer (ADCL/R -> HP/SPK L/R) here but +setting in ana_loop_event, just remeber state here*/ +#define SPRD_CODEC_MIXER_NOSET(xname, xreg)\ + SOC_SINGLE_EXT(xname, FUN_REG(xreg), 0, 1, 0, mixer_get, mixer_set_mem) + +/* ADCL Mixer */ +static const struct snd_kcontrol_new adcl_mixer_controls[] = { + SPRD_CODEC_MIXER("AILADCL Switch", + ID_FUN(SPRD_CODEC_AIL, SPRD_CODEC_LEFT)), + SPRD_CODEC_MIXER("AIRADCL Switch", + ID_FUN(SPRD_CODEC_AIR, SPRD_CODEC_LEFT)), + SPRD_CODEC_MIXER("MainMICADCL Switch", + ID_FUN(SPRD_CODEC_MAIN_MIC, SPRD_CODEC_LEFT)), + SPRD_CODEC_MIXER("AuxMICADCL Switch", + ID_FUN(SPRD_CODEC_AUX_MIC, SPRD_CODEC_LEFT)), + SPRD_CODEC_MIXER("HPMICADCL Switch", + ID_FUN(SPRD_CODEC_HP_MIC, SPRD_CODEC_LEFT)), +}; + +/* ADCR Mixer */ +static const struct snd_kcontrol_new adcr_mixer_controls[] = { + SPRD_CODEC_MIXER("AILADCR Switch", + ID_FUN(SPRD_CODEC_AIL, SPRD_CODEC_RIGHT)), + SPRD_CODEC_MIXER("AIRADCR Switch", + ID_FUN(SPRD_CODEC_AIR, SPRD_CODEC_RIGHT)), + SPRD_CODEC_MIXER("MainMICADCR Switch", + ID_FUN(SPRD_CODEC_MAIN_MIC, SPRD_CODEC_RIGHT)), + SPRD_CODEC_MIXER("AuxMICADCR Switch", + ID_FUN(SPRD_CODEC_AUX_MIC, SPRD_CODEC_RIGHT)), + SPRD_CODEC_MIXER("HPMICADCR Switch", + ID_FUN(SPRD_CODEC_HP_MIC, SPRD_CODEC_RIGHT)), +}; + +/* HPL Mixer */ +static const struct snd_kcontrol_new hpl_mixer_controls[] = { + SPRD_CODEC_MIXER("DACLHPL Switch", + ID_FUN(SPRD_CODEC_HP_DACL, SPRD_CODEC_LEFT)), + SPRD_CODEC_MIXER("DACRHPL Switch", + ID_FUN(SPRD_CODEC_HP_DACR, SPRD_CODEC_LEFT)), + SPRD_CODEC_MIXER_NOSET("ADCLHPL Switch", + ID_FUN(SPRD_CODEC_HP_ADCL, SPRD_CODEC_LEFT)), + SPRD_CODEC_MIXER_NOSET("ADCRHPL Switch", + ID_FUN(SPRD_CODEC_HP_ADCR, SPRD_CODEC_LEFT)), +}; + +/* HPR Mixer */ +static const struct snd_kcontrol_new hpr_mixer_controls[] = { + SPRD_CODEC_MIXER("DACLHPR Switch", + ID_FUN(SPRD_CODEC_HP_DACL, SPRD_CODEC_RIGHT)), + SPRD_CODEC_MIXER("DACRHPR Switch", + ID_FUN(SPRD_CODEC_HP_DACR, SPRD_CODEC_RIGHT)), + SPRD_CODEC_MIXER_NOSET("ADCLHPR Switch", + ID_FUN(SPRD_CODEC_HP_ADCL, SPRD_CODEC_RIGHT)), + SPRD_CODEC_MIXER_NOSET("ADCRHPR Switch", + ID_FUN(SPRD_CODEC_HP_ADCR, SPRD_CODEC_RIGHT)), +}; + +/* SPKL Mixer */ +static const struct snd_kcontrol_new spkl_mixer_controls[] = { + SPRD_CODEC_MIXER("DACLSPKL Switch", + ID_FUN(SPRD_CODEC_SPK_DACL, SPRD_CODEC_LEFT)), + SPRD_CODEC_MIXER("DACRSPKL Switch", + ID_FUN(SPRD_CODEC_SPK_DACR, SPRD_CODEC_LEFT)), + SPRD_CODEC_MIXER_NOSET("ADCLSPKL Switch", + ID_FUN(SPRD_CODEC_SPK_ADCL, SPRD_CODEC_LEFT)), + SPRD_CODEC_MIXER_NOSET("ADCRSPKL Switch", + ID_FUN(SPRD_CODEC_SPK_ADCR, SPRD_CODEC_LEFT)), +}; + +/* SPKR Mixer */ +static const struct snd_kcontrol_new spkr_mixer_controls[] = { + SPRD_CODEC_MIXER("DACLSPKR Switch", + ID_FUN(SPRD_CODEC_SPK_DACL, SPRD_CODEC_RIGHT)), + SPRD_CODEC_MIXER("DACRSPKR Switch", + ID_FUN(SPRD_CODEC_SPK_DACR, SPRD_CODEC_RIGHT)), + SPRD_CODEC_MIXER_NOSET("ADCLSPKR Switch", + ID_FUN(SPRD_CODEC_SPK_ADCL, SPRD_CODEC_RIGHT)), + SPRD_CODEC_MIXER_NOSET("ADCRSPKR Switch", + ID_FUN(SPRD_CODEC_SPK_ADCR, SPRD_CODEC_RIGHT)), +}; + +/*ANA LOOP SWITCH*/ +#define SPRD_CODEC_LOOP_SWITCH(xname, xreg)\ + SND_SOC_DAPM_PGA_S(xname, SPRD_CODEC_ANA_MIXER_ORDER, FUN_REG(xreg), 0, 0, ana_loop_event,\ + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD) + +#ifndef SND_SOC_DAPM_MICBIAS_E +#define SND_SOC_DAPM_MICBIAS_E(wname, wreg, wshift, winvert, wevent, wflags) \ +{ .id = snd_soc_dapm_micbias, .name = wname, .reg = wreg, .shift = wshift, \ + .invert = winvert, .kcontrol_news = NULL, .num_kcontrols = 0, \ + .event = wevent, .event_flags = wflags} +#endif + +static const struct snd_soc_dapm_widget sprd_codec_dapm_widgets[] = { + SND_SOC_DAPM_REGULATOR_SUPPLY("VBO", 0, 0), + SND_SOC_DAPM_REGULATOR_SUPPLY("VCOM_BUF", 0, 0), + SND_SOC_DAPM_SUPPLY_S("Digital Power", 1, SND_SOC_NOPM, 0, 0, + digital_power_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY_S("Analog Power", 2, SND_SOC_NOPM, 0, 0, + analog_power_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY_S("DA Clk", 3, SOC_REG(CCR), DAC_CLK_EN, 0, NULL, + 0), + SND_SOC_DAPM_SUPPLY_S("DRV Clk", 4, SOC_REG(CCR), DRV_CLK_EN, 0, NULL, + 0), + SND_SOC_DAPM_SUPPLY_S("AD IBUF", 3, SOC_REG(AACR2_AACR1), ADC_IBUF_PD, + 1, + NULL, 0), + SND_SOC_DAPM_SUPPLY_S("AD Clk", 3, SOC_REG(CCR), ADC_CLK_EN, 0, NULL, + 0), + + SND_SOC_DAPM_PGA_S("Digital DACL Switch", 4, SOC_REG(AUD_TOP_CTL), + DAC_EN_L, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("Digital DACR Switch", 4, SOC_REG(AUD_TOP_CTL), + DAC_EN_R, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("ADie Digital DACL Switch", 5, SOC_REG(AUDIF_ENB), + AUDIFA_DACL_EN, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("ADie Digital DACR Switch", 5, SOC_REG(AUDIF_ENB), + AUDIFA_DACR_EN, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("DACL Switch", 6, SOC_REG(DACGR_DACR), DACL_EN, 0, + NULL, + 0), + SND_SOC_DAPM_PGA_S("DACR Switch", 6, SOC_REG(DACGR_DACR), DACR_EN, 0, + NULL, + 0), + SND_SOC_DAPM_PGA_S("DACL Mute", 6, FUN_REG(SPRD_CODEC_PGA_DACL), 0, 0, + pga_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_S("DACR Mute", 6, FUN_REG(SPRD_CODEC_PGA_DACR), 0, 0, + pga_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_DAC_E("DAC", "Playback-Vaudio", + FUN_REG(SPRD_CODEC_PLAYBACK), 0, + 0, + chan_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("DFM-OUT", "DFM-Output", + SND_SOC_NOPM, 0, + 0, + dfm_out_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +#ifdef CONFIG_SND_SOC_SPRD_CODEC_NO_HP_POP + SND_SOC_DAPM_PGA_S("HP POP", SPRD_CODEC_HP_POP_ORDER, SND_SOC_NOPM, 0, + 0, hp_pop_event, + SND_SOC_DAPM_POST_PMU), +#else + SND_SOC_DAPM_SUPPLY_S("HP POP", 5, SND_SOC_NOPM, 0, 0, hp_pop_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), +#endif + SND_SOC_DAPM_PGA_S("HPL Switch", SPRD_CODEC_HP_SWITCH_ORDER, + SOC_REG(DCR2_DCR1), HPL_EN, 0, + hp_switch_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_S("HPR Switch", SPRD_CODEC_HP_SWITCH_ORDER, + SOC_REG(DCR2_DCR1), HPR_EN, 0, + hp_switch_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_S("HPL Mute", 6, FUN_REG(SPRD_CODEC_PGA_HPL), 0, 0, + pga_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_S("HPR Mute", 6, FUN_REG(SPRD_CODEC_PGA_HPR), 0, 0, + pga_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MIXER("HPL Mixer", SND_SOC_NOPM, 0, 0, + &hpl_mixer_controls[0], + ARRAY_SIZE(hpl_mixer_controls)), + SND_SOC_DAPM_MIXER("HPR Mixer", SND_SOC_NOPM, 0, 0, + &hpr_mixer_controls[0], + ARRAY_SIZE(hpr_mixer_controls)), + SND_SOC_DAPM_MIXER("SPKL Mixer", SND_SOC_NOPM, 0, 0, + &spkl_mixer_controls[0], + ARRAY_SIZE(spkl_mixer_controls)), + SND_SOC_DAPM_MIXER("SPKR Mixer", SND_SOC_NOPM, 0, 0, + &spkr_mixer_controls[0], + ARRAY_SIZE(spkr_mixer_controls)), + SPRD_CODEC_LOOP_SWITCH("ADCLHPL Loop Switch", + ID_FUN(SPRD_CODEC_HP_ADCL, SPRD_CODEC_LEFT)), + SPRD_CODEC_LOOP_SWITCH("ADCRHPL Loop Switch", + ID_FUN(SPRD_CODEC_HP_ADCR, SPRD_CODEC_LEFT)), + SPRD_CODEC_LOOP_SWITCH("ADCLHPR Loop Switch", + ID_FUN(SPRD_CODEC_HP_ADCL, SPRD_CODEC_RIGHT)), + SPRD_CODEC_LOOP_SWITCH("ADCRHPR Loop Switch", + ID_FUN(SPRD_CODEC_HP_ADCR, SPRD_CODEC_RIGHT)), + SPRD_CODEC_LOOP_SWITCH("ADCLSPKL Loop Switch", + ID_FUN(SPRD_CODEC_SPK_ADCL, SPRD_CODEC_LEFT)), + SPRD_CODEC_LOOP_SWITCH("ADCRSPKL Loop Switch", + ID_FUN(SPRD_CODEC_SPK_ADCR, SPRD_CODEC_LEFT)), + SPRD_CODEC_LOOP_SWITCH("ADCLSPKR Loop Switch", + ID_FUN(SPRD_CODEC_SPK_ADCL, SPRD_CODEC_RIGHT)), + SPRD_CODEC_LOOP_SWITCH("ADCRSPKR Loop Switch", + ID_FUN(SPRD_CODEC_SPK_ADCR, SPRD_CODEC_RIGHT)), + SND_SOC_DAPM_PGA_S("SPKL Switch", 5, SOC_REG(DCR2_DCR1), AOL_EN, 0, + spk_switch_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_S("SPKR Switch", 5, SOC_REG(DCR2_DCR1), AOR_EN, 0, + spk_switch_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_S("SPKL Mute", 6, FUN_REG(SPRD_CODEC_PGA_SPKL), 0, 0, + pga_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_S("SPKR Mute", 6, FUN_REG(SPRD_CODEC_PGA_SPKR), 0, 0, + pga_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_S("EAR Mixer", 5, + FUN_REG(ID_FUN + (SPRD_CODEC_EAR_DACL, SPRD_CODEC_LEFT)), 0, + 0, mixer_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), +#ifdef CONFIG_SND_SOC_SPRD_CODEC_EAR_WITH_IN_SPK + SND_SOC_DAPM_PGA_S("EAR Switch", 6, SOC_REG(DCR2_DCR1), EAR_EN, 0, + ear_switch_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +#else + SND_SOC_DAPM_PGA_S("EAR Switch", 6, SOC_REG(DCR2_DCR1), EAR_EN, 0, NULL, + 0), +#endif + + SND_SOC_DAPM_PGA_S("EAR Mute", 7, FUN_REG(SPRD_CODEC_PGA_EAR), 0, 0, + pga_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_MIXER("ADCL Mixer", SND_SOC_NOPM, 0, 0, + &adcl_mixer_controls[0], + ARRAY_SIZE(adcl_mixer_controls)), + SND_SOC_DAPM_MIXER("ADCR Mixer", SND_SOC_NOPM, 0, 0, + &adcr_mixer_controls[0], + ARRAY_SIZE(adcr_mixer_controls)), + SND_SOC_DAPM_PGA_S("Digital ADCL Switch", 3, SOC_REG(AUD_TOP_CTL), + ADC_EN_L, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("Digital ADCR Switch", 3, SOC_REG(AUD_TOP_CTL), + ADC_EN_R, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("ADie Digital ADCL Switch", 2, SOC_REG(AUDIF_ENB), + AUDIFA_ADCL_EN, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("ADie Digital ADCR Switch", 2, SOC_REG(AUDIF_ENB), + AUDIFA_ADCR_EN, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("ADCL Switch", 1, SOC_REG(AACR2_AACR1), ADCL_PD, 1, + NULL, + 0), + SND_SOC_DAPM_PGA_S("ADCR Switch", 1, SOC_REG(AACR2_AACR1), ADCR_PD, 1, + adcpgar_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_E("ADCL PGA", SND_SOC_NOPM, SPRD_CODEC_LEFT, 0, NULL, + 0, + adc_switch_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("ADCR PGA", SND_SOC_NOPM, SPRD_CODEC_RIGHT, 0, NULL, + 0, + adc_switch_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_S("ADCL Mute", 3, FUN_REG(SPRD_CODEC_PGA_ADCL), 0, 0, + pga_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_S("ADCR Mute", 3, FUN_REG(SPRD_CODEC_PGA_ADCR), 0, 0, + pga_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("ADC", "Main-Capture-Vaudio", + FUN_REG(SPRD_CODEC_CAPTRUE), + 0, 0, + chan_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_PGA_S("MIC Boost", 3, FUN_REG(SPRD_CODEC_PGA_MIC), 0, 0, + pga_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_S("AUXMIC Boost", 3, FUN_REG(SPRD_CODEC_PGA_AUXMIC), 0, + 0, + pga_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_S("HEADMIC Boost", 3, FUN_REG(SPRD_CODEC_PGA_HEADMIC), + 0, 0, + pga_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_S("AIL Boost", 3, FUN_REG(SPRD_CODEC_PGA_AIL), 0, 0, + pga_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_S("AIR Boost", 3, FUN_REG(SPRD_CODEC_PGA_AIR), 0, 0, + pga_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_MICBIAS_E("Mic Bias", FUN_REG(SPRD_CODEC_MIC_BIAS), 0, 0, + mic_bias_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MICBIAS_E("AuxMic Bias", FUN_REG(SPRD_CODEC_AUXMIC_BIAS), + 0, 0, + mic_bias_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MICBIAS_E("HeadMic Bias", FUN_REG(SPRD_CODEC_HEADMIC_BIAS), + 0, 0, + mic_bias_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_S("Digital ADC1L Switch", 5, SOC_REG(AUD_TOP_CTL), + ADC1_EN_L, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("Digital ADC1R Switch", 5, SOC_REG(AUD_TOP_CTL), + ADC1_EN_R, 0, NULL, 0), + SND_SOC_DAPM_ADC_E("ADC1", "Ext-Capture-Vaudio", + FUN_REG(SPRD_CODEC_CAPTRUE1), + 0, 0, + chan_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + /*add DMIC */ + SND_SOC_DAPM_PGA_S("Digital ADC DMIC In", 4, SOC_REG(AUD_TOP_CTL), + ADC_DMIC_SEL, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("Digital ADC1 DMIC In", 4, SOC_REG(AUD_TOP_CTL), + ADC1_DMIC1_SEL, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("DMIC Switch", 3, SOC_REG(AUD_DMIC_CTL), + ADC_DMIC_EN, 0, + NULL, 0), + SND_SOC_DAPM_PGA_S("DMIC1 Switch", 3, SOC_REG(AUD_DMIC_CTL), + ADC1_DMIC1_EN, 0, + NULL, 0), + + SND_SOC_DAPM_PGA_S("HP PA Switch", SPRD_CODEC_HP_PA_ORDER, SND_SOC_NOPM, + 0, 0, hp_pa_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_S("HP PA POST Switch", SPRD_CODEC_HP_PA_POST_ORDER, + SND_SOC_NOPM, 0, 0, hp_pa_post_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_S("Spk PA Switch", SPRD_CODEC_PA_ORDER, SND_SOC_NOPM, + 0, 0, + spk_pa_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_INPUT("DMIC"), + SND_SOC_DAPM_INPUT("DMIC1"), + SND_SOC_DAPM_OUTPUT("HEAD_P_L"), + SND_SOC_DAPM_OUTPUT("HEAD_P_R"), + SND_SOC_DAPM_OUTPUT("AOL"), + SND_SOC_DAPM_OUTPUT("AOR"), + SND_SOC_DAPM_OUTPUT("EAR"), + + SND_SOC_DAPM_OUTPUT("HP PA"), + SND_SOC_DAPM_OUTPUT("Spk PA"), + SND_SOC_DAPM_OUTPUT("Spk2 PA"), + + SND_SOC_DAPM_INPUT("MIC"), + SND_SOC_DAPM_INPUT("AUXMIC"), + SND_SOC_DAPM_INPUT("HPMIC"), + SND_SOC_DAPM_INPUT("AIL"), + SND_SOC_DAPM_INPUT("AIR"), +}; + +/* sprd_codec supported interconnection*/ +static const struct snd_soc_dapm_route sprd_codec_intercon[] = { + /* Power */ + {"Analog Power", NULL, "VCOM_BUF"}, + {"Analog Power", NULL, "VBO"}, + {"DA Clk", NULL, "Analog Power"}, + {"DA Clk", NULL, "Digital Power"}, + {"DAC", NULL, "DA Clk"}, + {"DFM-OUT", NULL, "DA Clk"}, + + {"AD IBUF", NULL, "Analog Power"}, + {"AD Clk", NULL, "Digital Power"}, + {"AD Clk", NULL, "AD IBUF"}, + {"ADC", NULL, "AD Clk"}, + + {"ADC1", NULL, "AD Clk"}, + + {"ADCL PGA", NULL, "AD IBUF"}, + {"ADCR PGA", NULL, "AD IBUF"}, + + {"HP POP", NULL, "DRV Clk"}, + {"HPL Switch", NULL, "DRV Clk"}, + {"HPR Switch", NULL, "DRV Clk"}, + {"SPKL Switch", NULL, "DRV Clk"}, + {"SPKR Switch", NULL, "DRV Clk"}, + {"EAR Switch", NULL, "DRV Clk"}, + + /* Playback */ + {"Digital DACL Switch", NULL, "DFM-OUT"}, + {"Digital DACR Switch", NULL, "DFM-OUT"}, + {"Digital DACL Switch", NULL, "DAC"}, + {"Digital DACR Switch", NULL, "DAC"}, + {"ADie Digital DACL Switch", NULL, "Digital DACL Switch"}, + {"ADie Digital DACR Switch", NULL, "Digital DACR Switch"}, + {"DACL Mute", NULL, "ADie Digital DACL Switch"}, + {"DACR Mute", NULL, "ADie Digital DACR Switch"}, + {"DACL Switch", NULL, "DACL Mute"}, + {"DACR Switch", NULL, "DACR Mute"}, + + /* Output */ + {"HPL Mixer", "DACLHPL Switch", "DACL Switch"}, + {"HPL Mixer", "DACRHPL Switch", "DACR Switch"}, + {"HPR Mixer", "DACLHPR Switch", "DACL Switch"}, + {"HPR Mixer", "DACRHPR Switch", "DACR Switch"}, + + {"ADCLHPL Loop Switch", NULL, "ADCL PGA"}, + {"ADCLHPR Loop Switch", NULL, "ADCL PGA"}, + {"ADCRHPL Loop Switch", NULL, "ADCR PGA"}, + {"ADCRHPR Loop Switch", NULL, "ADCR PGA"}, + + {"HPL Mixer", "ADCLHPL Switch", "ADCLHPL Loop Switch"}, + {"HPL Mixer", "ADCRHPL Switch", "ADCRHPL Loop Switch"}, + {"HPR Mixer", "ADCLHPR Switch", "ADCLHPR Loop Switch"}, + {"HPR Mixer", "ADCRHPR Switch", "ADCRHPR Loop Switch"}, + +#ifdef CONFIG_SND_SOC_SPRD_CODEC_NO_HP_POP + {"HEAD_P_L", NULL, "HP POP"}, + {"HEAD_P_R", NULL, "HP POP"}, + {"HP POP", NULL, "HPL Switch"}, + {"HP POP", NULL, "HPR Switch"}, +#else + {"HPL Switch", NULL, "HP POP"}, + {"HPR Switch", NULL, "HP POP"}, +#endif + {"HPL Switch", NULL, "HPL Mixer"}, + {"HPR Switch", NULL, "HPR Mixer"}, + {"HPL Mute", NULL, "HPL Switch"}, + {"HPR Mute", NULL, "HPR Switch"}, + {"HEAD_P_L", NULL, "HPL Mute"}, + {"HEAD_P_R", NULL, "HPR Mute"}, + + {"HP PA", NULL, "HP PA POST Switch"}, + {"HP PA POST Switch", NULL, "HP PA Switch"}, + {"HP PA Switch", NULL, "HPL Mute"}, + {"HP PA Switch", NULL, "HPR Mute"}, + + {"SPKL Mixer", "DACLSPKL Switch", "DACL Switch"}, + {"SPKL Mixer", "DACRSPKL Switch", "DACR Switch"}, + {"SPKR Mixer", "DACLSPKR Switch", "DACL Switch"}, + {"SPKR Mixer", "DACRSPKR Switch", "DACR Switch"}, + + {"ADCLSPKL Loop Switch", NULL, "ADCL PGA"}, + {"ADCLSPKR Loop Switch", NULL, "ADCL PGA"}, + {"ADCRSPKL Loop Switch", NULL, "ADCR PGA"}, + {"ADCRSPKR Loop Switch", NULL, "ADCR PGA"}, + + {"SPKL Mixer", "ADCLSPKL Switch", "ADCLSPKL Loop Switch"}, + {"SPKL Mixer", "ADCRSPKL Switch", "ADCRSPKL Loop Switch"}, + {"SPKR Mixer", "ADCLSPKR Switch", "ADCLSPKR Loop Switch"}, + {"SPKR Mixer", "ADCRSPKR Switch", "ADCRSPKR Loop Switch"}, + + {"SPKL Switch", NULL, "SPKL Mixer"}, + {"SPKR Switch", NULL, "SPKR Mixer"}, + {"SPKL Mute", NULL, "SPKL Switch"}, + {"SPKR Mute", NULL, "SPKR Switch"}, + {"AOL", NULL, "SPKL Mute"}, + {"AOR", NULL, "SPKR Mute"}, + + {"Spk PA", NULL, "Spk PA Switch"}, + {"Spk PA Switch", NULL, "SPKL Mute"}, + + {"EAR Mixer", NULL, "DACL Switch"}, + {"EAR Switch", NULL, "EAR Mixer"}, + {"EAR Mute", NULL, "EAR Switch"}, + {"EAR", NULL, "EAR Mute"}, + + {"ADCL Mute", NULL, "ADCL Mixer"}, + {"ADCR Mute", NULL, "ADCR Mixer"}, + {"ADCL PGA", NULL, "ADCL Mute"}, + {"ADCR PGA", NULL, "ADCR Mute"}, + /* LineIN */ + {"ADCL Mixer", "AILADCL Switch", "AIL Boost"}, + {"ADCR Mixer", "AILADCR Switch", "AIL Boost"}, + {"ADCL Mixer", "AIRADCL Switch", "AIR Boost"}, + {"ADCR Mixer", "AIRADCR Switch", "AIR Boost"}, + {"AIL Boost", NULL, "AIL"}, + {"AIR Boost", NULL, "AIR"}, + /* Input */ + {"ADCL Mixer", "MainMICADCL Switch", "MIC Boost"}, + {"ADCR Mixer", "MainMICADCR Switch", "MIC Boost"}, + {"MIC Boost", NULL, "Mic Bias"}, + {"ADCL Mixer", "AuxMICADCL Switch", "AUXMIC Boost"}, + {"ADCR Mixer", "AuxMICADCR Switch", "AUXMIC Boost"}, + {"AUXMIC Boost", NULL, "AuxMic Bias"}, + {"ADCL Mixer", "HPMICADCL Switch", "HEADMIC Boost"}, + {"ADCR Mixer", "HPMICADCR Switch", "HEADMIC Boost"}, + {"HEADMIC Boost", NULL, "HeadMic Bias"}, + /* Captrue */ + {"ADCL Switch", NULL, "ADCL PGA"}, + {"ADCR Switch", NULL, "ADCR PGA"}, + {"ADie Digital ADCL Switch", NULL, "ADCL Switch"}, + {"ADie Digital ADCR Switch", NULL, "ADCR Switch"}, + {"Digital ADCL Switch", NULL, "ADie Digital ADCL Switch"}, + {"Digital ADCR Switch", NULL, "ADie Digital ADCR Switch"}, + {"ADC", NULL, "Digital ADCL Switch"}, + {"ADC", NULL, "Digital ADCR Switch"}, + + {"Digital ADC1L Switch", NULL, "ADie Digital ADCL Switch"}, + {"Digital ADC1R Switch", NULL, "ADie Digital ADCR Switch"}, + {"ADC1", NULL, "Digital ADC1L Switch"}, + {"ADC1", NULL, "Digital ADC1R Switch"}, + + {"Mic Bias", NULL, "MIC"}, + {"AuxMic Bias", NULL, "AUXMIC"}, + {"HeadMic Bias", NULL, "HPMIC"}, + + /* DMIC0 */ + {"DMIC Switch", NULL, "DMIC"}, + {"Digital ADC DMIC In", NULL, "DMIC Switch"}, + {"Digital ADCL Switch", NULL, "Digital ADC DMIC In"}, + {"Digital ADCR Switch", NULL, "Digital ADC DMIC In"}, + /* DMIC1 */ + {"DMIC1 Switch", NULL, "DMIC1"}, + {"Digital ADC1 DMIC In", NULL, "DMIC1 Switch"}, + {"Digital ADC1L Switch", NULL, "Digital ADC1 DMIC In"}, + {"Digital ADC1R Switch", NULL, "Digital ADC1 DMIC In"}, + + /* Bias independent */ + {"Mic Bias", NULL, "Analog Power"}, + {"AuxMic Bias", NULL, "Analog Power"}, + {"HeadMic Bias", NULL, "Analog Power"}, +}; + +static int sprd_codec_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); + unsigned int id = GET_PGA_ID(FUN_REG(mc->reg)); + int max = mc->max; + unsigned int mask = (1 << fls(max)) - 1; + unsigned int invert = mc->invert; + unsigned int val; + struct sprd_codec_pga_op *pga = &(sprd_codec->pga[id]); + int ret = 0; + + sp_asoc_pr_info("Set PGA[%s] to %ld\n", sprd_codec_pga_debug_str[id], + ucontrol->value.integer.value[0]); + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = max - val; + pga->pgaval = val; + if (pga->set) { + ret = pga->set(codec, pga->pgaval); + } + return ret; +} + +static int sprd_codec_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); + unsigned int id = GET_PGA_ID(FUN_REG(mc->reg)); + int max = mc->max; + unsigned int invert = mc->invert; + struct sprd_codec_pga_op *pga = &(sprd_codec->pga[id]); + + ucontrol->value.integer.value[0] = pga->pgaval; + if (invert) { + ucontrol->value.integer.value[0] = + max - ucontrol->value.integer.value[0]; + } + + return 0; +} + +static int sprd_codec_inter_pa_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); + int max = mc->max; + unsigned int mask = (1 << fls(max)) - 1; + unsigned int invert = mc->invert; + unsigned int val; + int ret = 0; + + sp_asoc_pr_info("Config inter PA 0x%08x\n", + (int)ucontrol->value.integer.value[0]); + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = max - val; + mutex_lock(&sprd_codec->inter_pa_mutex); + sprd_codec->inter_pa.value = (u32) val; + if (sprd_codec->inter_pa.set) { + mutex_unlock(&sprd_codec->inter_pa_mutex); + sprd_inter_speaker_pa(codec, 1); + } else { + mutex_unlock(&sprd_codec->inter_pa_mutex); + } + return ret; +} + +static int sprd_codec_inter_pa_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); + int max = mc->max; + unsigned int invert = mc->invert; + + mutex_lock(&sprd_codec->inter_pa_mutex); + ucontrol->value.integer.value[0] = sprd_codec->inter_pa.value; + mutex_unlock(&sprd_codec->inter_pa_mutex); + if (invert) { + ucontrol->value.integer.value[0] = + max - ucontrol->value.integer.value[0]; + } + + return 0; +} + +static int sprd_codec_inter_hp_pa_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); + int max = mc->max; + unsigned int mask = (1 << fls(max)) - 1; + unsigned int invert = mc->invert; + unsigned int val; + int ret = 0; + struct sprd_codec_inter_hp_pa *p_setting = + &sprd_codec->inter_hp_pa.setting; + + sp_asoc_pr_info("Config inter HP PA 0x%08x\n", + (int)ucontrol->value.integer.value[0]); + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = max - val; + mutex_lock(&sprd_codec->inter_hp_pa_mutex); + sprd_codec->inter_hp_pa.value = (u32) val; + if (sprd_codec->inter_hp_pa.set) { + mutex_unlock(&sprd_codec->inter_hp_pa_mutex); + sprd_codec_hp_pa_lpw(codec, p_setting->class_g_low_power); + sprd_codec_hp_pa_mode(codec, p_setting->class_g_mode); + sprd_codec_hp_pa_osc(codec, p_setting->class_g_osc); + sprd_codec_hp_pa_cgcal_en(codec, p_setting->class_g_cgcal); + } else { + mutex_unlock(&sprd_codec->inter_hp_pa_mutex); + } + return ret; +} + +static int sprd_codec_inter_hp_pa_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); + int max = mc->max; + unsigned int invert = mc->invert; + + mutex_lock(&sprd_codec->inter_hp_pa_mutex); + ucontrol->value.integer.value[0] = sprd_codec->inter_hp_pa.value; + mutex_unlock(&sprd_codec->inter_hp_pa_mutex); + if (invert) { + ucontrol->value.integer.value[0] = + max - ucontrol->value.integer.value[0]; + } + + return 0; +} + +static int sprd_codec_inter_hp_pa_delay_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); + int max = mc->max; + unsigned int mask = (1 << fls(max)) - 1; + unsigned int invert = mc->invert; + unsigned int val; + int ret = 0; + struct sprd_codec_inter_hp_pa *p_setting = + &sprd_codec->inter_hp_pa.setting; + + sp_asoc_pr_info("Config inter HP PA DELAY 0x%08x\n", + (int)ucontrol->value.integer.value[0]); + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = max - val; + mutex_lock(&sprd_codec->inter_hp_pa_delay_mutex); + sprd_codec->inter_hp_pa.delay_value = (u32) val; + sprd_codec->class_g_delay1 = sprd_codec->inter_hp_pa.delay_setting.class_g_delay1; + sprd_codec->class_g_delay2 = sprd_codec->inter_hp_pa.delay_setting.class_g_delay2; + mutex_unlock(&sprd_codec->inter_hp_pa_delay_mutex); + return ret; +} + +static int sprd_codec_inter_hp_pa_delay_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); + int max = mc->max; + unsigned int invert = mc->invert; + + mutex_lock(&sprd_codec->inter_hp_pa_delay_mutex); + ucontrol->value.integer.value[0] = sprd_codec->inter_hp_pa.delay_value; + mutex_unlock(&sprd_codec->inter_hp_pa_delay_mutex); + if (invert) { + ucontrol->value.integer.value[0] = + max - ucontrol->value.integer.value[0]; + } + + return 0; +} + +static int sprd_codec_mic_bias_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); + unsigned int reg = FUN_REG(mc->reg); + int max = mc->max; + unsigned int mask = (1 << fls(max)) - 1; + unsigned int invert = mc->invert; + unsigned int val; + int ret = 0; + int id = GET_MIC_BIAS_ID(reg); + + val = (ucontrol->value.integer.value[0] & mask); + + if (invert) + val = max - val; + if (sprd_codec->mic_bias[id] == val) { + return 0; + } + + sp_asoc_pr_info("%s Switch %s\n", mic_bias_name[id], STR_ON_OFF(val)); + + sprd_codec->mic_bias[id] = val; + if (val) { + ret = + snd_soc_dapm_force_enable_pin(&codec->card->dapm, + mic_bias_name[id]); + } else { + ret = + snd_soc_dapm_disable_pin(&codec->card->dapm, + mic_bias_name[id]); + } + + /* signal a DAPM event */ + snd_soc_dapm_sync(&codec->card->dapm); + + return ret; +} + +static int sprd_codec_mic_bias_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); + unsigned int reg = FUN_REG(mc->reg); + int max = mc->max; + unsigned int invert = mc->invert; + int id = GET_MIC_BIAS_ID(reg); + + ucontrol->value.integer.value[0] = sprd_codec->mic_bias[id]; + if (invert) { + ucontrol->value.integer.value[0] = + max - ucontrol->value.integer.value[0]; + } + + return 0; +} + +static const DECLARE_TLV_DB_SCALE(adc_tlv, -600, 75, 0); +static const DECLARE_TLV_DB_SCALE(hp_tlv, -3600, 300, 1); +static const DECLARE_TLV_DB_SCALE(ear_tlv, -3600, 300, 1); +static const DECLARE_TLV_DB_SCALE(spk_tlv, -2400, 300, 1); +static const DECLARE_TLV_DB_SCALE(dac_tlv, -350, 50, 1); +static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 600, 0); +static const DECLARE_TLV_DB_SCALE(auxmic_tlv, 0, 600, 0); +static const DECLARE_TLV_DB_SCALE(headmic_tlv, 0, 600, 0); +static const DECLARE_TLV_DB_SCALE(ailr_tlv, 0, 600, 0); + +#define SPRD_CODEC_PGA(xname, xreg, tlv_array) \ + SOC_SINGLE_EXT_TLV(xname, FUN_REG(xreg), 0, 15, 0, \ + sprd_codec_vol_get, sprd_codec_vol_put, tlv_array) + +#define SPRD_CODEC_PGA_MAX(xname, xreg, max, tlv_array) \ + SOC_SINGLE_EXT_TLV(xname, FUN_REG(xreg), 0, max, 0, \ + sprd_codec_vol_get, sprd_codec_vol_put, tlv_array) + +#define SPRD_CODEC_MIC_BIAS(xname, xreg) \ + SOC_SINGLE_EXT(xname, FUN_REG(xreg), 0, 1, 0, \ + sprd_codec_mic_bias_get, sprd_codec_mic_bias_put) + +static const struct snd_kcontrol_new sprd_codec_snd_controls[] = { + SPRD_CODEC_PGA("SPKL Playback Volume", SPRD_CODEC_PGA_SPKL, spk_tlv), + SPRD_CODEC_PGA("SPKR Playback Volume", SPRD_CODEC_PGA_SPKR, spk_tlv), + SPRD_CODEC_PGA("HPL Playback Volume", SPRD_CODEC_PGA_HPL, hp_tlv), + SPRD_CODEC_PGA("HPR Playback Volume", SPRD_CODEC_PGA_HPR, hp_tlv), + SPRD_CODEC_PGA("EAR Playback Volume", SPRD_CODEC_PGA_EAR, ear_tlv), + + SPRD_CODEC_PGA_MAX("ADCL Capture Volume", SPRD_CODEC_PGA_ADCL, 63, + adc_tlv), + SPRD_CODEC_PGA_MAX("ADCR Capture Volume", SPRD_CODEC_PGA_ADCR, 63, + adc_tlv), + + SPRD_CODEC_PGA_MAX("DACL Playback Volume", SPRD_CODEC_PGA_DACL, 7, + dac_tlv), + SPRD_CODEC_PGA_MAX("DACR Playback Volume", SPRD_CODEC_PGA_DACR, 7, + dac_tlv), + SPRD_CODEC_PGA_MAX("MIC Boost", SPRD_CODEC_PGA_MIC, 3, mic_tlv), + SPRD_CODEC_PGA_MAX("AUXMIC Boost", SPRD_CODEC_PGA_AUXMIC, 3, + auxmic_tlv), + SPRD_CODEC_PGA_MAX("HEADMIC Boost", SPRD_CODEC_PGA_HEADMIC, 3, + headmic_tlv), + SPRD_CODEC_PGA_MAX("Linein Boost", SPRD_CODEC_PGA_AIL, 3, ailr_tlv), + + SOC_SINGLE_EXT("Inter PA Config", 0, 0, LONG_MAX, 0, + sprd_codec_inter_pa_get, sprd_codec_inter_pa_put), + + SOC_SINGLE_EXT("Inter HP PA Config", 0, 0, LONG_MAX, 0, + sprd_codec_inter_hp_pa_get, sprd_codec_inter_hp_pa_put), + + SOC_SINGLE_EXT("Inter HP PA Delay", 0, 0, LONG_MAX, 0, + sprd_codec_inter_hp_pa_delay_get, sprd_codec_inter_hp_pa_delay_put), + + SPRD_CODEC_MIC_BIAS("MIC Bias Switch", SPRD_CODEC_MIC_BIAS), + + SPRD_CODEC_MIC_BIAS("AUXMIC Bias Switch", SPRD_CODEC_AUXMIC_BIAS), + + SPRD_CODEC_MIC_BIAS("HEADMIC Bias Switch", SPRD_CODEC_HEADMIC_BIAS), +}; + +static unsigned int sprd_codec_read(struct snd_soc_codec *codec, + unsigned int reg) +{ + /* Because snd_soc_update_bits reg is 16 bits short type, + so muse do following convert + */ + if (IS_SPRD_CODEC_AP_RANG(reg | SPRD_CODEC_AP_BASE_HI)) { + reg |= SPRD_CODEC_AP_BASE_HI; + return arch_audio_codec_read(reg); + } else if (IS_SPRD_CODEC_DP_RANG(reg | SPRD_CODEC_DP_BASE_HI)) { + reg |= SPRD_CODEC_DP_BASE_HI; + return __raw_readl((void __iomem *)(reg - CODEC_DP_BASE + sprd_codec_dp_base)); + } else if (IS_SPRD_CODEC_MIXER_RANG(FUN_REG(reg))) { + struct sprd_codec_priv *sprd_codec = + snd_soc_codec_get_drvdata(codec); + int id = GET_MIXER_ID(FUN_REG(reg)); + struct sprd_codec_mixer *mixer = &(sprd_codec->mixer[id]); + return mixer->on; + } else if (IS_SPRD_CODEC_PGA_RANG(FUN_REG(reg))) { + } else if (IS_SPRD_CODEC_MIC_BIAS_RANG(FUN_REG(reg))) { + } else + sp_asoc_pr_dbg("read the register is not codec's reg = 0x%x\n", + reg); + return 0; +} + +static int sprd_codec_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int val) +{ + int ret = 0; + if (IS_SPRD_CODEC_AP_RANG(reg | SPRD_CODEC_AP_BASE_HI)) { + reg |= SPRD_CODEC_AP_BASE_HI; + ret = arch_audio_codec_write(reg, val); + sp_asoc_pr_reg("A[0x%04x] W:[0x%08x] R:[0x%08x]\n", + (reg - CODEC_AP_BASE) & 0xFFFF, val, arch_audio_codec_read(reg)); + return ret; + } else if (IS_SPRD_CODEC_DP_RANG(reg | SPRD_CODEC_DP_BASE_HI)) { + reg |= SPRD_CODEC_DP_BASE_HI; + __raw_writel(val, (void __iomem *)(reg - CODEC_DP_BASE + sprd_codec_dp_base)); + sp_asoc_pr_reg("D[0x%04x] W:[0x%08x] R:[0x%08x]\n", + (reg - CODEC_DP_BASE) & 0xFFFF, val, + __raw_readl((void __iomem *)(reg - CODEC_DP_BASE + sprd_codec_dp_base))); + return ret; + } else if (IS_SPRD_CODEC_MIXER_RANG(FUN_REG(reg))) { + struct sprd_codec_priv *sprd_codec = + snd_soc_codec_get_drvdata(codec); + int id = GET_MIXER_ID(FUN_REG(reg)); + struct sprd_codec_mixer *mixer = &(sprd_codec->mixer[id]); + mixer->on = val ? 1 : 0; + } else if (IS_SPRD_CODEC_PGA_RANG(FUN_REG(reg))) { + } else if (IS_SPRD_CODEC_MIC_BIAS_RANG(FUN_REG(reg))) { + } else + sp_asoc_pr_dbg("write the register is not codec's reg = 0x%x\n", + reg); + return ret; +} + +static int sprd_codec_pcm_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 sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); + int mask = 0x0F; + int shift = 0; + int rate = params_rate(params); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + sprd_codec->da_sample_val = rate; + sp_asoc_pr_info("Playback rate is [%d]\n", rate); + sprd_codec_set_sample_rate(codec, rate, mask, shift); + } else { + sp_asoc_pr_info("Capture rate is [%d]\n", rate); + if (dai->id != SPRD_CODEC_IIS1_ID) { + sprd_codec->ad_sample_val = rate; + sprd_codec_set_ad_sample_rate(codec, rate, mask, shift); + } else { + sprd_codec->ad1_sample_val = rate; + sprd_codec_set_ad_sample_rate(codec, rate, + ADC1_SRC_N_MASK, + ADC1_SRC_N); + } + } + + return 0; +} + +static int sprd_codec_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + return 0; +} + +#if 0 +static void sprd_codec_dac_mute_irq_enable(struct snd_soc_codec *codec) +{ + int mask = BIT(DAC_MUTE_D); + snd_soc_update_bits(codec, SOC_REG(AUD_INT_CLR), mask, mask); + snd_soc_update_bits(codec, SOC_REG(AUD_INT_EN), mask, mask); +} +#endif + +static irqreturn_t sprd_codec_dp_irq(int irq, void *dev_id) +{ + int mask; + struct sprd_codec_priv *sprd_codec = dev_id; + struct snd_soc_codec *codec = sprd_codec->codec; + mask = snd_soc_read(codec, AUD_AUD_STS0); + sp_asoc_pr_dbg("dac mute irq mask = 0x%x\n", mask); + if (BIT(DAC_MUTE_D_MASK) & mask) { + mask = BIT(DAC_MUTE_D); + complete(&sprd_codec->completion_dac_mute); + } + if (BIT(DAC_MUTE_U_MASK) & mask) { + mask = BIT(DAC_MUTE_U); + } + snd_soc_update_bits(codec, SOC_REG(AUD_INT_EN), mask, 0); + return IRQ_HANDLED; +} + +#if 0 +static int sprd_codec_digital_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + sp_asoc_pr_dbg("%s\n", __func__); + if (dai->id == SPRD_CODEC_IIS1_ID) /*codec_dai_1 has no playback */ + return 0; + + if (atomic_read(&sprd_codec->ldo_refcount) >= 1) { + sp_asoc_pr_dbg("mute %i\n", mute); + + ret = + snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL), + BIT(DAC_MUTE_START), + mute ? BIT(DAC_MUTE_START) : 0); +#if 0 + if (mute && ret) { + int dac_mute_complete; + struct sprd_codec_priv *sprd_codec = + snd_soc_codec_get_drvdata(codec); + sp_asoc_pr_dbg("Dac Mute IRQ enable\n"); + sprd_codec_dac_mute_irq_enable(codec); + init_completion(&sprd_codec->completion_dac_mute); + dac_mute_complete = + wait_for_completion_timeout + (&sprd_codec->completion_dac_mute, + msecs_to_jiffies(SPRD_CODEC_DAC_MUTE_TIMEOUT)); + sp_asoc_pr_dbg("Dac Mute Completion %d\n", + dac_mute_complete); + if (!dac_mute_complete) { + pr_err("Dac Mute Timeout\n"); + } + } +#endif + + } + + return ret; +} +#endif + +static struct snd_soc_dai_ops sprd_codec_dai_ops = { + .hw_params = sprd_codec_pcm_hw_params, + .hw_free = sprd_codec_pcm_hw_free, +}; + +#ifdef CONFIG_PM +static int sprd_codec_soc_suspend(struct snd_soc_codec *codec) +{ + return 0; +} + +static int sprd_codec_soc_resume(struct snd_soc_codec *codec) +{ + return 0; +} +#else +#define sprd_codec_soc_suspend NULL +#define sprd_codec_soc_resume NULL +#endif + +/* + * proc interface + */ + +#ifdef CONFIG_PROC_FS +static void sprd_codec_proc_read(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct sprd_codec_priv *sprd_codec = entry->private_data; + struct snd_soc_codec *codec = sprd_codec->codec; + int reg; + + snd_iprintf(buffer, "%s digital part\n", codec->name); + for (reg = SPRD_CODEC_DP_BASE; reg < SPRD_CODEC_DP_END; reg += 0x10) { + snd_iprintf(buffer, "0x%04x | 0x%04x 0x%04x 0x%04x 0x%04x\n", + (unsigned int)(reg - SPRD_CODEC_DP_BASE) + , snd_soc_read(codec, reg + 0x00) + , snd_soc_read(codec, reg + 0x04) + , snd_soc_read(codec, reg + 0x08) + , snd_soc_read(codec, reg + 0x0C) + ); + } + snd_iprintf(buffer, "%s analog part\n", codec->name); + for (reg = SPRD_CODEC_AP_BASE; reg < SPRD_CODEC_AP_END; reg += 0x10) { + snd_iprintf(buffer, "0x%04x | 0x%04x 0x%04x 0x%04x 0x%04x\n", + (unsigned int)(reg - SPRD_CODEC_AP_BASE) + , snd_soc_read(codec, reg + 0x00) + , snd_soc_read(codec, reg + 0x04) + , snd_soc_read(codec, reg + 0x08) + , snd_soc_read(codec, reg + 0x0C) + ); + } +} + +static void sprd_codec_proc_init(struct sprd_codec_priv *sprd_codec) +{ + struct snd_info_entry *entry; + struct snd_soc_codec *codec = sprd_codec->codec; + + if (!snd_card_proc_new(codec->card->snd_card, "sprd-codec", &entry)) + snd_info_set_text_ops(entry, sprd_codec, sprd_codec_proc_read); +} +#else /* !CONFIG_PROC_FS */ +static inline void sprd_codec_proc_init(struct sprd_codec_priv *sprd_codec) +{ +} +#endif + +static void sprd_codec_power_changed(struct power_supply *psy) +{ + struct sprd_codec_priv *sprd_codec = 0; + sp_asoc_pr_dbg("%s %s\n", __func__, psy->name); +#if 1 + sprd_codec = container_of(psy, struct sprd_codec_priv, audio_ldo); + mutex_lock(&sprd_codec->inter_pa_mutex); + if (sprd_codec->inter_pa.set && sprd_codec->inter_pa.setting.is_LDO_mode + && sprd_codec->inter_pa.setting.is_auto_LDO_mode) { + sprd_codec_pa_ldo_auto(sprd_codec->codec, 0); + } + mutex_unlock(&sprd_codec->inter_pa_mutex); + if (atomic_read(&sprd_codec->ldo_refcount) >= 1) { + sprd_codec_vcom_ldo_auto(sprd_codec->codec, 0); + } +#endif +} + +static int sprd_codec_power_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + return -EINVAL; +} + +static int sprd_codec_audio_ldo(struct sprd_codec_priv *sprd_codec) +{ + int ret = 0; + struct snd_soc_codec *codec = sprd_codec->codec; + sprd_codec->audio_ldo.name = "audio-ldo"; + sprd_codec->audio_ldo.get_property = sprd_codec_power_get_property; + sprd_codec->audio_ldo.external_power_changed = sprd_codec_power_changed; + ret = power_supply_register(codec->dev, &sprd_codec->audio_ldo); + if (ret) { + pr_err("ERR:Register power supply error!\n"); + return -EFAULT; + } + return ret; +} + +#define SPRD_CODEC_PCM_RATES \ + (SNDRV_PCM_RATE_8000 | \ + SNDRV_PCM_RATE_11025 | \ + SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_22050 | \ + SNDRV_PCM_RATE_32000 | \ + SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_96000) + +#define SPRD_CODEC_PCM_AD_RATES \ + (SNDRV_PCM_RATE_8000 | \ + SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_32000 | \ + SRC_SUPPORT_RATE | \ + SNDRV_PCM_RATE_48000) + +/* PCM Playing and Recording default in full duplex mode */ +static struct snd_soc_dai_driver sprd_codec_dai[] = { + { + .name = "sprd-codec-v3-i2s", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SPRD_CODEC_PCM_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "Main-Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SPRD_CODEC_PCM_AD_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &sprd_codec_dai_ops, + }, +#ifdef CONFIG_SND_SOC_SPRD_VAUDIO + { + .name = "sprd-codec-v3-vaudio", + .playback = { + .stream_name = "Playback-Vaudio", + .channels_min = 1, + .channels_max = 2, + .rates = SPRD_CODEC_PCM_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "Main-Capture-Vaudio", + .channels_min = 1, + .channels_max = 2, + .rates = SPRD_CODEC_PCM_AD_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &sprd_codec_dai_ops, + }, +#endif + /*digital ad1 */ + { + .id = SPRD_CODEC_IIS1_ID, + .name = "codec-i2s-ext", + .capture = { + .stream_name = "Ext-Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SPRD_CODEC_PCM_AD_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &sprd_codec_dai_ops, + }, +#ifdef CONFIG_SND_SOC_SPRD_VAUDIO + /*digital ad1 */ + { + .id = SPRD_CODEC_IIS1_ID, + .name = "codec-vaudio-ext", + .capture = { + .stream_name = "Ext-Capture-Vaudio", + .channels_min = 1, + .channels_max = 2, + .rates = SPRD_CODEC_PCM_AD_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &sprd_codec_dai_ops, + }, +#endif + { + .name = "sprd-codec-v3-fm", + .playback = { + .stream_name = "DFM-Output", + .channels_min = 1, + .channels_max = 2, + .rates = SPRD_CODEC_PCM_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &sprd_codec_dai_ops, + }, +}; + +static int sprd_codec_soc_probe(struct snd_soc_codec *codec) +{ + struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); + int ret = 0; +#ifndef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13 + int hp_plug_state = get_hp_plug_state(); +#endif + + sp_asoc_pr_dbg("%s\n", __func__); + + codec->dapm.idle_bias_off = 1; + + sprd_codec->codec = codec; + + sprd_codec_proc_init(sprd_codec); + + sprd_codec_audio_ldo(sprd_codec); +#ifdef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13 + sprd_inter_headphone_pa_pre(sprd_codec, 1); +#else + sprd_inter_headphone_pa_pre(sprd_codec, hp_plug_state); +#endif + return ret; +} + +/* power down chip */ +static int sprd_codec_soc_remove(struct snd_soc_codec *codec) +{ + return 0; +} + +static int sprd_codec_power_regulator_init(struct sprd_codec_priv *sprd_codec, + struct device *dev) +{ + sprd_codec_power_get(dev, &sprd_codec->main_mic, "MICBIAS"); + sprd_codec_power_get(dev, &sprd_codec->aux_mic, "AUXMICBIAS"); + sprd_codec_power_get(dev, &sprd_codec->head_mic, "HEADMICBIAS"); + + return 0; +} + +static void sprd_codec_power_regulator_exit(struct sprd_codec_priv *sprd_codec) +{ + sprd_codec_power_put(&sprd_codec->main_mic); + sprd_codec_power_put(&sprd_codec->aux_mic); + sprd_codec_power_put(&sprd_codec->head_mic); +} + +static struct snd_soc_codec_driver soc_codec_dev_sprd_codec = { + .probe = sprd_codec_soc_probe, + .remove = sprd_codec_soc_remove, + .suspend = sprd_codec_soc_suspend, + .resume = sprd_codec_soc_resume, + .read = sprd_codec_read, + .write = sprd_codec_write, + .reg_word_size = sizeof(u16), + .reg_cache_step = 2, + .dapm_widgets = sprd_codec_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sprd_codec_dapm_widgets), + .dapm_routes = sprd_codec_intercon, + .num_dapm_routes = ARRAY_SIZE(sprd_codec_intercon), + .controls = sprd_codec_snd_controls, + .num_controls = ARRAY_SIZE(sprd_codec_snd_controls), +}; + +static int sprd_codec_probe(struct platform_device *pdev) +{ + struct sprd_codec_priv *sprd_codec; + int ret; + struct device_node *node = pdev->dev.of_node; + struct resource *res; + u32 ana_chip_id; + + sp_asoc_pr_dbg("%s\n", __func__); + + arch_audio_codec_switch(AUDIO_TO_ARM_CTRL); + + sprd_codec = + devm_kzalloc(&pdev->dev, sizeof(struct sprd_codec_priv), + GFP_KERNEL); + if (sprd_codec == NULL) + return -ENOMEM; + + sprd_codec->da_sample_val = 44100; /*inital value for FM route */ + + if (node) { + u32 val; + u32 val_arr[2]; + if (!of_property_read_u32(node, "sprd,hp_pa_ver", &val)) { + if (val < SPRD_CODEC_HP_PA_VER_MAX) { + sprd_codec->hp_ver = val; + sp_asoc_pr_dbg("Set HP PA Ver is %d!\n", val); + } else { + pr_err + ("ERR:This driver just support less %d version!\n", + SPRD_CODEC_HP_PA_VER_MAX); + return -EINVAL; + } + } + if (!of_property_read_u32(node, "sprd,dp_irq", &val)) { + sprd_codec->dp_irq = val; + sp_asoc_pr_dbg("Set DP IRQ is %d!\n", val); + } else { + pr_err("ERR:Must give me the DP IRQ!\n"); + return -EINVAL; + } + if (!of_property_read_u32(node, "sprd,def_da_fs", &val)) { + sprd_codec->da_sample_val = val; + sp_asoc_pr_dbg("Change DA default fs to %d!\n", val); + } + if (!of_property_read_u32_array(node, "sprd,reg_dp", &val_arr[0], 2)) { + sprd_codec_dp_base = ioremap_nocache(val_arr[0], val_arr[1]); + if(!sprd_codec_dp_base) { + pr_err("ERR: cannot create iomap address for codec DP!\n"); + return -EINVAL; + } + } else { + pr_err("ERR:Must give me the codec DP reg address!\n"); + return -EINVAL; + } + } else { + sprd_codec->ap_irq = CODEC_AP_IRQ; + sprd_codec->dp_irq = CODEC_DP_IRQ; + sprd_codec_dp_base = ioremap_nocache(CODEC_DP_BASE_DEFAULT, CODEC_DP_SIZE_DEFAULT); + if(!sprd_codec_dp_base) { + pr_err("ERR: cannot create iomap address for codec DP with default address!\n"); + return -EINVAL; + } + } + if(node) { + sprd_codec->ap_irq = irq_of_parse_and_map(node, 0); + } + ana_chip_id = sci_get_ana_chip_id() >> 16; + if (ana_chip_id == 0x2713) + sprd_codec->hp_ver = SPRD_CODEC_HP_PA_VER_1; + else if (ana_chip_id == 0x2711) + sprd_codec->hp_ver = SPRD_CODEC_HP_PA_VER_2; + else { + pr_info("ana_chip_id is 0x%x\n", (unsigned int)sci_get_ana_chip_id); + } + platform_set_drvdata(pdev, sprd_codec); + + ret = + request_irq(sprd_codec->ap_irq, sprd_codec_ap_irq, 0, + "sprd_codec_ap", sprd_codec); + if (ret) { + pr_err("ERR:Request irq ap failed!\n"); + goto err_irq; + } + + INIT_DELAYED_WORK(&sprd_codec->ovp_delayed_work, + sprd_codec_ovp_delay_worker); + + ret = + request_irq(sprd_codec->dp_irq, sprd_codec_dp_irq, 0, + "sprd_codec_dp", sprd_codec); + if (ret) { + pr_err("ERR:Request irq dp failed!\n"); + goto dp_err_irq; + } + + ret = snd_soc_register_codec(&pdev->dev, + &soc_codec_dev_sprd_codec, + sprd_codec_dai, + ARRAY_SIZE(sprd_codec_dai)); + if (ret != 0) { + pr_err("ERR:Failed to register CODEC: %d\n", ret); + goto reg_err_irq; + } + + sprd_codec_inter_pa_init(sprd_codec); + sprd_codec_inter_hp_pa_init(sprd_codec); + + sprd_codec_power_regulator_init(sprd_codec, &pdev->dev); + + mutex_init(&sprd_codec->inter_pa_mutex); + mutex_init(&sprd_codec->inter_hp_pa_mutex); + mutex_init(&sprd_codec->inter_hp_pa_delay_mutex); + spin_lock_init(&sprd_codec->sprd_codec_fun_lock); + spin_lock_init(&sprd_codec->sprd_codec_pa_sw_lock); + + sprd_codec_vcom_ldo_cfg(sprd_codec, ldo_v_map, ARRAY_SIZE(ldo_v_map)); + sprd_codec_pa_ldo_cfg(sprd_codec, ldo_v_map, ARRAY_SIZE(ldo_v_map)); + + sprd_codec->nb.notifier_call = hp_notifier_handler; +#ifndef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13 + hp_register_notifier(&sprd_codec->nb); +#endif + + return 0; +reg_err_irq: + free_irq(sprd_codec->dp_irq, sprd_codec); +dp_err_irq: + free_irq(sprd_codec->ap_irq, sprd_codec); +err_irq: + iounmap(sprd_codec_dp_base); + return -EINVAL; +} + +static int sprd_codec_remove(struct platform_device *pdev) +{ + struct sprd_codec_priv *sprd_codec = platform_get_drvdata(pdev); + sprd_codec_power_regulator_exit(sprd_codec); + free_irq(sprd_codec->ap_irq, sprd_codec); + free_irq(sprd_codec->dp_irq, sprd_codec); +#ifndef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13 + hp_unregister_notifier(&sprd_codec->nb); +#endif + snd_soc_unregister_codec(&pdev->dev); + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id codec_of_match[] = { + {.compatible = "sprd,sprd-codec-v3",}, + {}, +}; + +MODULE_DEVICE_TABLE(of, codec_of_match); +#endif + +static struct platform_driver sprd_codec_codec_driver = { + .driver = { + .name = "sprd-codec-v3", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(codec_of_match), + }, + .probe = sprd_codec_probe, + .remove = sprd_codec_remove, +}; + +module_platform_driver(sprd_codec_codec_driver); + +MODULE_DESCRIPTION("SPRD-CODEC ALSA SoC codec driver"); +MODULE_AUTHOR("Zhenfang Wang <zhenfang.wang@spreadtrum.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("codec:sprd-codec"); |
