diff --git a/asoc/codecs/wcd939x/internal.h b/asoc/codecs/wcd939x/internal.h index 0fea14c571..bbc55154bf 100644 --- a/asoc/codecs/wcd939x/internal.h +++ b/asoc/codecs/wcd939x/internal.h @@ -14,9 +14,12 @@ #include "wcd939x-mbhc.h" #include "wcd939x.h" -#define SWR_SCP_CONTROL 0x44 +#define SWR_SCP_CONTROL 0x44 #define SWR_SCP_HOST_CLK_DIV2_CTL_BANK 0xE0 #define WCD939X_MAX_MICBIAS 4 +#define MAX_XTALK_SCALE 31 +#define MIN_XTALK_ALPHA 0 +#define MAX_USBCSS_HS_IMPEDANCE_MOHMS 20000 /* Convert from vout ctl to micbias voltage in mV */ #define WCD_VOUT_CTL_TO_MICB(v) (1000 + v * 50) @@ -157,27 +160,81 @@ struct wcd939x_micbias_setting { u8 bias1_cfilt_sel; }; -struct wcd939x_xtalk_params { - u32 r_gnd_int_fet_mohms; - u32 r_gnd_par_route1_mohms; - u32 r_gnd_par_route2_mohms; +struct wcd939x_usbcss_hs_params { + /* Resistance of ground-side internal FET for SBU1 */ + u32 r_gnd_sbu1_int_fet_mohms; + /* Resistance of ground-side internal FET for SBU2 */ + u32 r_gnd_sbu2_int_fet_mohms; + /* Customer-characterized resistance for the ground-side external FET */ + u32 r_gnd_ext_fet_customer_mohms; + /* SW-computed resistance for the ground-side external FET */ u32 r_gnd_ext_fet_mohms; - u32 r_conn_par_load_neg_mohms; + /* Total ground-side parasitics between the WCD and external FET */ + u32 r_gnd_par_route1_mohms; + /* Total ground-side parasitics between the external FET and connector */ + u32 r_gnd_par_route2_mohms; + /* Total ground-side parasitics between the WCD and connector; sum of route1 and route2 */ + u32 r_gnd_par_tot_mohms; + /* Total ground-side resistance for SBU1 */ + u32 r_gnd_sbu1_res_tot_mohms; + /* Total ground-side resistance for SBU2 */ + u32 r_gnd_sbu2_res_tot_mohms; + /* Customer-characterized positive parasitics introduced from the connector */ + u32 r_conn_par_load_pos_mohms; + /* Resistance of left audio-side internal FET */ u32 r_aud_int_fet_l_mohms; + /* Resistance of right audio-side internal FET */ u32 r_aud_int_fet_r_mohms; + /* Resistance of left audio-side external FET */ u32 r_aud_ext_fet_l_mohms; + /* Resistance of right audio-side external FET */ u32 r_aud_ext_fet_r_mohms; - u32 r_conn_par_load_pos_l_mohms; - u32 r_conn_par_load_pos_r_mohms; - u32 r_gnd_res_tot_mohms; + /* Total left audio-side resistance */ u32 r_aud_res_tot_l_mohms; + /* Total right audio-side resistance */ u32 r_aud_res_tot_r_mohms; - u32 zl; - u32 zr; + /* Surge switch resistance */ + u32 r_surge_mohms; + /* Sum of left audio-side parasitics and the left side of the load */ + u32 r_load_eff_l_mohms; + /* Sum of right audio-side parasitics and the right side of the load */ + u32 r_load_eff_r_mohms; + /* Customer-characterized audio-side parasitics between the WCD and external FET, + * in milliohms + */ + u32 r3; + /* Customer-characterized ground-side parasitics between the external FET and connector, + * in milliohms + */ + u32 r4; + /* For digital crosstalk with remote sensed analog crosstalk mode, customer-characterized + * ground path parasitic resistance between the WCD SBU pin and the external MOSFET, + * in milliohms + */ + u32 r5; + /* For digital crosstalk with local sensed analog crosstalk mode, customer-characterized + * ground path parasitic resistance between the WCD GSBU tap point and the external MOSFET, + * in milliohms + */ + u32 r6; + /* For digital crosstalk with local sensed analog crosstalk mode, customer-characterized + * ground path parasitic resistance between the WCD GSBU tap point and the WCD SBU pin, + * in milliohms + */ + u32 r7; + /* Computed optimal d-xtalk left-side scale value */ u8 scale_l; + /* Computed optimal d-xtalk left-side alpha value */ u8 alpha_l; + /* Computed optimal d-xtalk right-side scale value */ u8 scale_r; + /* Computed optimal d-xtalk right-side alpha value */ u8 alpha_r; + /* Customer-tuned configuration for d-xtalk: + * 0 for digital crosstalk disabled, + * 1 for digital crosstalk with local sensed a-xtalk enabled, and + * 2 for digital crosstalk with remote sensed a-xtalk enabled. + */ enum xtalk_mode xtalk_config; }; @@ -186,7 +243,7 @@ struct wcd939x_pdata { struct device_node *rx_slave; struct device_node *tx_slave; struct wcd939x_micbias_setting micbias; - struct wcd939x_xtalk_params xtalk; + struct wcd939x_usbcss_hs_params usbcss_hs; struct cdc_regulator *regulator; int num_supplies; diff --git a/asoc/codecs/wcd939x/wcd939x-mbhc.c b/asoc/codecs/wcd939x/wcd939x-mbhc.c index cfa89bf0e4..1939f86415 100644 --- a/asoc/codecs/wcd939x/wcd939x-mbhc.c +++ b/asoc/codecs/wcd939x/wcd939x-mbhc.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved. */ #include #include @@ -21,6 +21,9 @@ #include #include "wcd939x-registers.h" #include "internal.h" +#if IS_ENABLED(CONFIG_QCOM_WCD_USBSS_I2C) +#include +#endif #define WCD939X_ZDET_SUPPORTED true /* Z value defined in milliohm */ @@ -36,9 +39,17 @@ #define WCD939X_MBHC_GET_X1(x) (x & 0x3FFF) /* Z value compared in milliOhm */ #define WCD939X_MBHC_IS_SECOND_RAMP_REQUIRED(z) false -#define WCD939X_MBHC_ZDET_CONST (1018 * 1024) +#define WCD939X_MBHC_ZDET_CONST (1071 * 1024) #define WCD939X_MBHC_MOISTURE_RREF R_24_KOHM +#define OHMS_TO_MILLIOHMS 1000 +#define FLOAT_TO_FIXED_XTALK (1UL << 16) +#define MAX_XTALK_ALPHA 255 +#define MIN_RL_EFF_MOHMS 1 +#define MAX_RL_EFF_MOHMS 900000 +#define HD2_CODE_BASE_VALUE 0x1D +#define HD2_CODE_INV_RESOLUTION 4201025 + static struct wcd_mbhc_register wcd_mbhc_registers[WCD_MBHC_REG_FUNC_MAX] = { WCD_MBHC_REGISTER("WCD_MBHC_L_DET_EN", @@ -439,8 +450,8 @@ static void wcd939x_mbhc_zdet_ramp(struct snd_soc_component *component, struct wcd939x_priv *wcd939x = dev_get_drvdata(component->dev); int32_t zdet = 0; - snd_soc_component_update_bits(component, WCD939X_ZDET_ANA_CTL, - 0x70, zdet_param->ldo_ctl << 4); + snd_soc_component_update_bits(component, WCD939X_ZDET_ANA_CTL, 0xF0, + 0x80 | (zdet_param->ldo_ctl << 4)); snd_soc_component_update_bits(component, WCD939X_MBHC_BTN5, 0xFC, zdet_param->btn5); snd_soc_component_update_bits(component, WCD939X_MBHC_BTN6, 0xFC, @@ -499,38 +510,266 @@ static inline void wcd939x_wcd_mbhc_qfuse_cal( *z_val = ((*z_val) * 10000) / q1_cal; } -static void wcd939x_wcd_mbhc_calc_impedance(struct wcd_mbhc *mbhc, uint32_t *zl, - uint32_t *zr) +static void update_hd2_codes(struct regmap *regmap, u32 r_gnd_res_tot_mohms, u32 r_load_eff) +{ + u64 hd2_delta = 0; + + if (!regmap) + return; + hd2_delta = (HD2_CODE_INV_RESOLUTION * (u64) r_gnd_res_tot_mohms + + FLOAT_TO_FIXED_XTALK * (u64) ((r_gnd_res_tot_mohms + r_load_eff) / 2)) / + (FLOAT_TO_FIXED_XTALK * (u64) (r_gnd_res_tot_mohms + r_load_eff)); + if (hd2_delta >= HD2_CODE_BASE_VALUE) { + regmap_update_bits(regmap, WCD939X_RDAC_HD2_CTL_L, 0x1F, 0x00); + regmap_update_bits(regmap, WCD939X_RDAC_HD2_CTL_R, 0x1F, 0x00); + } else { + regmap_update_bits(regmap, WCD939X_RDAC_HD2_CTL_L, 0x1F, + HD2_CODE_BASE_VALUE - hd2_delta); + regmap_update_bits(regmap, WCD939X_RDAC_HD2_CTL_R, 0x1F, + HD2_CODE_BASE_VALUE - hd2_delta); + } +} + +static u8 get_xtalk_scale(u32 gain) +{ + u8 i; + int target, residue; + + if (gain == 0) + return MAX_XTALK_SCALE; + + target = FLOAT_TO_FIXED_XTALK / ((int) gain); + residue = target; + + for (i = 0; i <= MAX_XTALK_SCALE; i++) { + residue = target - (1 << ((int)((u32) i))); + if (residue < 0) + return i; + } + return MAX_XTALK_SCALE; +} + +static u8 get_xtalk_alpha(u32 gain, u8 scale) +{ + u32 two_exp_scale, round_offset, alpha; + + if (gain == 0) + return MIN_XTALK_ALPHA; + + two_exp_scale = 1 << ((u32) scale); + round_offset = FLOAT_TO_FIXED_XTALK / 2; + alpha = (((gain * two_exp_scale - FLOAT_TO_FIXED_XTALK) * 255) + round_offset) + / FLOAT_TO_FIXED_XTALK; + return (alpha <= MAX_XTALK_ALPHA) ? ((u8) alpha) : MAX_XTALK_ALPHA; +} + +static u32 get_v_common_gnd_factor(u32 r_gnd_res_tot_mohms, u32 r_load_eff_mohms, + u32 r_aud_res_tot_mohms) +{ + /* Proof 1: The numerator does not overflow. + * r_gnd_res_tot_mohms = r_gnd_int_fet_mohms + r_gnd_ext_fet_mohms + r_gnd_par_tot_mohms = + * r_gnd_int_fet_mohms + r_gnd_ext_fet_mohms + r_gnd_par_route1_mohms + + * r_gnd_par_route2_mohms + * + * r_gnd_int_fet_mohms, r_gnd_ext_fet_mohms, r_gnd_par_route{1,2}_mohms are all less + * than MAX_USBCSS_HS_IMPEDANCE_MOHMS + * --> + * FLOAT_TO_FIXED_XTALK * r_gnd_res_tot_mohms <= + * FLOAT_TO_FIXED_XTALK * 4 * MAX_USBCSS_HS_IMPEDANCE_MOHMS = + * (1 << 16) * 4 * 20,000 = 65,536 * 80,000 = 3,932,160,000 <= 2^32 - 1 = + * 4,294,967,295 = U32_MAX + * + * Proof 2: The denominator is greater than 0. + * r_load_eff_mohms >= MIN_RL_EFF_MOHMS = 1 > 0 + * --> + * r_load_eff_mohms + r_aud_res_tot_mohms + r_gnd_res_tot_mohms > 0 + * + * Proof 3: The deonominator does not overflow. + * r_load_eff_mohms <= MAX_RL_EFF_MOHMS + * r_aud_res_tot_mohms and r_gnd_res_tot_mohms <= MAX_USBCSS_HS_IMPEDANCE_MOHMS + * --> + * r_load_eff_mohms + r_aud_res_tot_mohms + r_gnd_res_tot_mohms <= + * MAX_RL_EFF_MOHMS + 2 * MAX_USBCSS_HS_IMPEDANCE_MOHMS = 900,000 + 2 * 20,000 = 940,000 + * <= U32_MAX = 2^32 - 1 = 4,294,967,295 + */ + return FLOAT_TO_FIXED_XTALK * r_gnd_res_tot_mohms / + (r_load_eff_mohms + r_aud_res_tot_mohms + r_gnd_res_tot_mohms); +} + +static u32 get_v_feedback_tap_factor_digital(u32 r_gnd_int_fet_mohms, u32 r_gnd_par_route1_mohms, + u32 r_load_eff_mohms, u32 r_gnd_res_tot_mohms, + u32 r_aud_res_tot_mohms) +{ + /* Proof 4: The numerator does not overflow. + * r_gnd_int_fet_mohms and r_gnd_par_route1_mohms <= MAX_USBCSS_HS_IMPEDANCE_MOHMS + * --> + * FLOAT_TO_FIXED_XTALK * (r_gnd_int_fet_mohms + r_gnd_par_route1_mohms) <= + * FLOAT_TO_FIXED_XTALK * 2 * MAX_USBCSS_HS_IMPEDANCE_MOHMS = + * (1 << 16) * 2 * 20,000 = 65,536 * 40,000 = 2,621,440,000 <= 2^32 - 1 = + * 4,294,967,295 = U32_MAX + * + * The denominator is greater than 0: See Proof 2 + * The deonominator does not overflow: See Proof 3 + */ + return FLOAT_TO_FIXED_XTALK * (r_gnd_int_fet_mohms + r_gnd_par_route1_mohms) / + (r_load_eff_mohms + r_gnd_res_tot_mohms + r_aud_res_tot_mohms); +} + +static u32 get_v_feedback_tap_factor_analog(u32 r_gnd_par_route2_mohms, u32 r_load_eff_mohms, + u32 r_gnd_res_tot_mohms, u32 r_aud_res_tot_mohms) +{ + /* Proof 5: The numerator does not overflow. + * r_gnd_res_tot_mohms = r_gnd_int_fet_mohms + r_gnd_ext_fet_mohms + r_gnd_par_tot_mohms = + * r_gnd_int_fet_mohms + r_gnd_ext_fet_mohms + r_gnd_par_route1_mohms + + * r_gnd_par_route2_mohms + * + * r_gnd_res_tot_mohms - r_gnd_par_route2_mohms = + * r_gnd_int_fet_mohms + r_gnd_ext_fet_mohms + r_gnd_par_route1_mohms + * + * r_gnd_int_fet_mohms, r_gnd_ext_fet_mohms, r_gnd_par_route1_mohms + * <= MAX_USBCSS_HS_IMPEDANCE_MOHMS = 20,000 + * --> + * FLOAT_TO_FIXED_XTALK * (r_gnd_int_fet_mohms + r_gnd_ext_fet_mohms + + * r_gnd_par_route1_mohms) + * <= FLOAT_TO_FIXED_XTALK * 3 * MAX_USBCSS_HS_IMPEDANCE_MOHMS = + * (1 << 16) * 3 * 20,000 = 65,536 * 60,000 = 3,932,160,000 <= 2^32 - 1 = + * 4,294,967,295 = U32_MAX + * + * The denominator is greater than 0: See Proof 2 + * The deonominator does not overflow: See Proof 3 + */ + return FLOAT_TO_FIXED_XTALK * (r_gnd_res_tot_mohms - r_gnd_par_route2_mohms) / + (r_load_eff_mohms + r_gnd_res_tot_mohms + r_aud_res_tot_mohms); +} + +static u32 get_xtalk_gain(u32 v_common_gnd_factor, u32 v_feedback_tap_factor) +{ + return v_common_gnd_factor - v_feedback_tap_factor; +} + +static void update_xtalk_scale_and_alpha(struct wcd939x_pdata *pdata, struct regmap *regmap) +{ + u32 r_gnd_res_tot_mohms = 0, r_gnd_int_fet_mohms = 0, v_common_gnd_factor = 0; + u32 v_feedback_tap_factor = 0, xtalk_gain = 0; + + if (!pdata || pdata->usbcss_hs.xtalk_config == XTALK_NONE) + return; + + /* Orientation-dependent ground impedance parameters */ +#if IS_ENABLED(CONFIG_QCOM_WCD_USBSS_I2C) + if (wcd_usbss_get_sbu_switch_orientation() == GND_SBU2_ORIENTATION_A) { + r_gnd_res_tot_mohms = pdata->usbcss_hs.r_gnd_sbu2_res_tot_mohms; + r_gnd_int_fet_mohms = pdata->usbcss_hs.r_gnd_sbu2_int_fet_mohms; + } else if (wcd_usbss_get_sbu_switch_orientation() == GND_SBU1_ORIENTATION_B) { + r_gnd_res_tot_mohms = pdata->usbcss_hs.r_gnd_sbu1_res_tot_mohms; + r_gnd_int_fet_mohms = pdata->usbcss_hs.r_gnd_sbu1_int_fet_mohms; + } else { + pdata->usbcss_hs.scale_l = MAX_XTALK_SCALE; + pdata->usbcss_hs.alpha_l = MIN_XTALK_ALPHA; + pdata->usbcss_hs.scale_r = MAX_XTALK_SCALE; + pdata->usbcss_hs.alpha_r = MIN_XTALK_ALPHA; + return; + } +#endif + + /* Recall assumptions about L and R channel impedance parameters being equivalent */ + /* Xtalk gain calculation */ + v_common_gnd_factor = get_v_common_gnd_factor(r_gnd_res_tot_mohms, + pdata->usbcss_hs.r_load_eff_l_mohms, + pdata->usbcss_hs.r_aud_res_tot_l_mohms); + if (pdata->usbcss_hs.xtalk_config == XTALK_ANALOG) { + v_feedback_tap_factor = get_v_feedback_tap_factor_analog( + pdata->usbcss_hs.r_gnd_par_route2_mohms, + pdata->usbcss_hs.r_load_eff_l_mohms, + r_gnd_res_tot_mohms, + pdata->usbcss_hs.r_aud_res_tot_l_mohms); + /* Update HD2 codes for analog xtalk */ + update_hd2_codes(regmap, r_gnd_res_tot_mohms, pdata->usbcss_hs.r_load_eff_l_mohms); + } else { + v_feedback_tap_factor = get_v_feedback_tap_factor_digital( + r_gnd_int_fet_mohms, + pdata->usbcss_hs.r_gnd_par_route1_mohms, + pdata->usbcss_hs.r_load_eff_l_mohms, + r_gnd_res_tot_mohms, + pdata->usbcss_hs.r_aud_res_tot_l_mohms); + } + xtalk_gain = get_xtalk_gain(v_common_gnd_factor, v_feedback_tap_factor); + /* Store scale and alpha values */ + pdata->usbcss_hs.scale_l = get_xtalk_scale(xtalk_gain); + pdata->usbcss_hs.alpha_l = get_xtalk_alpha(xtalk_gain, pdata->usbcss_hs.scale_l); + pdata->usbcss_hs.scale_r = pdata->usbcss_hs.scale_l; + pdata->usbcss_hs.alpha_r = pdata->usbcss_hs.alpha_l; +} + +static void update_ext_fet_res(struct wcd939x_pdata *pdata, u32 r_gnd_ext_fet_mohms) +{ + if (!pdata) + return; + + pdata->usbcss_hs.r_gnd_ext_fet_mohms = (r_gnd_ext_fet_mohms > MAX_USBCSS_HS_IMPEDANCE_MOHMS) + ? MAX_USBCSS_HS_IMPEDANCE_MOHMS + : r_gnd_ext_fet_mohms; + pdata->usbcss_hs.r_aud_ext_fet_l_mohms = pdata->usbcss_hs.r_gnd_ext_fet_mohms; + pdata->usbcss_hs.r_aud_ext_fet_r_mohms = pdata->usbcss_hs.r_gnd_ext_fet_mohms; + pdata->usbcss_hs.r_gnd_sbu1_res_tot_mohms = get_r_gnd_res_tot_mohms( + pdata->usbcss_hs.r_gnd_sbu1_int_fet_mohms, + pdata->usbcss_hs.r_gnd_ext_fet_mohms, + pdata->usbcss_hs.r_gnd_par_tot_mohms); + pdata->usbcss_hs.r_gnd_sbu2_res_tot_mohms = get_r_gnd_res_tot_mohms( + pdata->usbcss_hs.r_gnd_sbu2_int_fet_mohms, + pdata->usbcss_hs.r_gnd_ext_fet_mohms, + pdata->usbcss_hs.r_gnd_par_tot_mohms); + pdata->usbcss_hs.r_aud_res_tot_l_mohms = get_r_aud_res_tot_mohms( + pdata->usbcss_hs.r_aud_int_fet_l_mohms, + pdata->usbcss_hs.r_aud_ext_fet_l_mohms); + pdata->usbcss_hs.r_aud_res_tot_r_mohms = get_r_aud_res_tot_mohms( + pdata->usbcss_hs.r_aud_int_fet_r_mohms, + pdata->usbcss_hs.r_aud_ext_fet_r_mohms); +} + +static void wcd939x_wcd_mbhc_calc_impedance(struct wcd_mbhc *mbhc, uint32_t *zl, uint32_t *zr) { struct snd_soc_component *component = mbhc->component; struct wcd939x_priv *wcd939x = dev_get_drvdata(component->dev); + struct wcd939x_pdata *pdata = dev_get_platdata(wcd939x->dev); s16 reg0, reg1, reg2, reg3, reg4; - int32_t z1L, z1R, z1Ls; + uint32_t zdiff_val = 0, r_gnd_int_fet_mohms = 0, rl_eff_mohms = 0, r_gnd_ext_fet_mohms = 0; + uint32_t *zdiff = &zdiff_val; + int32_t z1L, z1R, z1Ls, z1Diff; int zMono, z_diff1, z_diff2; bool is_fsm_disable = false; - struct wcd939x_mbhc_zdet_param zdet_param[] = { - {4, 0, 4, 0x08, 0x14, 0x18}, /* < 32ohm */ - {4, 0, 6, 0x18, 0x60, 0x78}, /* 32ohm < Z < 400ohm */ - {1, 4, 5, 0x18, 0x7C, 0x90}, /* 400ohm < Z < 1200ohm */ - {1, 6, 7, 0x18, 0x7C, 0x90}, /* >1200ohm */ - }; - struct wcd939x_mbhc_zdet_param *zdet_param_ptr = NULL; - s16 d1_a[][4] = { - {0, 30, 90, 30}, - {0, 30, 30, 6}, - {0, 30, 30, 5}, - {0, 30, 30, 5}, - }; - s16 *d1 = NULL; + struct wcd939x_mbhc_zdet_param zdet_param = {4, 0, 6, 0x18, 0x60, 0x78}; + struct wcd939x_mbhc_zdet_param *zdet_param_ptr = &zdet_param; + s16 d1[] = {0, 30, 30, 6}; WCD_MBHC_RSC_ASSERT_LOCKED(mbhc); + /* Turn on RX supplies */ + if (wcd939x->version == WCD939X_VERSION_2_0) { + /* Start up Buck/Flyback, Enable RX bias, Use MBHC RCO for MBHC Zdet, Enable Vneg */ + regmap_update_bits(wcd939x->regmap, WCD939X_ZDET_VNEG_CTL, 0x4C, 0x4C); + /* Wait 100us for settling */ + usleep_range(100, 110); + /* Enable VNEGDAC_LDO */ + regmap_update_bits(wcd939x->regmap, WCD939X_ZDET_VNEG_CTL, 0x10, 0x10); + /* Wait 100us for settling */ + usleep_range(100, 110); + /* Keep PA left/right channels disabled */ + regmap_update_bits(wcd939x->regmap, WCD939X_ZDET_VNEG_CTL, 0x01, 0x01); + /* Enable VPOS */ + regmap_update_bits(wcd939x->regmap, WCD939X_ZDET_VNEG_CTL, 0x20, 0x20); + /* Wait 500us for settling */ + usleep_range(500, 510); + } + + /* Store register values */ reg0 = snd_soc_component_read(component, WCD939X_MBHC_BTN5); reg1 = snd_soc_component_read(component, WCD939X_MBHC_BTN6); reg2 = snd_soc_component_read(component, WCD939X_MBHC_BTN7); reg3 = snd_soc_component_read(component, WCD939X_CTL_CLK); reg4 = snd_soc_component_read(component, WCD939X_ZDET_ANA_CTL); + /* Disable the detection FSM */ if (snd_soc_component_read(component, WCD939X_MBHC_ELECT) & 0x80) { is_fsm_disable = true; regmap_update_bits(wcd939x->regmap, @@ -554,76 +793,107 @@ static void wcd939x_wcd_mbhc_calc_impedance(struct wcd_mbhc *mbhc, uint32_t *zl, /* 1ms delay needed after disable surge protection */ usleep_range(1000, 1010); - /* First get impedance on Left */ - d1 = d1_a[1]; - zdet_param_ptr = &zdet_param[1]; - wcd939x_mbhc_zdet_ramp(component, zdet_param_ptr, &z1L, NULL, d1); - - if (!WCD939X_MBHC_IS_SECOND_RAMP_REQUIRED(z1L)) - goto left_ch_impedance; - - /* Second ramp for left ch */ - if (z1L < WCD939X_ZDET_VAL_32) { - zdet_param_ptr = &zdet_param[0]; - d1 = d1_a[0]; - } else if ((z1L > WCD939X_ZDET_VAL_400) && - (z1L <= WCD939X_ZDET_VAL_1200)) { - zdet_param_ptr = &zdet_param[2]; - d1 = d1_a[2]; - } else if (z1L > WCD939X_ZDET_VAL_1200) { - zdet_param_ptr = &zdet_param[3]; - d1 = d1_a[3]; +#if IS_ENABLED(CONFIG_QCOM_WCD_USBSS_I2C) + /* Disable sense switch and MIC for USB-C analog platforms */ + if (mbhc->mbhc_cfg->enable_usbc_analog) { + wcd_usbss_set_switch_settings_enable(SENSE_SWITCHES, USBSS_SWITCH_DISABLE); + wcd_usbss_set_switch_settings_enable(MIC_SWITCHES, USBSS_SWITCH_DISABLE); } - wcd939x_mbhc_zdet_ramp(component, zdet_param_ptr, &z1L, NULL, d1); +#endif -left_ch_impedance: - if ((z1L == WCD939X_ZDET_FLOATING_IMPEDANCE) || - (z1L > WCD939X_ZDET_VAL_100K)) { + /* L-channel impedance */ + wcd939x_mbhc_zdet_ramp(component, zdet_param_ptr, &z1L, NULL, d1); + if ((z1L == WCD939X_ZDET_FLOATING_IMPEDANCE) || (z1L > WCD939X_ZDET_VAL_100K)) { *zl = WCD939X_ZDET_FLOATING_IMPEDANCE; - zdet_param_ptr = &zdet_param[1]; - d1 = d1_a[1]; } else { - *zl = z1L/1000; + *zl = z1L; wcd939x_wcd_mbhc_qfuse_cal(component, zl, 0); } - dev_dbg(component->dev, "%s: impedance on HPH_L = %d(ohms)\n", + /* Differential measurement for USB-C analog platforms */ + if (mbhc->mbhc_cfg->enable_usbc_analog) { + dev_dbg(component->dev, "%s: effective impedance on HPH_L = %d(mohms)\n", + __func__, *zl); + goto diff_impedance; + } + dev_dbg(component->dev, "%s: impedance on HPH_L = %d(mohms)\n", __func__, *zl); - /* Start of right impedance ramp and calculation */ + /* R-channel impedance */ wcd939x_mbhc_zdet_ramp(component, zdet_param_ptr, NULL, &z1R, d1); - if (WCD939X_MBHC_IS_SECOND_RAMP_REQUIRED(z1R)) { - if (((z1R > WCD939X_ZDET_VAL_1200) && - (zdet_param_ptr->noff == 0x6)) || - ((*zl) != WCD939X_ZDET_FLOATING_IMPEDANCE)) - goto right_ch_impedance; - /* Second ramp for right ch */ - if (z1R < WCD939X_ZDET_VAL_32) { - zdet_param_ptr = &zdet_param[0]; - d1 = d1_a[0]; - } else if ((z1R > WCD939X_ZDET_VAL_400) && - (z1R <= WCD939X_ZDET_VAL_1200)) { - zdet_param_ptr = &zdet_param[2]; - d1 = d1_a[2]; - } else if (z1R > WCD939X_ZDET_VAL_1200) { - zdet_param_ptr = &zdet_param[3]; - d1 = d1_a[3]; - } - wcd939x_mbhc_zdet_ramp(component, zdet_param_ptr, NULL, &z1R, d1); - } -right_ch_impedance: - if ((z1R == WCD939X_ZDET_FLOATING_IMPEDANCE) || - (z1R > WCD939X_ZDET_VAL_100K)) { + if ((z1R == WCD939X_ZDET_FLOATING_IMPEDANCE) || (z1R > WCD939X_ZDET_VAL_100K)) { *zr = WCD939X_ZDET_FLOATING_IMPEDANCE; } else { - *zr = z1R/1000; + *zr = z1R; wcd939x_wcd_mbhc_qfuse_cal(component, zr, 1); } - dev_dbg(component->dev, "%s: impedance on HPH_R = %d(ohms)\n", + dev_dbg(component->dev, "%s: impedance on HPH_R = %d(mohms)\n", __func__, *zr); + /* Convert from mohms to ohms (rounded) */ + *zl = (*zl + OHMS_TO_MILLIOHMS / 2) / OHMS_TO_MILLIOHMS; + *zr = (*zr + OHMS_TO_MILLIOHMS / 2) / OHMS_TO_MILLIOHMS; + goto mono_stereo_detection; +diff_impedance: +#if IS_ENABLED(CONFIG_QCOM_WCD_USBSS_I2C) + /* Disable AGND switch */ + wcd_usbss_set_switch_settings_enable(AGND_SWITCHES, USBSS_SWITCH_DISABLE); +#endif + /* Enable HPHR NCLAMP */ + regmap_update_bits(wcd939x->regmap, WCD939X_HPHLR_SURGE_MISC1, 0x08, 0x08); + + /* Diffrential impedance */ + wcd939x_mbhc_zdet_ramp(component, zdet_param_ptr, &z1Diff, NULL, d1); + if ((z1Diff == WCD939X_ZDET_FLOATING_IMPEDANCE) || (z1Diff > WCD939X_ZDET_VAL_100K)) { + *zdiff = WCD939X_ZDET_FLOATING_IMPEDANCE; + } else { + *zdiff = z1Diff; + wcd939x_wcd_mbhc_qfuse_cal(component, zdiff, 0); + } + dev_dbg(component->dev, "%s: effective impedance on HPH_diff after calib = %d(mohms)\n", + __func__, *zdiff); + /* Disable HPHR NCLAMP */ + regmap_update_bits(wcd939x->regmap, WCD939X_HPHLR_SURGE_MISC1, 0x08, 0x00); +#if IS_ENABLED(CONFIG_QCOM_WCD_USBSS_I2C) + /* Enable AGND switch */ + wcd_usbss_set_switch_settings_enable(AGND_SWITCHES, USBSS_SWITCH_ENABLE); + /* Get ground internal resistance based on orientation */ + if (wcd_usbss_get_sbu_switch_orientation() == GND_SBU2_ORIENTATION_A) { + r_gnd_int_fet_mohms = pdata->usbcss_hs.r_gnd_sbu2_int_fet_mohms; + } else if (wcd_usbss_get_sbu_switch_orientation() == GND_SBU1_ORIENTATION_B) { + r_gnd_int_fet_mohms = pdata->usbcss_hs.r_gnd_sbu1_int_fet_mohms; + } else { + *zl = 0; + *zr = 0; + dev_dbg(component->dev, "%s: Invalid SBU switch orientation\n", __func__); + goto zdet_complete; + } +#endif + /* Compute external fet and effective load impedance */ + r_gnd_ext_fet_mohms = *zl - *zdiff / 2 + pdata->usbcss_hs.r_surge_mohms / 2 - + pdata->usbcss_hs.r_gnd_par_tot_mohms - r_gnd_int_fet_mohms; + rl_eff_mohms = *zdiff / 2 - pdata->usbcss_hs.r_aud_int_fet_r_mohms - + pdata->usbcss_hs.r_gnd_ext_fet_mohms - pdata->usbcss_hs.r_surge_mohms / 2 - + pdata->usbcss_hs.r_gnd_par_tot_mohms; + /* Store values */ + *zl = (rl_eff_mohms - pdata->usbcss_hs.r_conn_par_load_pos_mohms - pdata->usbcss_hs.r3 + + OHMS_TO_MILLIOHMS / 2) / OHMS_TO_MILLIOHMS; + *zr = *zl; + + /* Update USBC-SS HS params */ + if (rl_eff_mohms > MAX_RL_EFF_MOHMS) + rl_eff_mohms = MAX_RL_EFF_MOHMS; + else if (rl_eff_mohms == 0) + rl_eff_mohms = MIN_RL_EFF_MOHMS; + pdata->usbcss_hs.r_load_eff_l_mohms = rl_eff_mohms; + pdata->usbcss_hs.r_load_eff_r_mohms = rl_eff_mohms; + update_ext_fet_res(pdata, r_gnd_ext_fet_mohms); + update_xtalk_scale_and_alpha(pdata, wcd939x->regmap); + dev_dbg(component->dev, "%s: Xtalk scale is 0x%x and alpha is 0x%x\n", + __func__, pdata->usbcss_hs.scale_l, pdata->usbcss_hs.alpha_l); + +mono_stereo_detection: /* Mono/stereo detection */ - if ((*zl == WCD939X_ZDET_FLOATING_IMPEDANCE) && - (*zr == WCD939X_ZDET_FLOATING_IMPEDANCE)) { + if ((*zl == WCD939X_ZDET_FLOATING_IMPEDANCE) && (*zr == WCD939X_ZDET_FLOATING_IMPEDANCE)) { dev_dbg(component->dev, "%s: plug type is invalid or extension cable\n", __func__); @@ -641,7 +911,7 @@ right_ch_impedance: } snd_soc_component_update_bits(component, WCD939X_R_ATEST, 0x02, 0x02); snd_soc_component_update_bits(component, WCD939X_PA_CTL2, 0x40, 0x01); - wcd939x_mbhc_zdet_ramp(component, &zdet_param[1], &z1Ls, NULL, d1); + wcd939x_mbhc_zdet_ramp(component, zdet_param_ptr, &z1Ls, NULL, d1); snd_soc_component_update_bits(component, WCD939X_PA_CTL2, 0x40, 0x00); snd_soc_component_update_bits(component, WCD939X_R_ATEST, 0x02, 0x00); z1Ls /= 1000; @@ -660,10 +930,18 @@ right_ch_impedance: mbhc->hph_type = WCD_MBHC_HPH_MONO; } +zdet_complete: +#if IS_ENABLED(CONFIG_QCOM_WCD_USBSS_I2C) + /* Enable sense switch and MIC for USB-C analog platforms */ + if (mbhc->mbhc_cfg->enable_usbc_analog) { + wcd_usbss_set_switch_settings_enable(SENSE_SWITCHES, USBSS_SWITCH_ENABLE); + wcd_usbss_set_switch_settings_enable(MIC_SWITCHES, USBSS_SWITCH_ENABLE); + } +#endif /* Enable surge protection again after impedance detection */ regmap_update_bits(wcd939x->regmap, WCD939X_HPHLR_SURGE_EN, 0xC0, 0xC0); -zdet_complete: + snd_soc_component_write(component, WCD939X_MBHC_BTN5, reg0); snd_soc_component_write(component, WCD939X_MBHC_BTN6, reg1); snd_soc_component_write(component, WCD939X_MBHC_BTN7, reg2); @@ -681,6 +959,24 @@ zdet_complete: if (is_fsm_disable) regmap_update_bits(wcd939x->regmap, WCD939X_MBHC_ELECT, 0x80, 0x80); + + /* Turn off RX supplies */ + if (wcd939x->version == WCD939X_VERSION_2_0) { + /* Set VPOS to be controlled by RX */ + regmap_update_bits(wcd939x->regmap, WCD939X_ZDET_VNEG_CTL, 0x20, 0x00); + /* Wait 500us for settling */ + usleep_range(500, 510); + /* Set PA Left/Right channels and VNEGDAC_LDO to be controlled by RX */ + regmap_update_bits(wcd939x->regmap, WCD939X_ZDET_VNEG_CTL, 0x11, 0x00); + /* Wait 100us for settling */ + usleep_range(100, 110); + /* Set Vneg mode and enable to be controlled by RX */ + regmap_update_bits(wcd939x->regmap, WCD939X_ZDET_VNEG_CTL, 0x06, 0x00); + /* Wait 100us for settling */ + usleep_range(100, 110); + /* Set RX bias to be controlled by RX and set Buck/Flyback back to SWR Rx clock */ + regmap_update_bits(wcd939x->regmap, WCD939X_ZDET_VNEG_CTL, 0x48, 0x00); + } } static void wcd939x_mbhc_gnd_det_ctrl(struct snd_soc_component *component, diff --git a/asoc/codecs/wcd939x/wcd939x-mbhc.h b/asoc/codecs/wcd939x/wcd939x-mbhc.h index 47d0802377..3fad719161 100644 --- a/asoc/codecs/wcd939x/wcd939x-mbhc.h +++ b/asoc/codecs/wcd939x/wcd939x-mbhc.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef __WCD939X_MBHC_H__ #define __WCD939X_MBHC_H__ @@ -13,6 +13,17 @@ struct wcd939x_mbhc { struct fw_info *fw_data; }; +static inline u32 get_r_gnd_res_tot_mohms(u32 r_gnd_int_fet_mohms, u32 r_gnd_ext_fet_mohms, + u32 r_gnd_par_tot_mohms) +{ + return r_gnd_int_fet_mohms + r_gnd_ext_fet_mohms + r_gnd_par_tot_mohms; +} + +static inline u32 get_r_aud_res_tot_mohms(u32 r_aud_int_fet_mohms, u32 r_aud_ext_fet_mohms) +{ + return r_aud_int_fet_mohms + r_aud_ext_fet_mohms; +} + #if IS_ENABLED(CONFIG_SND_SOC_WCD939X) extern int wcd939x_mbhc_init(struct wcd939x_mbhc **mbhc, struct snd_soc_component *component, diff --git a/asoc/codecs/wcd939x/wcd939x.c b/asoc/codecs/wcd939x/wcd939x.c index 25f867bedc..7c8e5201bd 100644 --- a/asoc/codecs/wcd939x/wcd939x.c +++ b/asoc/codecs/wcd939x/wcd939x.c @@ -48,20 +48,13 @@ #define ADC_MODE_VAL_ULP2 0x0B #define HPH_IMPEDANCE_2VPK_MODE_OHMS 260 +#define XTALK_L_CH_NUM 0 +#define XTALK_R_CH_NUM 1 #define NUM_ATTEMPTS 5 #define COMP_MAX_COEFF 25 #define HPH_MODE_MAX 4 -#define FLOAT_TO_FIXED (1 << 12) -#define MAX_XTALK_SCALE 31 -#define MAX_XTALK_ALPHA 255 -#define MAX_RLOAD_OHMS 1000 -#define MAX_IMPEDANCE_MOHMS 20000 -#define OHMS_TO_MILLIOHMS 1000 -#define XTALK_L_CH_NUM 0 -#define XTALK_R_CH_NUM 1 - #define DAPM_MICBIAS1_STANDALONE "MIC BIAS1 Standalone" #define DAPM_MICBIAS2_STANDALONE "MIC BIAS2 Standalone" #define DAPM_MICBIAS3_STANDALONE "MIC BIAS3 Standalone" @@ -1037,180 +1030,12 @@ static int wcd939x_config_compander(struct snd_soc_component *component, return 0; } -static u8 get_xtalk_scale(u32 gain) -{ - u8 i; - int target = FLOAT_TO_FIXED / ((int) gain); - int residue = target; - - for (i = 0; i <= MAX_XTALK_SCALE; i++) { - residue = target - (1 << ((int)((u32) i))); - if (residue <= 0) - return i; - } - return MAX_XTALK_SCALE; -} - -static u8 get_xtalk_alpha(u32 gain, u8 scale) -{ - u32 two_exp_scale = 1 << ((u32) scale); - u32 round_offset = FLOAT_TO_FIXED / 2; - u32 alpha = (((gain * two_exp_scale - FLOAT_TO_FIXED) * 255) + round_offset) - / FLOAT_TO_FIXED; - return (alpha <= MAX_XTALK_ALPHA) ? ((u8) alpha) : MAX_XTALK_ALPHA; -} - -static u32 get_r_gnd_res_tot_mohms(u32 r_gnd_int_fet_mohms, u32 r_gnd_par_route1_mohms, - u32 r_gnd_par_route2_mohms, u32 r_gnd_ext_fet_mohms, - u32 r_conn_par_load_neg_mohms) -{ - return r_gnd_int_fet_mohms + r_gnd_par_route1_mohms + r_gnd_par_route2_mohms + - r_gnd_ext_fet_mohms + r_conn_par_load_neg_mohms; -} - -static u32 get_r_aud_res_tot_mohms(u32 r_aud_int_fet_mohms, u32 r_aud_ext_fet_mohms, - u32 r_conn_par_load_pos_mohms) -{ - return r_aud_int_fet_mohms + r_aud_ext_fet_mohms + r_conn_par_load_pos_mohms; -} - -static u32 get_v_common_gnd_factor(u32 r_gnd_res_tot_mohms, u32 r_load_mohms, - u32 r_aud_res_tot_mohms) -{ - return FLOAT_TO_FIXED * r_gnd_res_tot_mohms / - (r_load_mohms + r_aud_res_tot_mohms + r_gnd_res_tot_mohms); -} - -static u32 get_v_feedback_tap_factor(u32 r_gnd_int_fet_mohms, u32 r_gnd_par_route1_mohms, - u32 r_load_mohms, u32 r_gnd_res_tot_mohms, - u32 r_aud_res_tot_mohms) -{ - return FLOAT_TO_FIXED * (r_gnd_int_fet_mohms + r_gnd_par_route1_mohms) / - (r_load_mohms + r_gnd_res_tot_mohms + r_aud_res_tot_mohms); -} - -static u32 get_v_feedback_tap_factor_analog(u32 r_conn_par_load_neg_mohms, u32 r_load_mohms, - u32 r_gnd_res_tot_mohms, u32 r_aud_res_tot_mohms) -{ - return FLOAT_TO_FIXED * (r_gnd_res_tot_mohms - r_conn_par_load_neg_mohms) / - (r_load_mohms + r_gnd_res_tot_mohms + r_aud_res_tot_mohms); -} - -static u32 get_xtalk_gain(u32 v_common_gnd_factor, u32 v_feedback_tap_factor) -{ - return v_common_gnd_factor - v_feedback_tap_factor; -} - -static void get_xtalk_scale_and_alpha(struct snd_soc_component *component, int xtalk_indx, - u8 *scale, u8 *alpha) -{ - u32 r_aud_int_fet_mohms = 0, r_aud_ext_fet_mohms = 0, r_conn_par_load_pos_mohms = 0, - r_load_mohms = 32360, r_aud_res_tot_mohms = 0, v_common_gnd_factor = 0, - v_feedback_tap_factor = 0, xtalk_gain = 0, zl = 0, zr = 0; - struct wcd939x_priv *wcd939x = NULL; - struct wcd939x_pdata *pdata = NULL; - - if ((xtalk_indx != XTALK_L_CH_NUM) && (xtalk_indx != XTALK_R_CH_NUM)) - goto err_data; - wcd939x = snd_soc_component_get_drvdata(component); - if (!wcd939x->dev) - goto err_data; - pdata = dev_get_platdata(wcd939x->dev); - if (pdata->xtalk.xtalk_config == XTALK_NONE) - goto err_data; - - /* Get headphone impedance for r_load */ - wcd939x_mbhc_get_impedance(wcd939x->mbhc, &zl, &zr); - if (xtalk_indx == XTALK_L_CH_NUM) { - if (zl > MAX_RLOAD_OHMS || zl == 0) { - pdata->xtalk.scale_l = MAX_XTALK_SCALE; - pdata->xtalk.alpha_l = MAX_XTALK_ALPHA; - pdata->xtalk.zl = 0; - goto err_data; - } - /* Use cached alpha and scale for the same headphone load */ - if (zl == pdata->xtalk.zl) { - *alpha = pdata->xtalk.alpha_l; - *scale = pdata->xtalk.scale_l; - return; - } - pdata->xtalk.zl = zl; - } else { - if (zr > MAX_RLOAD_OHMS || zr == 0) { - pdata->xtalk.scale_r = MAX_XTALK_SCALE; - pdata->xtalk.alpha_r = MAX_XTALK_ALPHA; - pdata->xtalk.zr = 0; - goto err_data; - } - /* Use cached alpha and scale for the same headphone load */ - if (zr == pdata->xtalk.zr) { - *alpha = pdata->xtalk.alpha_r; - *scale = pdata->xtalk.scale_r; - return; - } - pdata->xtalk.zr = zr; - } - - /* Channel-dependent impedance parameters */ - if (xtalk_indx == XTALK_L_CH_NUM) { - r_aud_int_fet_mohms = pdata->xtalk.r_aud_int_fet_l_mohms; - r_aud_ext_fet_mohms = pdata->xtalk.r_aud_ext_fet_l_mohms; - r_conn_par_load_pos_mohms = pdata->xtalk.r_conn_par_load_pos_l_mohms; - r_aud_res_tot_mohms = pdata->xtalk.r_aud_res_tot_l_mohms; - r_load_mohms = pdata->xtalk.zl * OHMS_TO_MILLIOHMS; - } else { - r_aud_int_fet_mohms = pdata->xtalk.r_aud_int_fet_r_mohms; - r_aud_ext_fet_mohms = pdata->xtalk.r_aud_ext_fet_r_mohms; - r_conn_par_load_pos_mohms = pdata->xtalk.r_conn_par_load_pos_r_mohms; - r_aud_res_tot_mohms = pdata->xtalk.r_aud_res_tot_r_mohms; - r_load_mohms = pdata->xtalk.zr * OHMS_TO_MILLIOHMS; - } - - /* Xtalk gain calculation */ - v_common_gnd_factor = get_v_common_gnd_factor(pdata->xtalk.r_gnd_res_tot_mohms, - r_load_mohms, - r_aud_res_tot_mohms); - if (pdata->xtalk.xtalk_config == XTALK_ANALOG) { - v_feedback_tap_factor = get_v_feedback_tap_factor_analog( - pdata->xtalk.r_conn_par_load_neg_mohms, - r_load_mohms, - pdata->xtalk.r_gnd_res_tot_mohms, - r_aud_res_tot_mohms); - } else { - v_feedback_tap_factor = get_v_feedback_tap_factor( - pdata->xtalk.r_gnd_int_fet_mohms, - pdata->xtalk.r_gnd_par_route1_mohms, - r_load_mohms, - pdata->xtalk.r_gnd_res_tot_mohms, - r_aud_res_tot_mohms); - } - xtalk_gain = get_xtalk_gain(v_common_gnd_factor, v_feedback_tap_factor); - - /* Store scale and alpha values */ - *scale = get_xtalk_scale(xtalk_gain); - *alpha = get_xtalk_alpha(xtalk_gain, *scale); - if (xtalk_indx == XTALK_L_CH_NUM) { - pdata->xtalk.scale_l = *scale; - pdata->xtalk.alpha_l = *alpha; - } else { - pdata->xtalk.scale_r = *scale; - pdata->xtalk.alpha_r = *alpha; - } - - return; - -err_data: - *scale = MAX_XTALK_SCALE; - *alpha = MAX_XTALK_ALPHA; -} - static int wcd939x_config_xtalk(struct snd_soc_component *component, int event, int xtalk_indx) { - u8 scale = MAX_XTALK_SCALE, alpha = MAX_XTALK_ALPHA; u16 xtalk_sec0 = 0, xtalk_sec1 = 0, xtalk_sec2 = 0, xtalk_sec3 = 0; struct wcd939x_priv *wcd939x = NULL; - + struct wcd939x_pdata *pdata = NULL; if (!component) { pr_err_ratelimited("%s: Invalid params, NULL component\n", __func__); return -EINVAL; @@ -1221,6 +1046,8 @@ static int wcd939x_config_xtalk(struct snd_soc_component *component, if (!wcd939x->xtalk_enabled[xtalk_indx]) return 0; + pdata = dev_get_platdata(wcd939x->dev); + dev_dbg(component->dev, "%s xtalk_indx = %d event = %d\n", __func__, xtalk_indx, event); @@ -1232,10 +1059,25 @@ static int wcd939x_config_xtalk(struct snd_soc_component *component, xtalk_sec2 = WCD939X_HPHL_RX_PATH_SEC2 + (xtalk_indx * WCD939X_XTALK_OFFSET); xtalk_sec3 = WCD939X_HPHL_RX_PATH_SEC3 + (xtalk_indx * WCD939X_XTALK_OFFSET); - /* Determine scale and alpha */ - get_xtalk_scale_and_alpha(component, xtalk_indx, &scale, &alpha); - snd_soc_component_update_bits(component, xtalk_sec1, 0xFF, alpha); - snd_soc_component_update_bits(component, xtalk_sec0, 0x1F, scale); + /* Write scale and alpha based on channel */ + if (xtalk_indx == XTALK_L_CH_NUM) { + snd_soc_component_update_bits(component, xtalk_sec1, 0xFF, + pdata->usbcss_hs.alpha_l); + snd_soc_component_update_bits(component, xtalk_sec0, 0x1F, + pdata->usbcss_hs.scale_l); + } else if (xtalk_indx == XTALK_R_CH_NUM) { + snd_soc_component_update_bits(component, xtalk_sec1, 0xFF, + pdata->usbcss_hs.alpha_r); + snd_soc_component_update_bits(component, xtalk_sec0, 0x1F, + pdata->usbcss_hs.scale_r); + } else { + snd_soc_component_update_bits(component, xtalk_sec1, 0xFF, MIN_XTALK_ALPHA); + snd_soc_component_update_bits(component, xtalk_sec0, 0x1F, MAX_XTALK_SCALE); + } + + dev_dbg(component->dev, "%s Scale = 0x%x, Alpha = 0x%x\n", __func__, + snd_soc_component_read(component, xtalk_sec0), + snd_soc_component_read(component, xtalk_sec1)); snd_soc_component_update_bits(component, xtalk_sec3, 0xFF, 0x4F); snd_soc_component_update_bits(component, xtalk_sec2, 0x1F, 0x11); @@ -1252,7 +1094,6 @@ static int wcd939x_config_xtalk(struct snd_soc_component *component, 0x00, 0x00); break; } - return 0; } @@ -4908,26 +4749,37 @@ static void wcd939x_dt_parse_micbias_info(struct device *dev, } } -static void init_xtalk_params(struct wcd939x_xtalk_params *xtalk) +static void init_usbcss_hs_params(struct wcd939x_usbcss_hs_params *usbcss_hs) { - xtalk->r_gnd_int_fet_mohms = 200; - xtalk->r_gnd_par_route1_mohms = 50; - xtalk->r_gnd_par_route2_mohms = 50; - xtalk->r_gnd_ext_fet_mohms = 650; - xtalk->r_conn_par_load_neg_mohms = 125; - xtalk->r_aud_int_fet_l_mohms = 200; - xtalk->r_aud_int_fet_r_mohms = 200; - xtalk->r_aud_ext_fet_l_mohms = 650; - xtalk->r_aud_ext_fet_r_mohms = 650; - xtalk->r_conn_par_load_pos_l_mohms = 7550; - xtalk->r_conn_par_load_pos_r_mohms = 7550; - xtalk->zl = 0; - xtalk->zr = 0; - xtalk->scale_l = MAX_XTALK_SCALE; - xtalk->alpha_l = MAX_XTALK_ALPHA; - xtalk->scale_r = MAX_XTALK_SCALE; - xtalk->alpha_r = MAX_XTALK_ALPHA; - xtalk->xtalk_config = XTALK_ANALOG; + usbcss_hs->r_gnd_sbu1_int_fet_mohms = 145; + usbcss_hs->r_gnd_sbu2_int_fet_mohms = 185; + usbcss_hs->r_gnd_ext_fet_customer_mohms = 0; + usbcss_hs->r_gnd_ext_fet_mohms = 0; /* to be computed during MBHC zdet */ + usbcss_hs->r_gnd_par_route1_mohms = 5; + usbcss_hs->r_gnd_par_route2_mohms = 330; + usbcss_hs->r_gnd_par_tot_mohms = 0; + usbcss_hs->r_gnd_sbu1_res_tot_mohms = 0; + usbcss_hs->r_gnd_sbu2_res_tot_mohms = 0; + usbcss_hs->r_conn_par_load_pos_mohms = 7550; + usbcss_hs->r_aud_int_fet_l_mohms = 303; + usbcss_hs->r_aud_int_fet_r_mohms = 275; + usbcss_hs->r_aud_ext_fet_l_mohms = 0; /* to be computed during MBHC zdet */ + usbcss_hs->r_aud_ext_fet_r_mohms = 0; /* to be computed during MBHC zdet */ + usbcss_hs->r_aud_res_tot_l_mohms = 0; + usbcss_hs->r_aud_res_tot_r_mohms = 0; + usbcss_hs->r_surge_mohms = 272; + usbcss_hs->r_load_eff_l_mohms = 0; /* to be computed during MBHC zdet */ + usbcss_hs->r_load_eff_r_mohms = 0; /* to be computed during MBHC zdet */ + usbcss_hs->r3 = 1; + usbcss_hs->r4 = 330; + usbcss_hs->r5 = 5; + usbcss_hs->r6 = 1; + usbcss_hs->r7 = 5; + usbcss_hs->scale_l = MAX_XTALK_SCALE; + usbcss_hs->alpha_l = MIN_XTALK_ALPHA; + usbcss_hs->scale_r = MAX_XTALK_SCALE; + usbcss_hs->alpha_r = MIN_XTALK_ALPHA; + usbcss_hs->xtalk_config = XTALK_NONE; } static void parse_xtalk_param(struct device *dev, u32 default_val, u32 *prop_val_p, @@ -4937,7 +4789,7 @@ static void parse_xtalk_param(struct device *dev, u32 default_val, u32 *prop_val if (of_find_property(dev->of_node, prop, NULL)) { rc = wcd939x_read_of_property_u32(dev, prop, prop_val_p); - if ((!rc) && (*prop_val_p <= MAX_IMPEDANCE_MOHMS) && (*prop_val_p > 0)) + if ((!rc) && (*prop_val_p <= MAX_USBCSS_HS_IMPEDANCE_MOHMS) && (*prop_val_p > 0)) return; *prop_val_p = default_val; dev_dbg(dev, "%s: %s OOB. Default value of %d will be used.\n", __func__, prop, @@ -4949,86 +4801,79 @@ static void parse_xtalk_param(struct device *dev, u32 default_val, u32 *prop_val } } -static void wcd939x_dt_parse_xtalk_info(struct device *dev, struct wcd939x_xtalk_params *xtalk) +static void wcd939x_dt_parse_usbcss_hs_info(struct device *dev, + struct wcd939x_usbcss_hs_params *usbcss_hs) { u32 prop_val = 0; int rc = 0; - init_xtalk_params(xtalk); + /* Default values for parameters */ + init_usbcss_hs_params(usbcss_hs); /* xtalk_config: Determine type of crosstalk: none (0), digital (1), or analog (2) */ - if (of_find_property(dev->of_node, "qcom,xtalk-config", NULL)) { - rc = wcd939x_read_of_property_u32(dev, "qcom,xtalk-config", &prop_val); + if (of_find_property(dev->of_node, "qcom,usbcss-hs-xtalk-config", NULL)) { + rc = wcd939x_read_of_property_u32(dev, "qcom,usbcss-hs-xtalk-config", &prop_val); if ((!rc) && (prop_val == XTALK_NONE || prop_val == XTALK_DIGITAL || prop_val == XTALK_ANALOG)) { - xtalk->xtalk_config = (enum xtalk_mode) prop_val; + usbcss_hs->xtalk_config = (enum xtalk_mode) prop_val; } else - dev_dbg(dev, "%s: qcom,xtalk-config OOB. Default value of %s used.\n", - __func__, "XTALK_NONE"); + dev_dbg(dev, "%s: %s OOB. Default value of %s used.\n", + __func__, "qcom,usbcss-hs-xtalk-config", "XTALK_NONE"); } else - dev_dbg(dev, - "%s: qcom,xtalk-config property not found. Default value of %s used.\n", - __func__, "XTALK_NONE"); - if (xtalk->xtalk_config == XTALK_NONE) - goto post_get_params; + dev_dbg(dev, "%s: %s property not found. Default value of %s used.\n", + __func__, "qcom,usbcss-hs-xtalk-config", "XTALK_NONE"); - /* r_gnd_int_fet_mohms */ - parse_xtalk_param(dev, xtalk->r_gnd_int_fet_mohms, &prop_val, - "qcom,xtalk-r-gnd-int-fet-mohms"); - xtalk->r_gnd_int_fet_mohms = prop_val; - /* r_gnd_par_route1_mohms */ - parse_xtalk_param(dev, xtalk->r_gnd_par_route1_mohms, &prop_val, - "qcom,xtalk-r-gnd-par-route1-mohms"); - xtalk->r_gnd_par_route1_mohms = prop_val; - /* r_gnd_par_route2_mohms */ - parse_xtalk_param(dev, xtalk->r_gnd_par_route2_mohms, &prop_val, - "qcom,xtalk-r-gnd-par-route2-mohms"); - xtalk->r_gnd_par_route2_mohms = prop_val; - /* r_gnd_ext_fet_mohms */ - parse_xtalk_param(dev, xtalk->r_gnd_ext_fet_mohms, &prop_val, - "qcom,xtalk-r-gnd-ext-fet-mohms"); - xtalk->r_gnd_ext_fet_mohms = prop_val; - /* r_conn_par_load_neg_mohms */ - parse_xtalk_param(dev, xtalk->r_conn_par_load_neg_mohms, &prop_val, - "qcom,xtalk-r-conn-par-load-neg-mohms"); - xtalk->r_conn_par_load_neg_mohms = prop_val; - /* r_aud_int_fet_l_mohms */ - parse_xtalk_param(dev, xtalk->r_aud_int_fet_l_mohms, &prop_val, - "qcom,xtalk-r-aud-int-fet-l-mohms"); - xtalk->r_aud_int_fet_l_mohms = prop_val; - /* r_aud_int_fet_r_mohms */ - parse_xtalk_param(dev, xtalk->r_aud_int_fet_r_mohms, &prop_val, - "qcom,xtalk-r-aud-int-fet-r-mohms"); - xtalk->r_aud_int_fet_r_mohms = prop_val; - /* r_aud_ext_fet_l_mohms */ - parse_xtalk_param(dev, xtalk->r_aud_ext_fet_l_mohms, &prop_val, - "qcom,xtalk-r-aud-ext-fet-l-mohms"); - xtalk->r_aud_ext_fet_l_mohms = prop_val; - /* r_aud_ext_fet_r_mohms */ - parse_xtalk_param(dev, xtalk->r_aud_ext_fet_r_mohms, &prop_val, - "qcom,xtalk-r-aud-ext-fet-r-mohms"); - xtalk->r_aud_ext_fet_r_mohms = prop_val; - /* r_conn_par_load_pos_l_mohms */ - parse_xtalk_param(dev, xtalk->r_conn_par_load_pos_l_mohms, &prop_val, - "qcom,xtalk-r-conn-par-load-pos-l-mohms"); - xtalk->r_conn_par_load_pos_l_mohms = prop_val; - /* r_conn_par_load_pos_r_mohms */ - parse_xtalk_param(dev, xtalk->r_conn_par_load_pos_r_mohms, &prop_val, - "qcom,xtalk-r-conn-par-load-pos-r-mohms"); - xtalk->r_conn_par_load_pos_r_mohms = prop_val; + /* r_gnd_ext_fet_customer_mohms */ + parse_xtalk_param(dev, usbcss_hs->r_gnd_ext_fet_customer_mohms, &prop_val, + "qcom,usbcss-hs-rdson"); + usbcss_hs->r_gnd_ext_fet_customer_mohms = prop_val; + /* r_conn_par_load_pos_mohm */ + parse_xtalk_param(dev, usbcss_hs->r_conn_par_load_pos_mohms, &prop_val, + "qcom,usbcss-hs-r2"); + usbcss_hs->r_conn_par_load_pos_mohms = prop_val; + /* r3 */ + parse_xtalk_param(dev, usbcss_hs->r3, &prop_val, + "qcom,usbcss-hs-r3"); + usbcss_hs->r3 = prop_val; + /* r4 */ + parse_xtalk_param(dev, usbcss_hs->r4, &prop_val, + "qcom,usbcss-hs-r4"); + usbcss_hs->r4 = prop_val; + /* r_gnd_par_route1_mohms and r_gnd_par_route2_mohms */ + if (usbcss_hs->xtalk_config == XTALK_ANALOG) { + parse_xtalk_param(dev, usbcss_hs->r5, &prop_val, + "qcom,usbcss-hs-r5"); + usbcss_hs->r5 = prop_val; + usbcss_hs->r_gnd_par_route1_mohms = usbcss_hs->r5 + usbcss_hs->r4; + usbcss_hs->r_gnd_par_route2_mohms = 125; + } else if (usbcss_hs->xtalk_config == XTALK_DIGITAL) { + parse_xtalk_param(dev, usbcss_hs->r6, &prop_val, + "qcom,usbcss-hs-r6"); + usbcss_hs->r6 = prop_val; + usbcss_hs->r_gnd_par_route2_mohms = usbcss_hs->r6 + usbcss_hs->r4; + parse_xtalk_param(dev, usbcss_hs->r_gnd_par_route1_mohms, &prop_val, + "qcom,usbcss-hs-r7"); + usbcss_hs->r7 = prop_val; + usbcss_hs->r_gnd_par_route1_mohms = prop_val; + } -post_get_params: - xtalk->r_gnd_res_tot_mohms = get_r_gnd_res_tot_mohms(xtalk->r_gnd_int_fet_mohms, - xtalk->r_gnd_par_route1_mohms, - xtalk->r_gnd_par_route2_mohms, - xtalk->r_gnd_ext_fet_mohms, - xtalk->r_conn_par_load_neg_mohms); - xtalk->r_aud_res_tot_l_mohms = get_r_aud_res_tot_mohms(xtalk->r_aud_int_fet_l_mohms, - xtalk->r_aud_ext_fet_l_mohms, - xtalk->r_conn_par_load_pos_l_mohms); - xtalk->r_aud_res_tot_r_mohms = get_r_aud_res_tot_mohms(xtalk->r_aud_int_fet_r_mohms, - xtalk->r_aud_ext_fet_r_mohms, - xtalk->r_conn_par_load_pos_r_mohms); + /* Compute total resistances */ + usbcss_hs->r_gnd_par_tot_mohms = usbcss_hs->r_gnd_par_route1_mohms + + usbcss_hs->r_gnd_par_route2_mohms; + usbcss_hs->r_gnd_sbu1_res_tot_mohms = get_r_gnd_res_tot_mohms( + usbcss_hs->r_gnd_sbu1_int_fet_mohms, + usbcss_hs->r_gnd_ext_fet_mohms, + usbcss_hs->r_gnd_par_tot_mohms); + usbcss_hs->r_gnd_sbu2_res_tot_mohms = get_r_gnd_res_tot_mohms( + usbcss_hs->r_gnd_sbu2_int_fet_mohms, + usbcss_hs->r_gnd_ext_fet_mohms, + usbcss_hs->r_gnd_par_tot_mohms); + usbcss_hs->r_aud_res_tot_l_mohms = get_r_aud_res_tot_mohms( + usbcss_hs->r_aud_int_fet_l_mohms, + usbcss_hs->r_aud_ext_fet_l_mohms); + usbcss_hs->r_aud_res_tot_r_mohms = get_r_aud_res_tot_mohms( + usbcss_hs->r_aud_int_fet_r_mohms, + usbcss_hs->r_aud_ext_fet_r_mohms); } static int wcd939x_reset_low(struct device *dev) @@ -5093,7 +4938,7 @@ struct wcd939x_pdata *wcd939x_populate_dt_data(struct device *dev) pdata->tx_slave = of_parse_phandle(dev->of_node, "qcom,tx-slave", 0); wcd939x_dt_parse_micbias_info(dev, &pdata->micbias); - wcd939x_dt_parse_xtalk_info(dev, &pdata->xtalk); + wcd939x_dt_parse_usbcss_hs_info(dev, &pdata->usbcss_hs); return pdata; }