Loading sound/soc/codecs/Makefile +1 −1 Original line number Diff line number Diff line Loading @@ -80,7 +80,7 @@ snd-soc-msm8x16-wcd-objs += wcd_cpe_services.o wcd_cpe_core.o snd-soc-wcd-cpe-objs := wcd_cpe_services.o wcd_cpe_core.o snd-soc-msm8909-wcd-objs := msm8x16-wcd.o msm8x16-wcd-tables.o wcd-mbhc-v2.o snd-soc-msm8909-wcd-objs += wcd9xxx-common.o wcd9xxx-resmgr.o wcd9xxx-mbhc.o snd-soc-msm8909-wcd-objs += wcd9306.o wcd9306-tables.o snd-soc-msm8909-wcd-objs += wcd9306.o wcd9306-tables.o wcdcal-hwdep.o snd-soc-wl1273-objs := wl1273.o snd-soc-wm-adsp-objs := wm_adsp.o snd-soc-wm0010-objs := wm0010.o Loading sound/soc/codecs/msm8x16-wcd.c +48 −1 Original line number Diff line number Diff line Loading @@ -215,12 +215,34 @@ static void msm8x16_wcd_compute_impedance(s16 l, s16 r, uint32_t *zl, *zr = rr; } static struct firmware_cal *msm8x16_wcd_get_hwdep_fw_cal( struct snd_soc_codec *codec, enum wcd_cal_type type) { struct msm8x16_wcd_priv *msm8x16_wcd; struct firmware_cal *hwdep_cal; if (!codec) { pr_err("%s: NULL codec pointer\n", __func__); return NULL; } msm8x16_wcd = snd_soc_codec_get_drvdata(codec); hwdep_cal = wcdcal_get_fw_cal(msm8x16_wcd->fw_data, type); if (!hwdep_cal) { dev_err(codec->dev, "%s: cal not sent by %d\n", __func__, type); return NULL; } return hwdep_cal; } static const struct wcd_mbhc_cb mbhc_cb = { .enable_mb_source = msm8x16_wcd_enable_ext_mb_source, .trim_btn_reg = msm8x16_trim_btn_reg, .compute_impedance = msm8x16_wcd_compute_impedance, .set_micbias_value = msm8x16_wcd_set_micb_v, .set_auto_zeroing = msm8x16_wcd_set_auto_zeroing, .get_hwdep_fw_cal = msm8x16_wcd_get_hwdep_fw_cal, }; static const uint32_t wcd_imped_val[] = {4, 8, 12, 16, Loading Loading @@ -3776,7 +3798,7 @@ static int msm8x16_wcd_codec_probe(struct snd_soc_codec *codec) { struct msm8x16_wcd_priv *msm8x16_wcd_priv; struct msm8x16_wcd *msm8x16_wcd; int i; int i, ret; dev_dbg(codec->dev, "%s()\n", __func__); Loading Loading @@ -3841,6 +3863,28 @@ static int msm8x16_wcd_codec_probe(struct snd_soc_codec *codec) BLOCKING_INIT_NOTIFIER_HEAD(&msm8x16_wcd_priv->notifier); msm8x16_wcd_priv->fw_data = kzalloc(sizeof(*(msm8x16_wcd_priv->fw_data)) , GFP_KERNEL); if (!msm8x16_wcd_priv->fw_data) { dev_err(codec->dev, "Failed to allocate fw_data\n"); iounmap(msm8x16_wcd->dig_base); kfree(msm8x16_wcd_priv); return -ENOMEM; } set_bit(WCD9XXX_ANC_CAL, msm8x16_wcd_priv->fw_data->cal_bit); set_bit(WCD9XXX_MAD_CAL, msm8x16_wcd_priv->fw_data->cal_bit); set_bit(WCD9XXX_MBHC_CAL, msm8x16_wcd_priv->fw_data->cal_bit); ret = wcd_cal_create_hwdep(msm8x16_wcd_priv->fw_data, WCD9XXX_CODEC_HWDEP_NODE, codec); if (ret < 0) { dev_err(codec->dev, "%s hwdep failed %d\n", __func__, ret); iounmap(msm8x16_wcd->dig_base); kfree(msm8x16_wcd_priv->fw_data); kfree(msm8x16_wcd_priv); return ret; } wcd_mbhc_init(&msm8x16_wcd_priv->mbhc, codec, &mbhc_cb, &intr_ids, true); Loading @@ -3859,6 +3903,7 @@ static int msm8x16_wcd_codec_probe(struct snd_soc_codec *codec) dev_err(codec->dev, "Failed to register modem state notifier\n" ); iounmap(msm8x16_wcd->dig_base); kfree(msm8x16_wcd_priv->fw_data); kfree(msm8x16_wcd_priv); registered_codec = NULL; return -ENOMEM; Loading @@ -3877,6 +3922,8 @@ static int msm8x16_wcd_codec_remove(struct snd_soc_codec *codec) msm8x16_wcd_priv->on_demand_list[ON_DEMAND_MICBIAS].supply = NULL; atomic_set(&msm8x16_wcd_priv->on_demand_list[ON_DEMAND_MICBIAS].ref, 0); iounmap(msm8x16_wcd->dig_base); kfree(msm8x16_wcd_priv->fw_data); kfree(msm8x16_wcd_priv); return 0; } Loading sound/soc/codecs/msm8x16-wcd.h +3 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ #include <sound/q6afe-v2.h> #include <linux/mfd/wcd9xxx/pdata.h> #include "wcd-mbhc-v2.h" #include "wcdcal-hwdep.h" #define MSM8X16_WCD_NUM_REGISTERS 0x6FF #define MSM8X16_WCD_MAX_REGISTER (MSM8X16_WCD_NUM_REGISTERS-1) Loading Loading @@ -231,6 +232,8 @@ struct msm8x16_wcd_priv { struct regulator *spkdrv_reg; /* mbhc module */ struct wcd_mbhc mbhc; /* cal info for codec */ struct fw_info *fw_data; struct blocking_notifier_head notifier; }; Loading sound/soc/codecs/wcd-mbhc-v2.c +135 −3 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ #include <linux/kernel.h> #include <linux/input.h> #include <linux/mfd/wcd9xxx/pdata.h> #include <linux/firmware.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> Loading @@ -35,6 +36,7 @@ #include "msm8x16_wcd_registers.h" #include "msm8916-wcd-irq.h" #include "msm8x16-wcd.h" #include "wcdcal-hwdep.h" #define WCD_MBHC_JACK_MASK (SND_JACK_HEADSET | SND_JACK_OC_HPHL | \ SND_JACK_OC_HPHR | SND_JACK_LINEOUT | \ Loading @@ -49,6 +51,8 @@ #define GND_MIC_SWAP_THRESHOLD 4 #define WCD_FAKE_REMOVAL_MIN_PERIOD_MS 100 #define HS_VREF_MIN_VAL 1400 #define FW_READ_ATTEMPTS 15 #define FW_READ_TIMEOUT 4000000 static int det_extn_cable_en; module_param(det_extn_cable_en, int, Loading Loading @@ -164,7 +168,7 @@ static void wcd_program_hs_vref(const struct wcd_mbhc *mbhc) struct snd_soc_card *card = codec->card; u32 reg_val; plug_type_cfg = WCD_MBHC_CAL_PLUG_DET_PTR(mbhc->mbhc_cfg->calibration); plug_type_cfg = WCD_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration); reg_val = ((plug_type_cfg->v_hs_max - HS_VREF_MIN_VAL) / 100); dev_dbg(card->dev, "%s: reg_val = %x\n", __func__, reg_val); Loading @@ -190,7 +194,8 @@ static void wcd_program_btn_threshold(const struct wcd_mbhc *mbhc, bool micbias) btn_det = WCD_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration); if (micbias) btn_voltage = btn_det->_v_btn_high; btn_voltage = ((void *)&btn_det->_v_btn_low) + (sizeof(btn_det->_v_btn_low[0]) * btn_det->num_btn); else btn_voltage = btn_det->_v_btn_low; for (i = 0; i < btn_det->num_btn; i++) { Loading Loading @@ -1599,6 +1604,30 @@ static void wcd_btn_lpress_fn(struct work_struct *work) wcd9xxx_spmi_unlock_sleep(); } static bool wcd_mbhc_fw_validate(const void *data, size_t size) { u32 cfg_offset; struct wcd_mbhc_btn_detect_cfg *btn_cfg; struct firmware_cal fw; fw.data = (void *)data; fw.size = size; if (fw.size < WCD_MBHC_CAL_MIN_SIZE) return false; /* * Previous check guarantees that there is enough fw data up * to num_btn */ btn_cfg = WCD_MBHC_CAL_BTN_DET_PTR(fw.data); cfg_offset = (u32) ((void *) btn_cfg - (void *) fw.data); if (fw.size < (cfg_offset + WCD_MBHC_CAL_BTN_SZ(btn_cfg))) return false; return true; } irqreturn_t wcd_mbhc_btn_press_handler(int irq, void *data) { struct wcd_mbhc *mbhc = data; Loading Loading @@ -1818,6 +1847,87 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc) return ret; } static void wcd_mbhc_fw_read(struct work_struct *work) { struct delayed_work *dwork; struct wcd_mbhc *mbhc; struct snd_soc_codec *codec; const struct firmware *fw; struct firmware_cal *fw_data = NULL; int ret = -1, retry = 0; bool use_default_cal = false; dwork = to_delayed_work(work); mbhc = container_of(dwork, struct wcd_mbhc, mbhc_firmware_dwork); codec = mbhc->codec; while (retry < FW_READ_ATTEMPTS) { retry++; pr_debug("%s:Attempt %d to request MBHC firmware\n", __func__, retry); if (mbhc->mbhc_cb->get_hwdep_fw_cal) fw_data = mbhc->mbhc_cb->get_hwdep_fw_cal(codec, WCD9XXX_MBHC_CAL); if (!fw_data) ret = request_firmware(&fw, "wcd9320/wcd9320_mbhc.bin", codec->dev); /* * if request_firmware and hwdep cal both fail then * sleep for 4sec for the userspace to send data to kernel * retry for few times before bailing out */ if ((ret != 0) && !fw_data) { usleep_range(FW_READ_TIMEOUT, FW_READ_TIMEOUT + WCD9XXX_USLEEP_RANGE_MARGIN_US); } else { pr_debug("%s: MBHC Firmware read succesful\n", __func__); break; } } if (!fw_data) pr_debug("%s: using request_firmware\n", __func__); else pr_debug("%s: using hwdep cal\n", __func__); if (ret != 0 && !fw_data) { pr_err("%s: Cannot load MBHC firmware use default cal\n", __func__); use_default_cal = true; } if (!use_default_cal) { const void *data; size_t size; if (fw_data) { data = fw_data->data; size = fw_data->size; } else { data = fw->data; size = fw->size; } if (wcd_mbhc_fw_validate(data, size) == false) { pr_err("%s: Invalid MBHC cal data size use default cal\n", __func__); if (!fw_data) release_firmware(fw); } else { if (fw_data) { mbhc->mbhc_cfg->calibration = (void *)fw_data->data; mbhc->mbhc_cal = fw_data; } else { mbhc->mbhc_cfg->calibration = (void *)fw->data; mbhc->mbhc_fw = fw; } } } (void) wcd_mbhc_initialise(mbhc); } int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg) { Loading @@ -1826,7 +1936,19 @@ int wcd_mbhc_start(struct wcd_mbhc *mbhc, pr_debug("%s: enter\n", __func__); /* update the mbhc config */ mbhc->mbhc_cfg = mbhc_cfg; if (!mbhc->mbhc_cfg->read_fw_bin || (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_fw) || (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_cal)) { rc = wcd_mbhc_initialise(mbhc); } else { if (!mbhc->mbhc_fw || !mbhc->mbhc_cal) schedule_delayed_work(&mbhc->mbhc_firmware_dwork, usecs_to_jiffies(FW_READ_TIMEOUT)); else pr_err("%s: Skipping to read mbhc fw, 0x%p %p\n", __func__, mbhc->mbhc_fw, mbhc->mbhc_cal); } pr_debug("%s: leave %d\n", __func__, rc); return rc; } Loading @@ -1837,6 +1959,14 @@ void wcd_mbhc_stop(struct wcd_mbhc *mbhc) pr_debug("%s: enter\n", __func__); wcd9xxx_spmi_disable_irq(mbhc->intr_ids->hph_left_ocp); wcd9xxx_spmi_disable_irq(mbhc->intr_ids->hph_right_ocp); if (mbhc->mbhc_fw || mbhc->mbhc_cal) { cancel_delayed_work_sync(&mbhc->mbhc_firmware_dwork); if (!mbhc->mbhc_cal) release_firmware(mbhc->mbhc_fw); mbhc->mbhc_fw = NULL; mbhc->mbhc_cal = NULL; } pr_debug("%s: leave\n", __func__); } EXPORT_SYMBOL(wcd_mbhc_stop); Loading Loading @@ -1926,6 +2056,8 @@ int wcd_mbhc_init(struct wcd_mbhc *mbhc, struct snd_soc_codec *codec, return ret; } INIT_DELAYED_WORK(&mbhc->mbhc_firmware_dwork, wcd_mbhc_fw_read); INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd_btn_lpress_fn); } Loading sound/soc/codecs/wcd-mbhc-v2.h +111 −7 Original line number Diff line number Diff line Loading @@ -13,6 +13,7 @@ #define __WCD_MBHC_V2_H__ #include <linux/wait.h> #include "wcdcal-hwdep.h" #define TOMBAK_MBHC_NC 0 #define TOMBAK_MBHC_NO 1 Loading Loading @@ -41,6 +42,33 @@ enum wcd_mbhc_event_state { WCD_MBHC_EVENT_PA_HPHL, WCD_MBHC_EVENT_PA_HPHR, }; struct wcd_mbhc_general_cfg { u8 t_ldoh; u8 t_bg_fast_settle; u8 t_shutdown_plug_rem; u8 mbhc_nsa; u8 mbhc_navg; u8 v_micbias_l; u8 v_micbias; u8 mbhc_reserved; u16 settle_wait; u16 t_micbias_rampup; u16 t_micbias_rampdown; u16 t_supply_bringup; } __packed; struct wcd_mbhc_plug_detect_cfg { u32 mic_current; u32 hph_current; u16 t_mic_pid; u16 t_ins_complete; u16 t_ins_retry; u16 v_removal_delta; u8 micbias_slow_ramp; u8 reserved0; u8 reserved1; u8 reserved2; } __packed; struct wcd_mbhc_plug_type_cfg { u8 av_detect; Loading @@ -56,9 +84,39 @@ struct wcd_mbhc_plug_type_cfg { } __packed; struct wcd_mbhc_btn_detect_cfg { s8 c[8]; u8 nc; u8 n_meas; u8 mbhc_nsc; u8 n_btn_meas; u8 n_btn_con; u8 num_btn; s16 _v_btn_low[WCD_MBHC_DEF_BUTTONS]; s16 _v_btn_high[WCD_MBHC_DEF_BUTTONS]; u8 reserved0; u8 reserved1; u16 t_poll; u16 t_bounce_wait; u16 t_rel_timeout; s16 v_btn_press_delta_sta; s16 v_btn_press_delta_cic; u16 t_btn0_timeout; s16 _v_btn_low[0]; /* v_btn_low[num_btn] */ s16 _v_btn_high[0]; /* v_btn_high[num_btn] */ u8 _n_ready[2]; u8 _n_cic[2]; u8 _gain[2]; } __packed; struct wcd_mbhc_imped_detect_cfg { u8 _hs_imped_detect; u8 _n_rload; u8 _hph_keep_on; u8 _repeat_rload_calc; u16 _t_dac_ramp_time; u16 _rhph_high; u16 _rhph_low; u16 _rload[0]; /* rload[n_rload] */ u16 _alpha[0]; /* alpha[n_rload] */ u16 _beta[3]; } __packed; struct wcd_mbhc_config { Loading Loading @@ -86,6 +144,8 @@ struct wcd_mbhc_cb { void (*compute_impedance) (s16 , s16 , uint32_t *, uint32_t *, bool); void (*set_micbias_value) (struct snd_soc_codec *); void (*set_auto_zeroing) (struct snd_soc_codec *, bool); struct firmware_cal * (*get_hwdep_fw_cal)(struct snd_soc_codec *, enum wcd_cal_type); }; struct wcd_mbhc { Loading Loading @@ -113,6 +173,10 @@ struct wcd_mbhc { bool is_hs_recording; struct snd_soc_codec *codec; /* Work to perform MBHC Firmware Read */ struct delayed_work mbhc_firmware_dwork; const struct firmware *mbhc_fw; struct firmware_cal *mbhc_cal; /* track PA/DAC state to sync with userspace */ unsigned long hph_pa_dac_state; Loading @@ -134,17 +198,57 @@ struct wcd_mbhc { struct work_struct correct_plug_swch; struct notifier_block nblock; }; #define WCD_MBHC_CAL_SIZE ( \ #define WCD_MBHC_CAL_SIZE(buttons, rload) ( \ sizeof(struct wcd_mbhc_general_cfg) + \ sizeof(struct wcd_mbhc_plug_detect_cfg) + \ ((sizeof(s16) + sizeof(s16)) * buttons) + \ sizeof(struct wcd_mbhc_plug_type_cfg) + \ sizeof(struct wcd_mbhc_btn_detect_cfg) \ sizeof(struct wcd_mbhc_btn_detect_cfg) + \ sizeof(struct wcd_mbhc_imped_detect_cfg) + \ ((sizeof(u16) + sizeof(u16)) * rload) \ ) #define WCD_MBHC_CAL_GENERAL_PTR(cali) ( \ (struct wcd_mbhc_general_cfg *) cali) #define WCD_MBHC_CAL_PLUG_DET_PTR(cali) ( \ (struct wcd_mbhc_plug_type_cfg *) cali) (struct wcd_mbhc_plug_detect_cfg *) \ &(WCD_MBHC_CAL_GENERAL_PTR(cali)[1])) #define WCD_MBHC_CAL_PLUG_TYPE_PTR(cali) ( \ (struct wcd_mbhc_plug_type_cfg *) \ &(WCD_MBHC_CAL_PLUG_DET_PTR(cali)[1])) #define WCD_MBHC_CAL_BTN_DET_PTR(cali) ( \ (struct wcd_mbhc_btn_detect_cfg *) \ &(WCD_MBHC_CAL_PLUG_DET_PTR(cali)[1])) &(WCD_MBHC_CAL_PLUG_TYPE_PTR(cali)[1])) #define WCD_MBHC_CAL_IMPED_DET_PTR(cali) ( \ (struct wcd_mbhc_imped_detect_cfg *) \ (((void *)&WCD_MBHC_CAL_BTN_DET_PTR(cali)[1]) + \ (WCD_MBHC_CAL_BTN_DET_PTR(cali)->num_btn * \ (sizeof(WCD_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_low[0]) + \ sizeof(WCD_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_high[0])))) \ ) #define WCD_MBHC_CAL_MIN_SIZE ( \ sizeof(struct wcd_mbhc_general_cfg) + \ sizeof(struct wcd_mbhc_plug_detect_cfg) + \ sizeof(struct wcd_mbhc_plug_type_cfg) + \ sizeof(struct wcd_mbhc_btn_detect_cfg) + \ sizeof(struct wcd_mbhc_imped_detect_cfg) + \ (sizeof(u16)*2) \ ) #define WCD_MBHC_CAL_BTN_SZ(cfg_ptr) ( \ sizeof(struct wcd_mbhc_btn_detect_cfg) + \ (cfg_ptr->num_btn * (sizeof(cfg_ptr->_v_btn_low[0]) + \ sizeof(cfg_ptr->_v_btn_high[0])))) #define WCD_MBHC_CAL_IMPED_MIN_SZ ( \ sizeof(struct wcd_mbhc_imped_detect_cfg) + sizeof(u16) * 2) #define WCD_MBHC_CAL_IMPED_SZ(cfg_ptr) ( \ sizeof(struct wcd_mbhc_imped_detect_cfg) + \ (cfg_ptr->_n_rload * \ (sizeof(cfg_ptr->_rload[0]) + sizeof(cfg_ptr->_alpha[0])))) int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg); Loading Loading
sound/soc/codecs/Makefile +1 −1 Original line number Diff line number Diff line Loading @@ -80,7 +80,7 @@ snd-soc-msm8x16-wcd-objs += wcd_cpe_services.o wcd_cpe_core.o snd-soc-wcd-cpe-objs := wcd_cpe_services.o wcd_cpe_core.o snd-soc-msm8909-wcd-objs := msm8x16-wcd.o msm8x16-wcd-tables.o wcd-mbhc-v2.o snd-soc-msm8909-wcd-objs += wcd9xxx-common.o wcd9xxx-resmgr.o wcd9xxx-mbhc.o snd-soc-msm8909-wcd-objs += wcd9306.o wcd9306-tables.o snd-soc-msm8909-wcd-objs += wcd9306.o wcd9306-tables.o wcdcal-hwdep.o snd-soc-wl1273-objs := wl1273.o snd-soc-wm-adsp-objs := wm_adsp.o snd-soc-wm0010-objs := wm0010.o Loading
sound/soc/codecs/msm8x16-wcd.c +48 −1 Original line number Diff line number Diff line Loading @@ -215,12 +215,34 @@ static void msm8x16_wcd_compute_impedance(s16 l, s16 r, uint32_t *zl, *zr = rr; } static struct firmware_cal *msm8x16_wcd_get_hwdep_fw_cal( struct snd_soc_codec *codec, enum wcd_cal_type type) { struct msm8x16_wcd_priv *msm8x16_wcd; struct firmware_cal *hwdep_cal; if (!codec) { pr_err("%s: NULL codec pointer\n", __func__); return NULL; } msm8x16_wcd = snd_soc_codec_get_drvdata(codec); hwdep_cal = wcdcal_get_fw_cal(msm8x16_wcd->fw_data, type); if (!hwdep_cal) { dev_err(codec->dev, "%s: cal not sent by %d\n", __func__, type); return NULL; } return hwdep_cal; } static const struct wcd_mbhc_cb mbhc_cb = { .enable_mb_source = msm8x16_wcd_enable_ext_mb_source, .trim_btn_reg = msm8x16_trim_btn_reg, .compute_impedance = msm8x16_wcd_compute_impedance, .set_micbias_value = msm8x16_wcd_set_micb_v, .set_auto_zeroing = msm8x16_wcd_set_auto_zeroing, .get_hwdep_fw_cal = msm8x16_wcd_get_hwdep_fw_cal, }; static const uint32_t wcd_imped_val[] = {4, 8, 12, 16, Loading Loading @@ -3776,7 +3798,7 @@ static int msm8x16_wcd_codec_probe(struct snd_soc_codec *codec) { struct msm8x16_wcd_priv *msm8x16_wcd_priv; struct msm8x16_wcd *msm8x16_wcd; int i; int i, ret; dev_dbg(codec->dev, "%s()\n", __func__); Loading Loading @@ -3841,6 +3863,28 @@ static int msm8x16_wcd_codec_probe(struct snd_soc_codec *codec) BLOCKING_INIT_NOTIFIER_HEAD(&msm8x16_wcd_priv->notifier); msm8x16_wcd_priv->fw_data = kzalloc(sizeof(*(msm8x16_wcd_priv->fw_data)) , GFP_KERNEL); if (!msm8x16_wcd_priv->fw_data) { dev_err(codec->dev, "Failed to allocate fw_data\n"); iounmap(msm8x16_wcd->dig_base); kfree(msm8x16_wcd_priv); return -ENOMEM; } set_bit(WCD9XXX_ANC_CAL, msm8x16_wcd_priv->fw_data->cal_bit); set_bit(WCD9XXX_MAD_CAL, msm8x16_wcd_priv->fw_data->cal_bit); set_bit(WCD9XXX_MBHC_CAL, msm8x16_wcd_priv->fw_data->cal_bit); ret = wcd_cal_create_hwdep(msm8x16_wcd_priv->fw_data, WCD9XXX_CODEC_HWDEP_NODE, codec); if (ret < 0) { dev_err(codec->dev, "%s hwdep failed %d\n", __func__, ret); iounmap(msm8x16_wcd->dig_base); kfree(msm8x16_wcd_priv->fw_data); kfree(msm8x16_wcd_priv); return ret; } wcd_mbhc_init(&msm8x16_wcd_priv->mbhc, codec, &mbhc_cb, &intr_ids, true); Loading @@ -3859,6 +3903,7 @@ static int msm8x16_wcd_codec_probe(struct snd_soc_codec *codec) dev_err(codec->dev, "Failed to register modem state notifier\n" ); iounmap(msm8x16_wcd->dig_base); kfree(msm8x16_wcd_priv->fw_data); kfree(msm8x16_wcd_priv); registered_codec = NULL; return -ENOMEM; Loading @@ -3877,6 +3922,8 @@ static int msm8x16_wcd_codec_remove(struct snd_soc_codec *codec) msm8x16_wcd_priv->on_demand_list[ON_DEMAND_MICBIAS].supply = NULL; atomic_set(&msm8x16_wcd_priv->on_demand_list[ON_DEMAND_MICBIAS].ref, 0); iounmap(msm8x16_wcd->dig_base); kfree(msm8x16_wcd_priv->fw_data); kfree(msm8x16_wcd_priv); return 0; } Loading
sound/soc/codecs/msm8x16-wcd.h +3 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ #include <sound/q6afe-v2.h> #include <linux/mfd/wcd9xxx/pdata.h> #include "wcd-mbhc-v2.h" #include "wcdcal-hwdep.h" #define MSM8X16_WCD_NUM_REGISTERS 0x6FF #define MSM8X16_WCD_MAX_REGISTER (MSM8X16_WCD_NUM_REGISTERS-1) Loading Loading @@ -231,6 +232,8 @@ struct msm8x16_wcd_priv { struct regulator *spkdrv_reg; /* mbhc module */ struct wcd_mbhc mbhc; /* cal info for codec */ struct fw_info *fw_data; struct blocking_notifier_head notifier; }; Loading
sound/soc/codecs/wcd-mbhc-v2.c +135 −3 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ #include <linux/kernel.h> #include <linux/input.h> #include <linux/mfd/wcd9xxx/pdata.h> #include <linux/firmware.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> Loading @@ -35,6 +36,7 @@ #include "msm8x16_wcd_registers.h" #include "msm8916-wcd-irq.h" #include "msm8x16-wcd.h" #include "wcdcal-hwdep.h" #define WCD_MBHC_JACK_MASK (SND_JACK_HEADSET | SND_JACK_OC_HPHL | \ SND_JACK_OC_HPHR | SND_JACK_LINEOUT | \ Loading @@ -49,6 +51,8 @@ #define GND_MIC_SWAP_THRESHOLD 4 #define WCD_FAKE_REMOVAL_MIN_PERIOD_MS 100 #define HS_VREF_MIN_VAL 1400 #define FW_READ_ATTEMPTS 15 #define FW_READ_TIMEOUT 4000000 static int det_extn_cable_en; module_param(det_extn_cable_en, int, Loading Loading @@ -164,7 +168,7 @@ static void wcd_program_hs_vref(const struct wcd_mbhc *mbhc) struct snd_soc_card *card = codec->card; u32 reg_val; plug_type_cfg = WCD_MBHC_CAL_PLUG_DET_PTR(mbhc->mbhc_cfg->calibration); plug_type_cfg = WCD_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration); reg_val = ((plug_type_cfg->v_hs_max - HS_VREF_MIN_VAL) / 100); dev_dbg(card->dev, "%s: reg_val = %x\n", __func__, reg_val); Loading @@ -190,7 +194,8 @@ static void wcd_program_btn_threshold(const struct wcd_mbhc *mbhc, bool micbias) btn_det = WCD_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration); if (micbias) btn_voltage = btn_det->_v_btn_high; btn_voltage = ((void *)&btn_det->_v_btn_low) + (sizeof(btn_det->_v_btn_low[0]) * btn_det->num_btn); else btn_voltage = btn_det->_v_btn_low; for (i = 0; i < btn_det->num_btn; i++) { Loading Loading @@ -1599,6 +1604,30 @@ static void wcd_btn_lpress_fn(struct work_struct *work) wcd9xxx_spmi_unlock_sleep(); } static bool wcd_mbhc_fw_validate(const void *data, size_t size) { u32 cfg_offset; struct wcd_mbhc_btn_detect_cfg *btn_cfg; struct firmware_cal fw; fw.data = (void *)data; fw.size = size; if (fw.size < WCD_MBHC_CAL_MIN_SIZE) return false; /* * Previous check guarantees that there is enough fw data up * to num_btn */ btn_cfg = WCD_MBHC_CAL_BTN_DET_PTR(fw.data); cfg_offset = (u32) ((void *) btn_cfg - (void *) fw.data); if (fw.size < (cfg_offset + WCD_MBHC_CAL_BTN_SZ(btn_cfg))) return false; return true; } irqreturn_t wcd_mbhc_btn_press_handler(int irq, void *data) { struct wcd_mbhc *mbhc = data; Loading Loading @@ -1818,6 +1847,87 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc) return ret; } static void wcd_mbhc_fw_read(struct work_struct *work) { struct delayed_work *dwork; struct wcd_mbhc *mbhc; struct snd_soc_codec *codec; const struct firmware *fw; struct firmware_cal *fw_data = NULL; int ret = -1, retry = 0; bool use_default_cal = false; dwork = to_delayed_work(work); mbhc = container_of(dwork, struct wcd_mbhc, mbhc_firmware_dwork); codec = mbhc->codec; while (retry < FW_READ_ATTEMPTS) { retry++; pr_debug("%s:Attempt %d to request MBHC firmware\n", __func__, retry); if (mbhc->mbhc_cb->get_hwdep_fw_cal) fw_data = mbhc->mbhc_cb->get_hwdep_fw_cal(codec, WCD9XXX_MBHC_CAL); if (!fw_data) ret = request_firmware(&fw, "wcd9320/wcd9320_mbhc.bin", codec->dev); /* * if request_firmware and hwdep cal both fail then * sleep for 4sec for the userspace to send data to kernel * retry for few times before bailing out */ if ((ret != 0) && !fw_data) { usleep_range(FW_READ_TIMEOUT, FW_READ_TIMEOUT + WCD9XXX_USLEEP_RANGE_MARGIN_US); } else { pr_debug("%s: MBHC Firmware read succesful\n", __func__); break; } } if (!fw_data) pr_debug("%s: using request_firmware\n", __func__); else pr_debug("%s: using hwdep cal\n", __func__); if (ret != 0 && !fw_data) { pr_err("%s: Cannot load MBHC firmware use default cal\n", __func__); use_default_cal = true; } if (!use_default_cal) { const void *data; size_t size; if (fw_data) { data = fw_data->data; size = fw_data->size; } else { data = fw->data; size = fw->size; } if (wcd_mbhc_fw_validate(data, size) == false) { pr_err("%s: Invalid MBHC cal data size use default cal\n", __func__); if (!fw_data) release_firmware(fw); } else { if (fw_data) { mbhc->mbhc_cfg->calibration = (void *)fw_data->data; mbhc->mbhc_cal = fw_data; } else { mbhc->mbhc_cfg->calibration = (void *)fw->data; mbhc->mbhc_fw = fw; } } } (void) wcd_mbhc_initialise(mbhc); } int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg) { Loading @@ -1826,7 +1936,19 @@ int wcd_mbhc_start(struct wcd_mbhc *mbhc, pr_debug("%s: enter\n", __func__); /* update the mbhc config */ mbhc->mbhc_cfg = mbhc_cfg; if (!mbhc->mbhc_cfg->read_fw_bin || (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_fw) || (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_cal)) { rc = wcd_mbhc_initialise(mbhc); } else { if (!mbhc->mbhc_fw || !mbhc->mbhc_cal) schedule_delayed_work(&mbhc->mbhc_firmware_dwork, usecs_to_jiffies(FW_READ_TIMEOUT)); else pr_err("%s: Skipping to read mbhc fw, 0x%p %p\n", __func__, mbhc->mbhc_fw, mbhc->mbhc_cal); } pr_debug("%s: leave %d\n", __func__, rc); return rc; } Loading @@ -1837,6 +1959,14 @@ void wcd_mbhc_stop(struct wcd_mbhc *mbhc) pr_debug("%s: enter\n", __func__); wcd9xxx_spmi_disable_irq(mbhc->intr_ids->hph_left_ocp); wcd9xxx_spmi_disable_irq(mbhc->intr_ids->hph_right_ocp); if (mbhc->mbhc_fw || mbhc->mbhc_cal) { cancel_delayed_work_sync(&mbhc->mbhc_firmware_dwork); if (!mbhc->mbhc_cal) release_firmware(mbhc->mbhc_fw); mbhc->mbhc_fw = NULL; mbhc->mbhc_cal = NULL; } pr_debug("%s: leave\n", __func__); } EXPORT_SYMBOL(wcd_mbhc_stop); Loading Loading @@ -1926,6 +2056,8 @@ int wcd_mbhc_init(struct wcd_mbhc *mbhc, struct snd_soc_codec *codec, return ret; } INIT_DELAYED_WORK(&mbhc->mbhc_firmware_dwork, wcd_mbhc_fw_read); INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd_btn_lpress_fn); } Loading
sound/soc/codecs/wcd-mbhc-v2.h +111 −7 Original line number Diff line number Diff line Loading @@ -13,6 +13,7 @@ #define __WCD_MBHC_V2_H__ #include <linux/wait.h> #include "wcdcal-hwdep.h" #define TOMBAK_MBHC_NC 0 #define TOMBAK_MBHC_NO 1 Loading Loading @@ -41,6 +42,33 @@ enum wcd_mbhc_event_state { WCD_MBHC_EVENT_PA_HPHL, WCD_MBHC_EVENT_PA_HPHR, }; struct wcd_mbhc_general_cfg { u8 t_ldoh; u8 t_bg_fast_settle; u8 t_shutdown_plug_rem; u8 mbhc_nsa; u8 mbhc_navg; u8 v_micbias_l; u8 v_micbias; u8 mbhc_reserved; u16 settle_wait; u16 t_micbias_rampup; u16 t_micbias_rampdown; u16 t_supply_bringup; } __packed; struct wcd_mbhc_plug_detect_cfg { u32 mic_current; u32 hph_current; u16 t_mic_pid; u16 t_ins_complete; u16 t_ins_retry; u16 v_removal_delta; u8 micbias_slow_ramp; u8 reserved0; u8 reserved1; u8 reserved2; } __packed; struct wcd_mbhc_plug_type_cfg { u8 av_detect; Loading @@ -56,9 +84,39 @@ struct wcd_mbhc_plug_type_cfg { } __packed; struct wcd_mbhc_btn_detect_cfg { s8 c[8]; u8 nc; u8 n_meas; u8 mbhc_nsc; u8 n_btn_meas; u8 n_btn_con; u8 num_btn; s16 _v_btn_low[WCD_MBHC_DEF_BUTTONS]; s16 _v_btn_high[WCD_MBHC_DEF_BUTTONS]; u8 reserved0; u8 reserved1; u16 t_poll; u16 t_bounce_wait; u16 t_rel_timeout; s16 v_btn_press_delta_sta; s16 v_btn_press_delta_cic; u16 t_btn0_timeout; s16 _v_btn_low[0]; /* v_btn_low[num_btn] */ s16 _v_btn_high[0]; /* v_btn_high[num_btn] */ u8 _n_ready[2]; u8 _n_cic[2]; u8 _gain[2]; } __packed; struct wcd_mbhc_imped_detect_cfg { u8 _hs_imped_detect; u8 _n_rload; u8 _hph_keep_on; u8 _repeat_rload_calc; u16 _t_dac_ramp_time; u16 _rhph_high; u16 _rhph_low; u16 _rload[0]; /* rload[n_rload] */ u16 _alpha[0]; /* alpha[n_rload] */ u16 _beta[3]; } __packed; struct wcd_mbhc_config { Loading Loading @@ -86,6 +144,8 @@ struct wcd_mbhc_cb { void (*compute_impedance) (s16 , s16 , uint32_t *, uint32_t *, bool); void (*set_micbias_value) (struct snd_soc_codec *); void (*set_auto_zeroing) (struct snd_soc_codec *, bool); struct firmware_cal * (*get_hwdep_fw_cal)(struct snd_soc_codec *, enum wcd_cal_type); }; struct wcd_mbhc { Loading Loading @@ -113,6 +173,10 @@ struct wcd_mbhc { bool is_hs_recording; struct snd_soc_codec *codec; /* Work to perform MBHC Firmware Read */ struct delayed_work mbhc_firmware_dwork; const struct firmware *mbhc_fw; struct firmware_cal *mbhc_cal; /* track PA/DAC state to sync with userspace */ unsigned long hph_pa_dac_state; Loading @@ -134,17 +198,57 @@ struct wcd_mbhc { struct work_struct correct_plug_swch; struct notifier_block nblock; }; #define WCD_MBHC_CAL_SIZE ( \ #define WCD_MBHC_CAL_SIZE(buttons, rload) ( \ sizeof(struct wcd_mbhc_general_cfg) + \ sizeof(struct wcd_mbhc_plug_detect_cfg) + \ ((sizeof(s16) + sizeof(s16)) * buttons) + \ sizeof(struct wcd_mbhc_plug_type_cfg) + \ sizeof(struct wcd_mbhc_btn_detect_cfg) \ sizeof(struct wcd_mbhc_btn_detect_cfg) + \ sizeof(struct wcd_mbhc_imped_detect_cfg) + \ ((sizeof(u16) + sizeof(u16)) * rload) \ ) #define WCD_MBHC_CAL_GENERAL_PTR(cali) ( \ (struct wcd_mbhc_general_cfg *) cali) #define WCD_MBHC_CAL_PLUG_DET_PTR(cali) ( \ (struct wcd_mbhc_plug_type_cfg *) cali) (struct wcd_mbhc_plug_detect_cfg *) \ &(WCD_MBHC_CAL_GENERAL_PTR(cali)[1])) #define WCD_MBHC_CAL_PLUG_TYPE_PTR(cali) ( \ (struct wcd_mbhc_plug_type_cfg *) \ &(WCD_MBHC_CAL_PLUG_DET_PTR(cali)[1])) #define WCD_MBHC_CAL_BTN_DET_PTR(cali) ( \ (struct wcd_mbhc_btn_detect_cfg *) \ &(WCD_MBHC_CAL_PLUG_DET_PTR(cali)[1])) &(WCD_MBHC_CAL_PLUG_TYPE_PTR(cali)[1])) #define WCD_MBHC_CAL_IMPED_DET_PTR(cali) ( \ (struct wcd_mbhc_imped_detect_cfg *) \ (((void *)&WCD_MBHC_CAL_BTN_DET_PTR(cali)[1]) + \ (WCD_MBHC_CAL_BTN_DET_PTR(cali)->num_btn * \ (sizeof(WCD_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_low[0]) + \ sizeof(WCD_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_high[0])))) \ ) #define WCD_MBHC_CAL_MIN_SIZE ( \ sizeof(struct wcd_mbhc_general_cfg) + \ sizeof(struct wcd_mbhc_plug_detect_cfg) + \ sizeof(struct wcd_mbhc_plug_type_cfg) + \ sizeof(struct wcd_mbhc_btn_detect_cfg) + \ sizeof(struct wcd_mbhc_imped_detect_cfg) + \ (sizeof(u16)*2) \ ) #define WCD_MBHC_CAL_BTN_SZ(cfg_ptr) ( \ sizeof(struct wcd_mbhc_btn_detect_cfg) + \ (cfg_ptr->num_btn * (sizeof(cfg_ptr->_v_btn_low[0]) + \ sizeof(cfg_ptr->_v_btn_high[0])))) #define WCD_MBHC_CAL_IMPED_MIN_SZ ( \ sizeof(struct wcd_mbhc_imped_detect_cfg) + sizeof(u16) * 2) #define WCD_MBHC_CAL_IMPED_SZ(cfg_ptr) ( \ sizeof(struct wcd_mbhc_imped_detect_cfg) + \ (cfg_ptr->_n_rload * \ (sizeof(cfg_ptr->_rload[0]) + sizeof(cfg_ptr->_alpha[0])))) int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg); Loading