// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved. * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "wcd9378-reg-masks.h" #include "wcd9378.h" #include "internal.h" #include "asoc/bolero-slave-internal.h" #define NUM_SWRS_DT_PARAMS 5 #define WCD9378_MOBILE_MODE 0x01 #define WCD9378_VERSION_1_0 1 #define WCD9378_VERSION_ENTRY_SIZE 32 #define SWR_BASECLK_19P2MHZ (0x01) #define SWR_BASECLK_24P576MHZ (0x03) #define SWR_BASECLK_22P5792MHZ (0x04) #define SWR_CLKSCALE_DIV2 (0x02) #define ADC_MODE_VAL_HIFI 0x01 #define ADC_MODE_VAL_NORMAL 0x03 #define ADC_MODE_VAL_LP 0x05 #define PWR_LEVEL_LOHIFI_VAL 0x00 #define PWR_LEVEL_LP_VAL 0x01 #define PWR_LEVEL_HIFI_VAL 0x02 #define PWR_LEVEL_ULP_VAL 0x03 #define WCD9378_MBQ_ENABLE_MASK 0x2000 #define MICB_USAGE_VAL_DISABLE 0x00 #define MICB_USAGE_VAL_PULL_DOWN 0x01 #define MICB_USAGE_VAL_1P2V 0x02 #define MICB_USAGE_VAL_1P8VORPULLUP 0x03 #define MICB_USAGE_VAL_2P5V 0x04 #define MICB_USAGE_VAL_2P75V 0x05 #define MICB_USAGE_VAL_2P2V 0xF0 #define MICB_USAGE_VAL_2P7V 0xF1 #define MICB_USAGE_VAL_2P8V 0xF2 #define MICB_USAGE_VAL_MICB1_TABLE_VAL 0xF3 #define MICB_USAGE_VAL_MICB2_TABLE_VAL 0xF4 #define MICB_USAGE_VAL_MICB3_TABLE_VAL 0xF5 #define WCD_TX_SYS_USAGE_BIT_MASK (0xFC) #define WCD_RX_SYS_USAGE_BIT_MASK (0x1F00) #define MICB_NUM_MAX 3 #define NUM_ATTEMPTS 20 #define WCD9378_RATES (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 WCD9378_FRAC_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800) #define WCD9378_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ SNDRV_PCM_FMTBIT_S24_LE |\ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) #define WCD9378_EAR_PA_GAIN_TLV(xname, reg, shift, max, invert, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ SNDRV_CTL_ELEM_ACCESS_READWRITE,\ .tlv.p = (tlv_array), \ .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\ .put = wcd9378_ear_pa_put_gain, \ .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) } #define WCD9378_AUX_PA_GAIN_TLV(xname, reg, shift, max, invert, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ SNDRV_CTL_ELEM_ACCESS_READWRITE,\ .tlv.p = (tlv_array), \ .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\ .put = wcd9378_aux_pa_put_gain, \ .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) } enum { CODEC_TX = 0, CODEC_RX, }; enum { RX2_HP_MODE, RX2_NORMAL_MODE, }; enum { CLASS_AB_EN = 0, TX1_FOR_JACK, TX2_AMIC4_EN, TX2_AMIC1_EN, TX1_AMIC3_EN, TX1_AMIC2_EN, TX0_AMIC2_EN, TX0_AMIC1_EN, RX2_EAR_EN, RX2_AUX_EN, RX1_AUX_EN, RX0_EAR_EN, RX0_RX1_HPH_EN, }; enum { WCD_ADC1 = 0, WCD_ADC2, WCD_ADC3, WCD_ADC4, ALLOW_BUCK_DISABLE, HPH_COMP_DELAY, HPH_PA_DELAY, AMIC2_BCS_ENABLE, WCD_SUPPLIES_LPM_MODE, WCD_ADC1_MODE, WCD_ADC2_MODE, WCD_ADC3_MODE, WCD_ADC4_MODE, WCD_AUX_EN, WCD_EAR_EN, }; enum { SYS_USAGE_0, SYS_USAGE_1, SYS_USAGE_2, SYS_USAGE_3, SYS_USAGE_4, SYS_USAGE_5, SYS_USAGE_6, SYS_USAGE_7, SYS_USAGE_8, SYS_USAGE_9, SYS_USAGE_10, SYS_USAGE_11, SYS_USAGE_12, SYS_USAGE_NUM, }; enum { NO_MICB_USED, MICB1, MICB2, MICB3, MICB_NUM, }; enum { ADC_MODE_INVALID = 0, ADC_MODE_HIFI, ADC_MODE_NORMAL, ADC_MODE_LP, }; static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(analog_gain, 0, 3000); static int wcd9378_reset(struct device *dev); static int wcd9378_reset_low(struct device *dev); static void wcd9378_class_load(struct snd_soc_component *component); /* sys_usage: * rx0_rx1_hph_en, * rx0_ear_en, rx1_aux_en, rx2_aux_en, rx2_ear_en, * tx0_amic1_en, tx0_amic2_en, tx1_amic2_en, tx1_amic3_en, * tx2_amic1_en, tx2_amic4_en, tx1_for_jack, class_ab_en; */ static const int sys_usage[SYS_USAGE_NUM] = { [SYS_USAGE_0] = 0x0c95, /*0b0 1100 1001 0101*/ [SYS_USAGE_1] = 0x12a7, /*0b1 0010 1010 0111*/ [SYS_USAGE_2] = 0x0c99, /*0b0 1100 1001 1001*/ [SYS_USAGE_3] = 0x1aab, /*0b1 1010 1010 1011*/ [SYS_USAGE_4] = 0x0894, /*0b0 1000 1001 0100*/ [SYS_USAGE_5] = 0x11a6, /*0b1 0001 1010 0110*/ [SYS_USAGE_6] = 0x0898, /*0b0 1000 1001 1000*/ [SYS_USAGE_7] = 0x11ab, /*0b1 0001 1010 1011*/ [SYS_USAGE_8] = 0x126a, /*0b1 0010 0110 1010*/ [SYS_USAGE_9] = 0x116b, /*0b1 0001 0110 1011*/ [SYS_USAGE_10] = 0x1ca7, /*0b1 1100 1010 0111*/ [SYS_USAGE_11] = 0x1195, /*0b1 0001 1001 0101*/ [SYS_USAGE_12] = 0x1296, /*0b1 0010 1001 0101*/ }; static const struct regmap_irq wcd9378_regmap_irqs[WCD9378_NUM_IRQS] = { REGMAP_IRQ_REG(WCD9378_IRQ_MBHC_BUTTON_PRESS_DET, 0, 0x01), REGMAP_IRQ_REG(WCD9378_IRQ_MBHC_BUTTON_RELEASE_DET, 0, 0x02), REGMAP_IRQ_REG(WCD9378_IRQ_MBHC_ELECT_INS_REM_DET, 0, 0x04), REGMAP_IRQ_REG(WCD9378_IRQ_MBHC_ELECT_INS_REM_LEG_DET, 0, 0x08), REGMAP_IRQ_REG(WCD9378_IRQ_MBHC_SW_DET, 0, 0x10), REGMAP_IRQ_REG(WCD9378_IRQ_HPHR_OCP_INT, 0, 0x20), REGMAP_IRQ_REG(WCD9378_IRQ_HPHR_CNP_INT, 0, 0x40), REGMAP_IRQ_REG(WCD9378_IRQ_HPHL_OCP_INT, 0, 0x80), REGMAP_IRQ_REG(WCD9378_IRQ_HPHL_CNP_INT, 1, 0x01), REGMAP_IRQ_REG(WCD9378_IRQ_EAR_CNP_INT, 1, 0x02), REGMAP_IRQ_REG(WCD9378_IRQ_EAR_SCD_INT, 1, 0x04), REGMAP_IRQ_REG(WCD9378_IRQ_AUX_CNP_INT, 1, 0x08), REGMAP_IRQ_REG(WCD9378_IRQ_AUX_SCD_INT, 1, 0x10), REGMAP_IRQ_REG(WCD9378_IRQ_HPHL_PDM_WD_INT, 1, 0x20), REGMAP_IRQ_REG(WCD9378_IRQ_HPHR_PDM_WD_INT, 1, 0x40), REGMAP_IRQ_REG(WCD9378_IRQ_AUX_PDM_WD_INT, 1, 0x80), REGMAP_IRQ_REG(WCD9378_IRQ_LDORT_SCD_INT, 2, 0x01), REGMAP_IRQ_REG(WCD9378_IRQ_MBHC_MOISTURE_INT, 2, 0x02), REGMAP_IRQ_REG(WCD9378_IRQ_HPHL_SURGE_DET_INT, 2, 0x04), REGMAP_IRQ_REG(WCD9378_IRQ_HPHR_SURGE_DET_INT, 2, 0x08), REGMAP_IRQ_REG(WCD9378_IRQ_SAPU_PROT_MODE_CHG, 2, 0x40), }; static int wcd9378_handle_post_irq(void *data) { struct wcd9378_priv *wcd9378 = data; u32 sts1 = 0, sts2 = 0, sts3 = 0; regmap_write(wcd9378->regmap, SWRS_SCP_SDCA_INTSTAT_1, 0xff); regmap_write(wcd9378->regmap, SWRS_SCP_SDCA_INTSTAT_2, 0xff); regmap_write(wcd9378->regmap, SWRS_SCP_SDCA_INTSTAT_3, 0xff); regmap_read(wcd9378->regmap, SWRS_SCP_SDCA_INTSTAT_1, &sts1); regmap_read(wcd9378->regmap, SWRS_SCP_SDCA_INTSTAT_2, &sts2); regmap_read(wcd9378->regmap, SWRS_SCP_SDCA_INTSTAT_3, &sts3); wcd9378->tx_swr_dev->slave_irq_pending = ((sts1 || sts2 || sts3) ? true : false); return IRQ_HANDLED; } static struct regmap_irq_chip wcd9378_regmap_irq_chip = { .name = "wcd9378", .irqs = wcd9378_regmap_irqs, .num_irqs = ARRAY_SIZE(wcd9378_regmap_irqs), .num_regs = 3, .status_base = SWRS_SCP_SDCA_INTSTAT_1, .unmask_base = SWRS_SCP_SDCA_INTMASK_1, .type_base = SWRS_SCP_SDCA_INTRTYPE_1, .ack_base = SWRS_SCP_SDCA_INTSTAT_1, .use_ack = 1, .runtime_pm = false, .handle_post_irq = wcd9378_handle_post_irq, .irq_drv_data = NULL, }; static int wcd9378_swr_slv_get_current_bank(struct swr_device *dev, u8 devnum) { int ret = 0; int bank = 0; ret = swr_read(dev, devnum, SWR_SCP_CONTROL, &bank, 1); if (ret) return -EINVAL; return ((bank & 0x40) ? 1 : 0); } static int wcd9378_swr_reset_check(struct wcd9378_priv *wcd9378, int path) { if (((path == TX_PATH) && (wcd9378->sys_usage_status & WCD_TX_SYS_USAGE_BIT_MASK)) || ((path == RX_PATH) && (wcd9378->sys_usage_status & WCD_RX_SYS_USAGE_BIT_MASK))) return false; return true; } static int wcd9378_swr_slvdev_datapath_control(struct device *dev, int path, bool enable) { struct wcd9378_priv *wcd9378 = NULL; struct swr_device *swr_dev = NULL; int bank = 0, ret = 0; u8 clk_rst = 0x00, scale_rst = 0x00; u8 swr_clk = 0, clk_scale = 0; u16 scale_reg = 0, scale_reg2 = 0; wcd9378 = dev_get_drvdata(dev); if (!wcd9378) return -EINVAL; if (path == RX_PATH) { swr_dev = wcd9378->rx_swr_dev; swr_clk = wcd9378->swr_base_clk; clk_scale = wcd9378->swr_clk_scale; } else { swr_dev = wcd9378->tx_swr_dev; swr_clk = SWR_BASECLK_19P2MHZ; clk_scale = SWR_CLKSCALE_DIV2; } bank = (wcd9378_swr_slv_get_current_bank(swr_dev, swr_dev->dev_num) ? 0 : 1); scale_reg = (bank ? SWRS_SCP_BUSCLOCK_SCALE_BANK1 : SWRS_SCP_BUSCLOCK_SCALE_BANK0); scale_reg2 = (!bank ? SWRS_SCP_BUSCLOCK_SCALE_BANK1 : SWRS_SCP_BUSCLOCK_SCALE_BANK0); if (enable) { swr_write(swr_dev, swr_dev->dev_num, SWRS_SCP_BASE_CLK_BASE, &swr_clk); swr_write(swr_dev, swr_dev->dev_num, scale_reg, &clk_scale); swr_write(swr_dev, swr_dev->dev_num, scale_reg2, &clk_scale); ret = swr_slvdev_datapath_control(swr_dev, swr_dev->dev_num, true); } else { if (wcd9378_swr_reset_check(wcd9378, path)) { swr_write(swr_dev, swr_dev->dev_num, SWRS_SCP_BASE_CLK_BASE, &clk_rst); swr_write(swr_dev, swr_dev->dev_num, scale_reg, &scale_rst); swr_write(swr_dev, swr_dev->dev_num, scale_reg2, &scale_rst); } ret = swr_slvdev_datapath_control(swr_dev, swr_dev->dev_num, false); } return ret; } static int wcd9378_init_reg(struct snd_soc_component *component) { struct wcd9378_priv *wcd9378 = snd_soc_component_get_drvdata(component); u32 val = 0; val = snd_soc_component_read(component, WCD9378_EFUSE_REG_16); if (!val) snd_soc_component_update_bits(component, WCD9378_MBHC_CTL_SPARE_1, WCD9378_MBHC_CTL_SPARE_1_BIASGEN_RES_CTRL_MASK, 0x03); else snd_soc_component_update_bits(component, WCD9378_MBHC_CTL_SPARE_1, WCD9378_MBHC_CTL_SPARE_1_BIASGEN_RES_CTRL_MASK, 0x01); /*0.9 Volts*/ snd_soc_component_update_bits(component, WCD9378_SLEEP_CTL, WCD9378_SLEEP_CTL_BG_CTL_MASK, 0x0E); /*BG_EN ENABLE*/ snd_soc_component_update_bits(component, WCD9378_SLEEP_CTL, WCD9378_SLEEP_CTL_BG_EN_MASK, 0x80); usleep_range(1000, 1010); /*LDOL_BG_SEL SLEEP_BG*/ snd_soc_component_update_bits(component, WCD9378_SLEEP_CTL, WCD9378_SLEEP_CTL_LDOL_BG_SEL_MASK, 0x40); usleep_range(1000, 1010); /*Start up analog master bias. Sequence cannot change*/ /*VBG_FINE_ADJ 0.005 Volts*/ snd_soc_component_update_bits(component, WCD9378_BIAS_VBG_FINE_ADJ, WCD9378_BIAS_VBG_FINE_ADJ_VBG_FINE_ADJ_MASK, 0xB0); /*ANALOG_BIAS_EN ENABLE*/ snd_soc_component_update_bits(component, WCD9378_ANA_BIAS, WCD9378_ANA_BIAS_ANALOG_BIAS_EN_MASK, 0x80); /*PRECHRG_EN ENABLE*/ snd_soc_component_update_bits(component, WCD9378_ANA_BIAS, WCD9378_ANA_BIAS_PRECHRG_EN_MASK, 0x40); usleep_range(10000, 10010); /*PRECHRG_EN DISABLE*/ snd_soc_component_update_bits(component, WCD9378_ANA_BIAS, WCD9378_ANA_BIAS_PRECHRG_EN_MASK, 0x00); /*End Analog Master Bias enable*/ /*ANA_TXSCBIAS_CLK_EN ENABLE*/ snd_soc_component_update_bits(component, WCD9378_CDC_ANA_TX_CLK_CTL, WCD9378_CDC_ANA_TX_CLK_CTL_ANA_TXSCBIAS_CLK_EN_MASK, 0x01); /*SEQ_BYPASS ENABLE*/ snd_soc_component_update_bits(component, WCD9378_TX_COM_TXFE_DIV_CTL, WCD9378_TX_COM_TXFE_DIV_CTL_SEQ_BYPASS_MASK, 0x80); /*TIME_OUT_SEL_PCM 160_CYCLES*/ snd_soc_component_update_bits(component, WCD9378_PDM_WD_CTL0, WCD9378_PDM_WD_CTL0_TIME_OUT_SEL_PCM_MASK, 0x10); /*TIME_OUT_SEL_PCM 160_CYCLES*/ snd_soc_component_update_bits(component, WCD9378_PDM_WD_CTL1, WCD9378_PDM_WD_CTL1_TIME_OUT_SEL_PCM_MASK, 0x10); /*IBIAS_LDO_DRIVER 5e-06*/ snd_soc_component_update_bits(component, WCD9378_MICB1_TEST_CTL_2, WCD9378_MICB1_TEST_CTL_2_IBIAS_LDO_DRIVER_MASK, 0x01); /*IBIAS_LDO_DRIVER 5e-06*/ snd_soc_component_update_bits(component, WCD9378_MICB2_TEST_CTL_2, WCD9378_MICB2_TEST_CTL_2_IBIAS_LDO_DRIVER_MASK, 0x01); /*IBIAS_LDO_DRIVER 5e-06*/ snd_soc_component_update_bits(component, WCD9378_MICB3_TEST_CTL_2, WCD9378_MICB3_TEST_CTL_2_IBIAS_LDO_DRIVER_MASK, 0x01); /*HD2_RES_DIV_CTL_L 82.77*/ snd_soc_component_update_bits(component, WCD9378_HPH_NEW_INT_RDAC_HD2_CTL_L, WCD9378_HPH_NEW_INT_RDAC_HD2_CTL_L_HD2_RES_DIV_CTL_L_MASK, 0x04); /*HD2_RES_DIV_CTL_R 82.77*/ snd_soc_component_update_bits(component, WCD9378_HPH_NEW_INT_RDAC_HD2_CTL_R, WCD9378_HPH_NEW_INT_RDAC_HD2_CTL_R_HD2_RES_DIV_CTL_R_MASK, 0x04); /*RDAC_GAINCTL 0.55*/ snd_soc_component_update_bits(component, WCD9378_HPH_NEW_INT_RDAC_GAIN_CTL, WCD9378_HPH_NEW_INT_RDAC_GAIN_CTL_RDAC_GAINCTL_MASK, 0x50); /*HPH_UP_T0: 0.002*/ snd_soc_component_update_bits(component, WCD9378_HPH_UP_T0, WCD9378_HPH_UP_T0_HPH_UP_T0_MASK, 0x05); /*HPH_UP_T9: 0.002*/ snd_soc_component_update_bits(component, WCD9378_HPH_UP_T9, WCD9378_HPH_UP_T9_HPH_UP_T9_MASK, 0x05); /*HPH_DN_T0: 0.007*/ snd_soc_component_update_bits(component, WCD9378_HPH_DN_T0, WCD9378_HPH_DN_T0_HPH_DN_T0_MASK, 0x06); /*SM0 MB SEL:MB1*/ snd_soc_component_update_bits(component, WCD9378_SM0_MB_SEL, WCD9378_SM0_MB_SEL_SM0_MB_SEL_MASK, 0x01); /*SM1 MB SEL:MB2*/ snd_soc_component_update_bits(component, WCD9378_SM1_MB_SEL, WCD9378_SM1_MB_SEL_SM1_MB_SEL_MASK, 0x02); /*SM2 MB SEL:MB3*/ snd_soc_component_update_bits(component, WCD9378_SM2_MB_SEL, WCD9378_SM2_MB_SEL_SM2_MB_SEL_MASK, 0x03); /*INIT SYS_USAGE*/ snd_soc_component_update_bits(component, WCD9378_SYS_USAGE_CTRL, WCD9378_SYS_USAGE_CTRL_SYS_USAGE_CTRL_MASK, 0); wcd9378->sys_usage = 0; wcd9378_class_load(component); return 0; } static int wcd9378_set_port_params(struct snd_soc_component *component, u8 slv_prt_type, u8 *port_id, u8 *num_ch, u8 *ch_mask, u32 *ch_rate, u8 *port_type, u8 path) { int i, j; u8 num_ports = 0; struct codec_port_info (*map)[MAX_PORT][MAX_CH_PER_PORT]; struct wcd9378_priv *wcd9378 = snd_soc_component_get_drvdata(component); switch (path) { case CODEC_RX: map = &wcd9378->rx_port_mapping; num_ports = wcd9378->num_rx_ports; break; case CODEC_TX: map = &wcd9378->tx_port_mapping; num_ports = wcd9378->num_tx_ports; break; default: dev_err(component->dev, "%s Invalid path selected %u\n", __func__, path); return -EINVAL; } for (i = 0; i <= num_ports; i++) { for (j = 0; j < MAX_CH_PER_PORT; j++) { if ((*map)[i][j].slave_port_type == slv_prt_type) goto found; } } found: if (i > num_ports || j == MAX_CH_PER_PORT) { dev_err(component->dev, "%s Failed to find slave port for type %u\n", __func__, slv_prt_type); return -EINVAL; } *port_id = i; *num_ch = (*map)[i][j].num_ch; *ch_mask = (*map)[i][j].ch_mask; *ch_rate = (*map)[i][j].ch_rate; *port_type = (*map)[i][j].master_port_type; return 0; } static int wcd9378_parse_port_params(struct device *dev, char *prop, u8 path) { u32 *dt_array, map_size, max_uc; int ret = 0; u32 cnt = 0; u32 i, j; struct swr_port_params (*map)[SWR_UC_MAX][SWR_NUM_PORTS]; struct swr_dev_frame_config (*map_uc)[SWR_UC_MAX]; struct wcd9378_priv *wcd9378 = dev_get_drvdata(dev); switch (path) { case CODEC_TX: map = &wcd9378->tx_port_params; map_uc = &wcd9378->swr_tx_port_params; break; default: ret = -EINVAL; goto err_port_map; } if (!of_find_property(dev->of_node, prop, &map_size)) { dev_err(dev, "missing port mapping prop %s\n", prop); ret = -EINVAL; goto err_port_map; } max_uc = map_size / (SWR_NUM_PORTS * SWR_PORT_PARAMS * sizeof(u32)); if (max_uc != SWR_UC_MAX) { dev_err(dev, "%s: port params not provided for all usecases\n", __func__); ret = -EINVAL; goto err_port_map; } dt_array = kzalloc(map_size, GFP_KERNEL); if (!dt_array) { ret = -ENOMEM; goto err_alloc; } ret = of_property_read_u32_array(dev->of_node, prop, dt_array, SWR_NUM_PORTS * SWR_PORT_PARAMS * max_uc); if (ret) { dev_err(dev, "%s: Failed to read port mapping from prop %s\n", __func__, prop); goto err_pdata_fail; } for (i = 0; i < max_uc; i++) { for (j = 0; j < SWR_NUM_PORTS; j++) { cnt = (i * SWR_NUM_PORTS + j) * SWR_PORT_PARAMS; (*map)[i][j].offset1 = dt_array[cnt]; (*map)[i][j].lane_ctrl = dt_array[cnt + 1]; } (*map_uc)[i].pp = &(*map)[i][0]; } kfree(dt_array); return 0; err_pdata_fail: kfree(dt_array); err_alloc: err_port_map: return ret; } static int wcd9378_parse_port_mapping(struct device *dev, char *prop, u8 path) { u32 *dt_array, map_size, map_length; u32 port_num = 0, ch_mask, ch_rate, old_port_num = 0; u32 slave_port_type, master_port_type; u32 i, ch_iter = 0; int ret = 0; u8 *num_ports = NULL; struct codec_port_info (*map)[MAX_PORT][MAX_CH_PER_PORT]; struct wcd9378_priv *wcd9378 = dev_get_drvdata(dev); switch (path) { case CODEC_RX: map = &wcd9378->rx_port_mapping; num_ports = &wcd9378->num_rx_ports; break; case CODEC_TX: map = &wcd9378->tx_port_mapping; num_ports = &wcd9378->num_tx_ports; break; default: dev_err(dev, "%s Invalid path selected %u\n", __func__, path); return -EINVAL; } if (!of_find_property(dev->of_node, prop, &map_size)) { dev_err(dev, "missing port mapping prop %s\n", prop); ret = -EINVAL; goto err_port_map; } map_length = map_size / (NUM_SWRS_DT_PARAMS * sizeof(u32)); dt_array = kzalloc(map_size, GFP_KERNEL); if (!dt_array) { ret = -ENOMEM; goto err_alloc; } ret = of_property_read_u32_array(dev->of_node, prop, dt_array, NUM_SWRS_DT_PARAMS * map_length); if (ret) { dev_err(dev, "%s: Failed to read port mapping from prop %s\n", __func__, prop); goto err_pdata_fail; } for (i = 0; i < map_length; i++) { port_num = dt_array[NUM_SWRS_DT_PARAMS * i]; slave_port_type = dt_array[NUM_SWRS_DT_PARAMS * i + 1]; ch_mask = dt_array[NUM_SWRS_DT_PARAMS * i + 2]; ch_rate = dt_array[NUM_SWRS_DT_PARAMS * i + 3]; master_port_type = dt_array[NUM_SWRS_DT_PARAMS * i + 4]; if (port_num != old_port_num) ch_iter = 0; (*map)[port_num][ch_iter].slave_port_type = slave_port_type; (*map)[port_num][ch_iter].ch_mask = ch_mask; (*map)[port_num][ch_iter].master_port_type = master_port_type; (*map)[port_num][ch_iter].num_ch = __sw_hweight8(ch_mask); (*map)[port_num][ch_iter++].ch_rate = ch_rate; old_port_num = port_num; } *num_ports = port_num; kfree(dt_array); return 0; err_pdata_fail: kfree(dt_array); err_alloc: err_port_map: return ret; } static int wcd9378_tx_connect_port(struct snd_soc_component *component, u8 slv_port_type, int clk_rate, u8 enable) { struct wcd9378_priv *wcd9378 = snd_soc_component_get_drvdata(component); u8 port_id, num_ch, ch_mask; u8 ch_type = 0; u32 ch_rate; int slave_ch_idx; u8 num_port = 1; int ret = 0; ret = wcd9378_set_port_params(component, slv_port_type, &port_id, &num_ch, &ch_mask, &ch_rate, &ch_type, CODEC_TX); if (ret) return ret; if (clk_rate) ch_rate = clk_rate; slave_ch_idx = wcd9378_slave_get_slave_ch_val(slv_port_type); if (slave_ch_idx != -EINVAL) ch_type = wcd9378->tx_master_ch_map[slave_ch_idx]; dev_dbg(component->dev, "%s slv_ch_idx: %d, mstr_ch_type: %d\n", __func__, slave_ch_idx, ch_type); if (enable) ret = swr_connect_port(wcd9378->tx_swr_dev, &port_id, num_port, &ch_mask, &ch_rate, &num_ch, &ch_type); else ret = swr_disconnect_port(wcd9378->tx_swr_dev, &port_id, num_port, &ch_mask, &ch_type); return ret; } static int wcd9378_rx_connect_port(struct snd_soc_component *component, u8 slv_port_type, u8 enable) { struct wcd9378_priv *wcd9378 = snd_soc_component_get_drvdata(component); u8 port_id, num_ch, ch_mask, port_type; u32 ch_rate; u8 num_port = 1; int ret = 0; ret = wcd9378_set_port_params(component, slv_port_type, &port_id, &num_ch, &ch_mask, &ch_rate, &port_type, CODEC_RX); if (ret) return ret; if (enable) ret = swr_connect_port(wcd9378->rx_swr_dev, &port_id, num_port, &ch_mask, &ch_rate, &num_ch, &port_type); else ret = swr_disconnect_port(wcd9378->rx_swr_dev, &port_id, num_port, &ch_mask, &port_type); return ret; } static int wcd9378_enable_clsh(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 wcd9378_priv *wcd9378 = snd_soc_component_get_drvdata(component); int mode = wcd9378->hph_mode; int ret = 0; dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__, w->name, event); if (mode == CLS_H_LOHIFI || mode == CLS_H_ULP || mode == CLS_H_HIFI || mode == CLS_H_LP) { wcd9378_rx_connect_port(component, CLSH, SND_SOC_DAPM_EVENT_ON(event)); } if (SND_SOC_DAPM_EVENT_OFF(event)) ret = wcd9378_swr_slvdev_datapath_control(wcd9378->dev, RX_PATH, false); return ret; } static int wcd9378_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); struct wcd9378_priv *wcd9378 = snd_soc_component_get_drvdata(component); u32 dmic_clk_reg, dmic_clk_en_reg; s32 *dmic_clk_cnt; u8 dmic_ctl_shift = 0; u8 dmic_clk_shift = 0; u8 dmic_clk_mask = 0; u32 dmic2_left_en = 0; int ret = 0; dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__, w->name, event); switch (w->shift) { case 0: case 1: dmic_clk_cnt = &(wcd9378->dmic_0_1_clk_cnt); dmic_clk_reg = WCD9378_CDC_DMIC_RATE_1_2; dmic_clk_en_reg = WCD9378_CDC_DMIC1_CTL; dmic_clk_mask = 0x0F; dmic_clk_shift = 0x00; dmic_ctl_shift = 0x00; break; case 2: dmic2_left_en = WCD9378_CDC_DMIC2_CTL; fallthrough; case 3: dmic_clk_cnt = &(wcd9378->dmic_2_3_clk_cnt); dmic_clk_reg = WCD9378_CDC_DMIC_RATE_1_2; dmic_clk_en_reg = WCD9378_CDC_DMIC2_CTL; dmic_clk_mask = 0xF0; dmic_clk_shift = 0x04; dmic_ctl_shift = 0x01; break; case 4: case 5: dmic_clk_cnt = &(wcd9378->dmic_4_5_clk_cnt); dmic_clk_reg = WCD9378_CDC_DMIC_RATE_3_4; dmic_clk_en_reg = WCD9378_CDC_DMIC3_CTL; dmic_clk_mask = 0x0F; dmic_clk_shift = 0x00; dmic_ctl_shift = 0x02; break; default: dev_err_ratelimited(component->dev, "%s: Invalid DMIC Selection\n", __func__); return -EINVAL; }; dev_dbg(component->dev, "%s: event %d DMIC%d dmic_clk_cnt %d\n", __func__, event, (w->shift + 1), *dmic_clk_cnt); switch (event) { case SND_SOC_DAPM_PRE_PMU: snd_soc_component_update_bits(component, WCD9378_CDC_AMIC_CTL, (0x01 << dmic_ctl_shift), 0x00); /* 250us sleep as per HW requirement */ usleep_range(250, 260); if (dmic2_left_en) snd_soc_component_update_bits(component, dmic2_left_en, 0x80, 0x80); /* Setting DMIC clock rate to 2.4MHz */ snd_soc_component_update_bits(component, dmic_clk_reg, dmic_clk_mask, (0x03 << dmic_clk_shift)); snd_soc_component_update_bits(component, dmic_clk_en_reg, 0x08, 0x08); /* enable clock scaling */ snd_soc_component_update_bits(component, WCD9378_CDC_DMIC_CTL, 0x06, 0x06); ret = swr_slvdev_datapath_control(wcd9378->tx_swr_dev, wcd9378->tx_swr_dev->dev_num, true); break; case SND_SOC_DAPM_POST_PMD: wcd9378_tx_connect_port(component, DMIC0 + (w->shift), 0, false); snd_soc_component_update_bits(component, WCD9378_CDC_AMIC_CTL, (0x01 << dmic_ctl_shift), (0x01 << dmic_ctl_shift)); if (dmic2_left_en) snd_soc_component_update_bits(component, dmic2_left_en, 0x80, 0x00); snd_soc_component_update_bits(component, dmic_clk_en_reg, 0x08, 0x00); break; }; return ret; } /* * wcd9378_get_micb_vout_ctl_val: converts micbias from volts to register value * @micb_mv: micbias in mv * * return register value converted */ int wcd9378_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; } EXPORT_SYMBOL_GPL(wcd9378_get_micb_vout_ctl_val); /* * wcd9378_mbhc_micb_adjust_voltage: adjust specific micbias voltage * @component: handle to snd_soc_component * * @req_volt: micbias voltage to be set * @micb_num: micbias to be set, e.g. micbias1 or micbias2 * * return 0 if adjustment is success or error code in case of failure */ static int wcd9378_micb_table_value_set(struct snd_soc_component *component, u32 micb_mv, int micb_num) { int vcout_ctl; switch (micb_mv) { case 2200: return MICB_USAGE_VAL_2P2V; case 2700: return MICB_USAGE_VAL_2P7V; case 2800: return MICB_USAGE_VAL_2P8V; default: vcout_ctl = wcd9378_get_micb_vout_ctl_val(micb_mv); if (micb_num == MIC_BIAS_1) { snd_soc_component_update_bits(component, WCD9378_MICB_REMAP_TABLE_VAL_3, WCD9378_MICB_REMAP_TABLE_VAL_3_MICB_REMAP_TABLE_VAL_3_MASK, vcout_ctl); return MICB_USAGE_VAL_MICB1_TABLE_VAL; } else if (micb_num == MIC_BIAS_2) { snd_soc_component_update_bits(component, WCD9378_MICB_REMAP_TABLE_VAL_4, WCD9378_MICB_REMAP_TABLE_VAL_4_MICB_REMAP_TABLE_VAL_4_MASK, vcout_ctl); return MICB_USAGE_VAL_MICB2_TABLE_VAL; } else if (micb_num == MIC_BIAS_3) { snd_soc_component_update_bits(component, WCD9378_MICB_REMAP_TABLE_VAL_5, WCD9378_MICB_REMAP_TABLE_VAL_5_MICB_REMAP_TABLE_VAL_5_MASK, vcout_ctl); return MICB_USAGE_VAL_MICB3_TABLE_VAL; } } return 0; } static int wcd9378_micb_usage_value_convert(struct snd_soc_component *component, u32 micb_mv, int micb_num) { switch (micb_mv) { case 0: return MICB_USAGE_VAL_PULL_DOWN; case 1200: return MICB_USAGE_VAL_1P2V; case 1800: return MICB_USAGE_VAL_1P8VORPULLUP; case 2500: return MICB_USAGE_VAL_2P5V; case 2750: return MICB_USAGE_VAL_2P75V; default: return wcd9378_micb_table_value_set(component, micb_mv, micb_num); } return MICB_USAGE_VAL_DISABLE; } int wcd9378_mbhc_micb_adjust_voltage(struct snd_soc_component *component, int req_volt, int micb_num) { struct wcd9378_priv *wcd9378 = snd_soc_component_get_drvdata(component); int micb_usage = 0, micb_mask = 0, req_vout_ctl = 0; if (wcd9378 == NULL) { dev_err(component->dev, "%s: wcd9378 private data is NULL\n", __func__); return -EINVAL; } switch (micb_num) { case MIC_BIAS_1: micb_usage = WCD9378_IT11_USAGE; micb_mask = WCD9378_IT11_MICB_IT11_MICB_MASK; break; case MIC_BIAS_2: micb_usage = WCD9378_SMP_MIC_CTRL1_IT11_MICB; micb_mask = WCD9378_SMP_MIC_CTRL1_IT11_MICB_IT11_MICB_MASK; break; case MIC_BIAS_3: micb_usage = WCD9378_SMP_MIC_CTRL2_IT11_MICB; micb_mask = WCD9378_SMP_MIC_CTRL2_IT11_MICB_IT11_MICB_MASK; break; default: dev_err(component->dev, "%s: wcd9378 private data is NULL\n", __func__); break; } mutex_lock(&wcd9378->micb_lock); req_vout_ctl = wcd9378_micb_usage_value_convert(component, req_volt, micb_num); snd_soc_component_update_bits(component, micb_usage, micb_mask, req_vout_ctl); if (micb_num == MIC_BIAS_2) { dev_err(component->dev, "%s: sj micbias set\n", __func__); snd_soc_component_update_bits(component, WCD9378_IT31_MICB, WCD9378_IT31_MICB_IT31_MICB_MASK, req_vout_ctl); wcd9378->curr_micbias2 = req_volt; } mutex_unlock(&wcd9378->micb_lock); return 0; } EXPORT_SYMBOL_GPL(wcd9378_mbhc_micb_adjust_voltage); void wcd9378_disable_bcs_before_slow_insert(struct snd_soc_component *component, bool bcs_disable) { struct wcd9378_priv *wcd9378 = snd_soc_component_get_drvdata(component); if (wcd9378->update_wcd_event) { if (bcs_disable) wcd9378->update_wcd_event(wcd9378->handle, SLV_BOLERO_EVT_BCS_CLK_OFF, 0); else wcd9378->update_wcd_event(wcd9378->handle, SLV_BOLERO_EVT_BCS_CLK_OFF, 1); } } static int wcd9378_get_clk_rate(int mode) { int rate; switch (mode) { case ADC_MODE_LP: rate = SWR_CLK_RATE_4P8MHZ; break; case ADC_MODE_INVALID: case ADC_MODE_NORMAL: case ADC_MODE_HIFI: default: rate = SWR_CLK_RATE_9P6MHZ; break; } pr_debug("%s: mode: %d, rate: %d\n", __func__, mode, rate); return rate; } static int wcd9378_get_adc_mode_val(int mode) { int ret = 0; switch (mode) { case ADC_MODE_INVALID: case ADC_MODE_NORMAL: ret = ADC_MODE_VAL_NORMAL; break; case ADC_MODE_HIFI: ret = ADC_MODE_VAL_HIFI; break; case ADC_MODE_LP: ret = ADC_MODE_VAL_LP; break; default: ret = -EINVAL; pr_err("%s: invalid ADC mode value %d\n", __func__, mode); break; } return ret; } static int wcd9378_sys_usage_auto_udpate(struct snd_soc_component *component, int sys_usage_bit, bool set_enable) { struct wcd9378_priv *wcd9378 = snd_soc_component_get_drvdata(component); int i = 0; dev_dbg(component->dev, "%s: enter, current sys_usage: %d, sys_usage_status: 0x%x, sys_usage_bit: %d, set_enable: %d\n", __func__, wcd9378->sys_usage, wcd9378->sys_usage_status, sys_usage_bit, set_enable); mutex_lock(&wcd9378->sys_usage_lock); if (set_enable) { set_bit(sys_usage_bit, &wcd9378->sys_usage_status); if ((sys_usage[wcd9378->sys_usage] & wcd9378->sys_usage_status) == wcd9378->sys_usage_status) goto exit; for (i = 0; i < SYS_USAGE_NUM; i++) { if ((sys_usage[i] & wcd9378->sys_usage_status) == wcd9378->sys_usage_status) { snd_soc_component_update_bits(component, WCD9378_SYS_USAGE_CTRL, WCD9378_SYS_USAGE_CTRL_SYS_USAGE_CTRL_MASK, i); wcd9378->sys_usage = i; dev_dbg(component->dev, "%s: update sys_usage: %d\n", __func__, wcd9378->sys_usage); goto exit; } } dev_dbg(component->dev, "%s: cannot find sys_usage\n", __func__); } else { clear_bit(sys_usage_bit, &wcd9378->sys_usage_status); } exit: mutex_unlock(&wcd9378->sys_usage_lock); return 0; } static int wcd9378_sys_usage_bit_get( struct snd_soc_component *component, u32 w_shift, int *sys_usage_bit, int event) { struct wcd9378_priv *wcd9378 = snd_soc_component_get_drvdata(component); dev_dbg(component->dev, "%s: wshift: %d event: %d\n", __func__, w_shift, event); switch (event) { case SND_SOC_DAPM_PRE_PMU: switch (w_shift) { case ADC1: if ((snd_soc_component_read(component, WCD9378_TX_NEW_TX_CH12_MUX) & WCD9378_TX_NEW_TX_CH12_MUX_CH1_SEL_MASK) == 0x01) { *sys_usage_bit = TX0_AMIC1_EN; } else if ((snd_soc_component_read(component, WCD9378_TX_NEW_TX_CH12_MUX) & WCD9378_TX_NEW_TX_CH12_MUX_CH1_SEL_MASK) == 0x02) { *sys_usage_bit = TX0_AMIC2_EN; } else { dev_err(component->dev, "%s: unsupport usecase, pls check\n", __func__); return -EINVAL; } break; case ADC2: if ((snd_soc_component_read(component, WCD9378_TX_NEW_TX_CH12_MUX) & WCD9378_TX_NEW_TX_CH12_MUX_CH2_SEL_MASK) == 0x10) { *sys_usage_bit = TX1_AMIC2_EN; } else if ((snd_soc_component_read(component, WCD9378_TX_NEW_TX_CH12_MUX) & WCD9378_TX_NEW_TX_CH12_MUX_CH2_SEL_MASK) == 0x18) { *sys_usage_bit = TX1_AMIC3_EN; } else { dev_err(component->dev, "%s: unsupport usecase, pls check\n", __func__); return -EINVAL; } break; case ADC3: if ((snd_soc_component_read(component, WCD9378_TX_NEW_TX_CH34_MUX) & WCD9378_TX_NEW_TX_CH34_MUX_CH3_SEL_MASK) == 0x01) { *sys_usage_bit = TX2_AMIC1_EN; } else if ((snd_soc_component_read(component, WCD9378_TX_NEW_TX_CH34_MUX) & WCD9378_TX_NEW_TX_CH34_MUX_CH3_SEL_MASK) == 0x03) { *sys_usage_bit = TX2_AMIC4_EN; } else { dev_err(component->dev, "%s: unsupport usecase, pls check\n", __func__); return -EINVAL; } break; default: break; } break; case SND_SOC_DAPM_POST_PMD: switch (w_shift) { case ADC1: if (test_bit(TX0_AMIC1_EN, &wcd9378->sys_usage_status)) *sys_usage_bit = TX0_AMIC1_EN; if (test_bit(TX0_AMIC2_EN, &wcd9378->sys_usage_status)) *sys_usage_bit = TX0_AMIC2_EN; break; case ADC2: if (test_bit(TX1_AMIC2_EN, &wcd9378->sys_usage_status)) *sys_usage_bit = TX1_AMIC2_EN; if (test_bit(TX1_AMIC3_EN, &wcd9378->sys_usage_status)) *sys_usage_bit = TX1_AMIC3_EN; break; case ADC3: if (test_bit(TX2_AMIC1_EN, &wcd9378->sys_usage_status)) *sys_usage_bit = TX2_AMIC1_EN; if (test_bit(TX2_AMIC4_EN, &wcd9378->sys_usage_status)) *sys_usage_bit = TX2_AMIC4_EN; break; default: break; } break; default: break; } dev_dbg(component->dev, "%s: done, event: %d, sys_usage_bit: %d\n", __func__, event, *sys_usage_bit); return 0; } static int wcd9378_tx_sequencer_enable(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 wcd9378_priv *wcd9378 = snd_soc_component_get_drvdata(component); int mode_val = 0, bank = 0, ret = 0, rate = 0; int act_ps = 0, sys_usage_bit = 0; bank = (wcd9378_swr_slv_get_current_bank(wcd9378->tx_swr_dev, wcd9378->tx_swr_dev->dev_num) ? 0 : 1); dev_dbg(component->dev, "%s wname: %s wshift: %d event: %d\n", __func__, w->name, w->shift, event); ret = wcd9378_sys_usage_bit_get(component, w->shift, &sys_usage_bit, event); if (ret < 0) return ret; switch (event) { case SND_SOC_DAPM_PRE_PMU: /*Update sys_usage*/ wcd9378_sys_usage_auto_udpate(component, sys_usage_bit, true); mode_val = wcd9378_get_adc_mode_val(wcd9378->tx_mode[w->shift - ADC1]); if (mode_val < 0) { dev_dbg(component->dev, "%s: invalid mode, setting to normal mode\n", __func__); mode_val = ADC_MODE_VAL_NORMAL; } rate = wcd9378_get_clk_rate(wcd9378->tx_mode[w->shift - ADC1]); if (w->shift == ADC2 && !((snd_soc_component_read(component, WCD9378_TX_NEW_TX_CH12_MUX) & WCD9378_TX_NEW_TX_CH12_MUX_CH2_SEL_MASK) == 0x10)) { if (!wcd9378->bcs_dis) { wcd9378_tx_connect_port(component, MBHC, SWR_CLK_RATE_4P8MHZ, true); set_bit(AMIC2_BCS_ENABLE, &wcd9378->status_mask); } } set_bit(w->shift - ADC1, &wcd9378->status_mask); wcd9378_tx_connect_port(component, w->shift, rate, true); switch (w->shift) { case ADC1: /*SMP MIC0 IT11 USAGE SET*/ snd_soc_component_update_bits(component, WCD9378_IT11_USAGE, WCD9378_IT11_USAGE_IT11_USAGE_MASK, mode_val); /*Hold TXFE in Initialization During Startup*/ snd_soc_component_update_bits(component, WCD9378_ANA_TX_CH2, WCD9378_ANA_TX_CH2_HPF1_INIT_MASK, 0x40); /*Power up TX0 sequencer*/ snd_soc_component_update_bits(component, WCD9378_PDE11_REQ_PS, WCD9378_PDE11_REQ_PS_PDE11_REQ_PS_MASK, 0x00); break; case ADC2: /*Check if amic2 is connected to ADC2 MUX*/ if ((snd_soc_component_read(component, WCD9378_TX_NEW_TX_CH12_MUX) & WCD9378_TX_NEW_TX_CH12_MUX_CH2_SEL_MASK) == 0x10) { /*SMP JACK IT31 USAGE SET*/ snd_soc_component_update_bits(component, WCD9378_IT31_USAGE, WCD9378_IT31_USAGE_IT31_USAGE_MASK, mode_val); /*Power up TX1 sequencer*/ snd_soc_component_update_bits(component, WCD9378_PDE34_REQ_PS, WCD9378_PDE34_REQ_PS_PDE34_REQ_PS_MASK, 0x00); } else { snd_soc_component_update_bits(component, WCD9378_SMP_MIC_CTRL1_IT11_USAGE, WCD9378_SMP_MIC_CTRL1_IT11_USAGE_IT11_USAGE_MASK, mode_val); /*Hold TXFE in Initialization During Startup*/ snd_soc_component_update_bits(component, WCD9378_ANA_TX_CH2, WCD9378_ANA_TX_CH2_HPF2_INIT_MASK, 0x20); /*Power up TX1 sequencer*/ snd_soc_component_update_bits(component, WCD9378_SMP_MIC_CTRL1_PDE11_REQ_PS, WCD9378_SMP_MIC_CTRL1_PDE11_REQ_PS_PDE11_REQ_PS_MASK, 0x00); } break; case ADC3: /*SMP MIC2 IT11 USAGE SET*/ snd_soc_component_update_bits(component, WCD9378_SMP_MIC_CTRL2_IT11_USAGE, WCD9378_SMP_MIC_CTRL2_IT11_USAGE_IT11_USAGE_MASK, mode_val); /*Hold TXFE in Initialization During Startup*/ snd_soc_component_update_bits(component, WCD9378_ANA_TX_CH3_HPF, WCD9378_ANA_TX_CH3_HPF_HPF3_INIT_MASK, 0x40); /*Power up TX2 sequencer*/ snd_soc_component_update_bits(component, WCD9378_SMP_MIC_CTRL2_PDE11_REQ_PS, WCD9378_SMP_MIC_CTRL2_PDE11_REQ_PS_PDE11_REQ_PS_MASK, 0x00); break; default: break; } /*default delay 800us*/ usleep_range(800, 810); wcd9378_swr_slvdev_datapath_control(wcd9378->dev, TX_PATH, true); switch (w->shift) { case ADC1: snd_soc_component_update_bits(component, WCD9378_ANA_TX_CH2, WCD9378_ANA_TX_CH2_HPF1_INIT_MASK, 0x00); act_ps = snd_soc_component_read(component, WCD9378_PDE11_ACT_PS); if (act_ps) dev_dbg(component->dev, "%s: TX0 sequencer power on failed\n", __func__); else dev_dbg(component->dev, "%s: TX0 sequencer power on success\n", __func__); break; case ADC2: snd_soc_component_update_bits(component, WCD9378_ANA_TX_CH2, WCD9378_ANA_TX_CH2_HPF2_INIT_MASK, 0x00); if (test_bit(TX1_AMIC2_EN, &wcd9378->sys_usage_status)) act_ps = snd_soc_component_read(component, WCD9378_PDE34_ACT_PS); else act_ps = snd_soc_component_read(component, WCD9378_SMP_MIC_CTRL1_PDE11_ACT_PS); if (act_ps) dev_dbg(component->dev, "%s: TX1 sequencer power on failed\n", __func__); else dev_dbg(component->dev, "%s: TX1 sequencer power on success\n", __func__); break; case ADC3: snd_soc_component_update_bits(component, WCD9378_ANA_TX_CH3_HPF, WCD9378_ANA_TX_CH3_HPF_HPF3_INIT_MASK, 0x00); act_ps = snd_soc_component_read(component, WCD9378_SMP_MIC_CTRL2_PDE11_ACT_PS); if (act_ps) dev_dbg(component->dev, "%s: TX2 sequencer power on failed\n", __func__); else dev_dbg(component->dev, "%s: TX2 sequencer power on success\n", __func__); break; }; break; case SND_SOC_DAPM_POST_PMD: wcd9378_tx_connect_port(component, w->shift, 0, false); if (w->shift == ADC2 && test_bit(AMIC2_BCS_ENABLE, &wcd9378->status_mask)) { wcd9378_tx_connect_port(component, MBHC, 0, false); clear_bit(AMIC2_BCS_ENABLE, &wcd9378->status_mask); } switch (w->shift) { case ADC1: /*Normal TXFE Startup*/ snd_soc_component_update_bits(component, WCD9378_ANA_TX_CH2, WCD9378_ANA_TX_CH2_HPF1_INIT_MASK, 0x00); /*tear down TX0 sequencer*/ snd_soc_component_update_bits(component, WCD9378_PDE11_REQ_PS, WCD9378_PDE11_REQ_PS_PDE11_REQ_PS_MASK, 0x03); break; case ADC2: if (test_bit(TX1_AMIC2_EN, &wcd9378->sys_usage_status)) /*tear down TX1 sequencer*/ snd_soc_component_update_bits(component, WCD9378_PDE34_REQ_PS, WCD9378_PDE34_REQ_PS_PDE34_REQ_PS_MASK, 0x03); if (test_bit(TX1_AMIC3_EN, &wcd9378->sys_usage_status)) { /*Normal TXFE Startup*/ snd_soc_component_update_bits(component, WCD9378_ANA_TX_CH2, WCD9378_ANA_TX_CH2_HPF1_INIT_MASK, 0x00); /*tear down TX1 sequencer*/ snd_soc_component_update_bits(component, WCD9378_SMP_MIC_CTRL1_PDE11_REQ_PS, WCD9378_SMP_MIC_CTRL1_PDE11_REQ_PS_PDE11_REQ_PS_MASK, 0x03); } break; case ADC3: /*Normal TXFE Startup*/ snd_soc_component_update_bits(component, WCD9378_ANA_TX_CH3_HPF, WCD9378_ANA_TX_CH3_HPF_HPF3_INIT_MASK, 0x00); /*tear down TX2 sequencer*/ snd_soc_component_update_bits(component, WCD9378_SMP_MIC_CTRL2_PDE11_REQ_PS, WCD9378_SMP_MIC_CTRL2_PDE11_REQ_PS_PDE11_REQ_PS_MASK, 0x03); break; default: break; } /*default delay 800us*/ usleep_range(800, 810); /*Disable sys_usage_status*/ wcd9378_sys_usage_auto_udpate(component, sys_usage_bit, false); wcd9378_swr_slvdev_datapath_control(wcd9378->dev, TX_PATH, false); break; default: break; } return ret; } static int wcd9378_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 wcd9378_priv *wcd9378 = snd_soc_component_get_drvdata(component); int ret = 0; switch (event) { case SND_SOC_DAPM_PRE_PMU: wcd9378_tx_connect_port(component, w->shift, SWR_CLK_RATE_2P4MHZ, true); break; case SND_SOC_DAPM_POST_PMD: ret = swr_slvdev_datapath_control(wcd9378->tx_swr_dev, wcd9378->tx_swr_dev->dev_num, false); break; }; return ret; } static int wcd9378_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 = 0; dev_dbg(component->dev, "%s: wname: %s, event: %d\n", __func__, w->name, event); if (strnstr(w->name, "MIC BIAS1", sizeof("MIC BIAS1"))) micb_num = MIC_BIAS_1; else if (strnstr(w->name, "MIC BIAS2", sizeof("MIC BIAS2"))) micb_num = MIC_BIAS_2; else if (strnstr(w->name, "MIC BIAS3", sizeof("MIC BIAS3"))) micb_num = MIC_BIAS_3; else return -EINVAL; switch (event) { case SND_SOC_DAPM_PRE_PMU: wcd9378_micbias_control(component, micb_num, MICB_ENABLE, true); break; case SND_SOC_DAPM_POST_PMU: usleep_range(1000, 1100); break; case SND_SOC_DAPM_POST_PMD: wcd9378_micbias_control(component, micb_num, MICB_DISABLE, true); break; }; return 0; } static int wcd9378_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 = 0; dev_dbg(component->dev, "%s: wname: %s, event: %d\n", __func__, w->name, event); if (strnstr(w->name, "VA MIC BIAS1", sizeof("VA MIC BIAS1"))) micb_num = MIC_BIAS_1; else if (strnstr(w->name, "VA MIC BIAS2", sizeof("VA MIC BIAS2"))) micb_num = MIC_BIAS_2; else if (strnstr(w->name, "VA MIC BIAS3", sizeof("VA MIC BIAS3"))) micb_num = MIC_BIAS_3; else return -EINVAL; switch (event) { case SND_SOC_DAPM_PRE_PMU: wcd9378_micbias_control(component, micb_num, MICB_PULLUP_ENABLE, true); break; case SND_SOC_DAPM_POST_PMU: usleep_range(1000, 1100); break; case SND_SOC_DAPM_POST_PMD: wcd9378_micbias_control(component, micb_num, MICB_PULLUP_DISABLE, true); break; }; return 0; } /* * wcd9378_soc_get_mbhc: get wcd9378_mbhc handle of corresponding component * @component: handle to snd_soc_component * * * return wcd9378_mbhc handle or error code in case of failure */ struct wcd9378_mbhc *wcd9378_soc_get_mbhc(struct snd_soc_component *component) { struct wcd9378_priv *wcd9378; if (!component) { pr_err_ratelimited("%s: Invalid params, NULL component\n", __func__); return NULL; } wcd9378 = snd_soc_component_get_drvdata(component); if (!wcd9378) { pr_err_ratelimited("%s: wcd9378 is NULL\n", __func__); return NULL; } return wcd9378->mbhc; } EXPORT_SYMBOL_GPL(wcd9378_soc_get_mbhc); static int wcd9378_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 wcd9378_priv *wcd9378 = 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: /*OCP FSM EN*/ snd_soc_component_update_bits(component, WCD9378_HPH_OCP_CTL, WCD9378_HPH_OCP_CTL_OCP_FSM_EN_MASK, 0x10); /*SCD OP EN*/ snd_soc_component_update_bits(component, WCD9378_HPH_OCP_CTL, WCD9378_HPH_OCP_CTL_SCD_OP_EN_MASK, 0x02); /*HPHL ENABLE*/ snd_soc_component_update_bits(component, WCD9378_CDC_HPH_GAIN_CTL, WCD9378_CDC_HPH_GAIN_CTL_HPHL_RX_EN_MASK, 0x04); /*OPAMP_CHOP_CLK DISABLE*/ snd_soc_component_update_bits(component, WCD9378_HPH_RDAC_CLK_CTL1, WCD9378_HPH_RDAC_CLK_CTL1_OPAMP_CHOP_CLK_EN_MASK, 0x00); wcd9378_rx_connect_port(component, HPH_L, true); if (wcd9378->comp1_enable) { snd_soc_component_update_bits(component, WCD9378_CDC_COMP_CTL_0, WCD9378_CDC_COMP_CTL_0_HPHL_COMP_EN_MASK, 0x02); wcd9378_rx_connect_port(component, COMP_L, true); } break; case SND_SOC_DAPM_POST_PMD: /*OCP FSM DISABLE*/ snd_soc_component_update_bits(component, WCD9378_HPH_OCP_CTL, WCD9378_HPH_OCP_CTL_OCP_FSM_EN_MASK, 0x00); /*SCD OP DISABLE*/ snd_soc_component_update_bits(component, WCD9378_HPH_OCP_CTL, WCD9378_HPH_OCP_CTL_SCD_OP_EN_MASK, 0x00); /*HPHL DISABLE*/ snd_soc_component_update_bits(component, WCD9378_CDC_HPH_GAIN_CTL, WCD9378_CDC_HPH_GAIN_CTL_HPHL_RX_EN_MASK, 0x00); wcd9378_rx_connect_port(component, HPH_L, false); if (wcd9378->comp1_enable) { snd_soc_component_update_bits(component, WCD9378_CDC_COMP_CTL_0, WCD9378_CDC_COMP_CTL_0_HPHL_COMP_EN_MASK, 0x00); wcd9378_rx_connect_port(component, COMP_L, false); } break; default: break; }; return 0; } static int wcd9378_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 wcd9378_priv *wcd9378 = 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: /*OCP FSM EN*/ snd_soc_component_update_bits(component, WCD9378_HPH_OCP_CTL, WCD9378_HPH_OCP_CTL_OCP_FSM_EN_MASK, 0x10); /*SCD OP EN*/ snd_soc_component_update_bits(component, WCD9378_HPH_OCP_CTL, WCD9378_HPH_OCP_CTL_SCD_OP_EN_MASK, 0x02); /*HPHR ENABLE*/ snd_soc_component_update_bits(component, WCD9378_CDC_HPH_GAIN_CTL, WCD9378_CDC_HPH_GAIN_CTL_HPHR_RX_EN_MASK, 0x08); /*OPAMP_CHOP_CLK DISABLE*/ snd_soc_component_update_bits(component, WCD9378_HPH_RDAC_CLK_CTL1, WCD9378_HPH_RDAC_CLK_CTL1_OPAMP_CHOP_CLK_EN_MASK, 0x00); wcd9378_rx_connect_port(component, HPH_R, true); if (wcd9378->comp2_enable) { snd_soc_component_update_bits(component, WCD9378_CDC_COMP_CTL_0, WCD9378_CDC_COMP_CTL_0_HPHR_COMP_EN_MASK, 0x01); wcd9378_rx_connect_port(component, COMP_R, true); } break; case SND_SOC_DAPM_POST_PMD: /*OCP FSM DISABLE*/ snd_soc_component_update_bits(component, WCD9378_HPH_OCP_CTL, WCD9378_HPH_OCP_CTL_OCP_FSM_EN_MASK, 0x00); /*SCD OP DISABLE*/ snd_soc_component_update_bits(component, WCD9378_HPH_OCP_CTL, WCD9378_HPH_OCP_CTL_SCD_OP_EN_MASK, 0x00); /*HPHR DISABLE*/ snd_soc_component_update_bits(component, WCD9378_CDC_HPH_GAIN_CTL, WCD9378_CDC_HPH_GAIN_CTL_HPHR_RX_EN_MASK, 0x00); wcd9378_rx_connect_port(component, HPH_R, false); if (wcd9378->comp2_enable) { snd_soc_component_update_bits(component, WCD9378_CDC_COMP_CTL_0, WCD9378_CDC_COMP_CTL_0_HPHR_COMP_EN_MASK, 0x00); wcd9378_rx_connect_port(component, COMP_R, false); } break; default: break; }; return 0; } static int wcd9378_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 wcd9378_priv *wcd9378 = snd_soc_component_get_drvdata(component); int bank = 0; int act_ps = 0; bank = (wcd9378_swr_slv_get_current_bank(wcd9378->rx_swr_dev, wcd9378->rx_swr_dev->dev_num) ? 0 : 1); dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__, w->name, event); switch (event) { case SND_SOC_DAPM_PRE_PMU: if (wcd9378->update_wcd_event) wcd9378->update_wcd_event(wcd9378->handle, SLV_BOLERO_EVT_RX_MUTE, (WCD_RX1 << 0x10 | 0x01)); if (wcd9378->update_wcd_event) wcd9378->update_wcd_event(wcd9378->handle, SLV_BOLERO_EVT_RX_MUTE, (WCD_RX1 << 0x10)); wcd_enable_irq(&wcd9378->irq_info, WCD9378_IRQ_HPHL_PDM_WD_INT); act_ps = snd_soc_component_read(component, WCD9378_PDE47_ACT_PS); if (act_ps) dev_dbg(component->dev, "%s: HPH sequencer power on failed\n", __func__); else dev_dbg(component->dev, "%s: HPH sequencer power on success\n", __func__); break; case SND_SOC_DAPM_POST_PMD: if (wcd9378->update_wcd_event) wcd9378->update_wcd_event(wcd9378->handle, SLV_BOLERO_EVT_RX_MUTE, (WCD_RX1 << 0x10 | 0x1)); wcd_disable_irq(&wcd9378->irq_info, WCD9378_IRQ_HPHL_PDM_WD_INT); if (wcd9378->update_wcd_event && wcd9378->comp1_enable) wcd9378->update_wcd_event(wcd9378->handle, SLV_BOLERO_EVT_RX_COMPANDER_SOFT_RST, (WCD_RX1 << 0x10)); blocking_notifier_call_chain(&wcd9378->mbhc->notifier, WCD_EVENT_POST_HPHL_PA_OFF, &wcd9378->mbhc->wcd_mbhc); break; default: break; }; return 0; } static int wcd9378_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 wcd9378_priv *wcd9378 = snd_soc_component_get_drvdata(component); int act_ps = 0; dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__, w->name, event); switch (event) { case SND_SOC_DAPM_PRE_PMU: if (wcd9378->update_wcd_event) wcd9378->update_wcd_event(wcd9378->handle, SLV_BOLERO_EVT_RX_MUTE, (WCD_RX2 << 0x10 | 0x1)); if (wcd9378->update_wcd_event) wcd9378->update_wcd_event(wcd9378->handle, SLV_BOLERO_EVT_RX_MUTE, (WCD_RX2 << 0x10)); wcd_enable_irq(&wcd9378->irq_info, WCD9378_IRQ_HPHR_PDM_WD_INT); act_ps = snd_soc_component_read(component, WCD9378_PDE47_ACT_PS); if (act_ps) dev_dbg(component->dev, "%s: HPH sequencer power on failed\n", __func__); else dev_dbg(component->dev, "%s: HPH sequencer power on success\n", __func__); break; case SND_SOC_DAPM_POST_PMD: if (wcd9378->update_wcd_event) wcd9378->update_wcd_event(wcd9378->handle, SLV_BOLERO_EVT_RX_MUTE, (WCD_RX2 << 0x10 | 0x1)); wcd_disable_irq(&wcd9378->irq_info, WCD9378_IRQ_HPHR_PDM_WD_INT); if (wcd9378->update_wcd_event && wcd9378->comp2_enable) wcd9378->update_wcd_event(wcd9378->handle, SLV_BOLERO_EVT_RX_COMPANDER_SOFT_RST, (WCD_RX2 << 0x10)); blocking_notifier_call_chain(&wcd9378->mbhc->notifier, WCD_EVENT_POST_HPHR_PA_OFF, &wcd9378->mbhc->wcd_mbhc); break; default: break; }; return 0; } static int wcd9378_codec_enable_aux_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 wcd9378_priv *wcd9378 = snd_soc_component_get_drvdata(component); int ret = 0, act_ps = 0; dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__, w->name, event); switch (event) { case SND_SOC_DAPM_PRE_PMU: wcd9378_swr_slvdev_datapath_control(wcd9378->dev, RX_PATH, true); if (test_bit(RX1_AUX_EN, &wcd9378->sys_usage_status)) { if (wcd9378->update_wcd_event) wcd9378->update_wcd_event(wcd9378->handle, SLV_BOLERO_EVT_RX_MUTE, (WCD_RX2 << 0x10)); wcd_enable_irq(&wcd9378->irq_info, WCD9378_IRQ_HPHR_PDM_WD_INT); } else { if (wcd9378->update_wcd_event) wcd9378->update_wcd_event(wcd9378->handle, SLV_BOLERO_EVT_RX_MUTE, (WCD_RX3 << 0x10)); wcd_enable_irq(&wcd9378->irq_info, WCD9378_IRQ_AUX_PDM_WD_INT); } act_ps = snd_soc_component_read(component, WCD9378_PDE23_ACT_PS); if (act_ps) dev_dbg(component->dev, "%s: SA sequencer power on failed\n", __func__); else dev_dbg(component->dev, "%s: SA sequencer power on success\n", __func__); break; case SND_SOC_DAPM_POST_PMD: if (test_bit(RX1_AUX_EN, &wcd9378->sys_usage_status)) { if (wcd9378->update_wcd_event) wcd9378->update_wcd_event(wcd9378->handle, SLV_BOLERO_EVT_RX_MUTE, (WCD_RX2 << 0x10 | 0x1)); wcd_disable_irq(&wcd9378->irq_info, WCD9378_IRQ_HPHR_PDM_WD_INT); } else { if (wcd9378->update_wcd_event) wcd9378->update_wcd_event(wcd9378->handle, SLV_BOLERO_EVT_RX_MUTE, (WCD_RX3 << 0x10 | 0x1)); wcd_disable_irq(&wcd9378->irq_info, WCD9378_IRQ_AUX_PDM_WD_INT); } break; }; return ret; } static int wcd9378_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 wcd9378_priv *wcd9378 = snd_soc_component_get_drvdata(component); int ret = 0, act_ps = 0; dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__, w->name, event); switch (event) { case SND_SOC_DAPM_PRE_PMU: wcd9378_swr_slvdev_datapath_control(wcd9378->dev, RX_PATH, true); if (test_bit(RX0_EAR_EN, &wcd9378->sys_usage_status)) { if (wcd9378->update_wcd_event) wcd9378->update_wcd_event(wcd9378->handle, SLV_BOLERO_EVT_RX_MUTE, (WCD_RX1 << 0x10)); wcd_enable_irq(&wcd9378->irq_info, WCD9378_IRQ_HPHL_PDM_WD_INT); } else { if (wcd9378->update_wcd_event) wcd9378->update_wcd_event(wcd9378->handle, SLV_BOLERO_EVT_RX_MUTE, (WCD_RX3 << 0x10)); wcd_enable_irq(&wcd9378->irq_info, WCD9378_IRQ_AUX_PDM_WD_INT); } act_ps = snd_soc_component_read(component, WCD9378_PDE23_ACT_PS); if (act_ps) dev_dbg(component->dev, "%s: SA sequencer power on failed\n", __func__); else dev_dbg(component->dev, "%s: SA sequencer power on successful\n", __func__); break; case SND_SOC_DAPM_POST_PMD: if (test_bit(RX0_EAR_EN, &wcd9378->sys_usage_status)) { if (wcd9378->update_wcd_event) wcd9378->update_wcd_event(wcd9378->handle, SLV_BOLERO_EVT_RX_MUTE, (WCD_RX1 << 0x10 | 0x1)); wcd_disable_irq(&wcd9378->irq_info, WCD9378_IRQ_HPHL_PDM_WD_INT); } else { if (wcd9378->update_wcd_event) wcd9378->update_wcd_event(wcd9378->handle, SLV_BOLERO_EVT_RX_MUTE, (WCD_RX3 << 0x10 | 0x1)); wcd_disable_irq(&wcd9378->irq_info, WCD9378_IRQ_AUX_PDM_WD_INT); } break; }; return ret; } static int wcd9378_get_hph_pwr_level(int hph_mode) { switch (hph_mode) { case CLS_H_LOHIFI: case CLS_AB_LOHIFI: return PWR_LEVEL_LOHIFI_VAL; case CLS_H_LP: case CLS_AB_LP: return PWR_LEVEL_LP_VAL; case CLS_H_HIFI: case CLS_AB_HIFI: return PWR_LEVEL_HIFI_VAL; case CLS_H_ULP: case CLS_AB: case CLS_H_NORMAL: default: return PWR_LEVEL_ULP_VAL; } return PWR_LEVEL_ULP_VAL; } static void wcd9378_hph_set_channel_volume(struct snd_soc_component *component) { struct wcd9378_priv *wcd9378 = snd_soc_component_get_drvdata(component); if ((!wcd9378->comp1_enable) && (!wcd9378->comp2_enable)) { snd_soc_component_update_bits(component, (WCD9378_FU42_CH_VOL_CH1 | WCD9378_MBQ_ENABLE_MASK), WCD9378_FU42_CH_VOL_CH1_FU42_CH_VOL_CH1_MASK, wcd9378->hph_gain >> 8); snd_soc_component_update_bits(component, WCD9378_FU42_CH_VOL_CH1, WCD9378_FU42_CH_VOL_CH1_FU42_CH_VOL_CH1_MASK, wcd9378->hph_gain & 0x00ff); snd_soc_component_update_bits(component, (WCD9378_FU42_CH_VOL_CH2 | WCD9378_MBQ_ENABLE_MASK), WCD9378_FU42_CH_VOL_CH2_FU42_CH_VOL_CH2_MASK, wcd9378->hph_gain >> 8); snd_soc_component_update_bits(component, WCD9378_FU42_CH_VOL_CH2, WCD9378_FU42_CH_VOL_CH2_FU42_CH_VOL_CH2_MASK, wcd9378->hph_gain & 0x00ff); } } static int wcd9378_hph_sequencer_enable(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 wcd9378_priv *wcd9378 = snd_soc_component_get_drvdata(component); int power_level, ret = 0; struct swr_device *swr_dev = wcd9378->tx_swr_dev; u8 scp_commit_val = 0x2; dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__, w->name, event); switch (event) { case SND_SOC_DAPM_PRE_PMU: wcd9378_sys_usage_auto_udpate(component, RX0_RX1_HPH_EN, true); if ((!wcd9378->comp1_enable) || (!wcd9378->comp2_enable)) { snd_soc_component_update_bits(component, WCD9378_HPH_UP_T7, WCD9378_HPH_UP_T7_HPH_UP_T7_MASK, 0x07); snd_soc_component_update_bits(component, WCD9378_HPH_DN_T1, WCD9378_HPH_DN_T1_HPH_DN_T1_MASK, 0x07); } if ((wcd9378->hph_mode == CLS_AB) || (wcd9378->hph_mode == CLS_AB_HIFI) || (wcd9378->hph_mode == CLS_AB_LP) || (wcd9378->hph_mode == CLS_AB_LOHIFI)) snd_soc_component_update_bits(component, WCD9378_CP_CP_DTOP_CTRL_14, WCD9378_CP_CP_DTOP_CTRL_14_OVERRIDE_VREF_MASK, 0x80); /*GET HPH_MODE*/ power_level = wcd9378_get_hph_pwr_level(wcd9378->hph_mode); /*SET HPH_MODE*/ snd_soc_component_update_bits(component, WCD9378_IT41_USAGE, WCD9378_IT41_USAGE_IT41_USAGE_MASK, power_level); /*TURN ON HPH SEQUENCER*/ snd_soc_component_update_bits(component, WCD9378_PDE47_REQ_PS, WCD9378_PDE47_REQ_PS_PDE47_REQ_PS_MASK, 0x00); wcd9378_hph_set_channel_volume(component); if ((!wcd9378->comp1_enable) || (!wcd9378->comp2_enable)) /*PA delay is 22400us*/ usleep_range(22500, 22510); else /*COMP delay is 9400us*/ usleep_range(9500, 9510); /*RX0 unmute*/ snd_soc_component_update_bits(component, WCD9378_FU42_MUTE_CH1, WCD9378_FU42_MUTE_CH1_FU42_MUTE_CH1_MASK, 0x00); /*RX1 unmute*/ snd_soc_component_update_bits(component, WCD9378_FU42_MUTE_CH2, WCD9378_FU42_MUTE_CH2_FU42_MUTE_CH2_MASK, 0x00); if (wcd9378->sys_usage == SYS_USAGE_10) /*FU23 UNMUTE*/ snd_soc_component_update_bits(component, WCD9378_FU23_MUTE, WCD9378_FU23_MUTE_FU23_MUTE_MASK, 0x00); swr_write(swr_dev, swr_dev->dev_num, 0x004c, &scp_commit_val); wcd9378_swr_slvdev_datapath_control(wcd9378->dev, RX_PATH, true); break; case SND_SOC_DAPM_POST_PMD: /*RX0 mute*/ snd_soc_component_update_bits(component, WCD9378_FU42_MUTE_CH1, WCD9378_FU42_MUTE_CH1_FU42_MUTE_CH1_MASK, 0x01); /*RX1 mute*/ snd_soc_component_update_bits(component, WCD9378_FU42_MUTE_CH2, WCD9378_FU42_MUTE_CH2_FU42_MUTE_CH2_MASK, 0x01); /*TEAR DOWN HPH SEQUENCER*/ snd_soc_component_update_bits(component, WCD9378_PDE47_REQ_PS, WCD9378_PDE47_REQ_PS_PDE47_REQ_PS_MASK, 0x03); if (!wcd9378->comp1_enable || !wcd9378->comp2_enable) /*PA delay is 24250us*/ usleep_range(24300, 24310); else /*COMP delay is 11250us*/ usleep_range(11300, 11310); wcd9378_sys_usage_auto_udpate(component, RX0_RX1_HPH_EN, false); break; default: break; }; return ret; } static int wcd9378_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 wcd9378_priv *wcd9378 = snd_soc_component_get_drvdata(component); int ear_rx2 = 0; dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__, w->name, event); ear_rx2 = snd_soc_component_read(component, WCD9378_CDC_AUX_GAIN_CTL) & WCD9378_CDC_AUX_GAIN_CTL_AUX_EN_MASK; switch (event) { case SND_SOC_DAPM_PRE_PMU: /*SHORT_PROT_EN ENABLE*/ snd_soc_component_update_bits(component, WCD9378_ANA_EAR, WCD9378_ANA_EAR_SHORT_PROT_EN_MASK, 0x40); if (!ear_rx2) { /*RX0 ENABLE*/ snd_soc_component_update_bits(component, WCD9378_CDC_HPH_GAIN_CTL, WCD9378_CDC_HPH_GAIN_CTL_HPHL_RX_EN_MASK, 0x04); wcd9378_sys_usage_auto_udpate(component, RX0_EAR_EN, true); if (wcd9378->comp1_enable) { snd_soc_component_update_bits(component, WCD9378_CDC_COMP_CTL_0, WCD9378_CDC_COMP_CTL_0_EAR_COMP_EN_MASK, 0x04); wcd9378_rx_connect_port(component, COMP_L, true); } wcd9378_rx_connect_port(component, HPH_L, true); } else { wcd9378_sys_usage_auto_udpate(component, RX2_EAR_EN, true); /*FORCE CLASS_AB EN*/ snd_soc_component_update_bits(component, WCD9378_SEQ_OVRRIDE_CTL0, WCD9378_SEQ_OVRRIDE_CTL0_CLASSAB_EN_OVR_MASK, 0x20); snd_soc_component_update_bits(component, WCD9378_CP_CP_DTOP_CTRL_14, WCD9378_CP_CP_DTOP_CTRL_14_OVERRIDE_VREF_MASK, 0x80); if (wcd9378->rx2_clk_mode) snd_soc_component_update_bits(component, WCD9378_CDC_PATH_MODE, WCD9378_CDC_PATH_MODE_RX2_CLK_RATE_MASK, 0x40); wcd9378_rx_connect_port(component, LO, true); } break; case SND_SOC_DAPM_POST_PMD: /*SHORT_PROT_EN DISABLE*/ snd_soc_component_update_bits(component, WCD9378_ANA_EAR, WCD9378_ANA_EAR_SHORT_PROT_EN_MASK, 0x00); if (test_bit(RX0_EAR_EN, &wcd9378->sys_usage_status)) { /*RX0 DISABLE*/ snd_soc_component_update_bits(component, WCD9378_CDC_HPH_GAIN_CTL, WCD9378_CDC_HPH_GAIN_CTL_HPHL_RX_EN_MASK, 0x00); wcd9378_rx_connect_port(component, HPH_L, false); if (wcd9378->comp1_enable) { snd_soc_component_update_bits(component, WCD9378_CDC_COMP_CTL_0, WCD9378_CDC_COMP_CTL_0_EAR_COMP_EN_MASK, 0x00); wcd9378_rx_connect_port(component, COMP_L, false); } wcd9378_sys_usage_auto_udpate(component, RX0_EAR_EN, false); } else { wcd9378_rx_connect_port(component, LO, false); wcd9378_sys_usage_auto_udpate(component, RX2_EAR_EN, false); wcd9378_swr_slvdev_datapath_control(wcd9378->dev, RX_PATH, false); } break; }; return 0; } static int wcd9378_codec_aux_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 wcd9378_priv *wcd9378 = snd_soc_component_get_drvdata(component); int aux_rx2 = 0; dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__, w->name, event); aux_rx2 = snd_soc_component_read(component, WCD9378_CDC_AUX_GAIN_CTL) & WCD9378_CDC_AUX_GAIN_CTL_AUX_EN_MASK; switch (event) { case SND_SOC_DAPM_PRE_PMU: /*AUXPA SHORT PROT ENABLE*/ snd_soc_component_update_bits(component, WCD9378_AUX_AUXPA, WCD9378_AUX_AUXPA_AUX_PA_SHORT_PROT_EN_MASK, 0x40); if (!aux_rx2) { /*RX1 ENABLE*/ snd_soc_component_update_bits(component, WCD9378_CDC_HPH_GAIN_CTL, WCD9378_CDC_HPH_GAIN_CTL_HPHR_RX_EN_MASK, 0x08); wcd9378_sys_usage_auto_udpate(component, RX1_AUX_EN, true); wcd9378_rx_connect_port(component, HPH_R, true); } else { wcd9378_sys_usage_auto_udpate(component, RX2_AUX_EN, true); if (wcd9378->rx2_clk_mode) snd_soc_component_update_bits(component, WCD9378_CDC_PATH_MODE, WCD9378_CDC_PATH_MODE_RX2_CLK_RATE_MASK, 0x40); wcd9378_rx_connect_port(component, LO, true); } break; case SND_SOC_DAPM_POST_PMD: /*AUXPA SHORT PROT DISABLE*/ snd_soc_component_update_bits(component, WCD9378_AUX_AUXPA, WCD9378_AUX_AUXPA_AUX_PA_SHORT_PROT_EN_MASK, 0x00); if (test_bit(RX1_AUX_EN, &wcd9378->sys_usage_status)) { wcd9378_rx_connect_port(component, HPH_R, false); wcd9378_sys_usage_auto_udpate(component, RX1_AUX_EN, false); } else { wcd9378_rx_connect_port(component, LO, false); wcd9378_sys_usage_auto_udpate(component, RX2_AUX_EN, false); wcd9378_swr_slvdev_datapath_control(wcd9378->dev, RX_PATH, false); } break; }; return 0; } static int wcd9378_sa_sequencer_enable(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__, w->name, event); switch (event) { case SND_SOC_DAPM_PRE_PMU: /*TURN ON AMP SEQUENCER*/ snd_soc_component_update_bits(component, WCD9378_PDE23_REQ_PS, WCD9378_PDE23_REQ_PS_PDE23_REQ_PS_MASK, 0x00); /*default delay 8550us*/ usleep_range(8600, 8610); /*FU23 UNMUTE*/ snd_soc_component_update_bits(component, WCD9378_FU23_MUTE, WCD9378_FU23_MUTE_FU23_MUTE_MASK, 0x00); break; case SND_SOC_DAPM_POST_PMD: /*FU23 MUTE*/ snd_soc_component_update_bits(component, WCD9378_FU23_MUTE, WCD9378_FU23_MUTE_FU23_MUTE_MASK, 0x01); /*TEAR DOWN AMP SEQUENCER*/ snd_soc_component_update_bits(component, WCD9378_PDE23_REQ_PS, WCD9378_PDE23_REQ_PS_PDE23_REQ_PS_MASK, 0x03); /*default delay 1530us*/ usleep_range(15400, 15410); break; default: break; }; return 0; } int wcd9378_micbias_control(struct snd_soc_component *component, int micb_num, int req, bool is_dapm) { struct wcd9378_priv *wcd9378 = snd_soc_component_get_drvdata(component); struct wcd9378_pdata *pdata = dev_get_platdata(wcd9378->dev); struct wcd9378_micbias_setting *mb = &pdata->micbias; int micb_usage = 0, micb_mask = 0, micb_usage_val = 0; int pre_off_event = 0, post_off_event = 0; int post_on_event = 0, post_dapm_off = 0; int post_dapm_on = 0; int pull_up_mask = 0, pull_up_en = 0; int micb_index = 0, ret = 0; switch (micb_num) { case MIC_BIAS_1: pull_up_mask = WCD9378_MB_PULLUP_EN_MB1_1P8V_OR_PULLUP_SEL_MASK; pull_up_en = 0x01; micb_usage = WCD9378_IT11_MICB; micb_mask = WCD9378_IT11_MICB_IT11_MICB_MASK; micb_usage_val = mb->micb1_usage_val; break; case MIC_BIAS_2: pull_up_mask = WCD9378_MB_PULLUP_EN_MB2_1P8V_OR_PULLUP_SEL_MASK; pull_up_en = 0x02; micb_usage = WCD9378_SMP_MIC_CTRL1_IT11_MICB; micb_mask = WCD9378_SMP_MIC_CTRL1_IT11_MICB_IT11_MICB_MASK; micb_usage_val = mb->micb2_usage_val; pre_off_event = WCD_EVENT_PRE_MICBIAS_2_OFF; post_off_event = WCD_EVENT_POST_MICBIAS_2_OFF; post_on_event = WCD_EVENT_POST_MICBIAS_2_ON; post_dapm_on = WCD_EVENT_POST_DAPM_MICBIAS_2_ON; post_dapm_off = WCD_EVENT_POST_DAPM_MICBIAS_2_OFF; break; case MIC_BIAS_3: micb_usage = WCD9378_SMP_MIC_CTRL2_IT11_MICB; micb_mask = WCD9378_SMP_MIC_CTRL2_IT11_MICB_IT11_MICB_MASK; pull_up_mask = WCD9378_MB_PULLUP_EN_MB3_1P8V_OR_PULLUP_SEL_MASK; pull_up_en = 0x04; micb_usage_val = mb->micb3_usage_val; break; default: dev_err(component->dev, "%s: Invalid micbias number: %d\n", __func__, micb_num); return -EINVAL; } mutex_lock(&wcd9378->micb_lock); micb_index = micb_num - 1; switch (req) { case MICB_PULLUP_ENABLE: wcd9378->pullup_ref[micb_index]++; if ((wcd9378->pullup_ref[micb_index] == 1) && (wcd9378->micb_ref[micb_index] == 0)) { snd_soc_component_update_bits(component, WCD9378_MB_PULLUP_EN, pull_up_mask, pull_up_en); snd_soc_component_update_bits(component, micb_usage, micb_mask, micb_usage_val); if (micb_num == MIC_BIAS_2) { snd_soc_component_update_bits(component, WCD9378_IT31_MICB, WCD9378_IT31_MICB_IT31_MICB_MASK, micb_usage_val); wcd9378->curr_micbias2 = mb->micb2_mv; } } break; case MICB_PULLUP_DISABLE: if (wcd9378->pullup_ref[micb_index] > 0) wcd9378->pullup_ref[micb_index]--; if ((wcd9378->pullup_ref[micb_index] == 0) && (wcd9378->micb_ref[micb_index] == 0)) { snd_soc_component_update_bits(component, micb_usage, micb_mask, 0x01); if (micb_num == MIC_BIAS_2) { snd_soc_component_update_bits(component, WCD9378_IT31_MICB, WCD9378_IT31_MICB_IT31_MICB_MASK, 0x01); wcd9378->curr_micbias2 = 0; } } break; case MICB_ENABLE: wcd9378->micb_ref[micb_index]++; if (wcd9378->micb_ref[micb_index] == 1) { dev_dbg(component->dev, "%s: enable micbias, micb_usage:0x%0x, val:0x%0x\n", __func__, micb_usage, micb_usage_val); snd_soc_component_update_bits(component, micb_usage, micb_mask, micb_usage_val); if (micb_num == MIC_BIAS_2) { snd_soc_component_update_bits(component, WCD9378_IT31_MICB, WCD9378_IT31_MICB_IT31_MICB_MASK, micb_usage_val); wcd9378->curr_micbias2 = mb->micb2_mv; } if (post_on_event) blocking_notifier_call_chain( &wcd9378->mbhc->notifier, post_on_event, &wcd9378->mbhc->wcd_mbhc); } if (is_dapm && post_dapm_on && wcd9378->mbhc) blocking_notifier_call_chain(&wcd9378->mbhc->notifier, post_dapm_on, &wcd9378->mbhc->wcd_mbhc); break; case MICB_DISABLE: if (wcd9378->micb_ref[micb_index] > 0) wcd9378->micb_ref[micb_index]--; if ((wcd9378->micb_ref[micb_index] == 0) && (wcd9378->pullup_ref[micb_index] > 0)) { snd_soc_component_update_bits(component, WCD9378_MB_PULLUP_EN, pull_up_mask, pull_up_en); if (micb_num == MIC_BIAS_2) wcd9378->curr_micbias2 = mb->micb2_mv; } else if ((wcd9378->micb_ref[micb_index] == 0) && (wcd9378->pullup_ref[micb_index] == 0)) { if (pre_off_event && wcd9378->mbhc) blocking_notifier_call_chain( &wcd9378->mbhc->notifier, pre_off_event, &wcd9378->mbhc->wcd_mbhc); snd_soc_component_update_bits(component, micb_usage, micb_mask, 0x00); if (micb_num == MIC_BIAS_2) { snd_soc_component_update_bits(component, WCD9378_IT31_MICB, WCD9378_IT31_MICB_IT31_MICB_MASK, 0x00); wcd9378->curr_micbias2 = 0; } if (post_off_event && wcd9378->mbhc) blocking_notifier_call_chain( &wcd9378->mbhc->notifier, post_off_event, &wcd9378->mbhc->wcd_mbhc); } if (is_dapm && post_dapm_off && wcd9378->mbhc) blocking_notifier_call_chain(&wcd9378->mbhc->notifier, post_dapm_off, &wcd9378->mbhc->wcd_mbhc); break; default: dev_err(component->dev, "%s: Invalid req event: %d\n", __func__, req); return -EINVAL; } dev_dbg(component->dev, "%s: micb_num:%d, micb_ref: %d, pullup_ref: %d\n", __func__, micb_num, wcd9378->micb_ref[micb_index], wcd9378->pullup_ref[micb_index]); mutex_unlock(&wcd9378->micb_lock); return ret; } EXPORT_SYMBOL_GPL(wcd9378_micbias_control); static int wcd9378_get_logical_addr(struct swr_device *swr_dev) { int ret = 0; uint8_t devnum = 0; int num_retry = NUM_ATTEMPTS; do { /* retry after 4ms */ usleep_range(4000, 4010); ret = swr_get_logical_dev_num(swr_dev, swr_dev->addr, &devnum); } while (ret && --num_retry); if (ret) dev_err(&swr_dev->dev, "%s get devnum %d for dev addr %llx failed\n", __func__, devnum, swr_dev->addr); swr_dev->dev_num = devnum; return 0; } static bool get_usbc_hs_status(struct snd_soc_component *component, struct wcd_mbhc_config *mbhc_cfg) { if (mbhc_cfg->enable_usbc_analog) { if (!(snd_soc_component_read(component, WCD9378_ANA_MBHC_MECH) & 0x20)) return true; } return false; } int wcd9378_swr_dmic_register_notifier(struct snd_soc_component *component, struct notifier_block *nblock, bool enable) { struct wcd9378_priv *wcd9378_priv = NULL; if (component == NULL) { pr_err_ratelimited("%s: wcd9378 component is NULL\n", __func__); return -EINVAL; } wcd9378_priv = snd_soc_component_get_drvdata(component); wcd9378_priv->notify_swr_dmic = enable; if (enable) return blocking_notifier_chain_register(&wcd9378_priv->notifier, nblock); else return blocking_notifier_chain_unregister( &wcd9378_priv->notifier, nblock); } EXPORT_SYMBOL_GPL(wcd9378_swr_dmic_register_notifier); static int wcd9378_event_notify(struct notifier_block *block, unsigned long val, void *data) { u16 event = (val & 0xffff); int ret = 0; struct wcd9378_priv *wcd9378 = dev_get_drvdata((struct device *)data); struct snd_soc_component *component = wcd9378->component; struct wcd_mbhc *mbhc; int rx_clk_type; switch (event) { case BOLERO_SLV_EVT_TX_CH_HOLD_CLEAR: if (test_bit(WCD_ADC1, &wcd9378->status_mask)) { snd_soc_component_update_bits(component, WCD9378_ANA_TX_CH2, 0x40, 0x00); set_bit(WCD_ADC1_MODE, &wcd9378->status_mask); clear_bit(WCD_ADC1, &wcd9378->status_mask); } if (test_bit(WCD_ADC2, &wcd9378->status_mask)) { snd_soc_component_update_bits(component, WCD9378_ANA_TX_CH2, 0x20, 0x00); set_bit(WCD_ADC2_MODE, &wcd9378->status_mask); clear_bit(WCD_ADC2, &wcd9378->status_mask); } if (test_bit(WCD_ADC3, &wcd9378->status_mask)) { snd_soc_component_update_bits(component, WCD9378_ANA_TX_CH3_HPF, 0x40, 0x00); set_bit(WCD_ADC3_MODE, &wcd9378->status_mask); clear_bit(WCD_ADC3, &wcd9378->status_mask); } break; case BOLERO_SLV_EVT_PA_OFF_PRE_SSR: snd_soc_component_update_bits(component, WCD9378_ANA_HPH, 0xC0, 0x00); snd_soc_component_update_bits(component, WCD9378_ANA_EAR, 0x80, 0x00); snd_soc_component_update_bits(component, WCD9378_AUX_AUXPA, 0x80, 0x00); break; case BOLERO_SLV_EVT_SSR_DOWN: if (wcd9378->notify_swr_dmic) blocking_notifier_call_chain(&wcd9378->notifier, WCD9378_EVT_SSR_DOWN, NULL); wcd9378->mbhc->wcd_mbhc.deinit_in_progress = true; mbhc = &wcd9378->mbhc->wcd_mbhc; wcd9378->usbc_hs_status = get_usbc_hs_status(component, mbhc->mbhc_cfg); wcd9378_mbhc_ssr_down(wcd9378->mbhc, component); wcd9378_reset_low(wcd9378->dev); break; case BOLERO_SLV_EVT_SSR_UP: wcd9378_reset(wcd9378->dev); /* allow reset to take effect */ usleep_range(10000, 10010); wcd9378_get_logical_addr(wcd9378->tx_swr_dev); wcd9378_get_logical_addr(wcd9378->rx_swr_dev); wcd9378->tx_swr_dev->scp1_val = 0; wcd9378->tx_swr_dev->scp2_val = 0; wcd9378->rx_swr_dev->scp1_val = 0; wcd9378->rx_swr_dev->scp2_val = 0; wcd9378_init_reg(component); regcache_mark_dirty(wcd9378->regmap); regcache_sync(wcd9378->regmap); /* Initialize MBHC module */ mbhc = &wcd9378->mbhc->wcd_mbhc; ret = wcd9378_mbhc_post_ssr_init(wcd9378->mbhc, component); if (ret) { dev_err(component->dev, "%s: mbhc initialization failed\n", __func__); } else { wcd9378_mbhc_hs_detect(component, mbhc->mbhc_cfg); } wcd9378->mbhc->wcd_mbhc.deinit_in_progress = false; if (wcd9378->notify_swr_dmic) blocking_notifier_call_chain(&wcd9378->notifier, WCD9378_EVT_SSR_UP, NULL); if (wcd9378->usbc_hs_status) mdelay(500); break; case BOLERO_SLV_EVT_CLK_NOTIFY: snd_soc_component_update_bits(component, WCD9378_TOP_CLK_CFG, 0x06, ((val >> 0x10) << 0x01)); rx_clk_type = (val >> 0x10); switch (rx_clk_type) { case RX_CLK_12P288MHZ: wcd9378->swr_base_clk = SWR_BASECLK_24P576MHZ; wcd9378->swr_clk_scale = SWR_CLKSCALE_DIV2; break; case RX_CLK_11P2896MHZ: wcd9378->swr_base_clk = SWR_BASECLK_22P5792MHZ; wcd9378->swr_clk_scale = SWR_CLKSCALE_DIV2; break; default: wcd9378->swr_base_clk = SWR_BASECLK_19P2MHZ; wcd9378->swr_clk_scale = SWR_CLKSCALE_DIV2; break; } dev_dbg(component->dev, "%s: base_clk:0x%0x, clk_scale:0x%x\n", __func__, wcd9378->swr_base_clk, wcd9378->swr_clk_scale); break; default: dev_dbg(component->dev, "%s: invalid event %d\n", __func__, event); break; } return 0; } static int wcd9378_wakeup(void *handle, bool enable) { struct wcd9378_priv *priv; int ret = 0; if (!handle) { pr_err("%s: NULL handle\n", __func__); return -EINVAL; } priv = (struct wcd9378_priv *)handle; if (!priv->tx_swr_dev) { pr_err("%s: tx swr dev is NULL\n", __func__); return -EINVAL; } mutex_lock(&priv->wakeup_lock); if (enable) ret = swr_device_wakeup_vote(priv->tx_swr_dev); else ret = swr_device_wakeup_unvote(priv->tx_swr_dev); mutex_unlock(&priv->wakeup_lock); return ret; } static inline int wcd9378_tx_path_get(const char *wname, unsigned int *path_num) { int ret = 0; char *widget_name = NULL; char *w_name = NULL; char *path_num_char = NULL; char *path_name = NULL; widget_name = kstrndup(wname, 9, GFP_KERNEL); if (!widget_name) return -EINVAL; w_name = widget_name; path_name = strsep(&widget_name, " "); if (!path_name) { pr_err("%s: Invalid widget name = %s\n", __func__, widget_name); ret = -EINVAL; goto err; } path_num_char = strpbrk(path_name, "0123"); if (!path_num_char) { pr_err("%s: tx path index not found\n", __func__); ret = -EINVAL; goto err; } ret = kstrtouint(path_num_char, 10, path_num); if (ret < 0) pr_err("%s: Invalid tx path = %s\n", __func__, w_name); err: kfree(w_name); return ret; } static int wcd9378_tx_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct wcd9378_priv *wcd9378 = NULL; int ret = 0; unsigned int path = 0; if (!component) return -EINVAL; wcd9378 = snd_soc_component_get_drvdata(component); if (!wcd9378) return -EINVAL; ret = wcd9378_tx_path_get(kcontrol->id.name, &path); if (ret < 0) return ret; ucontrol->value.integer.value[0] = wcd9378->tx_mode[path]; return 0; } static int wcd9378_tx_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct wcd9378_priv *wcd9378 = NULL; u32 mode_val; unsigned int path = 0; int ret = 0; if (!component) return -EINVAL; wcd9378 = snd_soc_component_get_drvdata(component); if (!wcd9378) return -EINVAL; ret = wcd9378_tx_path_get(kcontrol->id.name, &path); if (ret) return ret; mode_val = ucontrol->value.enumerated.item[0]; dev_dbg(component->dev, "%s: mode: %d\n", __func__, mode_val); wcd9378->tx_mode[path] = mode_val; return 0; } static int wcd9378_loopback_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); u32 loopback_mode = 0; if (!component) return -EINVAL; loopback_mode = (snd_soc_component_read(component, WCD9378_LOOP_BACK_MODE) & WCD9378_LOOP_BACK_MODE_LOOPBACK_MODE_MASK); ucontrol->value.integer.value[0] = loopback_mode; return 0; } static int wcd9378_loopback_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); u32 loopback_mode = 0; if (!component) return -EINVAL; loopback_mode = ucontrol->value.enumerated.item[0]; snd_soc_component_update_bits(component, WCD9378_LOOP_BACK_MODE, WCD9378_LOOP_BACK_MODE_LOOPBACK_MODE_MASK, loopback_mode); dev_dbg(component->dev, "%s: loopback_mode: %d\n", __func__, loopback_mode); return 0; } static int wcd9378_aux_dsm_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); u32 aux_dsm_in = 0; if (!component) return -EINVAL; aux_dsm_in = (snd_soc_component_read(component, WCD9378_LB_IN_SEL_CTL) & WCD9378_LB_IN_SEL_CTL_AUX_LB_IN_SEL_MASK); ucontrol->value.integer.value[0] = aux_dsm_in; return 0; } static int wcd9378_aux_dsm_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); u32 aux_dsm_in = 0; if (!component) return -EINVAL; aux_dsm_in = ucontrol->value.enumerated.item[0]; snd_soc_component_update_bits(component, WCD9378_LB_IN_SEL_CTL, WCD9378_LB_IN_SEL_CTL_AUX_LB_IN_SEL_MASK, aux_dsm_in); dev_dbg(component->dev, "%s: aux_dsm input: %d\n", __func__, aux_dsm_in); return 0; } static int wcd9378_hph_dsm_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); u32 hph_dsm_in = 0; if (!component) return -EINVAL; hph_dsm_in = (snd_soc_component_read(component, WCD9378_LB_IN_SEL_CTL) & WCD9378_LB_IN_SEL_CTL_HPH_LB_IN_SEL_MASK); ucontrol->value.integer.value[0] = hph_dsm_in; return 0; } static int wcd9378_hph_dsm_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); u32 hph_dsm_in = 0; if (!component) return -EINVAL; hph_dsm_in = ucontrol->value.enumerated.item[0]; snd_soc_component_update_bits(component, WCD9378_LB_IN_SEL_CTL, WCD9378_LB_IN_SEL_CTL_HPH_LB_IN_SEL_MASK, hph_dsm_in); dev_dbg(component->dev, "%s: hph_dsm input: %d\n", __func__, hph_dsm_in); return 0; } static int wcd9378_hph_put_gain(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct wcd9378_priv *wcd9378 = snd_soc_component_get_drvdata(component); u16 offset = ucontrol->value.enumerated.item[0]; u32 temp = 0; temp = 0x00 - offset * 0x180; wcd9378->hph_gain = (u16)(temp & 0xffff); dev_dbg(component->dev, "%s: hph gain is 0x%0x\n", __func__, wcd9378->hph_gain); return 0; } static int wcd9378_hph_get_gain(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct wcd9378_priv *wcd9378 = snd_soc_component_get_drvdata(component); u32 temp = 0; u16 offset = 0; temp = 0 - wcd9378->hph_gain; offset = (u16)(temp & 0xffff); offset /= 0x180; ucontrol->value.enumerated.item[0] = offset; dev_dbg(component->dev, "%s: offset is 0x%0x\n", __func__, offset); return 0; } static int wcd9378_ear_pa_gain_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); int ear_gain = 0; if (component == NULL) return -EINVAL; ear_gain = snd_soc_component_read(component, WCD9378_ANA_EAR_COMPANDER_CTL) & WCD9378_ANA_EAR_COMPANDER_CTL_EAR_GAIN_MASK; ucontrol->value.enumerated.item[0] = ear_gain; dev_dbg(component->dev, "%s: get ear_gain val: 0x%x\n", __func__, ear_gain); return 0; } static int wcd9378_ear_pa_gain_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); int ear_gain = 0; if (component == NULL) return -EINVAL; if (ucontrol->value.integer.value[0] < 0 || ucontrol->value.integer.value[0] > 0x10) { dev_err(component->dev, "%s: Unsupported gain val %ld\n", __func__, ucontrol->value.integer.value[0]); return -EINVAL; } ear_gain = ucontrol->value.integer.value[0]; snd_soc_component_update_bits(component, WCD9378_ANA_EAR_COMPANDER_CTL, WCD9378_ANA_EAR_COMPANDER_CTL_EAR_GAIN_MASK, ear_gain); dev_dbg(component->dev, "%s: set ear_gain val: 0x%x\n", __func__, ear_gain); return 0; } static int wcd9378_aux_pa_gain_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); int aux_gain = 0; if (component == NULL) return -EINVAL; aux_gain = snd_soc_component_read(component, WCD9378_AUX_INT_MISC) & WCD9378_AUX_INT_MISC_PA_GAIN_MASK; ucontrol->value.enumerated.item[0] = aux_gain; dev_dbg(component->dev, "%s: get aux_gain val: 0x%x\n", __func__, aux_gain); return 0; } static int wcd9378_aux_pa_gain_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); int aux_gain = 0; if (component == NULL) return -EINVAL; if (ucontrol->value.integer.value[0] < 0 || ucontrol->value.integer.value[0] > 0x8) { dev_err(component->dev, "%s: Unsupported gain val %ld\n", __func__, ucontrol->value.integer.value[0]); return -EINVAL; } aux_gain = ucontrol->value.integer.value[0]; snd_soc_component_update_bits(component, WCD9378_AUX_INT_MISC, WCD9378_AUX_INT_MISC_PA_GAIN_MASK, aux_gain); dev_dbg(component->dev, "%s: set aux_gain val: 0x%x\n", __func__, aux_gain); return 0; } static int wcd9378_rx2_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct wcd9378_priv *wcd9378 = snd_soc_component_get_drvdata(component); if (ucontrol->value.enumerated.item[0]) wcd9378->rx2_clk_mode = RX2_NORMAL_MODE; else wcd9378->rx2_clk_mode = RX2_HP_MODE; return 1; } static int wcd9378_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 wcd9378_priv *wcd9378 = snd_soc_component_get_drvdata(component); ucontrol->value.enumerated.item[0] = wcd9378->hph_mode; return 0; } static int wcd9378_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 wcd9378_priv *wcd9378 = snd_soc_component_get_drvdata(component); if (wcd9378->hph_mode == ucontrol->value.enumerated.item[0]) return 0; wcd9378->hph_mode = ucontrol->value.enumerated.item[0]; return 1; } /* wcd9378_codec_get_dev_num - returns swr device number * @component: Codec instance * * Return: swr device number on success or negative error * code on failure. */ int wcd9378_codec_get_dev_num(struct snd_soc_component *component) { struct wcd9378_priv *wcd9378; if (!component) return -EINVAL; wcd9378 = snd_soc_component_get_drvdata(component); if (!wcd9378 || !wcd9378->rx_swr_dev) { pr_err("%s: wcd9378 component is NULL\n", __func__); return -EINVAL; } return wcd9378->rx_swr_dev->dev_num; } EXPORT_SYMBOL_GPL(wcd9378_codec_get_dev_num); static int wcd9378_get_compander(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct wcd9378_priv *wcd9378 = snd_soc_component_get_drvdata(component); bool hphr; struct soc_multi_mixer_control *mc; mc = (struct soc_multi_mixer_control *)(kcontrol->private_value); hphr = mc->shift; ucontrol->value.integer.value[0] = hphr ? wcd9378->comp2_enable : wcd9378->comp1_enable; return 0; } static int wcd9378_set_compander(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct wcd9378_priv *wcd9378 = snd_soc_component_get_drvdata(component); int value = ucontrol->value.integer.value[0]; bool hphr; struct soc_multi_mixer_control *mc; mc = (struct soc_multi_mixer_control *)(kcontrol->private_value); hphr = mc->shift; if (hphr) wcd9378->comp2_enable = value; else wcd9378->comp1_enable = value; dev_dbg(component->dev, "%s: set compander: %d\n", __func__, value); return 0; } static int wcd9378_codec_enable_vdd_buck(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 wcd9378_priv *wcd9378 = snd_soc_component_get_drvdata(component); struct wcd9378_pdata *pdata = NULL; int ret = 0; pdata = dev_get_platdata(wcd9378->dev); if (!pdata) { dev_err(component->dev, "%s: pdata is NULL\n", __func__); return -EINVAL; } if (!msm_cdc_is_ondemand_supply(wcd9378->dev, wcd9378->supplies, pdata->regulator, pdata->num_supplies, "cdc-vdd-buck")) return 0; dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__, w->name, event); switch (event) { case SND_SOC_DAPM_PRE_PMU: if (test_bit(ALLOW_BUCK_DISABLE, &wcd9378->status_mask)) { dev_dbg(component->dev, "%s: buck already in enabled state\n", __func__); clear_bit(ALLOW_BUCK_DISABLE, &wcd9378->status_mask); return 0; } ret = msm_cdc_enable_ondemand_supply(wcd9378->dev, wcd9378->supplies, pdata->regulator, pdata->num_supplies, "cdc-vdd-buck"); if (ret == -EINVAL) { dev_err(component->dev, "%s: vdd buck is not enabled\n", __func__); return ret; } clear_bit(ALLOW_BUCK_DISABLE, &wcd9378->status_mask); /* * 200us sleep is required after LDO is enabled as per * HW requirement */ usleep_range(200, 250); break; case SND_SOC_DAPM_POST_PMD: set_bit(ALLOW_BUCK_DISABLE, &wcd9378->status_mask); break; } return 0; } static void wcd9378_tx_get_slave_ch_type_idx(const char *wname, int *ch_idx) { u8 ch_type = 0; if (strnstr(wname, "ADC1", sizeof("ADC1"))) ch_type = ADC1; else if (strnstr(wname, "ADC2", sizeof("ADC2"))) ch_type = ADC2; else if (strnstr(wname, "ADC3", sizeof("ADC3"))) ch_type = ADC3; else if (strnstr(wname, "ADC4", sizeof("ADC4"))) ch_type = ADC4; else if (strnstr(wname, "DMIC0", sizeof("DMIC0"))) ch_type = DMIC0; else if (strnstr(wname, "DMIC1", sizeof("DMIC1"))) ch_type = DMIC1; else if (strnstr(wname, "MBHC", sizeof("MBHC"))) ch_type = MBHC; else if (strnstr(wname, "DMIC2", sizeof("DMIC2"))) ch_type = DMIC2; else if (strnstr(wname, "DMIC3", sizeof("DMIC3"))) ch_type = DMIC3; else if (strnstr(wname, "DMIC4", sizeof("DMIC4"))) ch_type = DMIC4; else if (strnstr(wname, "DMIC5", sizeof("DMIC5"))) ch_type = DMIC5; else pr_err("%s: port name: %s is not listed\n", __func__, wname); if (ch_type) *ch_idx = wcd9378_slave_get_slave_ch_val(ch_type); else *ch_idx = -EINVAL; } static int wcd9378_tx_master_ch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct wcd9378_priv *wcd9378 = NULL; int slave_ch_idx = -EINVAL; if (component == NULL) return -EINVAL; wcd9378 = snd_soc_component_get_drvdata(component); if (wcd9378 == NULL) return -EINVAL; wcd9378_tx_get_slave_ch_type_idx(kcontrol->id.name, &slave_ch_idx); if (slave_ch_idx < 0 || slave_ch_idx >= WCD9378_MAX_SLAVE_CH_TYPES) return -EINVAL; ucontrol->value.integer.value[0] = wcd9378_slave_get_master_ch_val( wcd9378->tx_master_ch_map[slave_ch_idx]); return 0; } static int wcd9378_tx_master_ch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct wcd9378_priv *wcd9378 = NULL; int slave_ch_idx = -EINVAL, idx = 0; if (component == NULL) return -EINVAL; wcd9378 = snd_soc_component_get_drvdata(component); if (wcd9378 == NULL) return -EINVAL; wcd9378_tx_get_slave_ch_type_idx(kcontrol->id.name, &slave_ch_idx); if (slave_ch_idx < 0 || slave_ch_idx >= WCD9378_MAX_SLAVE_CH_TYPES) return -EINVAL; dev_dbg(component->dev, "%s: slave_ch_idx: %d", __func__, slave_ch_idx); dev_dbg(component->dev, "%s: ucontrol->value.enumerated.item[0] = %ld\n", __func__, ucontrol->value.enumerated.item[0]); idx = ucontrol->value.enumerated.item[0]; if (idx < 0 || idx >= ARRAY_SIZE(wcd9378_swr_master_ch_map)) return -EINVAL; wcd9378->tx_master_ch_map[slave_ch_idx] = wcd9378_slave_get_master_ch(idx); return 0; } static int wcd9378_bcs_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct wcd9378_priv *wcd9378 = snd_soc_component_get_drvdata(component); ucontrol->value.integer.value[0] = wcd9378->bcs_dis; return 0; } static int wcd9378_bcs_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct wcd9378_priv *wcd9378 = snd_soc_component_get_drvdata(component); wcd9378->bcs_dis = ucontrol->value.integer.value[0]; return 0; } static const char * const loopback_mode_text[] = { "NO_LP", "SWR_LP1", "SWR_LP2", "SWR_LP3", }; static const struct soc_enum loopback_mode_enum = SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(loopback_mode_text), loopback_mode_text); static const char * const aux_dsm_text[] = { "TX2->AUX", "TX3->AUX", "TX0->AUX", "TX1->AUX", }; static const struct soc_enum aux_dsm_enum = SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(aux_dsm_text), aux_dsm_text); static const char * const hph_dsm_text[] = { "HPH_DSM_IN0", "HPH_DSM_IN1", "HPH_DSM_IN2", "HPH_DSM_IN3", }; static const struct soc_enum hph_dsm_enum = SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(hph_dsm_text), hph_dsm_text); static const char * const tx_mode_mux_text[] = { "ADC_INVALID", "ADC_HIFI", "ADC_NORMAL", "ADC_LP", }; static const struct soc_enum tx_mode_mux_enum = SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tx_mode_mux_text), tx_mode_mux_text); static const char * const rx2_mode_text[] = { "HP", "NORMAL", }; static const struct soc_enum rx2_mode_enum = SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx2_mode_text), rx2_mode_text); static const char * const rx_hph_mode_mux_text[] = { "CLS_H_INVALID", "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 char * const ear_pa_gain_text[] = { "GAIN_6DB", "GAIN_4P5DB", "GAIN_3DB", "GAIN_1P5DB", "GAIN_0DB", "GAIN_M1P5DB", "GAIN_M3DB", "GAIN_M4P5DB", "GAIN_M6DB", "GAIN_M7P5DB", "GAIN_M9DB", "GAIN_M10P5DB", "GAIN_M12DB", "GAIN_M13P5DB", "GAIN_M15DB", "GAIN_M16P5DB", "GAIN_M18DB", }; static const struct soc_enum ear_pa_gain_enum = SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ear_pa_gain_text), ear_pa_gain_text); static const char * const aux_pa_gain_text[] = { "GAIN_6DB", "GAIN_4P5DB", "GAIN_3DB", "GAIN_1P5DB", "GAIN_0DB", "GAIN_M1P5DB", "GAIN_M3DB", "GAIN_M4P5DB", "GAIN_M6DB", }; static const struct soc_enum aux_pa_gain_enum = SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(aux_pa_gain_text), aux_pa_gain_text); const char * const tx_master_ch_text[] = { "ZERO", "SWRM_TX1_CH1", "SWRM_TX1_CH2", "SWRM_TX1_CH3", "SWRM_TX1_CH4", "SWRM_TX2_CH1", "SWRM_TX2_CH2", "SWRM_TX2_CH3", "SWRM_TX2_CH4", "SWRM_TX3_CH1", "SWRM_TX3_CH2", "SWRM_TX3_CH3", "SWRM_TX3_CH4", "SWRM_PCM_IN", }; const struct soc_enum tx_master_ch_enum = SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tx_master_ch_text), tx_master_ch_text); static const struct snd_kcontrol_new wcd9378_snd_controls[] = { SOC_SINGLE_EXT("HPHL_COMP Switch", SND_SOC_NOPM, 0, 1, 0, wcd9378_get_compander, wcd9378_set_compander), SOC_SINGLE_EXT("HPHR_COMP Switch", SND_SOC_NOPM, 1, 1, 0, wcd9378_get_compander, wcd9378_set_compander), SOC_SINGLE_EXT("ADC2_BCS Disable", SND_SOC_NOPM, 0, 1, 0, wcd9378_bcs_get, wcd9378_bcs_put), SOC_ENUM_EXT("LOOPBACK Mode", loopback_mode_enum, wcd9378_loopback_mode_get, wcd9378_loopback_mode_put), SOC_ENUM_EXT("AUX_LB_IN SEL", aux_dsm_enum, wcd9378_aux_dsm_get, wcd9378_aux_dsm_put), SOC_ENUM_EXT("HPH_LB_IN SEL", hph_dsm_enum, wcd9378_hph_dsm_get, wcd9378_hph_dsm_put), SOC_ENUM_EXT("TX0 MODE", tx_mode_mux_enum, wcd9378_tx_mode_get, wcd9378_tx_mode_put), SOC_ENUM_EXT("TX1 MODE", tx_mode_mux_enum, wcd9378_tx_mode_get, wcd9378_tx_mode_put), SOC_ENUM_EXT("TX2 MODE", tx_mode_mux_enum, wcd9378_tx_mode_get, wcd9378_tx_mode_put), SOC_ENUM_EXT("RX2 Mode", rx2_mode_enum, NULL, wcd9378_rx2_mode_put), SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum, wcd9378_rx_hph_mode_get, wcd9378_rx_hph_mode_put), SOC_SINGLE_EXT("HPH Volume", SND_SOC_NOPM, 0, 0x14, 0, wcd9378_hph_get_gain, wcd9378_hph_put_gain), SOC_ENUM_EXT("EAR_PA Gain", ear_pa_gain_enum, wcd9378_ear_pa_gain_get, wcd9378_ear_pa_gain_put), SOC_ENUM_EXT("AUX_PA Gain", aux_pa_gain_enum, wcd9378_aux_pa_gain_get, wcd9378_aux_pa_gain_put), SOC_SINGLE_TLV("ADC1 Volume", WCD9378_ANA_TX_CH1, 0, 20, 0, analog_gain), SOC_SINGLE_TLV("ADC2 Volume", WCD9378_ANA_TX_CH2, 0, 20, 0, analog_gain), SOC_SINGLE_TLV("ADC3 Volume", WCD9378_ANA_TX_CH3, 0, 20, 0, analog_gain), SOC_ENUM_EXT("ADC1 ChMap", tx_master_ch_enum, wcd9378_tx_master_ch_get, wcd9378_tx_master_ch_put), SOC_ENUM_EXT("ADC2 ChMap", tx_master_ch_enum, wcd9378_tx_master_ch_get, wcd9378_tx_master_ch_put), SOC_ENUM_EXT("ADC3 ChMap", tx_master_ch_enum, wcd9378_tx_master_ch_get, wcd9378_tx_master_ch_put), SOC_ENUM_EXT("DMIC0 ChMap", tx_master_ch_enum, wcd9378_tx_master_ch_get, wcd9378_tx_master_ch_put), SOC_ENUM_EXT("DMIC1 ChMap", tx_master_ch_enum, wcd9378_tx_master_ch_get, wcd9378_tx_master_ch_put), SOC_ENUM_EXT("MBHC ChMap", tx_master_ch_enum, wcd9378_tx_master_ch_get, wcd9378_tx_master_ch_put), SOC_ENUM_EXT("DMIC2 ChMap", tx_master_ch_enum, wcd9378_tx_master_ch_get, wcd9378_tx_master_ch_put), SOC_ENUM_EXT("DMIC3 ChMap", tx_master_ch_enum, wcd9378_tx_master_ch_get, wcd9378_tx_master_ch_put), SOC_ENUM_EXT("DMIC4 ChMap", tx_master_ch_enum, wcd9378_tx_master_ch_get, wcd9378_tx_master_ch_put), SOC_ENUM_EXT("DMIC5 ChMap", tx_master_ch_enum, wcd9378_tx_master_ch_get, wcd9378_tx_master_ch_put), }; static const struct snd_kcontrol_new amic1_switch[] = { SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) }; static const struct snd_kcontrol_new amic2_switch[] = { SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) }; static const struct snd_kcontrol_new amic3_switch[] = { SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) }; static const struct snd_kcontrol_new amic4_switch[] = { SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) }; static const struct snd_kcontrol_new va_amic1_switch[] = { SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) }; static const struct snd_kcontrol_new va_amic2_switch[] = { SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) }; static const struct snd_kcontrol_new va_amic3_switch[] = { SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) }; static const struct snd_kcontrol_new va_amic4_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 char * const adc1_mux_text[] = { "CH1_AMIC_DISABLE", "CH1_AMIC1", "CH1_AMIC2", "CH1_AMIC3", "CH1_AMIC4" }; static const char * const adc2_mux_text[] = { "CH2_AMIC_DISABLE", "CH2_AMIC1", "CH2_AMIC2", "CH2_AMIC3", "CH2_AMIC4" }; static const char * const adc3_mux_text[] = { "CH3_AMIC_DISABLE", "CH3_AMIC1", "CH3_AMIC3", "CH3_AMIC4" }; static const char * const ear_mux_text[] = { "RX0", "RX2" }; static const char * const aux_mux_text[] = { "RX1", "RX2" }; static const struct soc_enum adc1_enum = SOC_ENUM_SINGLE(WCD9378_TX_NEW_TX_CH12_MUX, WCD9378_TX_NEW_TX_CH12_MUX_CH1_SEL_SHIFT, ARRAY_SIZE(adc1_mux_text), adc1_mux_text); static const struct soc_enum adc2_enum = SOC_ENUM_SINGLE(WCD9378_TX_NEW_TX_CH12_MUX, WCD9378_TX_NEW_TX_CH12_MUX_CH2_SEL_SHIFT, ARRAY_SIZE(adc2_mux_text), adc2_mux_text); static const struct soc_enum adc3_enum = SOC_ENUM_SINGLE(WCD9378_TX_NEW_TX_CH34_MUX, WCD9378_TX_NEW_TX_CH34_MUX_CH3_SEL_SHIFT, ARRAY_SIZE(adc3_mux_text), adc3_mux_text); static const struct soc_enum ear_enum = SOC_ENUM_SINGLE(WCD9378_CDC_AUX_GAIN_CTL, WCD9378_CDC_AUX_GAIN_CTL_AUX_EN_SHIFT, ARRAY_SIZE(ear_mux_text), ear_mux_text); static const struct soc_enum aux_enum = SOC_ENUM_SINGLE(WCD9378_CDC_AUX_GAIN_CTL, WCD9378_CDC_AUX_GAIN_CTL_AUX_EN_SHIFT, ARRAY_SIZE(aux_mux_text), aux_mux_text); static const struct snd_kcontrol_new tx_adc1_mux = SOC_DAPM_ENUM("ADC1 MUX Mux", adc1_enum); static const struct snd_kcontrol_new tx_adc2_mux = SOC_DAPM_ENUM("ADC2 MUX Mux", adc2_enum); static const struct snd_kcontrol_new tx_adc3_mux = SOC_DAPM_ENUM("ADC3 MUX Mux", adc3_enum); static const struct snd_kcontrol_new ear_mux = SOC_DAPM_ENUM("EAR Mux", ear_enum); static const struct snd_kcontrol_new aux_mux = SOC_DAPM_ENUM("AUX Mux", aux_enum); static const struct snd_kcontrol_new dac1_switch[] = { SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) }; static const struct snd_kcontrol_new dac2_switch[] = { SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) }; static const struct snd_kcontrol_new ear_mixer_switch[] = { SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) }; static const struct snd_kcontrol_new aux_mixer_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 struct snd_kcontrol_new rx0_switch[] = { SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) }; static const struct snd_kcontrol_new rx1_switch[] = { SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) }; static const struct snd_soc_dapm_widget wcd9378_dapm_widgets[] = { /*input widgets*/ SND_SOC_DAPM_INPUT("AMIC1"), SND_SOC_DAPM_INPUT("AMIC2"), SND_SOC_DAPM_INPUT("AMIC3"), SND_SOC_DAPM_INPUT("AMIC4"), SND_SOC_DAPM_INPUT("VA AMIC1"), SND_SOC_DAPM_INPUT("VA AMIC2"), SND_SOC_DAPM_INPUT("VA AMIC3"), SND_SOC_DAPM_INPUT("VA AMIC4"), SND_SOC_DAPM_INPUT("IN1_HPHL"), SND_SOC_DAPM_INPUT("IN2_HPHR"), SND_SOC_DAPM_INPUT("IN3_AUX"), /*tx widgets*/ SND_SOC_DAPM_MIXER_E("TX0 SEQUENCER", SND_SOC_NOPM, ADC1, 0, NULL, 0, wcd9378_tx_sequencer_enable, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MIXER_E("TX1 SEQUENCER", SND_SOC_NOPM, ADC2, 0, NULL, 0, wcd9378_tx_sequencer_enable, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MIXER_E("TX2 SEQUENCER", SND_SOC_NOPM, ADC3, 0, NULL, 0, wcd9378_tx_sequencer_enable, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MUX("ADC1 MUX", SND_SOC_NOPM, 0, 0, &tx_adc1_mux), SND_SOC_DAPM_MUX("ADC2 MUX", SND_SOC_NOPM, 0, 0, &tx_adc2_mux), SND_SOC_DAPM_MUX("ADC3 MUX", SND_SOC_NOPM, 0, 0, &tx_adc3_mux), SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0, wcd9378_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 1, 0, wcd9378_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_ADC_E("DMIC3", NULL, SND_SOC_NOPM, 2, 0, wcd9378_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_ADC_E("DMIC4", NULL, SND_SOC_NOPM, 3, 0, wcd9378_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_ADC_E("DMIC5", NULL, SND_SOC_NOPM, 4, 0, wcd9378_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_ADC_E("DMIC6", NULL, SND_SOC_NOPM, 5, 0, wcd9378_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), /*rx widgets*/ SND_SOC_DAPM_DAC_E("RDAC1", NULL, SND_SOC_NOPM, 0, 0, wcd9378_codec_hphl_dac_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_DAC_E("RDAC2", NULL, SND_SOC_NOPM, 0, 0, wcd9378_codec_hphr_dac_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MIXER_E("HPH SEQUENCER", SND_SOC_NOPM, 0, 0, NULL, 0, wcd9378_hph_sequencer_enable, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_PGA_E("HPHL PGA", SND_SOC_NOPM, 0, 0, NULL, 0, wcd9378_codec_enable_hphl_pa, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_PGA_E("HPHR PGA", SND_SOC_NOPM, 0, 0, NULL, 0, wcd9378_codec_enable_hphr_pa, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MIXER_E("SA SEQUENCER", SND_SOC_NOPM, 0, 0, NULL, 0, wcd9378_sa_sequencer_enable, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_DAC_E("EAR_RDAC", NULL, SND_SOC_NOPM, 0, 0, wcd9378_codec_ear_dac_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_DAC_E("AUX_RDAC", NULL, SND_SOC_NOPM, 0, 0, wcd9378_codec_aux_dac_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_PGA_E("EAR PGA", SND_SOC_NOPM, 0, 0, NULL, 0, wcd9378_codec_enable_ear_pa, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_PGA_E("AUX PGA", SND_SOC_NOPM, 0, 0, NULL, 0, wcd9378_codec_enable_aux_pa, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SUPPLY("VDD_BUCK", SND_SOC_NOPM, 0, 0, wcd9378_codec_enable_vdd_buck, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SUPPLY_S("CLS_H_PORT", 1, SND_SOC_NOPM, 0, 0, wcd9378_enable_clsh, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MIXER_E("AMIC1_MIXER", SND_SOC_NOPM, 0, 0, amic1_switch, ARRAY_SIZE(amic1_switch), NULL, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MIXER_E("AMIC2_MIXER", SND_SOC_NOPM, 0, 0, amic2_switch, ARRAY_SIZE(amic2_switch), NULL, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MIXER_E("AMIC3_MIXER", SND_SOC_NOPM, 0, 0, amic3_switch, ARRAY_SIZE(amic3_switch), NULL, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MIXER_E("AMIC4_MIXER", SND_SOC_NOPM, 0, 0, amic4_switch, ARRAY_SIZE(amic4_switch), NULL, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MIXER_E("VA_AMIC1_MIXER", SND_SOC_NOPM, 0, 0, va_amic1_switch, ARRAY_SIZE(va_amic1_switch), NULL, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MIXER_E("VA_AMIC2_MIXER", SND_SOC_NOPM, 0, 0, va_amic2_switch, ARRAY_SIZE(va_amic2_switch), NULL, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MIXER_E("VA_AMIC3_MIXER", SND_SOC_NOPM, 0, 0, va_amic3_switch, ARRAY_SIZE(va_amic3_switch), NULL, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MIXER_E("VA_AMIC4_MIXER", SND_SOC_NOPM, 0, 0, va_amic4_switch, ARRAY_SIZE(va_amic4_switch), NULL, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MIXER_E("DMIC1_MIXER", SND_SOC_NOPM, DMIC1, 0, dmic1_switch, ARRAY_SIZE(dmic1_switch), wcd9378_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MIXER_E("DMIC2_MIXER", SND_SOC_NOPM, DMIC2, 0, dmic2_switch, ARRAY_SIZE(dmic2_switch), wcd9378_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MIXER_E("DMIC3_MIXER", SND_SOC_NOPM, DMIC3, 0, dmic3_switch, ARRAY_SIZE(dmic3_switch), wcd9378_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MIXER_E("DMIC4_MIXER", SND_SOC_NOPM, DMIC4, 0, dmic4_switch, ARRAY_SIZE(dmic4_switch), wcd9378_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MIXER_E("DMIC5_MIXER", SND_SOC_NOPM, DMIC5, 0, dmic5_switch, ARRAY_SIZE(dmic5_switch), wcd9378_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MIXER_E("DMIC6_MIXER", SND_SOC_NOPM, DMIC6, 0, dmic6_switch, ARRAY_SIZE(dmic6_switch), wcd9378_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), /* micbias widgets*/ SND_SOC_DAPM_SUPPLY("MIC BIAS1", SND_SOC_NOPM, 0, 0, wcd9378_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SUPPLY("MIC BIAS2", SND_SOC_NOPM, 0, 0, wcd9378_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SUPPLY("MIC BIAS3", SND_SOC_NOPM, 0, 0, wcd9378_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), /* micbias pull up widgets*/ SND_SOC_DAPM_SUPPLY("VA MIC BIAS1", SND_SOC_NOPM, 0, 0, wcd9378_codec_enable_micbias_pullup, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SUPPLY("VA MIC BIAS2", SND_SOC_NOPM, 0, 0, wcd9378_codec_enable_micbias_pullup, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SUPPLY("VA MIC BIAS3", SND_SOC_NOPM, 0, 0, wcd9378_codec_enable_micbias_pullup, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), /* rx mixer widgets*/ SND_SOC_DAPM_MUX("EAR_MUX", SND_SOC_NOPM, 0, 0, &ear_mux), SND_SOC_DAPM_MUX("AUX_MUX", SND_SOC_NOPM, 0, 0, &aux_mux), SND_SOC_DAPM_MIXER("EAR_MIXER", SND_SOC_NOPM, 0, 0, ear_mixer_switch, ARRAY_SIZE(ear_mixer_switch)), SND_SOC_DAPM_MIXER("AUX_MIXER", SND_SOC_NOPM, 0, 0, aux_mixer_switch, ARRAY_SIZE(aux_mixer_switch)), SND_SOC_DAPM_MIXER("DAC1", SND_SOC_NOPM, 0, 0, dac1_switch, ARRAY_SIZE(dac1_switch)), SND_SOC_DAPM_MIXER("DAC2", SND_SOC_NOPM, 0, 0, dac2_switch, ARRAY_SIZE(dac2_switch)), SND_SOC_DAPM_MIXER("HPHL_RDAC", SND_SOC_NOPM, 0, 0, hphl_rdac_switch, ARRAY_SIZE(hphl_rdac_switch)), SND_SOC_DAPM_MIXER("HPHR_RDAC", SND_SOC_NOPM, 0, 0, hphr_rdac_switch, ARRAY_SIZE(hphr_rdac_switch)), /*output widgets tx*/ SND_SOC_DAPM_OUTPUT("ADC1_OUTPUT"), SND_SOC_DAPM_OUTPUT("ADC2_OUTPUT"), SND_SOC_DAPM_OUTPUT("ADC3_OUTPUT"), SND_SOC_DAPM_OUTPUT("DMIC1_OUTPUT"), SND_SOC_DAPM_OUTPUT("DMIC2_OUTPUT"), SND_SOC_DAPM_OUTPUT("DMIC3_OUTPUT"), SND_SOC_DAPM_OUTPUT("DMIC4_OUTPUT"), SND_SOC_DAPM_OUTPUT("DMIC5_OUTPUT"), SND_SOC_DAPM_OUTPUT("DMIC6_OUTPUT"), /*output widgets rx*/ SND_SOC_DAPM_OUTPUT("EAR"), SND_SOC_DAPM_OUTPUT("AUX"), SND_SOC_DAPM_OUTPUT("HPHL"), SND_SOC_DAPM_OUTPUT("HPHR"), }; static const struct snd_soc_dapm_route wcd9378_audio_map[] = { /*ADC-1 (channel-1)*/ {"ADC1_OUTPUT", NULL, "TX0 SEQUENCER"}, {"TX0 SEQUENCER", NULL, "ADC1 MUX"}, {"ADC1 MUX", "CH1_AMIC1", "AMIC1_MIXER"}, {"ADC1 MUX", "CH1_AMIC2", "AMIC2_MIXER"}, {"ADC1 MUX", "CH1_AMIC3", "AMIC3_MIXER"}, {"ADC1 MUX", "CH1_AMIC4", "AMIC4_MIXER"}, /*ADC-2 (channel-2)*/ {"ADC2_OUTPUT", NULL, "TX1 SEQUENCER"}, {"TX1 SEQUENCER", NULL, "ADC2 MUX"}, {"ADC2 MUX", "CH2_AMIC1", "AMIC1_MIXER"}, {"ADC2 MUX", "CH2_AMIC2", "AMIC2_MIXER"}, {"ADC2 MUX", "CH2_AMIC3", "AMIC3_MIXER"}, {"ADC2 MUX", "CH2_AMIC4", "AMIC4_MIXER"}, /*ADC-3 (channel-3)*/ {"ADC3_OUTPUT", NULL, "TX2 SEQUENCER"}, {"TX2 SEQUENCER", NULL, "ADC3 MUX"}, {"ADC3 MUX", "CH3_AMIC1", "AMIC1_MIXER"}, {"ADC3 MUX", "CH3_AMIC3", "AMIC3_MIXER"}, {"ADC3 MUX", "CH3_AMIC4", "AMIC4_MIXER"}, {"AMIC1_MIXER", "Switch", "AMIC1"}, {"AMIC1_MIXER", NULL, "VA_AMIC1_MIXER"}, {"VA_AMIC1_MIXER", "Switch", "VA AMIC1"}, {"AMIC2_MIXER", "Switch", "AMIC2"}, {"AMIC2_MIXER", NULL, "VA_AMIC2_MIXER"}, {"VA_AMIC2_MIXER", "Switch", "VA AMIC2"}, {"AMIC3_MIXER", "Switch", "AMIC3"}, {"AMIC3_MIXER", NULL, "VA_AMIC3_MIXER"}, {"VA_AMIC3_MIXER", "Switch", "VA AMIC3"}, {"AMIC4_MIXER", "Switch", "AMIC4"}, {"AMIC4_MIXER", NULL, "VA_AMIC4_MIXER"}, {"VA_AMIC4_MIXER", "Switch", "VA AMIC4"}, {"DMIC1_OUTPUT", NULL, "DMIC1_MIXER"}, {"DMIC1_MIXER", "Switch", "DMIC1"}, {"DMIC2_OUTPUT", NULL, "DMIC2_MIXER"}, {"DMIC2_MIXER", "Switch", "DMIC2"}, {"DMIC3_OUTPUT", NULL, "DMIC3_MIXER"}, {"DMIC3_MIXER", "Switch", "DMIC3"}, {"DMIC4_OUTPUT", NULL, "DMIC4_MIXER"}, {"DMIC4_MIXER", "Switch", "DMIC4"}, {"DMIC5_OUTPUT", NULL, "DMIC5_MIXER"}, {"DMIC5_MIXER", "Switch", "DMIC5"}, {"DMIC6_OUTPUT", NULL, "DMIC6_MIXER"}, {"DMIC6_MIXER", "Switch", "DMIC6"}, /*Headphone playback*/ {"IN1_HPHL", NULL, "VDD_BUCK"}, {"IN1_HPHL", NULL, "CLS_H_PORT"}, {"HPH SEQUENCER", NULL, "IN1_HPHL"}, {"RDAC1", NULL, "HPH SEQUENCER"}, {"HPHL_RDAC", "Switch", "RDAC1"}, {"HPHL PGA", NULL, "HPHL_RDAC"}, {"HPHL", NULL, "HPHL PGA"}, {"IN2_HPHR", NULL, "VDD_BUCK"}, {"IN2_HPHR", NULL, "CLS_H_PORT"}, {"HPH SEQUENCER", NULL, "IN2_HPHR"}, {"RDAC2", NULL, "HPH SEQUENCER"}, {"HPHR_RDAC", "Switch", "RDAC2"}, {"HPHR PGA", NULL, "HPHR_RDAC"}, {"HPHR", NULL, "HPHR PGA"}, /*Amplier playback*/ {"IN3_AUX", NULL, "VDD_BUCK"}, {"EAR_MUX", "RX0", "IN1_HPHL"}, {"EAR_MUX", "RX2", "IN3_AUX"}, {"DAC1", "Switch", "EAR_MUX"}, {"EAR_RDAC", NULL, "DAC1"}, {"SA SEQUENCER", NULL, "EAR_RDAC"}, {"EAR_MIXER", "Switch", "SA SEQUENCER"}, {"EAR PGA", NULL, "EAR_MIXER"}, {"EAR", NULL, "EAR PGA"}, {"AUX_MUX", "RX1", "IN2_HPHR"}, {"AUX_MUX", "RX2", "IN3_AUX"}, {"DAC2", "Switch", "AUX_MUX"}, {"AUX_RDAC", NULL, "DAC2"}, {"SA SEQUENCER", NULL, "AUX_RDAC"}, {"AUX_MIXER", "Switch", "SA SEQUENCER",}, {"AUX PGA", NULL, "AUX_MIXER"}, {"AUX", NULL, "AUX PGA"}, }; static ssize_t wcd9378_version_read(struct snd_info_entry *entry, void *file_private_data, struct file *file, char __user *buf, size_t count, loff_t pos) { struct wcd9378_priv *priv; char buffer[WCD9378_VERSION_ENTRY_SIZE]; int len = 0; priv = (struct wcd9378_priv *) entry->private_data; if (!priv) { pr_err("%s: wcd9378 priv is null\n", __func__); return -EINVAL; } switch (priv->version) { case WCD9378_VERSION_1_0: len = scnprintf(buffer, sizeof(buffer), "WCD9378_1_0\n"); break; default: len = scnprintf(buffer, sizeof(buffer), "VER_UNDEFINED\n"); } return simple_read_from_buffer(buf, count, &pos, buffer, len); } static struct snd_info_entry_ops wcd9378_info_ops = { .read = wcd9378_version_read, }; /* * wcd9378_info_create_codec_entry - creates wcd9378 module * @codec_root: The parent directory * @component: component instance * * Creates wcd9378 module, version entry under the given * parent directory. * * Return: 0 on success or negative error code on failure. */ int wcd9378_info_create_codec_entry(struct snd_info_entry *codec_root, struct snd_soc_component *component) { struct snd_info_entry *version_entry; struct wcd9378_priv *priv; struct snd_soc_card *card; if (!codec_root || !component) return -EINVAL; priv = snd_soc_component_get_drvdata(component); if (priv->entry) { dev_dbg(priv->dev, "%s:wcd9378 module already created\n", __func__); return 0; } card = component->card; priv->entry = snd_info_create_module_entry(codec_root->module, "wcd9378", codec_root); if (!priv->entry) { dev_dbg(component->dev, "%s: failed to create wcd9378 entry\n", __func__); return -ENOMEM; } priv->entry->mode = S_IFDIR | 0555; if (snd_info_register(priv->entry) < 0) { snd_info_free_entry(priv->entry); return -ENOMEM; } version_entry = snd_info_create_card_entry(card->snd_card, "version", priv->entry); if (!version_entry) { dev_dbg(component->dev, "%s: failed to create wcd9378 version entry\n", __func__); snd_info_free_entry(priv->entry); return -ENOMEM; } version_entry->private_data = priv; version_entry->size = WCD9378_VERSION_ENTRY_SIZE; version_entry->content = SNDRV_INFO_CONTENT_DATA; version_entry->c.ops = &wcd9378_info_ops; if (snd_info_register(version_entry) < 0) { snd_info_free_entry(version_entry); snd_info_free_entry(priv->entry); return -ENOMEM; } priv->version_entry = version_entry; return 0; } EXPORT_SYMBOL_GPL(wcd9378_info_create_codec_entry); static void wcd9378_class_load(struct snd_soc_component *component) { /*SMP AMP CLASS LOADING*/ snd_soc_component_update_bits(component, WCD9378_FUNC_ACT, WCD9378_FUNC_ACT_FUNC_ACT_MASK, 0x01); usleep_range(20000, 20010); snd_soc_component_update_bits(component, WCD9378_SMP_AMP_FUNC_STAT, WCD9378_SMP_AMP_FUNC_STAT_FUNC_STAT_MASK, 0xFF); /*SMP JACK CLASS LOADING*/ snd_soc_component_update_bits(component, WCD9378_SMP_JACK_FUNC_ACT, WCD9378_SMP_JACK_FUNC_ACT_FUNC_ACT_MASK, 0x01); usleep_range(30000, 30010); snd_soc_component_update_bits(component, WCD9378_CMT_GRP_MASK, WCD9378_CMT_GRP_MASK_CMT_GRP_MASK_MASK, 0x02); snd_soc_component_update_bits(component, WCD9378_SMP_JACK_FUNC_STAT, WCD9378_SMP_JACK_FUNC_STAT_FUNC_STAT_MASK, 0xFF); /*SMP MIC0 CLASS LOADING*/ snd_soc_component_update_bits(component, WCD9378_SMP_MIC_CTRL0_FUNC_ACT, WCD9378_SMP_MIC_CTRL0_FUNC_ACT_FUNC_ACT_MASK, 0x01); usleep_range(5000, 5010); snd_soc_component_update_bits(component, WCD9378_SMP_MIC_CTRL0_FUNC_STAT, WCD9378_SMP_MIC_CTRL0_FUNC_STAT_FUNC_STAT_MASK, 0xFF); /*SMP MIC1 CLASS LOADING*/ snd_soc_component_update_bits(component, WCD9378_SMP_MIC_CTRL1_FUNC_ACT, WCD9378_SMP_MIC_CTRL1_FUNC_ACT_FUNC_ACT_MASK, 0x01); usleep_range(5000, 5010); snd_soc_component_update_bits(component, WCD9378_SMP_MIC_CTRL1_FUNC_STAT, WCD9378_SMP_MIC_CTRL1_FUNC_STAT_FUNC_STAT_MASK, 0xFF); /*SMP MIC2 CLASS LOADING*/ snd_soc_component_update_bits(component, WCD9378_SMP_MIC_CTRL2_FUNC_ACT, WCD9378_SMP_MIC_CTRL2_FUNC_ACT_FUNC_ACT_MASK, 0x01); usleep_range(5000, 5010); snd_soc_component_update_bits(component, WCD9378_SMP_MIC_CTRL2_FUNC_STAT, WCD9378_SMP_MIC_CTRL2_FUNC_STAT_FUNC_STAT_MASK, 0xFF); } static void wcd9378_micb_value_convert(struct snd_soc_component *component) { struct wcd9378_priv *wcd9378 = snd_soc_component_get_drvdata(component); struct wcd9378_pdata *pdata = dev_get_platdata(wcd9378->dev); struct wcd9378_micbias_setting *mb = &pdata->micbias; mb->micb1_usage_val = wcd9378_micb_usage_value_convert(component, mb->micb1_mv, MIC_BIAS_1); mb->micb2_usage_val = wcd9378_micb_usage_value_convert(component, mb->micb2_mv, MIC_BIAS_2); mb->micb3_usage_val = wcd9378_micb_usage_value_convert(component, mb->micb3_mv, MIC_BIAS_3); pr_debug("%s: micb1_usage: 0x%x, micb2_usage: 0x%x, micb3_usage: 0x%x\n", __func__, mb->micb1_usage_val, mb->micb2_usage_val, mb->micb3_usage_val); } static int wcd9378_wcd_mode_check(struct snd_soc_component *component) { struct wcd9378_priv *wcd9378 = snd_soc_component_get_drvdata(component); if (snd_soc_component_read(component, WCD9378_EFUSE_REG_29) & WCD9378_EFUSE_REG_29_PLATFORM_BLOWN_MASK) { if (((snd_soc_component_read(component, WCD9378_EFUSE_REG_29) & WCD9378_EFUSE_REG_29_PLATFORM_MASK) >> 1) == wcd9378->wcd_mode) return true; else return false; } else { if ((snd_soc_component_read(component, WCD9378_PLATFORM_CTL) & WCD9378_PLATFORM_CTL_MODE_MASK) == wcd9378->wcd_mode) return true; else return false; } return 0; } static int wcd9378_soc_codec_probe(struct snd_soc_component *component) { struct wcd9378_priv *wcd9378 = snd_soc_component_get_drvdata(component); struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); int ret = -EINVAL; wcd9378 = snd_soc_component_get_drvdata(component); if (!wcd9378) return -EINVAL; wcd9378->component = component; snd_soc_component_init_regmap(component, wcd9378->regmap); devm_regmap_qti_debugfs_register(&wcd9378->tx_swr_dev->dev, wcd9378->regmap); ret = wcd9378_wcd_mode_check(component); if (!ret) { dev_err(component->dev, "wcd mode check failed\n"); ret = -EINVAL; goto exit; } ret = wcd9378_mbhc_init(&wcd9378->mbhc, component); if (ret) { pr_err("%s: mbhc initialization failed\n", __func__); ret = -EINVAL; goto exit; } snd_soc_dapm_ignore_suspend(dapm, "AMIC1"); snd_soc_dapm_ignore_suspend(dapm, "AMIC2"); snd_soc_dapm_ignore_suspend(dapm, "AMIC3"); snd_soc_dapm_ignore_suspend(dapm, "AMIC4"); snd_soc_dapm_ignore_suspend(dapm, "VA AMIC1"); snd_soc_dapm_ignore_suspend(dapm, "VA AMIC2"); snd_soc_dapm_ignore_suspend(dapm, "VA AMIC3"); snd_soc_dapm_ignore_suspend(dapm, "VA AMIC4"); snd_soc_dapm_ignore_suspend(dapm, "IN1_HPHL"); snd_soc_dapm_ignore_suspend(dapm, "IN2_HPHR"); snd_soc_dapm_ignore_suspend(dapm, "IN3_AUX"); snd_soc_dapm_ignore_suspend(dapm, "ADC1_OUTPUT"); snd_soc_dapm_ignore_suspend(dapm, "ADC2_OUTPUT"); snd_soc_dapm_ignore_suspend(dapm, "ADC3_OUTPUT"); snd_soc_dapm_ignore_suspend(dapm, "EAR"); snd_soc_dapm_ignore_suspend(dapm, "AUX"); snd_soc_dapm_ignore_suspend(dapm, "HPHL"); snd_soc_dapm_ignore_suspend(dapm, "HPHR"); snd_soc_dapm_sync(dapm); wcd_cls_h_init(&wcd9378->clsh_info); wcd9378_init_reg(component); wcd9378_micb_value_convert(component); wcd9378->version = WCD9378_VERSION_1_0; /* Register event notifier */ wcd9378->nblock.notifier_call = wcd9378_event_notify; if (wcd9378->register_notifier) { ret = wcd9378->register_notifier(wcd9378->handle, &wcd9378->nblock, true); if (ret) { dev_err(component->dev, "%s: Failed to register notifier %d\n", __func__, ret); return ret; } } exit: return ret; } static void wcd9378_soc_codec_remove(struct snd_soc_component *component) { struct wcd9378_priv *wcd9378 = snd_soc_component_get_drvdata(component); if (!wcd9378) { dev_err(component->dev, "%s: wcd9378 is already NULL\n", __func__); return; } if (wcd9378->register_notifier) wcd9378->register_notifier(wcd9378->handle, &wcd9378->nblock, false); } static int wcd9378_soc_codec_suspend(struct snd_soc_component *component) { struct wcd9378_priv *wcd9378 = snd_soc_component_get_drvdata(component); if (!wcd9378) return 0; wcd9378->dapm_bias_off = true; return 0; } static int wcd9378_soc_codec_resume(struct snd_soc_component *component) { struct wcd9378_priv *wcd9378 = snd_soc_component_get_drvdata(component); if (!wcd9378) return 0; wcd9378->dapm_bias_off = false; return 0; } static const struct snd_soc_component_driver soc_codec_dev_wcd9378 = { .name = WCD9378_DRV_NAME, .probe = wcd9378_soc_codec_probe, .remove = wcd9378_soc_codec_remove, .controls = wcd9378_snd_controls, .num_controls = ARRAY_SIZE(wcd9378_snd_controls), .dapm_widgets = wcd9378_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(wcd9378_dapm_widgets), .dapm_routes = wcd9378_audio_map, .num_dapm_routes = ARRAY_SIZE(wcd9378_audio_map), .suspend = wcd9378_soc_codec_suspend, .resume = wcd9378_soc_codec_resume, }; static int wcd9378_reset(struct device *dev) { struct wcd9378_priv *wcd9378 = NULL; int rc = 0; int value = 0; if (!dev) return -ENODEV; wcd9378 = dev_get_drvdata(dev); if (!wcd9378) return -EINVAL; if (!wcd9378->rst_np) { dev_err(dev, "%s: reset gpio device node not specified\n", __func__); return -EINVAL; } value = msm_cdc_pinctrl_get_state(wcd9378->rst_np); if (value > 0) return 0; rc = msm_cdc_pinctrl_select_sleep_state(wcd9378->rst_np); if (rc) { dev_err(dev, "%s: wcd sleep state request fail!\n", __func__); return -EPROBE_DEFER; } /* 20us sleep required after pulling the reset gpio to LOW */ usleep_range(80, 85); rc = msm_cdc_pinctrl_select_active_state(wcd9378->rst_np); if (rc) { dev_err(dev, "%s: wcd active state request fail!\n", __func__); return -EPROBE_DEFER; } /* 20us sleep required after pulling the reset gpio to HIGH */ usleep_range(80, 85); return rc; } static int wcd9378_read_of_property_u32(struct device *dev, const char *name, u32 *val) { int rc = 0; rc = of_property_read_u32(dev->of_node, name, val); if (rc) dev_err(dev, "%s: Looking up %s property in node %s failed\n", __func__, name, dev->of_node->full_name); return rc; } static void wcd9378_dt_parse_micbias_info(struct device *dev, struct wcd9378_micbias_setting *mb) { u32 prop_val = 0; int rc = 0; /* MB1 */ if (of_find_property(dev->of_node, "qcom,cdc-micbias1-mv", NULL)) { rc = wcd9378_read_of_property_u32(dev, "qcom,cdc-micbias1-mv", &prop_val); if (!rc) mb->micb1_mv = prop_val; } else { dev_info(dev, "%s: Micbias1 DT property not found\n", __func__); } /* MB2 */ if (of_find_property(dev->of_node, "qcom,cdc-micbias2-mv", NULL)) { rc = wcd9378_read_of_property_u32(dev, "qcom,cdc-micbias2-mv", &prop_val); if (!rc) mb->micb2_mv = prop_val; } else { dev_info(dev, "%s: Micbias2 DT property not found\n", __func__); } /* MB3 */ if (of_find_property(dev->of_node, "qcom,cdc-micbias3-mv", NULL)) { rc = wcd9378_read_of_property_u32(dev, "qcom,cdc-micbias3-mv", &prop_val); if (!rc) mb->micb3_mv = prop_val; } else { dev_info(dev, "%s: Micbias3 DT property not found\n", __func__); } } static int wcd9378_reset_low(struct device *dev) { struct wcd9378_priv *wcd9378 = NULL; int rc = 0; if (!dev) return -ENODEV; wcd9378 = dev_get_drvdata(dev); if (!wcd9378) return -EINVAL; if (!wcd9378->rst_np) { dev_err(dev, "%s: reset gpio device node not specified\n", __func__); return -EINVAL; } rc = msm_cdc_pinctrl_select_sleep_state(wcd9378->rst_np); if (rc) { dev_err(dev, "%s: wcd sleep state request fail!\n", __func__); return rc; } /* 20us sleep required after pulling the reset gpio to LOW */ usleep_range(20, 30); return rc; } struct wcd9378_pdata *wcd9378_populate_dt_data(struct device *dev) { struct wcd9378_pdata *pdata = NULL; pdata = devm_kzalloc(dev, sizeof(struct wcd9378_pdata), GFP_KERNEL); if (!pdata) return NULL; pdata->rst_np = of_parse_phandle(dev->of_node, "qcom,wcd-rst-gpio-node", 0); if (!pdata->rst_np) { dev_err(dev, "%s: Looking up %s property in node %s failed\n", __func__, "qcom,wcd-rst-gpio-node", dev->of_node->full_name); return NULL; } /* Parse power supplies */ msm_cdc_get_power_supplies(dev, &pdata->regulator, &pdata->num_supplies); if (!pdata->regulator || (pdata->num_supplies <= 0)) { dev_err(dev, "%s: no power supplies defined for codec\n", __func__); return NULL; } pdata->rx_slave = of_parse_phandle(dev->of_node, "qcom,rx-slave", 0); pdata->tx_slave = of_parse_phandle(dev->of_node, "qcom,tx-slave", 0); wcd9378_dt_parse_micbias_info(dev, &pdata->micbias); return pdata; } static struct snd_soc_dai_driver wcd9378_dai[] = { { .name = "wcd9378_cdc", .playback = { .stream_name = "WCD9378_AIF Playback", .rates = WCD9378_RATES | WCD9378_FRAC_RATES, .formats = WCD9378_FORMATS, .rate_max = 384000, .rate_min = 8000, .channels_min = 1, .channels_max = 4, }, .capture = { .stream_name = "WCD9378_AIF Capture", .rates = WCD9378_RATES | WCD9378_FRAC_RATES, .formats = WCD9378_FORMATS, .rate_max = 384000, .rate_min = 8000, .channels_min = 1, .channels_max = 4, }, }, }; static irqreturn_t wcd9378_wd_handle_irq(int irq, void *data) { pr_err_ratelimited("%s: Watchdog interrupt for irq =%d triggered\n", __func__, irq); return IRQ_HANDLED; } static int wcd9378_bind(struct device *dev) { int ret = 0; struct wcd9378_pdata *pdata = dev_get_platdata(dev); struct wcd9378_priv *wcd9378 = dev_get_drvdata(dev); /* * Add 5msec delay to provide sufficient time for * soundwire auto enumeration of slave devices as * per HW requirement. */ usleep_range(5000, 5010); ret = component_bind_all(dev, wcd9378); if (ret) { dev_err(dev, "%s: Slave bind failed, ret = %d\n", __func__, ret); return ret; } wcd9378->rx_swr_dev = get_matching_swr_slave_device(pdata->rx_slave); if (!wcd9378->rx_swr_dev) { dev_err(dev, "%s: Could not find RX swr slave device\n", __func__); ret = -ENODEV; goto err; } wcd9378->rx_swr_dev->paging_support = true; wcd9378->tx_swr_dev = get_matching_swr_slave_device(pdata->tx_slave); if (!wcd9378->tx_swr_dev) { dev_err(dev, "%s: Could not find TX swr slave device\n", __func__); ret = -ENODEV; goto err; } wcd9378->tx_swr_dev->paging_support = true; swr_init_port_params(wcd9378->tx_swr_dev, SWR_NUM_PORTS, wcd9378->swr_tx_port_params); wcd9378->regmap = devm_regmap_init_swr(wcd9378->tx_swr_dev, &wcd9378_regmap_config); if (!wcd9378->regmap) { dev_err(dev, "%s: Regmap init failed\n", __func__); goto err; } regmap_write(wcd9378->regmap, SWRS_SCP_SDCA_INTRTYPE_1, 0xff); regmap_write(wcd9378->regmap, SWRS_SCP_SDCA_INTRTYPE_2, 0x0b); regmap_write(wcd9378->regmap, SWRS_SCP_SDCA_INTRTYPE_3, 0xff); wcd9378_regmap_irq_chip.irq_drv_data = wcd9378; wcd9378->irq_info.wcd_regmap_irq_chip = &wcd9378_regmap_irq_chip; wcd9378->irq_info.codec_name = "WCD9378"; wcd9378->irq_info.regmap = wcd9378->regmap; wcd9378->irq_info.dev = dev; ret = wcd_irq_init(&wcd9378->irq_info, &wcd9378->virq); if (ret) { dev_err(wcd9378->dev, "%s: IRQ init failed: %d\n", __func__, ret); goto err; } dev_err(wcd9378->dev, "%s: wcd irq init done\n", __func__); wcd9378->tx_swr_dev->slave_irq = wcd9378->virq; /* Request for watchdog interrupt */ wcd_request_irq(&wcd9378->irq_info, WCD9378_IRQ_HPHR_PDM_WD_INT, "HPHR PDM WD INT", wcd9378_wd_handle_irq, NULL); wcd_request_irq(&wcd9378->irq_info, WCD9378_IRQ_HPHL_PDM_WD_INT, "HPHL PDM WD INT", wcd9378_wd_handle_irq, NULL); wcd_request_irq(&wcd9378->irq_info, WCD9378_IRQ_AUX_PDM_WD_INT, "AUX PDM WD INT", wcd9378_wd_handle_irq, NULL); /* Disable watchdog interrupt for HPH and AUX */ wcd_disable_irq(&wcd9378->irq_info, WCD9378_IRQ_HPHR_PDM_WD_INT); wcd_disable_irq(&wcd9378->irq_info, WCD9378_IRQ_HPHL_PDM_WD_INT); wcd_disable_irq(&wcd9378->irq_info, WCD9378_IRQ_AUX_PDM_WD_INT); ret = snd_soc_register_component(dev, &soc_codec_dev_wcd9378, wcd9378_dai, ARRAY_SIZE(wcd9378_dai)); if (ret) { dev_err(dev, "%s: Codec registration failed\n", __func__); goto err_irq; } return ret; err_irq: wcd_irq_exit(&wcd9378->irq_info, wcd9378->virq); err: component_unbind_all(dev, wcd9378); return ret; } static void wcd9378_unbind(struct device *dev) { struct wcd9378_priv *wcd9378 = dev_get_drvdata(dev); wcd_free_irq(&wcd9378->irq_info, WCD9378_IRQ_HPHR_PDM_WD_INT, NULL); wcd_free_irq(&wcd9378->irq_info, WCD9378_IRQ_HPHL_PDM_WD_INT, NULL); wcd_free_irq(&wcd9378->irq_info, WCD9378_IRQ_AUX_PDM_WD_INT, NULL); wcd_irq_exit(&wcd9378->irq_info, wcd9378->virq); snd_soc_unregister_component(dev); component_unbind_all(dev, wcd9378); } static const struct of_device_id wcd9378_dt_match[] = { { .compatible = "qcom,wcd9378-codec", .data = "wcd9378"}, {} }; static const struct component_master_ops wcd9378_comp_ops = { .bind = wcd9378_bind, .unbind = wcd9378_unbind, }; static int wcd9378_compare_of(struct device *dev, void *data) { return dev->of_node == data; } static void wcd9378_release_of(struct device *dev, void *data) { of_node_put(data); } static int wcd9378_add_slave_components(struct device *dev, struct component_match **matchptr) { struct device_node *np, *rx_node, *tx_node; np = dev->of_node; rx_node = of_parse_phandle(np, "qcom,rx-slave", 0); if (!rx_node) { dev_err(dev, "%s: Rx-slave node not defined\n", __func__); return -ENODEV; } of_node_get(rx_node); component_match_add_release(dev, matchptr, wcd9378_release_of, wcd9378_compare_of, rx_node); tx_node = of_parse_phandle(np, "qcom,tx-slave", 0); if (!tx_node) { dev_err(dev, "%s: Tx-slave node not defined\n", __func__); return -ENODEV; } of_node_get(tx_node); component_match_add_release(dev, matchptr, wcd9378_release_of, wcd9378_compare_of, tx_node); return 0; } static int wcd9378_probe(struct platform_device *pdev) { struct component_match *match = NULL; struct wcd9378_priv *wcd9378 = NULL; struct wcd9378_pdata *pdata = NULL; struct wcd_ctrl_platform_data *plat_data = NULL; struct device *dev = &pdev->dev; int ret; wcd9378 = devm_kzalloc(dev, sizeof(struct wcd9378_priv), GFP_KERNEL); if (!wcd9378) return -ENOMEM; dev_set_drvdata(dev, wcd9378); wcd9378->dev = dev; pdata = wcd9378_populate_dt_data(dev); if (!pdata) { dev_err(dev, "%s: Fail to obtain platform data\n", __func__); return -EINVAL; } dev->platform_data = pdata; wcd9378->rst_np = pdata->rst_np; ret = msm_cdc_init_supplies(dev, &wcd9378->supplies, pdata->regulator, pdata->num_supplies); if (!wcd9378->supplies) { dev_err(dev, "%s: Cannot init wcd supplies\n", __func__); return ret; } plat_data = dev_get_platdata(dev->parent); if (!plat_data) { dev_err(dev, "%s: platform data from parent is NULL\n", __func__); return -EINVAL; } wcd9378->handle = (void *)plat_data->handle; if (!wcd9378->handle) { dev_err(dev, "%s: handle is NULL\n", __func__); return -EINVAL; } wcd9378->update_wcd_event = plat_data->update_wcd_event; if (!wcd9378->update_wcd_event) { dev_err(dev, "%s: update_wcd_event api is null!\n", __func__); return -EINVAL; } wcd9378->register_notifier = plat_data->register_notifier; if (!wcd9378->register_notifier) { dev_err(dev, "%s: register_notifier api is null!\n", __func__); return -EINVAL; } ret = of_property_read_u32(dev->of_node, "qcom,wcd-mode", &wcd9378->wcd_mode); if (ret) { dev_dbg(dev, "%s: wcd-mode read failed, use mobile mode\n", __func__); wcd9378->wcd_mode = WCD9378_MOBILE_MODE; } ret = msm_cdc_enable_static_supplies(&pdev->dev, wcd9378->supplies, pdata->regulator, pdata->num_supplies); if (ret) { dev_err(dev, "%s: wcd static supply enable failed!\n", __func__); return ret; } ret = wcd9378_parse_port_mapping(dev, "qcom,rx_swr_ch_map", CODEC_RX); ret |= wcd9378_parse_port_mapping(dev, "qcom,tx_swr_ch_map", CODEC_TX); if (ret) { dev_err(dev, "Failed to read port mapping\n"); goto err; } ret = wcd9378_parse_port_params(dev, "qcom,swr-tx-port-params", CODEC_TX); if (ret) { dev_err(dev, "Failed to read port params\n"); goto err; } mutex_init(&wcd9378->wakeup_lock); mutex_init(&wcd9378->micb_lock); mutex_init(&wcd9378->sys_usage_lock); ret = wcd9378_add_slave_components(dev, &match); if (ret) goto err_lock_init; ret = wcd9378_reset(dev); if (ret == -EPROBE_DEFER) { dev_err(dev, "%s: wcd reset failed!\n", __func__); goto err_lock_init; } wcd9378->wakeup = wcd9378_wakeup; return component_master_add_with_match(dev, &wcd9378_comp_ops, match); err_lock_init: mutex_destroy(&wcd9378->micb_lock); mutex_destroy(&wcd9378->wakeup_lock); mutex_destroy(&wcd9378->sys_usage_lock); err: return ret; } static int wcd9378_remove(struct platform_device *pdev) { struct wcd9378_priv *wcd9378 = NULL; wcd9378 = platform_get_drvdata(pdev); component_master_del(&pdev->dev, &wcd9378_comp_ops); mutex_destroy(&wcd9378->micb_lock); mutex_destroy(&wcd9378->wakeup_lock); mutex_destroy(&wcd9378->sys_usage_lock); dev_set_drvdata(&pdev->dev, NULL); return 0; } #ifdef CONFIG_PM_SLEEP static int wcd9378_suspend(struct device *dev) { struct wcd9378_priv *wcd9378 = NULL; int ret = 0; struct wcd9378_pdata *pdata = NULL; if (!dev) return -ENODEV; wcd9378 = dev_get_drvdata(dev); if (!wcd9378) return -EINVAL; pdata = dev_get_platdata(wcd9378->dev); if (!pdata) { dev_err(dev, "%s: pdata is NULL\n", __func__); return -EINVAL; } if (test_bit(ALLOW_BUCK_DISABLE, &wcd9378->status_mask)) { ret = msm_cdc_disable_ondemand_supply(wcd9378->dev, wcd9378->supplies, pdata->regulator, pdata->num_supplies, "cdc-vdd-buck"); if (ret == -EINVAL) { dev_err(dev, "%s: vdd buck is not disabled\n", __func__); return 0; } clear_bit(ALLOW_BUCK_DISABLE, &wcd9378->status_mask); } if (wcd9378->dapm_bias_off || (wcd9378->component && (snd_soc_component_get_bias_level(wcd9378->component) == SND_SOC_BIAS_OFF))) { msm_cdc_set_supplies_lpm_mode(wcd9378->dev, wcd9378->supplies, pdata->regulator, pdata->num_supplies, true); set_bit(WCD_SUPPLIES_LPM_MODE, &wcd9378->status_mask); } return 0; } static int wcd9378_resume(struct device *dev) { struct wcd9378_priv *wcd9378 = NULL; struct wcd9378_pdata *pdata = NULL; if (!dev) return -ENODEV; wcd9378 = dev_get_drvdata(dev); if (!wcd9378) return -EINVAL; pdata = dev_get_platdata(wcd9378->dev); if (!pdata) { dev_err(dev, "%s: pdata is NULL\n", __func__); return -EINVAL; } if (test_bit(WCD_SUPPLIES_LPM_MODE, &wcd9378->status_mask)) { msm_cdc_set_supplies_lpm_mode(wcd9378->dev, wcd9378->supplies, pdata->regulator, pdata->num_supplies, false); clear_bit(WCD_SUPPLIES_LPM_MODE, &wcd9378->status_mask); } return 0; } static const struct dev_pm_ops wcd9378_dev_pm_ops = { .suspend_late = wcd9378_suspend, .resume_early = wcd9378_resume, }; #endif static struct platform_driver wcd9378_codec_driver = { .probe = wcd9378_probe, .remove = wcd9378_remove, .driver = { .name = "wcd9378_codec", .of_match_table = of_match_ptr(wcd9378_dt_match), #ifdef CONFIG_PM_SLEEP .pm = &wcd9378_dev_pm_ops, #endif .suppress_bind_attrs = true, }, }; module_platform_driver(wcd9378_codec_driver); MODULE_DESCRIPTION("WCD9378 Codec driver"); MODULE_LICENSE("GPL");