/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef __WCD_MBHC_V2_H__ #define __WCD_MBHC_V2_H__ #include #include #include "wcdcal-hwdep.h" #define TOMBAK_MBHC_NC 0 #define TOMBAK_MBHC_NO 1 #define WCD_MBHC_DEF_BUTTONS 8 #define WCD_MBHC_KEYCODE_NUM 8 #define WCD_MBHC_USLEEP_RANGE_MARGIN_US 100 #define WCD_MBHC_THR_HS_MICB_MV 2700 /* z value defined in Ohms */ #define WCD_MONO_HS_MIN_THR 2 #define WCD_MBHC_STRINGIFY(s) __stringify(s) enum { WCD_MBHC_ELEC_HS_INS, WCD_MBHC_ELEC_HS_REM, }; struct wcd_mbhc; enum wcd_mbhc_register_function { WCD_MBHC_L_DET_EN, WCD_MBHC_GND_DET_EN, WCD_MBHC_MECH_DETECTION_TYPE, WCD_MBHC_MIC_CLAMP_CTL, WCD_MBHC_ELECT_DETECTION_TYPE, WCD_MBHC_HS_L_DET_PULL_UP_CTRL, WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, WCD_MBHC_HPHL_PLUG_TYPE, WCD_MBHC_GND_PLUG_TYPE, WCD_MBHC_SW_HPH_LP_100K_TO_GND, WCD_MBHC_ELECT_SCHMT_ISRC, WCD_MBHC_FSM_EN, WCD_MBHC_INSREM_DBNC, WCD_MBHC_BTN_DBNC, WCD_MBHC_HS_VREF, WCD_MBHC_HS_COMP_RESULT, WCD_MBHC_MIC_SCHMT_RESULT, WCD_MBHC_HPHL_SCHMT_RESULT, WCD_MBHC_HPHR_SCHMT_RESULT, WCD_MBHC_OCP_FSM_EN, WCD_MBHC_BTN_RESULT, WCD_MBHC_BTN_ISRC_CTL, WCD_MBHC_ELECT_RESULT, WCD_MBHC_MICB_CTRL, /* Pull-up and micb control */ WCD_MBHC_HPH_CNP_WG_TIME, WCD_MBHC_HPHR_PA_EN, WCD_MBHC_HPHL_PA_EN, WCD_MBHC_HPH_PA_EN, WCD_MBHC_SWCH_LEVEL_REMOVE, WCD_MBHC_MOISTURE_VREF, WCD_MBHC_PULLDOWN_CTRL, WCD_MBHC_ANC_DET_EN, WCD_MBHC_FSM_STATUS, WCD_MBHC_MUX_CTL, WCD_MBHC_REG_FUNC_MAX, }; enum wcd_mbhc_plug_type { MBHC_PLUG_TYPE_INVALID = -1, MBHC_PLUG_TYPE_NONE, MBHC_PLUG_TYPE_HEADSET, MBHC_PLUG_TYPE_HEADPHONE, MBHC_PLUG_TYPE_HIGH_HPH, MBHC_PLUG_TYPE_GND_MIC_SWAP, MBHC_PLUG_TYPE_ANC_HEADPHONE, }; enum pa_dac_ack_flags { WCD_MBHC_HPHL_PA_OFF_ACK = 0, WCD_MBHC_HPHR_PA_OFF_ACK, }; enum wcd_mbhc_btn_det_mem { WCD_MBHC_BTN_DET_V_BTN_LOW, WCD_MBHC_BTN_DET_V_BTN_HIGH }; enum { MIC_BIAS_1 = 1, MIC_BIAS_2, MIC_BIAS_3, MIC_BIAS_4 }; enum { MICB_PULLUP_ENABLE, MICB_PULLUP_DISABLE, MICB_ENABLE, MICB_DISABLE, }; enum { MBHC_COMMON_MICB_PRECHARGE, MBHC_COMMON_MICB_SET_VAL, MBHC_COMMON_MICB_TAIL_CURR, }; enum wcd_notify_event { WCD_EVENT_INVALID, /* events for micbias ON and OFF */ WCD_EVENT_PRE_MICBIAS_2_OFF, WCD_EVENT_POST_MICBIAS_2_OFF, WCD_EVENT_PRE_MICBIAS_2_ON, WCD_EVENT_POST_MICBIAS_2_ON, WCD_EVENT_PRE_DAPM_MICBIAS_2_OFF, WCD_EVENT_POST_DAPM_MICBIAS_2_OFF, WCD_EVENT_PRE_DAPM_MICBIAS_2_ON, WCD_EVENT_POST_DAPM_MICBIAS_2_ON, /* events for PA ON and OFF */ WCD_EVENT_PRE_HPHL_PA_ON, WCD_EVENT_POST_HPHL_PA_OFF, WCD_EVENT_PRE_HPHR_PA_ON, WCD_EVENT_POST_HPHR_PA_OFF, WCD_EVENT_PRE_HPHL_PA_OFF, WCD_EVENT_PRE_HPHR_PA_OFF, WCD_EVENT_LAST, }; 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; u8 mono_detect; u8 num_ins_tries; u8 reserved0; s16 v_no_mic; s16 v_av_min; s16 v_av_max; s16 v_hs_min; s16 v_hs_max; u16 reserved1; } __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; 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; enum wcd_mbhc_hph_type { WCD_MBHC_HPH_NONE = 0, WCD_MBHC_HPH_MONO, WCD_MBHC_HPH_STEREO, }; /* * These enum definitions are directly mapped to the register * definitions */ enum mbhc_moisture_vref { V_OFF, V_45_MV, V_100_MV, V_225_MV, }; enum mbhc_hs_pullup_iref { I_DEFAULT = -1, I_OFF = 0, I_1P0_UA, I_2P0_UA, I_3P0_UA, }; struct wcd_mbhc_moisture_cfg { enum mbhc_moisture_vref m_vref_ctl; enum mbhc_hs_pullup_iref m_iref_ctl; }; struct wcd_mbhc_config { bool read_fw_bin; void *calibration; bool detect_extn_cable; bool mono_stero_detection; bool (*swap_gnd_mic)(struct snd_soc_codec *codec); bool hs_ext_micbias; bool gnd_det_en; int key_code[WCD_MBHC_KEYCODE_NUM]; uint32_t linein_th; struct wcd_mbhc_moisture_cfg moist_cfg; int mbhc_micbias; int anc_micbias; bool enable_anc_mic_detect; }; struct wcd_mbhc_intr { int mbhc_sw_intr; int mbhc_btn_press_intr; int mbhc_btn_release_intr; int mbhc_hs_ins_intr; int mbhc_hs_rem_intr; int hph_left_ocp; int hph_right_ocp; }; struct wcd_mbhc_register { const char *id; u16 reg; u8 mask; u8 offset; u8 invert; }; #define WCD_MBHC_REGISTER(rid, rreg, rmask, rshift, rinvert) \ { .id = rid, .reg = rreg, .mask = rmask, .offset = rshift, .invert = rinvert } #define WCD_MBHC_RSC_LOCK(mbhc) \ { \ pr_debug("%s: Acquiring BCL\n", __func__); \ mutex_lock(&mbhc->codec_resource_lock); \ pr_debug("%s: Acquiring BCL done\n", __func__); \ } #define WCD_MBHC_RSC_UNLOCK(mbhc) \ { \ pr_debug("%s: Release BCL\n", __func__); \ mutex_unlock(&mbhc->codec_resource_lock); \ } #define WCD_MBHC_RSC_ASSERT_LOCKED(mbhc) \ { \ WARN_ONCE(!mutex_is_locked(&mbhc->codec_resource_lock), \ "%s: BCL should have acquired\n", __func__); \ } /* * Macros to update and read mbhc register bits. Check for * "0" before updating or reading the register, because it * is possible that one codec wants to write to that bit and * other codec does not. */ #define WCD_MBHC_REG_UPDATE_BITS(function, val) \ do { \ if (mbhc->wcd_mbhc_regs[function].reg) { \ snd_soc_update_bits(mbhc->codec, \ mbhc->wcd_mbhc_regs[function].reg, \ mbhc->wcd_mbhc_regs[function].mask, \ val << (mbhc->wcd_mbhc_regs[function].offset)); \ } \ } while (0) #define WCD_MBHC_REG_READ(function, val) \ do { \ if (mbhc->wcd_mbhc_regs[function].reg) { \ val = (((snd_soc_read(mbhc->codec, \ mbhc->wcd_mbhc_regs[function].reg)) & \ (mbhc->wcd_mbhc_regs[function].mask)) >> \ (mbhc->wcd_mbhc_regs[function].offset)); \ } \ } while (0) struct wcd_mbhc_cb { int (*enable_mb_source)(struct snd_soc_codec *, bool); void (*trim_btn_reg)(struct snd_soc_codec *); void (*compute_impedance)(struct wcd_mbhc *, uint32_t *, uint32_t *); 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); void (*set_cap_mode)(struct snd_soc_codec *, bool, bool); int (*register_notifier)(struct snd_soc_codec *, struct notifier_block *nblock, bool enable); int (*request_irq)(struct snd_soc_codec *, int, irq_handler_t, const char *, void *); void (*irq_control)(struct snd_soc_codec *, int irq, bool enable); int (*free_irq)(struct snd_soc_codec *, int irq, void *); void (*clk_setup)(struct snd_soc_codec *, bool); int (*map_btn_code_to_num)(struct snd_soc_codec *); bool (*lock_sleep)(struct wcd_mbhc *, bool); bool (*micbias_enable_status)(struct wcd_mbhc *, int); void (*mbhc_bias)(struct snd_soc_codec *, bool); void (*mbhc_common_micb_ctrl)(struct snd_soc_codec *, int event, bool); void (*micb_internal)(struct snd_soc_codec *, int micb_num, bool); bool (*hph_pa_on_status)(struct snd_soc_codec *); void (*set_btn_thr)(struct snd_soc_codec *, s16 *, s16 *, int num_btn, bool); void (*hph_pull_up_control)(struct snd_soc_codec *, enum mbhc_hs_pullup_iref); int (*mbhc_micbias_control)(struct snd_soc_codec *, int, int req); void (*mbhc_micb_ramp_control)(struct snd_soc_codec *, bool); void (*skip_imped_detect)(struct snd_soc_codec *); bool (*extn_use_mb)(struct snd_soc_codec *); int (*mbhc_micb_ctrl_thr_mic)(struct snd_soc_codec *, int, bool); void (*mbhc_gnd_det_ctrl)(struct snd_soc_codec *, bool); void (*hph_pull_down_ctrl)(struct snd_soc_codec *, bool); }; struct wcd_mbhc { /* Delayed work to report long button press */ struct delayed_work mbhc_btn_dwork; int buttons_pressed; struct wcd_mbhc_config *mbhc_cfg; const struct wcd_mbhc_cb *mbhc_cb; u32 hph_status; /* track headhpone status */ u8 hphlocp_cnt; /* headphone left ocp retry */ u8 hphrocp_cnt; /* headphone right ocp retry */ wait_queue_head_t wait_btn_press; bool is_btn_press; u8 current_plug; bool in_swch_irq_handler; bool hphl_swh; /*track HPHL switch NC / NO */ bool gnd_swh; /*track GND switch NC / NO */ u8 micbias1_cap_mode; /* track ext cap setting */ u8 micbias2_cap_mode; /* track ext cap setting */ bool hs_detect_work_stop; bool micbias_enable; bool btn_press_intr; bool is_hs_recording; bool is_extn_cable; bool skip_imped_detection; bool is_btn_already_regd; 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; unsigned long event_state; unsigned long jiffies_atreport; /* impedance of hphl and hphr */ uint32_t zl, zr; bool impedance_detect; /* Holds type of Headset - Mono/Stereo */ enum wcd_mbhc_hph_type hph_type; struct snd_soc_jack headset_jack; struct snd_soc_jack button_jack; struct mutex codec_resource_lock; /* Holds codec specific interrupt mapping */ const struct wcd_mbhc_intr *intr_ids; /* Work to correct accessory type */ struct work_struct correct_plug_swch; struct notifier_block nblock; struct wcd_mbhc_register *wcd_mbhc_regs; struct completion btn_press_compl; struct mutex hphl_pa_lock; struct mutex hphr_pa_lock; unsigned long intr_status; }; #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_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_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_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])))) #ifdef CONFIG_SND_SOC_WCD_MBHC int wcd_mbhc_set_keycode(struct wcd_mbhc *mbhc); int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg); void wcd_mbhc_stop(struct wcd_mbhc *mbhc); int wcd_mbhc_init(struct wcd_mbhc *mbhc, struct snd_soc_codec *codec, const struct wcd_mbhc_cb *mbhc_cb, const struct wcd_mbhc_intr *mbhc_cdc_intr_ids, struct wcd_mbhc_register *mbhc_reg, bool impedance_det_en); int wcd_mbhc_get_impedance(struct wcd_mbhc *mbhc, uint32_t *zl, uint32_t *zr); void wcd_mbhc_deinit(struct wcd_mbhc *mbhc); #else static inline void wcd_mbhc_stop(struct wcd_mbhc *mbhc) { return; } static inline int wcd_mbhc_init(struct wcd_mbhc *mbhc, struct snd_soc_codec *codec, const struct wcd_mbhc_cb *mbhc_cb, const struct wcd_mbhc_intr *mbhc_cdc_intr_ids, struct wcd_mbhc_register *mbhc_reg, bool impedance_det_en) { return 0; } static inline int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg) { return 0; } static inline int wcd_mbhc_get_impedance(struct wcd_mbhc *mbhc, uint32_t *zl, uint32_t *zr) { *zl = 0; *zr = 0; return -EINVAL; } static inline void wcd_mbhc_deinit(struct wcd_mbhc *mbhc) { } #endif #endif /* __WCD_MBHC_V2_H__ */