Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/media/ffvpx/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 7 kB image not shown  

Quelle  wcd939x.c   Sprache: unbekannt

 
// 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

[ Seitenstruktur0.13Drucken  etwas mehr zur Ethik  ]