Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/sound/soc/codecs/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 117 kB image not shown  

Quelle  wcd939x.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved.
 * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
 * Copyright (c) 2023, Linaro Limited
 */


#include <linux/module.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/kernel.h>
#include <linux/pm_runtime.h>
#include <linux/component.h>
#include <sound/tlv.h>
#include <linux/of_graph.h>
#include <linux/of.h>
#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <linux/regmap.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <linux/regulator/consumer.h>
#include <linux/usb/typec_mux.h>
#include <linux/usb/typec_altmode.h>

#include "wcd-clsh-v2.h"
#include "wcd-mbhc-v2.h"
#include "wcd939x.h"

#define WCD939X_MAX_MICBIAS  (4)
#define WCD939X_MBHC_MAX_BUTTONS (8)
#define TX_ADC_MAX   (4)
#define WCD_MBHC_HS_V_MAX  1600

#define CHIPID_WCD9390   0x0
#define CHIPID_WCD9395   0x5

/* Version major: 1.x */
#define CHIPID_WCD939X_VER_MAJOR_1 0x0
/* Version minor: x.1 */
#define CHIPID_WCD939X_VER_MINOR_1 0x3

enum {
 WCD939X_VERSION_1_0 = 0,
 WCD939X_VERSION_1_1,
 WCD939X_VERSION_2_0,
};

#define WCD939X_RATES_MASK (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
       SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
       SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000 |\
       SNDRV_PCM_RATE_384000)
/* Fractional Rates */
#define WCD939X_FRAC_RATES_MASK (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\
     SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800)
#define WCD939X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
    SNDRV_PCM_FMTBIT_S24_LE |\
    SNDRV_PCM_FMTBIT_S24_3LE |\
    SNDRV_PCM_FMTBIT_S32_LE)

/* Convert from vout ctl to micbias voltage in mV */
#define WCD_VOUT_CTL_TO_MICB(v)  (1000 + (v) * 50)
#define SWR_CLK_RATE_0P6MHZ  (600000)
#define SWR_CLK_RATE_1P2MHZ  (1200000)
#define SWR_CLK_RATE_2P4MHZ  (2400000)
#define SWR_CLK_RATE_4P8MHZ  (4800000)
#define SWR_CLK_RATE_9P6MHZ  (9600000)
#define SWR_CLK_RATE_11P2896MHZ  (1128960)

#define ADC_MODE_VAL_HIFI  0x01
#define ADC_MODE_VAL_LO_HIF  0x02
#define ADC_MODE_VAL_NORMAL  0x03
#define ADC_MODE_VAL_LP   0x05
#define ADC_MODE_VAL_ULP1  0x09
#define ADC_MODE_VAL_ULP2  0x0B

/* Z value defined in milliohm */
#define WCD939X_ZDET_VAL_32  (32000)
#define WCD939X_ZDET_VAL_400  (400000)
#define WCD939X_ZDET_VAL_1200  (1200000)
#define WCD939X_ZDET_VAL_100K  (100000000)

/* Z floating defined in ohms */
#define WCD939X_ZDET_FLOATING_IMPEDANCE (0x0FFFFFFE)
#define WCD939X_ZDET_NUM_MEASUREMENTS (900)
#define WCD939X_MBHC_GET_C1(c)  (((c) & 0xC000) >> 14)
#define WCD939X_MBHC_GET_X1(x)  ((x) & 0x3FFF)

/* Z value compared in milliOhm */
#define WCD939X_ANA_MBHC_ZDET_CONST (1018 * 1024)

enum {
 /* INTR_CTRL_INT_MASK_0 */
 WCD939X_IRQ_MBHC_BUTTON_PRESS_DET = 0,
 WCD939X_IRQ_MBHC_BUTTON_RELEASE_DET,
 WCD939X_IRQ_MBHC_ELECT_INS_REM_DET,
 WCD939X_IRQ_MBHC_ELECT_INS_REM_LEG_DET,
 WCD939X_IRQ_MBHC_SW_DET,
 WCD939X_IRQ_HPHR_OCP_INT,
 WCD939X_IRQ_HPHR_CNP_INT,
 WCD939X_IRQ_HPHL_OCP_INT,

 /* INTR_CTRL_INT_MASK_1 */
 WCD939X_IRQ_HPHL_CNP_INT,
 WCD939X_IRQ_EAR_CNP_INT,
 WCD939X_IRQ_EAR_SCD_INT,
 WCD939X_IRQ_HPHL_PDM_WD_INT,
 WCD939X_IRQ_HPHR_PDM_WD_INT,
 WCD939X_IRQ_EAR_PDM_WD_INT,

 /* INTR_CTRL_INT_MASK_2 */
 WCD939X_IRQ_MBHC_MOISTURE_INT,
 WCD939X_IRQ_HPHL_SURGE_DET_INT,
 WCD939X_IRQ_HPHR_SURGE_DET_INT,
 WCD939X_NUM_IRQS,
};

enum {
 MICB_BIAS_DISABLE = 0,
 MICB_BIAS_ENABLE,
 MICB_BIAS_PULL_UP,
 MICB_BIAS_PULL_DOWN,
};

enum {
 WCD_ADC1 = 0,
 WCD_ADC2,
 WCD_ADC3,
 WCD_ADC4,
 HPH_PA_DELAY,
};

enum {
 ADC_MODE_INVALID = 0,
 ADC_MODE_HIFI,
 ADC_MODE_LO_HIF,
 ADC_MODE_NORMAL,
 ADC_MODE_LP,
 ADC_MODE_ULP1,
 ADC_MODE_ULP2,
};

enum {
 AIF1_PB = 0,
 AIF1_CAP,
 NUM_CODEC_DAIS,
};

static u8 tx_mode_bit[] = {
 [ADC_MODE_INVALID] = 0x00,
 [ADC_MODE_HIFI] = 0x01,
 [ADC_MODE_LO_HIF] = 0x02,
 [ADC_MODE_NORMAL] = 0x04,
 [ADC_MODE_LP] = 0x08,
 [ADC_MODE_ULP1] = 0x10,
 [ADC_MODE_ULP2] = 0x20,
};

struct zdet_param {
 u16 ldo_ctl;
 u16 noff;
 u16 nshift;
 u16 btn5;
 u16 btn6;
 u16 btn7;
};

struct wcd939x_priv {
 struct sdw_slave *tx_sdw_dev;
 struct wcd939x_sdw_priv *sdw_priv[NUM_CODEC_DAIS];
 struct device *txdev;
 struct device *rxdev;
 struct device_node *rxnode, *txnode;
 struct regmap *regmap;
 struct snd_soc_component *component;
 /* micb setup lock */
 struct mutex micb_lock;
 /* typec handling */
 bool typec_analog_mux;
#if IS_ENABLED(CONFIG_TYPEC)
 enum typec_orientation typec_orientation;
 unsigned long typec_mode;
 struct typec_switch *typec_switch;
#endif /* CONFIG_TYPEC */
 /* mbhc module */
 struct wcd_mbhc *wcd_mbhc;
 struct wcd_mbhc_config mbhc_cfg;
 struct wcd_mbhc_intr intr_ids;
 struct wcd_clsh_ctrl *clsh_info;
 struct irq_domain *virq;
 struct regmap_irq_chip_data *irq_chip;
 struct snd_soc_jack *jack;
 unsigned long status_mask;
 s32 micb_ref[WCD939X_MAX_MICBIAS];
 s32 pullup_ref[WCD939X_MAX_MICBIAS];
 u32 hph_mode;
 u32 tx_mode[TX_ADC_MAX];
 int variant;
 struct gpio_desc *reset_gpio;
 u32 micb1_mv;
 u32 micb2_mv;
 u32 micb3_mv;
 u32 micb4_mv;
 int hphr_pdm_wd_int;
 int hphl_pdm_wd_int;
 int ear_pdm_wd_int;
 bool comp1_enable;
 bool comp2_enable;
 bool ldoh;
};

static const char * const wcd939x_supplies[] = {
 "vdd-rxtx""vdd-io""vdd-buck""vdd-mic-bias""vdd-px",
};

static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(ear_pa_gain, 600, -1800);
static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1);
static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1);

static const struct wcd_mbhc_field wcd_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = {
 WCD_MBHC_FIELD(WCD_MBHC_L_DET_EN, WCD939X_ANA_MBHC_MECH, 0x80),
 WCD_MBHC_FIELD(WCD_MBHC_GND_DET_EN, WCD939X_ANA_MBHC_MECH, 0x40),
 WCD_MBHC_FIELD(WCD_MBHC_MECH_DETECTION_TYPE, WCD939X_ANA_MBHC_MECH, 0x20),
 WCD_MBHC_FIELD(WCD_MBHC_MIC_CLAMP_CTL, WCD939X_MBHC_NEW_PLUG_DETECT_CTL, 0x30),
 WCD_MBHC_FIELD(WCD_MBHC_ELECT_DETECTION_TYPE, WCD939X_ANA_MBHC_ELECT, 0x08),
 WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_CTRL, WCD939X_MBHC_NEW_INT_MECH_DET_CURRENT, 0x1F),
 WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, WCD939X_ANA_MBHC_MECH, 0x04),
 WCD_MBHC_FIELD(WCD_MBHC_HPHL_PLUG_TYPE, WCD939X_ANA_MBHC_MECH, 0x10),
 WCD_MBHC_FIELD(WCD_MBHC_GND_PLUG_TYPE, WCD939X_ANA_MBHC_MECH, 0x08),
 WCD_MBHC_FIELD(WCD_MBHC_SW_HPH_LP_100K_TO_GND, WCD939X_ANA_MBHC_MECH, 0x01),
 WCD_MBHC_FIELD(WCD_MBHC_ELECT_SCHMT_ISRC, WCD939X_ANA_MBHC_ELECT, 0x06),
 WCD_MBHC_FIELD(WCD_MBHC_FSM_EN, WCD939X_ANA_MBHC_ELECT, 0x80),
 WCD_MBHC_FIELD(WCD_MBHC_INSREM_DBNC, WCD939X_MBHC_NEW_PLUG_DETECT_CTL, 0x0F),
 WCD_MBHC_FIELD(WCD_MBHC_BTN_DBNC, WCD939X_MBHC_NEW_CTL_1, 0x03),
 WCD_MBHC_FIELD(WCD_MBHC_HS_VREF, WCD939X_MBHC_NEW_CTL_2, 0x03),
 WCD_MBHC_FIELD(WCD_MBHC_HS_COMP_RESULT, WCD939X_ANA_MBHC_RESULT_3, 0x08),
 WCD_MBHC_FIELD(WCD_MBHC_IN2P_CLAMP_STATE, WCD939X_ANA_MBHC_RESULT_3, 0x10),
 WCD_MBHC_FIELD(WCD_MBHC_MIC_SCHMT_RESULT, WCD939X_ANA_MBHC_RESULT_3, 0x20),
 WCD_MBHC_FIELD(WCD_MBHC_HPHL_SCHMT_RESULT, WCD939X_ANA_MBHC_RESULT_3, 0x80),
 WCD_MBHC_FIELD(WCD_MBHC_HPHR_SCHMT_RESULT, WCD939X_ANA_MBHC_RESULT_3, 0x40),
 WCD_MBHC_FIELD(WCD_MBHC_OCP_FSM_EN, WCD939X_HPH_OCP_CTL, 0x10),
 WCD_MBHC_FIELD(WCD_MBHC_BTN_RESULT, WCD939X_ANA_MBHC_RESULT_3, 0x07),
 WCD_MBHC_FIELD(WCD_MBHC_BTN_ISRC_CTL, WCD939X_ANA_MBHC_ELECT, 0x70),
 WCD_MBHC_FIELD(WCD_MBHC_ELECT_RESULT, WCD939X_ANA_MBHC_RESULT_3, 0xFF),
 WCD_MBHC_FIELD(WCD_MBHC_MICB_CTRL, WCD939X_ANA_MICB2, 0xC0),
 WCD_MBHC_FIELD(WCD_MBHC_HPH_CNP_WG_TIME, WCD939X_HPH_CNP_WG_TIME, 0xFF),
 WCD_MBHC_FIELD(WCD_MBHC_HPHR_PA_EN, WCD939X_ANA_HPH, 0x40),
 WCD_MBHC_FIELD(WCD_MBHC_HPHL_PA_EN, WCD939X_ANA_HPH, 0x80),
 WCD_MBHC_FIELD(WCD_MBHC_HPH_PA_EN, WCD939X_ANA_HPH, 0xC0),
 WCD_MBHC_FIELD(WCD_MBHC_SWCH_LEVEL_REMOVE, WCD939X_ANA_MBHC_RESULT_3, 0x10),
 WCD_MBHC_FIELD(WCD_MBHC_ANC_DET_EN, WCD939X_MBHC_CTL_BCS, 0x02),
 WCD_MBHC_FIELD(WCD_MBHC_FSM_STATUS, WCD939X_MBHC_NEW_FSM_STATUS, 0x01),
 WCD_MBHC_FIELD(WCD_MBHC_MUX_CTL, WCD939X_MBHC_NEW_CTL_2, 0x70),
 WCD_MBHC_FIELD(WCD_MBHC_MOISTURE_STATUS, WCD939X_MBHC_NEW_FSM_STATUS, 0x20),
 WCD_MBHC_FIELD(WCD_MBHC_HPHR_GND, WCD939X_HPH_PA_CTL2, 0x40),
 WCD_MBHC_FIELD(WCD_MBHC_HPHL_GND, WCD939X_HPH_PA_CTL2, 0x10),
 WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_DET_EN, WCD939X_HPH_L_TEST, 0x01),
 WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_DET_EN, WCD939X_HPH_R_TEST, 0x01),
 WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_STATUS, WCD939X_DIGITAL_INTR_STATUS_0, 0x80),
 WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_STATUS, WCD939X_DIGITAL_INTR_STATUS_0, 0x20),
 WCD_MBHC_FIELD(WCD_MBHC_ADC_EN, WCD939X_MBHC_NEW_CTL_1, 0x08),
 WCD_MBHC_FIELD(WCD_MBHC_ADC_COMPLETE, WCD939X_MBHC_NEW_FSM_STATUS, 0x40),
 WCD_MBHC_FIELD(WCD_MBHC_ADC_TIMEOUT, WCD939X_MBHC_NEW_FSM_STATUS, 0x80),
 WCD_MBHC_FIELD(WCD_MBHC_ADC_RESULT, WCD939X_MBHC_NEW_ADC_RESULT, 0xFF),
 WCD_MBHC_FIELD(WCD_MBHC_MICB2_VOUT, WCD939X_ANA_MICB2, 0x3F),
 WCD_MBHC_FIELD(WCD_MBHC_ADC_MODE, WCD939X_MBHC_NEW_CTL_1, 0x10),
 WCD_MBHC_FIELD(WCD_MBHC_DETECTION_DONE, WCD939X_MBHC_NEW_CTL_1, 0x04),
 WCD_MBHC_FIELD(WCD_MBHC_ELECT_ISRC_EN, WCD939X_ANA_MBHC_ZDET, 0x02),
};

static const struct regmap_irq wcd939x_irqs[WCD939X_NUM_IRQS] = {
 REGMAP_IRQ_REG(WCD939X_IRQ_MBHC_BUTTON_PRESS_DET, 0, 0x01),
 REGMAP_IRQ_REG(WCD939X_IRQ_MBHC_BUTTON_RELEASE_DET, 0, 0x02),
 REGMAP_IRQ_REG(WCD939X_IRQ_MBHC_ELECT_INS_REM_DET, 0, 0x04),
 REGMAP_IRQ_REG(WCD939X_IRQ_MBHC_ELECT_INS_REM_LEG_DET, 0, 0x08),
 REGMAP_IRQ_REG(WCD939X_IRQ_MBHC_SW_DET, 0, 0x10),
 REGMAP_IRQ_REG(WCD939X_IRQ_HPHR_OCP_INT, 0, 0x20),
 REGMAP_IRQ_REG(WCD939X_IRQ_HPHR_CNP_INT, 0, 0x40),
 REGMAP_IRQ_REG(WCD939X_IRQ_HPHL_OCP_INT, 0, 0x80),
 REGMAP_IRQ_REG(WCD939X_IRQ_HPHL_CNP_INT, 1, 0x01),
 REGMAP_IRQ_REG(WCD939X_IRQ_EAR_CNP_INT, 1, 0x02),
 REGMAP_IRQ_REG(WCD939X_IRQ_EAR_SCD_INT, 1, 0x04),
 REGMAP_IRQ_REG(WCD939X_IRQ_HPHL_PDM_WD_INT, 1, 0x20),
 REGMAP_IRQ_REG(WCD939X_IRQ_HPHR_PDM_WD_INT, 1, 0x40),
 REGMAP_IRQ_REG(WCD939X_IRQ_EAR_PDM_WD_INT, 1, 0x80),
 REGMAP_IRQ_REG(WCD939X_IRQ_MBHC_MOISTURE_INT, 2, 0x02),
 REGMAP_IRQ_REG(WCD939X_IRQ_HPHL_SURGE_DET_INT, 2, 0x04),
 REGMAP_IRQ_REG(WCD939X_IRQ_HPHR_SURGE_DET_INT, 2, 0x08),
};

static const struct regmap_irq_chip wcd939x_regmap_irq_chip = {
 .name = "wcd939x",
 .irqs = wcd939x_irqs,
 .num_irqs = ARRAY_SIZE(wcd939x_irqs),
 .num_regs = 3,
 .status_base = WCD939X_DIGITAL_INTR_STATUS_0,
 .mask_base = WCD939X_DIGITAL_INTR_MASK_0,
 .ack_base = WCD939X_DIGITAL_INTR_CLEAR_0,
 .use_ack = 1,
 .runtime_pm = true,
 .irq_drv_data = NULL,
};

static int wcd939x_get_clk_rate(int mode)
{
 int rate;

 switch (mode) {
 case ADC_MODE_ULP2:
  rate = SWR_CLK_RATE_0P6MHZ;
  break;
 case ADC_MODE_ULP1:
  rate = SWR_CLK_RATE_1P2MHZ;
  break;
 case ADC_MODE_LP:
  rate = SWR_CLK_RATE_4P8MHZ;
  break;
 case ADC_MODE_NORMAL:
 case ADC_MODE_LO_HIF:
 case ADC_MODE_HIFI:
 case ADC_MODE_INVALID:
 default:
  rate = SWR_CLK_RATE_9P6MHZ;
  break;
 }

 return rate;
}

static int wcd939x_set_swr_clk_rate(struct snd_soc_component *component, int rate, int bank)
{
 u8 mask = (bank ? 0xF0 : 0x0F);
 u8 val = 0;

 switch (rate) {
 case SWR_CLK_RATE_0P6MHZ:
  val = 6;
  break;
 case SWR_CLK_RATE_1P2MHZ:
  val = 5;
  break;
 case SWR_CLK_RATE_2P4MHZ:
  val = 3;
  break;
 case SWR_CLK_RATE_4P8MHZ:
  val = 1;
  break;
 case SWR_CLK_RATE_9P6MHZ:
 default:
  val = 0;
  break;
 }

 snd_soc_component_write_field(component, WCD939X_DIGITAL_SWR_TX_CLK_RATE, mask, val);

 return 0;
}

static int wcd939x_io_init(struct snd_soc_component *component)
{
 snd_soc_component_write_field(component, WCD939X_ANA_BIAS,
          WCD939X_BIAS_ANALOG_BIAS_EN, true);
 snd_soc_component_write_field(component, WCD939X_ANA_BIAS,
          WCD939X_BIAS_PRECHRG_EN, true);

 /* 10 msec delay as per HW requirement */
 usleep_range(10000, 10010);
 snd_soc_component_write_field(component, WCD939X_ANA_BIAS,
          WCD939X_BIAS_PRECHRG_EN, false);

 snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_L,
          WCD939X_RDAC_HD2_CTL_L_HD2_RES_DIV_CTL_L, 0x15);
 snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_R,
          WCD939X_RDAC_HD2_CTL_R_HD2_RES_DIV_CTL_R, 0x15);
 snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DMIC_CTL,
          WCD939X_CDC_DMIC_CTL_CLK_SCALE_EN, true);

 snd_soc_component_write_field(component, WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_ULP,
          WCD939X_FE_ICTRL_STG2CASC_ULP_ICTRL_SCBIAS_ULP0P6M, 1);
 snd_soc_component_write_field(component, WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_ULP,
          WCD939X_FE_ICTRL_STG2CASC_ULP_VALUE, 4);

 snd_soc_component_write_field(component, WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_ULP,
          WCD939X_FE_ICTRL_STG2MAIN_ULP_VALUE, 8);

 snd_soc_component_write_field(component, WCD939X_MICB1_TEST_CTL_1,
          WCD939X_TEST_CTL_1_NOISE_FILT_RES_VAL, 7);
 snd_soc_component_write_field(component, WCD939X_MICB2_TEST_CTL_1,
          WCD939X_TEST_CTL_1_NOISE_FILT_RES_VAL, 7);
 snd_soc_component_write_field(component, WCD939X_MICB3_TEST_CTL_1,
          WCD939X_TEST_CTL_1_NOISE_FILT_RES_VAL, 7);
 snd_soc_component_write_field(component, WCD939X_MICB4_TEST_CTL_1,
          WCD939X_TEST_CTL_1_NOISE_FILT_RES_VAL, 7);
 snd_soc_component_write_field(component, WCD939X_TX_3_4_TEST_BLK_EN2,
          WCD939X_TEST_BLK_EN2_TXFE2_MBHC_CLKRST_EN, false);

 snd_soc_component_write_field(component, WCD939X_HPH_SURGE_EN,
          WCD939X_EN_EN_SURGE_PROTECTION_HPHL, false);
 snd_soc_component_write_field(component, WCD939X_HPH_SURGE_EN,
          WCD939X_EN_EN_SURGE_PROTECTION_HPHR, false);

 snd_soc_component_write_field(component, WCD939X_HPH_OCP_CTL,
          WCD939X_OCP_CTL_OCP_FSM_EN, true);
 snd_soc_component_write_field(component, WCD939X_HPH_OCP_CTL,
          WCD939X_OCP_CTL_SCD_OP_EN, true);

 snd_soc_component_write(component, WCD939X_E_CFG0,
    WCD939X_CFG0_IDLE_STEREO |
    WCD939X_CFG0_AUTO_DISABLE_ANC);

 return 0;
}

static int wcd939x_sdw_connect_port(const struct wcd939x_sdw_ch_info *ch_info,
        struct sdw_port_config *port_config,
        u8 enable)
{
 u8 ch_mask, port_num;

 port_num = ch_info->port_num;
 ch_mask = ch_info->ch_mask;

 port_config->num = port_num;

 if (enable)
  port_config->ch_mask |= ch_mask;
 else
  port_config->ch_mask &= ~ch_mask;

 return 0;
}

static int wcd939x_connect_port(struct wcd939x_sdw_priv *wcd, u8 port_num, u8 ch_id, u8 enable)
{
 return wcd939x_sdw_connect_port(&wcd->ch_info[ch_id],
     &wcd->port_config[port_num - 1],
     enable);
}

static int wcd939x_codec_enable_rxclk(struct snd_soc_dapm_widget *w,
          struct snd_kcontrol *kcontrol,
          int event)
{
 struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);

 switch (event) {
 case SND_SOC_DAPM_PRE_PMU:
  snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES,
           WCD939X_RX_SUPPLIES_RX_BIAS_ENABLE, true);

  /* Analog path clock controls */
  snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL,
           WCD939X_CDC_ANA_CLK_CTL_ANA_RX_CLK_EN, true);
  snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL,
           WCD939X_CDC_ANA_CLK_CTL_ANA_RX_DIV2_CLK_EN,
           true);
  snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL,
           WCD939X_CDC_ANA_CLK_CTL_ANA_RX_DIV4_CLK_EN,
           true);

  /* Digital path clock controls */
  snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
           WCD939X_CDC_DIG_CLK_CTL_RXD0_CLK_EN, true);
  snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
           WCD939X_CDC_DIG_CLK_CTL_RXD1_CLK_EN, true);
  snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
           WCD939X_CDC_DIG_CLK_CTL_RXD2_CLK_EN, true);
  break;
 case SND_SOC_DAPM_POST_PMD:
  snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES,
           WCD939X_RX_SUPPLIES_VNEG_EN, false);
  snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES,
           WCD939X_RX_SUPPLIES_VPOS_EN, false);

  snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
           WCD939X_CDC_DIG_CLK_CTL_RXD2_CLK_EN, false);
  snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
           WCD939X_CDC_DIG_CLK_CTL_RXD1_CLK_EN, false);
  snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
           WCD939X_CDC_DIG_CLK_CTL_RXD0_CLK_EN, false);

  snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL,
           WCD939X_CDC_ANA_CLK_CTL_ANA_RX_DIV4_CLK_EN,
           false);
  snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL,
           WCD939X_CDC_ANA_CLK_CTL_ANA_RX_DIV2_CLK_EN,
           false);
  snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL,
           WCD939X_CDC_ANA_CLK_CTL_ANA_RX_CLK_EN, false);

  snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES,
           WCD939X_RX_SUPPLIES_RX_BIAS_ENABLE, false);

  break;
 }

 return 0;
}

static int wcd939x_codec_hphl_dac_event(struct snd_soc_dapm_widget *w,
     struct snd_kcontrol *kcontrol,
     int event)
{
 struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
 struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);

 switch (event) {
 case SND_SOC_DAPM_PRE_PMU:
  snd_soc_component_write_field(component, WCD939X_HPH_RDAC_CLK_CTL1,
           WCD939X_RDAC_CLK_CTL1_OPAMP_CHOP_CLK_EN,
           false);

  snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_HPH_GAIN_CTL,
           WCD939X_CDC_HPH_GAIN_CTL_HPHL_RX_EN, true);
  break;
 case SND_SOC_DAPM_POST_PMU:
  snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_L,
           WCD939X_RDAC_HD2_CTL_L_HD2_RES_DIV_CTL_L, 0x1d);
  if (wcd939x->comp1_enable) {
   snd_soc_component_write_field(component,
            WCD939X_DIGITAL_CDC_COMP_CTL_0,
            WCD939X_CDC_COMP_CTL_0_HPHL_COMP_EN,
            true);
   /* 5msec compander delay as per HW requirement */
   if (!wcd939x->comp2_enable ||
       snd_soc_component_read_field(component,
        WCD939X_DIGITAL_CDC_COMP_CTL_0,
        WCD939X_CDC_COMP_CTL_0_HPHR_COMP_EN))
    usleep_range(5000, 5010);

   snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_TIMER1,
            WCD939X_TIMER1_AUTOCHOP_TIMER_CTL_EN,
            false);
  } else {
   snd_soc_component_write_field(component,
            WCD939X_DIGITAL_CDC_COMP_CTL_0,
            WCD939X_CDC_COMP_CTL_0_HPHL_COMP_EN,
            false);
   snd_soc_component_write_field(component, WCD939X_HPH_L_EN,
            WCD939X_L_EN_GAIN_SOURCE_SEL, true);
  }
  break;
 case SND_SOC_DAPM_POST_PMD:
  snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_L,
           WCD939X_RDAC_HD2_CTL_L_HD2_RES_DIV_CTL_L, 1);
  snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_HPH_GAIN_CTL,
           WCD939X_CDC_HPH_GAIN_CTL_HPHL_RX_EN, false);
  break;
 }

 return 0;
}

static int wcd939x_codec_hphr_dac_event(struct snd_soc_dapm_widget *w,
     struct snd_kcontrol *kcontrol,
     int event)
{
 struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
 struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);

 dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__,
  w->name, event);

 switch (event) {
 case SND_SOC_DAPM_PRE_PMU:
  snd_soc_component_write_field(component, WCD939X_HPH_RDAC_CLK_CTL1,
           WCD939X_RDAC_CLK_CTL1_OPAMP_CHOP_CLK_EN,
           false);

  snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_HPH_GAIN_CTL,
           WCD939X_CDC_HPH_GAIN_CTL_HPHR_RX_EN, true);
  break;
 case SND_SOC_DAPM_POST_PMU:
  snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_R,
           WCD939X_RDAC_HD2_CTL_R_HD2_RES_DIV_CTL_R, 0x1d);
  if (wcd939x->comp2_enable) {
   snd_soc_component_write_field(component,
            WCD939X_DIGITAL_CDC_COMP_CTL_0,
            WCD939X_CDC_COMP_CTL_0_HPHR_COMP_EN,
            true);
   /* 5msec compander delay as per HW requirement */
   if (!wcd939x->comp1_enable ||
       snd_soc_component_read_field(component,
        WCD939X_DIGITAL_CDC_COMP_CTL_0,
        WCD939X_CDC_COMP_CTL_0_HPHL_COMP_EN))
    usleep_range(5000, 5010);
   snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_TIMER1,
            WCD939X_TIMER1_AUTOCHOP_TIMER_CTL_EN,
            false);
  } else {
   snd_soc_component_write_field(component,
            WCD939X_DIGITAL_CDC_COMP_CTL_0,
            WCD939X_CDC_COMP_CTL_0_HPHR_COMP_EN,
            false);
   snd_soc_component_write_field(component, WCD939X_HPH_R_EN,
            WCD939X_R_EN_GAIN_SOURCE_SEL, true);
  }
  break;
 case SND_SOC_DAPM_POST_PMD:
  snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_R,
           WCD939X_RDAC_HD2_CTL_R_HD2_RES_DIV_CTL_R, 1);
  snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_HPH_GAIN_CTL,
           WCD939X_CDC_HPH_GAIN_CTL_HPHR_RX_EN, false);
  break;
 }

 return 0;
}

static int wcd939x_codec_ear_dac_event(struct snd_soc_dapm_widget *w,
           struct snd_kcontrol *kcontrol,
           int event)
{
 struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
 struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);

 switch (event) {
 case SND_SOC_DAPM_PRE_PMU:
  snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_EAR_GAIN_CTL,
           WCD939X_CDC_EAR_GAIN_CTL_EAR_EN, true);

  snd_soc_component_write_field(component, WCD939X_EAR_DAC_CON,
           WCD939X_DAC_CON_DAC_SAMPLE_EDGE_SEL, false);

  /* 5 msec delay as per HW requirement */
  usleep_range(5000, 5010);
  wcd_clsh_ctrl_set_state(wcd939x->clsh_info, WCD_CLSH_EVENT_PRE_DAC,
     WCD_CLSH_STATE_EAR, CLS_AB_HIFI);

  snd_soc_component_write_field(component, WCD939X_FLYBACK_VNEG_CTRL_4,
           WCD939X_VNEG_CTRL_4_ILIM_SEL, 0xd);
  break;
 case SND_SOC_DAPM_POST_PMD:
  snd_soc_component_write_field(component, WCD939X_EAR_DAC_CON,
           WCD939X_DAC_CON_DAC_SAMPLE_EDGE_SEL, true);
  break;
 }

 return 0;
}

static int wcd939x_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w,
     struct snd_kcontrol *kcontrol,
     int event)
{
 struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
 struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
 int hph_mode = wcd939x->hph_mode;

 switch (event) {
 case SND_SOC_DAPM_PRE_PMU:
  if (wcd939x->ldoh)
   snd_soc_component_write_field(component, WCD939X_LDOH_MODE,
            WCD939X_MODE_LDOH_EN, true);

  wcd_clsh_ctrl_set_state(wcd939x->clsh_info, WCD_CLSH_EVENT_PRE_DAC,
     WCD_CLSH_STATE_HPHR, hph_mode);
  wcd_clsh_set_hph_mode(wcd939x->clsh_info, CLS_H_HIFI);

  if (hph_mode == CLS_H_LP || hph_mode == CLS_H_LOHIFI || hph_mode == CLS_H_ULP)
   snd_soc_component_write_field(component,
     WCD939X_HPH_REFBUFF_LP_CTL,
     WCD939X_REFBUFF_LP_CTL_PREREF_FILT_BYPASS, true);
  if (hph_mode == CLS_H_LOHIFI)
   snd_soc_component_write_field(component, WCD939X_ANA_HPH,
             WCD939X_HPH_PWR_LEVEL, 0);

  snd_soc_component_write_field(component, WCD939X_FLYBACK_VNEG_CTRL_4,
           WCD939X_VNEG_CTRL_4_ILIM_SEL, 0xd);
  snd_soc_component_write_field(component, WCD939X_ANA_HPH,
           WCD939X_HPH_HPHR_REF_ENABLE, true);

  if (snd_soc_component_read_field(component, WCD939X_ANA_HPH,
       WCD939X_HPH_HPHL_REF_ENABLE))
   usleep_range(2500, 2600); /* 2.5msec delay as per HW requirement */

  set_bit(HPH_PA_DELAY, &wcd939x->status_mask);
  snd_soc_component_write_field(component, WCD939X_DIGITAL_PDM_WD_CTL1,
           WCD939X_PDM_WD_CTL1_PDM_WD_EN, 3);
  break;
 case SND_SOC_DAPM_POST_PMU:
  /*
 * 7ms sleep is required if compander is enabled as per
 * HW requirement. If compander is disabled, then
 * 20ms delay is required.
 */

  if (test_bit(HPH_PA_DELAY, &wcd939x->status_mask)) {
   if (!wcd939x->comp2_enable)
    usleep_range(20000, 20100);
   else
    usleep_range(7000, 7100);

   if (hph_mode == CLS_H_LP || hph_mode == CLS_H_LOHIFI ||
       hph_mode == CLS_H_ULP)
    snd_soc_component_write_field(component,
      WCD939X_HPH_REFBUFF_LP_CTL,
      WCD939X_REFBUFF_LP_CTL_PREREF_FILT_BYPASS,
      false);
   clear_bit(HPH_PA_DELAY, &wcd939x->status_mask);
  }
  snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_TIMER1,
           WCD939X_TIMER1_AUTOCHOP_TIMER_CTL_EN, true);
  if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI ||
      hph_mode == CLS_AB_LP || hph_mode == CLS_AB_LOHIFI)
   snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES,
            WCD939X_RX_SUPPLIES_REGULATOR_MODE,
            true);

  enable_irq(wcd939x->hphr_pdm_wd_int);
  break;
 case SND_SOC_DAPM_PRE_PMD:
  disable_irq_nosync(wcd939x->hphr_pdm_wd_int);
  /*
 * 7ms sleep is required if compander is enabled as per
 * HW requirement. If compander is disabled, then
 * 20ms delay is required.
 */

  if (!wcd939x->comp2_enable)
   usleep_range(20000, 20100);
  else
   usleep_range(7000, 7100);

  snd_soc_component_write_field(component, WCD939X_ANA_HPH,
           WCD939X_HPH_HPHR_ENABLE, false);

  wcd_mbhc_event_notify(wcd939x->wcd_mbhc,
          WCD_EVENT_PRE_HPHR_PA_OFF);
  set_bit(HPH_PA_DELAY, &wcd939x->status_mask);
  break;
 case SND_SOC_DAPM_POST_PMD:
  /*
 * 7ms sleep is required if compander is enabled as per
 * HW requirement. If compander is disabled, then
 * 20ms delay is required.
 */

  if (test_bit(HPH_PA_DELAY, &wcd939x->status_mask)) {
   if (!wcd939x->comp2_enable)
    usleep_range(20000, 20100);
   else
    usleep_range(7000, 7100);
   clear_bit(HPH_PA_DELAY, &wcd939x->status_mask);
  }
  wcd_mbhc_event_notify(wcd939x->wcd_mbhc,
          WCD_EVENT_POST_HPHR_PA_OFF);

  snd_soc_component_write_field(component, WCD939X_ANA_HPH,
           WCD939X_HPH_HPHR_REF_ENABLE, false);
  snd_soc_component_write_field(component, WCD939X_DIGITAL_PDM_WD_CTL1,
           WCD939X_PDM_WD_CTL1_PDM_WD_EN, 0);

  wcd_clsh_ctrl_set_state(wcd939x->clsh_info, WCD_CLSH_EVENT_POST_PA,
     WCD_CLSH_STATE_HPHR, hph_mode);
  if (wcd939x->ldoh)
   snd_soc_component_write_field(component, WCD939X_LDOH_MODE,
            WCD939X_MODE_LDOH_EN, false);
  break;
 }

 return 0;
}

static int wcd939x_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w,
     struct snd_kcontrol *kcontrol,
     int event)
{
 struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
 struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
 int hph_mode = wcd939x->hph_mode;

 dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__,
  w->name, event);

 switch (event) {
 case SND_SOC_DAPM_PRE_PMU:
  if (wcd939x->ldoh)
   snd_soc_component_write_field(component, WCD939X_LDOH_MODE,
            WCD939X_MODE_LDOH_EN, true);
  wcd_clsh_ctrl_set_state(wcd939x->clsh_info, WCD_CLSH_EVENT_PRE_DAC,
     WCD_CLSH_STATE_HPHL, hph_mode);
  wcd_clsh_set_hph_mode(wcd939x->clsh_info, CLS_H_HIFI);

  if (hph_mode == CLS_H_LP || hph_mode == CLS_H_LOHIFI || hph_mode == CLS_H_ULP)
   snd_soc_component_write_field(component,
      WCD939X_HPH_REFBUFF_LP_CTL,
      WCD939X_REFBUFF_LP_CTL_PREREF_FILT_BYPASS,
      true);
  if (hph_mode == CLS_H_LOHIFI)
   snd_soc_component_write_field(component, WCD939X_ANA_HPH,
             WCD939X_HPH_PWR_LEVEL, 0);

  snd_soc_component_write_field(component, WCD939X_FLYBACK_VNEG_CTRL_4,
           WCD939X_VNEG_CTRL_4_ILIM_SEL, 0xd);
  snd_soc_component_write_field(component, WCD939X_ANA_HPH,
           WCD939X_HPH_HPHL_REF_ENABLE, true);

  if (snd_soc_component_read_field(component, WCD939X_ANA_HPH,
       WCD939X_HPH_HPHR_REF_ENABLE))
   usleep_range(2500, 2600); /* 2.5msec delay as per HW requirement */

  set_bit(HPH_PA_DELAY, &wcd939x->status_mask);
  snd_soc_component_write_field(component, WCD939X_DIGITAL_PDM_WD_CTL0,
           WCD939X_PDM_WD_CTL0_PDM_WD_EN, 3);
  break;
 case SND_SOC_DAPM_POST_PMU:
  /*
 * 7ms sleep is required if compander is enabled as per
 * HW requirement. If compander is disabled, then
 * 20ms delay is required.
 */

  if (test_bit(HPH_PA_DELAY, &wcd939x->status_mask)) {
   if (!wcd939x->comp1_enable)
    usleep_range(20000, 20100);
   else
    usleep_range(7000, 7100);
   if (hph_mode == CLS_H_LP || hph_mode == CLS_H_LOHIFI ||
       hph_mode == CLS_H_ULP)
    snd_soc_component_write_field(component,
      WCD939X_HPH_REFBUFF_LP_CTL,
      WCD939X_REFBUFF_LP_CTL_PREREF_FILT_BYPASS,
      false);
   clear_bit(HPH_PA_DELAY, &wcd939x->status_mask);
  }
  snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_TIMER1,
           WCD939X_TIMER1_AUTOCHOP_TIMER_CTL_EN, true);
  if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI ||
      hph_mode == CLS_AB_LP || hph_mode == CLS_AB_LOHIFI)
   snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES,
            WCD939X_RX_SUPPLIES_REGULATOR_MODE,
            true);
  enable_irq(wcd939x->hphl_pdm_wd_int);
  break;
 case SND_SOC_DAPM_PRE_PMD:
  disable_irq_nosync(wcd939x->hphl_pdm_wd_int);
  /*
 * 7ms sleep is required if compander is enabled as per
 * HW requirement. If compander is disabled, then
 * 20ms delay is required.
 */

  if (!wcd939x->comp1_enable)
   usleep_range(20000, 20100);
  else
   usleep_range(7000, 7100);

  snd_soc_component_write_field(component, WCD939X_ANA_HPH,
           WCD939X_HPH_HPHL_ENABLE, false);

  wcd_mbhc_event_notify(wcd939x->wcd_mbhc, WCD_EVENT_PRE_HPHL_PA_OFF);
  set_bit(HPH_PA_DELAY, &wcd939x->status_mask);
  break;
 case SND_SOC_DAPM_POST_PMD:
  /*
 * 7ms sleep is required if compander is enabled as per
 * HW requirement. If compander is disabled, then
 * 20ms delay is required.
 */

  if (test_bit(HPH_PA_DELAY, &wcd939x->status_mask)) {
   if (!wcd939x->comp1_enable)
    usleep_range(21000, 21100);
   else
    usleep_range(7000, 7100);
   clear_bit(HPH_PA_DELAY, &wcd939x->status_mask);
  }
  wcd_mbhc_event_notify(wcd939x->wcd_mbhc,
          WCD_EVENT_POST_HPHL_PA_OFF);
  snd_soc_component_write_field(component, WCD939X_ANA_HPH,
           WCD939X_HPH_HPHL_REF_ENABLE, false);
  snd_soc_component_write_field(component, WCD939X_DIGITAL_PDM_WD_CTL0,
           WCD939X_PDM_WD_CTL0_PDM_WD_EN, 0);
  wcd_clsh_ctrl_set_state(wcd939x->clsh_info, WCD_CLSH_EVENT_POST_PA,
     WCD_CLSH_STATE_HPHL, hph_mode);
  if (wcd939x->ldoh)
   snd_soc_component_write_field(component, WCD939X_LDOH_MODE,
            WCD939X_MODE_LDOH_EN, false);
  break;
 }

 return 0;
}

static int wcd939x_codec_enable_ear_pa(struct snd_soc_dapm_widget *w,
           struct snd_kcontrol *kcontrol, int event)
{
 struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
 struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);

 switch (event) {
 case SND_SOC_DAPM_PRE_PMU:
  /* Enable watchdog interrupt for HPHL */
  snd_soc_component_write_field(component, WCD939X_DIGITAL_PDM_WD_CTL0,
           WCD939X_PDM_WD_CTL0_PDM_WD_EN, 3);
  /* For EAR, use CLASS_AB regulator mode */
  snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES,
           WCD939X_RX_SUPPLIES_REGULATOR_MODE, true);
  snd_soc_component_write_field(component, WCD939X_ANA_EAR_COMPANDER_CTL,
           WCD939X_EAR_COMPANDER_CTL_GAIN_OVRD_REG, true);
  break;
 case SND_SOC_DAPM_POST_PMU:
  /* 6 msec delay as per HW requirement */
  usleep_range(6000, 6010);
  enable_irq(wcd939x->ear_pdm_wd_int);
  break;
 case SND_SOC_DAPM_PRE_PMD:
  disable_irq_nosync(wcd939x->ear_pdm_wd_int);
  break;
 case SND_SOC_DAPM_POST_PMD:
  snd_soc_component_write_field(component, WCD939X_ANA_EAR_COMPANDER_CTL,
           WCD939X_EAR_COMPANDER_CTL_GAIN_OVRD_REG,
           false);
  /* 7 msec delay as per HW requirement */
  usleep_range(7000, 7010);
  snd_soc_component_write_field(component, WCD939X_DIGITAL_PDM_WD_CTL0,
           WCD939X_PDM_WD_CTL0_PDM_WD_EN, 0);
  wcd_clsh_ctrl_set_state(wcd939x->clsh_info, WCD_CLSH_EVENT_POST_PA,
     WCD_CLSH_STATE_EAR, CLS_AB_HIFI);
  break;
 }

 return 0;
}

/* TX Controls */

static int wcd939x_codec_enable_dmic(struct snd_soc_dapm_widget *w,
         struct snd_kcontrol *kcontrol,
         int event)
{
 struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
 u16 dmic_clk_reg, dmic_clk_en_reg;
 u8 dmic_clk_en_mask;
 u8 dmic_ctl_mask;
 u8 dmic_clk_mask;

 switch (w->shift) {
 case 0:
 case 1:
  dmic_clk_reg = WCD939X_DIGITAL_CDC_DMIC_RATE_1_2;
  dmic_clk_en_reg = WCD939X_DIGITAL_CDC_DMIC1_CTL;
  dmic_clk_en_mask = WCD939X_CDC_DMIC1_CTL_DMIC_CLK_EN;
  dmic_clk_mask = WCD939X_CDC_DMIC_RATE_1_2_DMIC1_RATE;
  dmic_ctl_mask = WCD939X_CDC_AMIC_CTL_AMIC1_IN_SEL;
  break;
 case 2:
 case 3:
  dmic_clk_reg = WCD939X_DIGITAL_CDC_DMIC_RATE_1_2;
  dmic_clk_en_reg = WCD939X_DIGITAL_CDC_DMIC2_CTL;
  dmic_clk_en_mask = WCD939X_CDC_DMIC2_CTL_DMIC_CLK_EN;
  dmic_clk_mask = WCD939X_CDC_DMIC_RATE_1_2_DMIC2_RATE;
  dmic_ctl_mask = WCD939X_CDC_AMIC_CTL_AMIC3_IN_SEL;
  break;
 case 4:
 case 5:
  dmic_clk_reg = WCD939X_DIGITAL_CDC_DMIC_RATE_3_4;
  dmic_clk_en_reg = WCD939X_DIGITAL_CDC_DMIC3_CTL;
  dmic_clk_en_mask = WCD939X_CDC_DMIC3_CTL_DMIC_CLK_EN;
  dmic_clk_mask = WCD939X_CDC_DMIC_RATE_3_4_DMIC3_RATE;
  dmic_ctl_mask = WCD939X_CDC_AMIC_CTL_AMIC4_IN_SEL;
  break;
 case 6:
 case 7:
  dmic_clk_reg = WCD939X_DIGITAL_CDC_DMIC_RATE_3_4;
  dmic_clk_en_reg = WCD939X_DIGITAL_CDC_DMIC4_CTL;
  dmic_clk_en_mask = WCD939X_CDC_DMIC4_CTL_DMIC_CLK_EN;
  dmic_clk_mask = WCD939X_CDC_DMIC_RATE_3_4_DMIC4_RATE;
  dmic_ctl_mask = WCD939X_CDC_AMIC_CTL_AMIC5_IN_SEL;
  break;
 default:
  dev_err(component->dev, "%s: Invalid DMIC Selection\n", __func__);
  return -EINVAL;
 }

 switch (event) {
 case SND_SOC_DAPM_PRE_PMU:
  snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_AMIC_CTL,
           dmic_ctl_mask, false);
  /* 250us sleep as per HW requirement */
  usleep_range(250, 260);
  if (w->shift == 2)
   snd_soc_component_write_field(component,
            WCD939X_DIGITAL_CDC_DMIC2_CTL,
            WCD939X_CDC_DMIC2_CTL_DMIC_LEFT_EN,
            true);
  /* Setting DMIC clock rate to 2.4MHz */
  snd_soc_component_write_field(component, dmic_clk_reg,
           dmic_clk_mask, 3);
  snd_soc_component_write_field(component, dmic_clk_en_reg,
           dmic_clk_en_mask, true);
  /* enable clock scaling */
  snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DMIC_CTL,
           WCD939X_CDC_DMIC_CTL_CLK_SCALE_EN, true);
  snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DMIC_CTL,
           WCD939X_CDC_DMIC_CTL_DMIC_DIV_BAK_EN, true);
  break;
 case SND_SOC_DAPM_POST_PMD:
  snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_AMIC_CTL,
           dmic_ctl_mask, 1);
  if (w->shift == 2)
   snd_soc_component_write_field(component,
            WCD939X_DIGITAL_CDC_DMIC2_CTL,
            WCD939X_CDC_DMIC2_CTL_DMIC_LEFT_EN,
            false);
  snd_soc_component_write_field(component, dmic_clk_en_reg,
           dmic_clk_en_mask, 0);
  break;
 }
 return 0;
}

static int wcd939x_tx_swr_ctrl(struct snd_soc_dapm_widget *w,
          struct snd_kcontrol *kcontrol, int event)
{
 struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
 struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
 int bank;
 int rate;

 bank = wcd939x_swr_get_current_bank(wcd939x->sdw_priv[AIF1_CAP]->sdev);

 switch (event) {
 case SND_SOC_DAPM_PRE_PMU:
  if (strnstr(w->name, "ADC"sizeof("ADC"))) {
   int mode = 0;

   if (test_bit(WCD_ADC1, &wcd939x->status_mask))
    mode |= tx_mode_bit[wcd939x->tx_mode[WCD_ADC1]];
   if (test_bit(WCD_ADC2, &wcd939x->status_mask))
    mode |= tx_mode_bit[wcd939x->tx_mode[WCD_ADC2]];
   if (test_bit(WCD_ADC3, &wcd939x->status_mask))
    mode |= tx_mode_bit[wcd939x->tx_mode[WCD_ADC3]];
   if (test_bit(WCD_ADC4, &wcd939x->status_mask))
    mode |= tx_mode_bit[wcd939x->tx_mode[WCD_ADC4]];

   if (mode)
    rate = wcd939x_get_clk_rate(ffs(mode) - 1);
   else
    rate = wcd939x_get_clk_rate(ADC_MODE_INVALID);
   wcd939x_set_swr_clk_rate(component, rate, bank);
   wcd939x_set_swr_clk_rate(component, rate, !bank);
  }
  break;
 case SND_SOC_DAPM_POST_PMD:
  if (strnstr(w->name, "ADC"sizeof("ADC"))) {
   rate = wcd939x_get_clk_rate(ADC_MODE_INVALID);
   wcd939x_set_swr_clk_rate(component, rate, !bank);
   wcd939x_set_swr_clk_rate(component, rate, bank);
  }
  break;
 }

 return 0;
}

static int wcd939x_get_adc_mode(int val)
{
 int ret = 0;

 switch (val) {
 case ADC_MODE_INVALID:
  ret = ADC_MODE_VAL_NORMAL;
  break;
 case ADC_MODE_HIFI:
  ret = ADC_MODE_VAL_HIFI;
  break;
 case ADC_MODE_LO_HIF:
  ret = ADC_MODE_VAL_LO_HIF;
  break;
 case ADC_MODE_NORMAL:
  ret = ADC_MODE_VAL_NORMAL;
  break;
 case ADC_MODE_LP:
  ret = ADC_MODE_VAL_LP;
  break;
 case ADC_MODE_ULP1:
  ret = ADC_MODE_VAL_ULP1;
  break;
 case ADC_MODE_ULP2:
  ret = ADC_MODE_VAL_ULP2;
  break;
 default:
  ret = -EINVAL;
  break;
 }
 return ret;
}

static int wcd939x_codec_enable_adc(struct snd_soc_dapm_widget *w,
        struct snd_kcontrol *kcontrol, int event)
{
 struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
 struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);

 switch (event) {
 case SND_SOC_DAPM_PRE_PMU:
  snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL,
           WCD939X_CDC_ANA_CLK_CTL_ANA_TX_CLK_EN, true);
  snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL,
           WCD939X_CDC_ANA_CLK_CTL_ANA_TX_DIV2_CLK_EN,
           true);
  set_bit(w->shift, &wcd939x->status_mask);
  break;
 case SND_SOC_DAPM_POST_PMD:
  snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL,
           WCD939X_CDC_ANA_CLK_CTL_ANA_TX_DIV2_CLK_EN,
           false);
  snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL,
           WCD939X_CDC_ANA_CLK_CTL_ANA_TX_CLK_EN,
           false);
  clear_bit(w->shift, &wcd939x->status_mask);
  break;
 }

 return 0;
}

static void wcd939x_tx_channel_config(struct snd_soc_component *component,
          int channel, bool init)
{
 int reg, mask;

 switch (channel) {
 case 0:
  reg = WCD939X_ANA_TX_CH2;
  mask = WCD939X_TX_CH2_HPF1_INIT;
  break;
 case 1:
  reg = WCD939X_ANA_TX_CH2;
  mask = WCD939X_TX_CH2_HPF2_INIT;
  break;
 case 2:
  reg = WCD939X_ANA_TX_CH4;
  mask = WCD939X_TX_CH4_HPF3_INIT;
  break;
 case 3:
  reg = WCD939X_ANA_TX_CH4;
  mask = WCD939X_TX_CH4_HPF4_INIT;
  break;
 default:
  return;
 }

 snd_soc_component_write_field(component, reg, mask, init);
}

static int wcd939x_adc_enable_req(struct snd_soc_dapm_widget *w,
      struct snd_kcontrol *kcontrol, int event)
{
 struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
 struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
 int mode;

 switch (event) {
 case SND_SOC_DAPM_PRE_PMU:
  snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_REQ_CTL,
           WCD939X_CDC_REQ_CTL_FS_RATE_4P8, true);
  snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_REQ_CTL,
           WCD939X_CDC_REQ_CTL_NO_NOTCH, false);

  wcd939x_tx_channel_config(component, w->shift, true);
  mode = wcd939x_get_adc_mode(wcd939x->tx_mode[w->shift]);
  if (mode < 0) {
   dev_info(component->dev, "Invalid ADC mode\n");
   return -EINVAL;
  }

  switch (w->shift) {
  case 0:
   snd_soc_component_write_field(component,
            WCD939X_DIGITAL_CDC_TX_ANA_MODE_0_1,
            WCD939X_CDC_TX_ANA_MODE_0_1_TXD0_MODE,
            mode);
   snd_soc_component_write_field(component,
            WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
            WCD939X_CDC_DIG_CLK_CTL_TXD0_CLK_EN,
            true);
   break;
  case 1:
   snd_soc_component_write_field(component,
            WCD939X_DIGITAL_CDC_TX_ANA_MODE_0_1,
            WCD939X_CDC_TX_ANA_MODE_0_1_TXD1_MODE,
            mode);
   snd_soc_component_write_field(component,
            WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
            WCD939X_CDC_DIG_CLK_CTL_TXD1_CLK_EN,
            true);
   break;
  case 2:
   snd_soc_component_write_field(component,
            WCD939X_DIGITAL_CDC_TX_ANA_MODE_2_3,
            WCD939X_CDC_TX_ANA_MODE_2_3_TXD2_MODE,
            mode);
   snd_soc_component_write_field(component,
            WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
            WCD939X_CDC_DIG_CLK_CTL_TXD2_CLK_EN,
            true);
   break;
  case 3:
   snd_soc_component_write_field(component,
            WCD939X_DIGITAL_CDC_TX_ANA_MODE_2_3,
            WCD939X_CDC_TX_ANA_MODE_2_3_TXD3_MODE,
            mode);
   snd_soc_component_write_field(component,
            WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
            WCD939X_CDC_DIG_CLK_CTL_TXD3_CLK_EN,
            true);
   break;
  default:
   break;
  }

  wcd939x_tx_channel_config(component, w->shift, false);
  break;
 case SND_SOC_DAPM_POST_PMD:
  switch (w->shift) {
  case 0:
   snd_soc_component_write_field(component,
            WCD939X_DIGITAL_CDC_TX_ANA_MODE_0_1,
            WCD939X_CDC_TX_ANA_MODE_0_1_TXD0_MODE,
            false);
   snd_soc_component_write_field(component,
            WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
            WCD939X_CDC_DIG_CLK_CTL_TXD0_CLK_EN,
            false);
   break;
  case 1:
   snd_soc_component_write_field(component,
            WCD939X_DIGITAL_CDC_TX_ANA_MODE_0_1,
            WCD939X_CDC_TX_ANA_MODE_0_1_TXD1_MODE,
            false);
   snd_soc_component_write_field(component,
            WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
            WCD939X_CDC_DIG_CLK_CTL_TXD1_CLK_EN,
            false);
   break;
  case 2:
   snd_soc_component_write_field(component,
            WCD939X_DIGITAL_CDC_TX_ANA_MODE_2_3,
            WCD939X_CDC_TX_ANA_MODE_2_3_TXD2_MODE,
            false);
   snd_soc_component_write_field(component,
            WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
            WCD939X_CDC_DIG_CLK_CTL_TXD2_CLK_EN,
            false);
   break;
  case 3:
   snd_soc_component_write_field(component,
            WCD939X_DIGITAL_CDC_TX_ANA_MODE_2_3,
            WCD939X_CDC_TX_ANA_MODE_2_3_TXD3_MODE,
            false);
   snd_soc_component_write_field(component,
            WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
            WCD939X_CDC_DIG_CLK_CTL_TXD3_CLK_EN,
            false);
   break;
  default:
   break;
  }
  break;
 }

 return 0;
}

static int wcd939x_micbias_control(struct snd_soc_component *component,
       int micb_num, int req, bool is_dapm)
{
 struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
 int micb_index = micb_num - 1;
 u16 micb_reg;

 switch (micb_num) {
 case MIC_BIAS_1:
  micb_reg = WCD939X_ANA_MICB1;
  break;
 case MIC_BIAS_2:
  micb_reg = WCD939X_ANA_MICB2;
  break;
 case MIC_BIAS_3:
  micb_reg = WCD939X_ANA_MICB3;
  break;
 case MIC_BIAS_4:
  micb_reg = WCD939X_ANA_MICB4;
  break;
 default:
  dev_err(component->dev, "%s: Invalid micbias number: %d\n",
   __func__, micb_num);
  return -EINVAL;
 }

 switch (req) {
 case MICB_PULLUP_ENABLE:
  wcd939x->pullup_ref[micb_index]++;
  if (wcd939x->pullup_ref[micb_index] == 1 &&
      wcd939x->micb_ref[micb_index] == 0)
   snd_soc_component_write_field(component, micb_reg,
            WCD939X_MICB_ENABLE,
            MICB_BIAS_PULL_UP);
  break;
 case MICB_PULLUP_DISABLE:
  if (wcd939x->pullup_ref[micb_index] > 0)
   wcd939x->pullup_ref[micb_index]--;
  if (wcd939x->pullup_ref[micb_index] == 0 &&
      wcd939x->micb_ref[micb_index] == 0)
   snd_soc_component_write_field(component, micb_reg,
            WCD939X_MICB_ENABLE,
            MICB_BIAS_DISABLE);
  break;
 case MICB_ENABLE:
  wcd939x->micb_ref[micb_index]++;
  if (wcd939x->micb_ref[micb_index] == 1) {
   snd_soc_component_write_field(component,
      WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
      WCD939X_CDC_DIG_CLK_CTL_TXD3_CLK_EN, true);
   snd_soc_component_write_field(component,
      WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
      WCD939X_CDC_DIG_CLK_CTL_TXD2_CLK_EN, true);
   snd_soc_component_write_field(component,
      WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
      WCD939X_CDC_DIG_CLK_CTL_TXD1_CLK_EN, true);
   snd_soc_component_write_field(component,
      WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
      WCD939X_CDC_DIG_CLK_CTL_TXD0_CLK_EN, true);
   snd_soc_component_write_field(component,
      WCD939X_DIGITAL_CDC_ANA_CLK_CTL,
      WCD939X_CDC_ANA_CLK_CTL_ANA_TX_DIV2_CLK_EN,
      true);
   snd_soc_component_write_field(component,
      WCD939X_DIGITAL_CDC_ANA_TX_CLK_CTL,
      WCD939X_CDC_ANA_TX_CLK_CTL_ANA_TXSCBIAS_CLK_EN,
      true);
   snd_soc_component_write_field(component,
      WCD939X_MICB1_TEST_CTL_2,
      WCD939X_TEST_CTL_2_IBIAS_LDO_DRIVER, true);
   snd_soc_component_write_field(component,
      WCD939X_MICB2_TEST_CTL_2,
      WCD939X_TEST_CTL_2_IBIAS_LDO_DRIVER, true);
   snd_soc_component_write_field(component,
      WCD939X_MICB3_TEST_CTL_2,
      WCD939X_TEST_CTL_2_IBIAS_LDO_DRIVER, true);
   snd_soc_component_write_field(component,
      WCD939X_MICB4_TEST_CTL_2,
      WCD939X_TEST_CTL_2_IBIAS_LDO_DRIVER, true);
   snd_soc_component_write_field(component, micb_reg,
            WCD939X_MICB_ENABLE,
            MICB_BIAS_ENABLE);
   if (micb_num == MIC_BIAS_2)
    wcd_mbhc_event_notify(wcd939x->wcd_mbhc,
            WCD_EVENT_POST_MICBIAS_2_ON);
  }
  if (micb_num == MIC_BIAS_2 && is_dapm)
   wcd_mbhc_event_notify(wcd939x->wcd_mbhc,
           WCD_EVENT_POST_DAPM_MICBIAS_2_ON);
  break;
 case MICB_DISABLE:
  if (wcd939x->micb_ref[micb_index] > 0)
   wcd939x->micb_ref[micb_index]--;

  if (wcd939x->micb_ref[micb_index] == 0 &&
      wcd939x->pullup_ref[micb_index] > 0)
   snd_soc_component_write_field(component, micb_reg,
            WCD939X_MICB_ENABLE,
            MICB_BIAS_PULL_UP);
  else if (wcd939x->micb_ref[micb_index] == 0 &&
    wcd939x->pullup_ref[micb_index] == 0) {
   if (micb_num  == MIC_BIAS_2)
    wcd_mbhc_event_notify(wcd939x->wcd_mbhc,
            WCD_EVENT_PRE_MICBIAS_2_OFF);

   snd_soc_component_write_field(component, micb_reg,
            WCD939X_MICB_ENABLE,
            MICB_BIAS_DISABLE);
   if (micb_num  == MIC_BIAS_2)
    wcd_mbhc_event_notify(wcd939x->wcd_mbhc,
            WCD_EVENT_POST_MICBIAS_2_OFF);
  }
  if (is_dapm && micb_num  == MIC_BIAS_2)
   wcd_mbhc_event_notify(wcd939x->wcd_mbhc,
           WCD_EVENT_POST_DAPM_MICBIAS_2_OFF);
  break;
 }

 return 0;
}

static int wcd939x_codec_enable_micbias(struct snd_soc_dapm_widget *w,
     struct snd_kcontrol *kcontrol,
     int event)
{
 struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
 int micb_num = w->shift;

 switch (event) {
 case SND_SOC_DAPM_PRE_PMU:
  wcd939x_micbias_control(component, micb_num, MICB_ENABLE, true);
  break;
 case SND_SOC_DAPM_POST_PMU:
  /* 1 msec delay as per HW requirement */
  usleep_range(1000, 1100);
  break;
 case SND_SOC_DAPM_POST_PMD:
  wcd939x_micbias_control(component, micb_num, MICB_DISABLE, true);
  break;
 }

 return 0;
}

static int wcd939x_codec_enable_micbias_pullup(struct snd_soc_dapm_widget *w,
            struct snd_kcontrol *kcontrol,
            int event)
{
 struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
 int micb_num = w->shift;

 switch (event) {
 case SND_SOC_DAPM_PRE_PMU:
  wcd939x_micbias_control(component, micb_num,
     MICB_PULLUP_ENABLE, true);
  break;
 case SND_SOC_DAPM_POST_PMU:
  /* 1 msec delay as per HW requirement */
  usleep_range(1000, 1100);
  break;
 case SND_SOC_DAPM_POST_PMD:
  wcd939x_micbias_control(component, micb_num,
     MICB_PULLUP_DISABLE, true);
  break;
 }

 return 0;
}

static int wcd939x_tx_mode_get(struct snd_kcontrol *kcontrol,
          struct snd_ctl_elem_value *ucontrol)
{
 struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
 struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
 struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
 int path = e->shift_l;

 ucontrol->value.enumerated.item[0] = wcd939x->tx_mode[path];

 return 0;
}

static int wcd939x_tx_mode_put(struct snd_kcontrol *kcontrol,
          struct snd_ctl_elem_value *ucontrol)
{
 struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
 struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
 struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
 int path = e->shift_l;

 if (wcd939x->tx_mode[path] == ucontrol->value.enumerated.item[0])
  return 0;

 wcd939x->tx_mode[path] = ucontrol->value.enumerated.item[0];

 return 1;
}

/* RX Controls */

static int wcd939x_rx_hph_mode_get(struct snd_kcontrol *kcontrol,
       struct snd_ctl_elem_value *ucontrol)
{
 struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
 struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);

 ucontrol->value.integer.value[0] = wcd939x->hph_mode;

 return 0;
}

static int wcd939x_rx_hph_mode_put(struct snd_kcontrol *kcontrol,
       struct snd_ctl_elem_value *ucontrol)
{
 struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
 struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
 u32 mode_val;

 mode_val = ucontrol->value.enumerated.item[0];

 if (mode_val == wcd939x->hph_mode)
  return 0;

 if (wcd939x->variant == CHIPID_WCD9390) {
  switch (mode_val) {
  case CLS_H_NORMAL:
  case CLS_H_LP:
  case CLS_AB:
  case CLS_H_LOHIFI:
  case CLS_H_ULP:
  case CLS_AB_LP:
  case CLS_AB_LOHIFI:
   wcd939x->hph_mode = mode_val;
   return 1;
  }
 } else {
  switch (mode_val) {
  case CLS_H_NORMAL:
  case CLS_H_HIFI:
  case CLS_H_LP:
  case CLS_AB:
  case CLS_H_LOHIFI:
  case CLS_H_ULP:
  case CLS_AB_HIFI:
  case CLS_AB_LP:
  case CLS_AB_LOHIFI:
   wcd939x->hph_mode = mode_val;
   return 1;
  }
 }

 dev_dbg(component->dev, "%s: Invalid HPH Mode\n", __func__);
 return -EINVAL;
}

static int wcd939x_get_compander(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_component *component = snd_soc_kcontrol_component(kcontrol);
 struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);

 if (mc->shift)
  ucontrol->value.integer.value[0] = wcd939x->comp2_enable ? 1 : 0;
 else
  ucontrol->value.integer.value[0] = wcd939x->comp1_enable ? 1 : 0;

 return 0;
}

static int wcd939x_set_compander(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_component *component = snd_soc_kcontrol_component(kcontrol);
 struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
 struct wcd939x_sdw_priv *wcd = wcd939x->sdw_priv[AIF1_PB];
 bool value = !!ucontrol->value.integer.value[0];
 int portidx = wcd->ch_info[mc->reg].port_num;

 if (mc->shift)
  wcd939x->comp2_enable = value;
 else
  wcd939x->comp1_enable = value;

 if (value)
  wcd939x_connect_port(wcd, portidx, mc->reg, true);
 else
  wcd939x_connect_port(wcd, portidx, mc->reg, false);

 return 1;
}

static int wcd939x_ldoh_get(struct snd_kcontrol *kcontrol,
       struct snd_ctl_elem_value *ucontrol)
{
 struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
 struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);

 ucontrol->value.integer.value[0] = wcd939x->ldoh ? 1 : 0;

 return 0;
}

static int wcd939x_ldoh_put(struct snd_kcontrol *kcontrol,
       struct snd_ctl_elem_value *ucontrol)
{
 struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
 struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);

 if (wcd939x->ldoh == !!ucontrol->value.integer.value[0])
  return 0;

 wcd939x->ldoh = !!ucontrol->value.integer.value[0];

 return 1;
}

static const char * const tx_mode_mux_text_wcd9390[] = {
 "ADC_INVALID""ADC_HIFI""ADC_LO_HIF""ADC_NORMAL""ADC_LP",
};

static const struct soc_enum tx0_mode_mux_enum_wcd9390 =
 SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tx_mode_mux_text_wcd9390),
   tx_mode_mux_text_wcd9390);

static const struct soc_enum tx1_mode_mux_enum_wcd9390 =
 SOC_ENUM_SINGLE(SND_SOC_NOPM, 1, ARRAY_SIZE(tx_mode_mux_text_wcd9390),
   tx_mode_mux_text_wcd9390);

static const struct soc_enum tx2_mode_mux_enum_wcd9390 =
 SOC_ENUM_SINGLE(SND_SOC_NOPM, 2, ARRAY_SIZE(tx_mode_mux_text_wcd9390),
   tx_mode_mux_text_wcd9390);

static const struct soc_enum tx3_mode_mux_enum_wcd9390 =
 SOC_ENUM_SINGLE(SND_SOC_NOPM, 3, ARRAY_SIZE(tx_mode_mux_text_wcd9390),
   tx_mode_mux_text_wcd9390);

static const char * const tx_mode_mux_text[] = {
 "ADC_INVALID""ADC_HIFI""ADC_LO_HIF""ADC_NORMAL""ADC_LP",
 "ADC_ULP1""ADC_ULP2",
};

static const struct soc_enum tx0_mode_mux_enum =
 SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tx_mode_mux_text),
   tx_mode_mux_text);

static const struct soc_enum tx1_mode_mux_enum =
 SOC_ENUM_SINGLE(SND_SOC_NOPM, 1, ARRAY_SIZE(tx_mode_mux_text),
   tx_mode_mux_text);

static const struct soc_enum tx2_mode_mux_enum =
 SOC_ENUM_SINGLE(SND_SOC_NOPM, 2, ARRAY_SIZE(tx_mode_mux_text),
   tx_mode_mux_text);

static const struct soc_enum tx3_mode_mux_enum =
 SOC_ENUM_SINGLE(SND_SOC_NOPM, 3, ARRAY_SIZE(tx_mode_mux_text),
   tx_mode_mux_text);

static const char * const rx_hph_mode_mux_text_wcd9390[] = {
 "CLS_H_NORMAL""CLS_H_INVALID_1""CLS_H_LP""CLS_AB",
 "CLS_H_LOHIFI""CLS_H_ULP""CLS_H_INVALID_2""CLS_AB_LP",
 "CLS_AB_LOHIFI",
};

static const struct soc_enum rx_hph_mode_mux_enum_wcd9390 =
 SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text_wcd9390),
       rx_hph_mode_mux_text_wcd9390);

static const char * const rx_hph_mode_mux_text[] = {
 "CLS_H_NORMAL""CLS_H_HIFI""CLS_H_LP""CLS_AB""CLS_H_LOHIFI",
 "CLS_H_ULP""CLS_AB_HIFI""CLS_AB_LP""CLS_AB_LOHIFI",
};

static const struct soc_enum rx_hph_mode_mux_enum =
 SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text),
       rx_hph_mode_mux_text);

static const struct snd_kcontrol_new wcd9390_snd_controls[] = {
 SOC_SINGLE_TLV("EAR_PA Volume", WCD939X_ANA_EAR_COMPANDER_CTL,
         2, 0x10, 0, ear_pa_gain),

 SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum_wcd9390,
       wcd939x_rx_hph_mode_get, wcd939x_rx_hph_mode_put),

 SOC_ENUM_EXT("TX0 MODE", tx0_mode_mux_enum_wcd9390,
       wcd939x_tx_mode_get, wcd939x_tx_mode_put),
 SOC_ENUM_EXT("TX1 MODE", tx1_mode_mux_enum_wcd9390,
       wcd939x_tx_mode_get, wcd939x_tx_mode_put),
 SOC_ENUM_EXT("TX2 MODE", tx2_mode_mux_enum_wcd9390,
       wcd939x_tx_mode_get, wcd939x_tx_mode_put),
 SOC_ENUM_EXT("TX3 MODE", tx3_mode_mux_enum_wcd9390,
       wcd939x_tx_mode_get, wcd939x_tx_mode_put),
};

static const struct snd_kcontrol_new wcd9395_snd_controls[] = {
 SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum,
       wcd939x_rx_hph_mode_get, wcd939x_rx_hph_mode_put),

 SOC_ENUM_EXT("TX0 MODE", tx0_mode_mux_enum,
       wcd939x_tx_mode_get, wcd939x_tx_mode_put),
 SOC_ENUM_EXT("TX1 MODE", tx1_mode_mux_enum,
       wcd939x_tx_mode_get, wcd939x_tx_mode_put),
 SOC_ENUM_EXT("TX2 MODE", tx2_mode_mux_enum,
       wcd939x_tx_mode_get, wcd939x_tx_mode_put),
 SOC_ENUM_EXT("TX3 MODE", tx3_mode_mux_enum,
       wcd939x_tx_mode_get, wcd939x_tx_mode_put),
};

static const struct snd_kcontrol_new adc1_switch[] = {
 SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
};

static const struct snd_kcontrol_new adc2_switch[] = {
 SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
};

static const struct snd_kcontrol_new adc3_switch[] = {
 SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
};

static const struct snd_kcontrol_new adc4_switch[] = {
 SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
};

static const struct snd_kcontrol_new dmic1_switch[] = {
 SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
};

static const struct snd_kcontrol_new dmic2_switch[] = {
 SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
};

static const struct snd_kcontrol_new dmic3_switch[] = {
 SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
};

static const struct snd_kcontrol_new dmic4_switch[] = {
 SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
};

static const struct snd_kcontrol_new dmic5_switch[] = {
 SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
};

static const struct snd_kcontrol_new dmic6_switch[] = {
 SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
};

static const struct snd_kcontrol_new dmic7_switch[] = {
 SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
};

static const struct snd_kcontrol_new dmic8_switch[] = {
 SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
};

static const struct snd_kcontrol_new ear_rdac_switch[] = {
 SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
};

static const struct snd_kcontrol_new hphl_rdac_switch[] = {
 SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
};

static const struct snd_kcontrol_new hphr_rdac_switch[] = {
 SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
};

static const char * const adc1_mux_text[] = {
 "CH1_AMIC_DISABLE""CH1_AMIC1""CH1_AMIC2""CH1_AMIC3""CH1_AMIC4""CH1_AMIC5"
};

static const struct soc_enum adc1_enum =
 SOC_ENUM_SINGLE(WCD939X_TX_NEW_CH12_MUX, 0,
   ARRAY_SIZE(adc1_mux_text), adc1_mux_text);

static const struct snd_kcontrol_new tx_adc1_mux =
 SOC_DAPM_ENUM("ADC1 MUX Mux", adc1_enum);

static const char * const adc2_mux_text[] = {
 "CH2_AMIC_DISABLE""CH2_AMIC1""CH2_AMIC2""CH2_AMIC3""CH2_AMIC4""CH2_AMIC5"
};

static const struct soc_enum adc2_enum =
 SOC_ENUM_SINGLE(WCD939X_TX_NEW_CH12_MUX, 3,
   ARRAY_SIZE(adc2_mux_text), adc2_mux_text);

static const struct snd_kcontrol_new tx_adc2_mux =
 SOC_DAPM_ENUM("ADC2 MUX Mux", adc2_enum);

static const char * const adc3_mux_text[] = {
 "CH3_AMIC_DISABLE""CH3_AMIC1""CH3_AMIC3""CH3_AMIC4""CH3_AMIC5"
};

static const struct soc_enum adc3_enum =
 SOC_ENUM_SINGLE(WCD939X_TX_NEW_CH34_MUX, 0,
   ARRAY_SIZE(adc3_mux_text), adc3_mux_text);

static const struct snd_kcontrol_new tx_adc3_mux =
 SOC_DAPM_ENUM("ADC3 MUX Mux", adc3_enum);

static const char * const adc4_mux_text[] = {
 "CH4_AMIC_DISABLE""CH4_AMIC1""CH4_AMIC3""CH4_AMIC4""CH4_AMIC5"
};

static const struct soc_enum adc4_enum =
 SOC_ENUM_SINGLE(WCD939X_TX_NEW_CH34_MUX, 3,
   ARRAY_SIZE(adc4_mux_text), adc4_mux_text);

static const struct snd_kcontrol_new tx_adc4_mux =
 SOC_DAPM_ENUM("ADC4 MUX Mux", adc4_enum);

static const char * const rdac3_mux_text[] = {
 "RX3""RX1"
};

static const struct soc_enum rdac3_enum =
 SOC_ENUM_SINGLE(WCD939X_DIGITAL_CDC_EAR_PATH_CTL, 0,
   ARRAY_SIZE(rdac3_mux_text), rdac3_mux_text);

static const struct snd_kcontrol_new rx_rdac3_mux =
 SOC_DAPM_ENUM("RDAC3_MUX Mux", rdac3_enum);

static int wcd939x_get_swr_port(struct snd_kcontrol *kcontrol,
    struct snd_ctl_elem_value *ucontrol)
{
 struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value;
 struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
 struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(comp);
 struct wcd939x_sdw_priv *wcd = wcd939x->sdw_priv[mixer->shift];
 unsigned int portidx = wcd->ch_info[mixer->reg].port_num;

 ucontrol->value.integer.value[0] = wcd->port_enable[portidx] ? 1 : 0;

 return 0;
}

static const char *version_to_str(u32 version)
{
 switch (version) {
 case WCD939X_VERSION_1_0:
  return __stringify(WCD939X_1_0);
 case WCD939X_VERSION_1_1:
  return __stringify(WCD939X_1_1);
 case WCD939X_VERSION_2_0:
  return __stringify(WCD939X_2_0);
 }
 return NULL;
}

static int wcd939x_set_swr_port(struct snd_kcontrol *kcontrol,
    struct snd_ctl_elem_value *ucontrol)
{
 struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value;
 struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
 struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(comp);
 struct wcd939x_sdw_priv *wcd = wcd939x->sdw_priv[mixer->shift];
 unsigned int portidx = wcd->ch_info[mixer->reg].port_num;

 wcd->port_enable[portidx] = !!ucontrol->value.integer.value[0];

 wcd939x_connect_port(wcd, portidx, mixer->reg, wcd->port_enable[portidx]);

 return 1;
}

/* MBHC Related */

static void wcd939x_mbhc_clk_setup(struct snd_soc_component *component,
       bool enable)
{
 snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_1,
          WCD939X_CTL_1_RCO_EN, enable);
}

static void wcd939x_mbhc_mbhc_bias_control(struct snd_soc_component *component,
        bool enable)
{
 snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ELECT,
          WCD939X_MBHC_ELECT_BIAS_EN, enable);
}

static void wcd939x_mbhc_program_btn_thr(struct snd_soc_component *component,
      int *btn_low, int *btn_high,
      int num_btn, bool is_micbias)
{
 int i, vth;

 if (num_btn > WCD_MBHC_DEF_BUTTONS) {
  dev_err(component->dev, "%s: invalid number of buttons: %d\n",
   __func__, num_btn);
  return;
 }

 for (i = 0; i < num_btn; i++) {
  vth = (btn_high[i] * 2) / 25;
  snd_soc_component_write_field(component, WCD939X_ANA_MBHC_BTN0 + i,
           WCD939X_MBHC_BTN0_VTH, vth);
  dev_dbg(component->dev, "%s: btn_high[%d]: %d, vth: %d\n",
   __func__, i, btn_high[i], vth);
 }
}

static bool wcd939x_mbhc_micb_en_status(struct snd_soc_component *component, int micb_num)
{
 if (micb_num == MIC_BIAS_2) {
  u8 val;

  val = FIELD_GET(WCD939X_MICB_ENABLE,
    snd_soc_component_read(component, WCD939X_ANA_MICB2));
  if (val == MICB_BIAS_ENABLE)
   return true;
 }

 return false;
}

static void wcd939x_mbhc_hph_l_pull_up_control(struct snd_soc_component *component,
            int pull_up_cur)
{
 /* Default pull up current to 2uA */
 if (pull_up_cur > HS_PULLUP_I_OFF ||
     pull_up_cur < HS_PULLUP_I_3P0_UA ||
     pull_up_cur == HS_PULLUP_I_DEFAULT)
  pull_up_cur = HS_PULLUP_I_2P0_UA;

 dev_dbg(component->dev, "%s: HS pull up current:%d\n",
  __func__, pull_up_cur);

 snd_soc_component_write_field(component, WCD939X_MBHC_NEW_INT_MECH_DET_CURRENT,
          WCD939X_MECH_DET_CURRENT_HSDET_PULLUP_CTL, pull_up_cur);
}

static int wcd939x_mbhc_request_micbias(struct snd_soc_component *component,
     int micb_num, int req)
{
 return wcd939x_micbias_control(component, micb_num, req, false);
}

static void wcd939x_mbhc_micb_ramp_control(struct snd_soc_component *component,
        bool enable)
{
 if (enable) {
  snd_soc_component_write_field(component, WCD939X_ANA_MICB2_RAMP,
           WCD939X_MICB2_RAMP_SHIFT_CTL, 3);
  snd_soc_component_write_field(component, WCD939X_ANA_MICB2_RAMP,
           WCD939X_MICB2_RAMP_RAMP_ENABLE, true);
 } else {
  snd_soc_component_write_field(component, WCD939X_ANA_MICB2_RAMP,
           WCD939X_MICB2_RAMP_RAMP_ENABLE, false);
  snd_soc_component_write_field(component, WCD939X_ANA_MICB2_RAMP,
           WCD939X_MICB2_RAMP_SHIFT_CTL, 0);
 }
}

static int wcd939x_get_micb_vout_ctl_val(u32 micb_mv)
{
 /* min micbias voltage is 1V and maximum is 2.85V */
 if (micb_mv < 1000 || micb_mv > 2850) {
  pr_err("%s: unsupported micbias voltage\n", __func__);
  return -EINVAL;
 }

 return (micb_mv - 1000) / 50;
}

static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component,
         int req_volt, int micb_num)
{
 struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
 unsigned int micb_reg, cur_vout_ctl, micb_en;
 int req_vout_ctl;
 int ret = 0;

 switch (micb_num) {
 case MIC_BIAS_1:
  micb_reg = WCD939X_ANA_MICB1;
  break;
 case MIC_BIAS_2:
  micb_reg = WCD939X_ANA_MICB2;
  break;
 case MIC_BIAS_3:
  micb_reg = WCD939X_ANA_MICB3;
  break;
 case MIC_BIAS_4:
  micb_reg = WCD939X_ANA_MICB4;
  break;
 default:
  return -EINVAL;
 }
 mutex_lock(&wcd939x->micb_lock);

 /*
 * If requested micbias voltage is same as current micbias
 * voltage, then just return. Otherwise, adjust voltage as
 * per requested value. If micbias is already enabled, then
 * to avoid slow micbias ramp-up or down enable pull-up
 * momentarily, change the micbias value and then re-enable
 * micbias.
 */

 micb_en = snd_soc_component_read_field(component, micb_reg,
            WCD939X_MICB_ENABLE);
 cur_vout_ctl = snd_soc_component_read_field(component, micb_reg,
          WCD939X_MICB_VOUT_CTL);

 req_vout_ctl = wcd939x_get_micb_vout_ctl_val(req_volt);
 if (req_vout_ctl < 0) {
  ret = req_vout_ctl;
  goto exit;
 }

 if (cur_vout_ctl == req_vout_ctl) {
  ret = 0;
  goto exit;
 }

 dev_dbg(component->dev, "%s: micb_num: %d, cur_mv: %d, req_mv: %d, micb_en: %d\n",
  __func__, micb_num, WCD_VOUT_CTL_TO_MICB(cur_vout_ctl),
   req_volt, micb_en);

 if (micb_en == MICB_BIAS_ENABLE)
  snd_soc_component_write_field(component, micb_reg,
           WCD939X_MICB_ENABLE,
           MICB_BIAS_PULL_DOWN);

 snd_soc_component_write_field(component, micb_reg,
          WCD939X_MICB_VOUT_CTL, req_vout_ctl);

 if (micb_en == MICB_BIAS_ENABLE) {
  snd_soc_component_write_field(component, micb_reg,
           WCD939X_MICB_ENABLE,
           MICB_BIAS_ENABLE);
  /*
 * Add 2ms delay as per HW requirement after enabling
 * micbias
 */

  usleep_range(2000, 2100);
 }

exit:
 mutex_unlock(&wcd939x->micb_lock);
 return ret;
}

static int wcd939x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *component,
      int micb_num, bool req_en)
{
 struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
 int micb_mv;

 if (micb_num != MIC_BIAS_2)
  return -EINVAL;
 /*
 * If device tree micbias level is already above the minimum
 * voltage needed to detect threshold microphone, then do
 * not change the micbias, just return.
 */

 if (wcd939x->micb2_mv >= WCD_MBHC_THR_HS_MICB_MV)
  return 0;

 micb_mv = req_en ? WCD_MBHC_THR_HS_MICB_MV : wcd939x->micb2_mv;

 return wcd939x_mbhc_micb_adjust_voltage(component, micb_mv, MIC_BIAS_2);
}

/* Selected by WCD939X_MBHC_GET_C1() */
static const s16 wcd939x_wcd_mbhc_d1_a[4] = {
 0, 30, 30, 6
};

--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=95 H=95 G=94

¤ Dauer der Verarbeitung: 0.23 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.