入口函数:初始化MBHC ADC相关函数指针到MBHC结构
void wcd_mbhc_adc_init(struct wcd_mbhc *mbhc)
{
if (!mbhc) {
pr_err("%s: mbhc is NULL\n", __func__);
return;
}
mbhc->mbhc_fn = &mbhc_fn;
INIT_WORK(&mbhc->correct_plug_swch, wcd_correct_swch_plug);
}
这里添加了一个工作队列wcd_correct_swch_plug,只有在使用的时候去激活它。
static struct wcd_mbhc_fn mbhc_fn = {
.wcd_mbhc_hs_ins_irq = wcd_mbhc_adc_hs_ins_irq,
.wcd_mbhc_hs_rem_irq = wcd_mbhc_adc_hs_rem_irq,
.wcd_mbhc_detect_plug_type = wcd_mbhc_adc_detect_plug_type,
.wcd_mbhc_detect_anc_plug_type = wcd_mbhc_adc_detect_anc_plug_type,
.wcd_cancel_hs_detect_plug = wcd_cancel_hs_detect_plug,
};
wcd_mbhc_adc_detect_plug_type() 在获取编解码器资源锁时调用
static void wcd_mbhc_adc_detect_plug_type(struct wcd_mbhc *mbhc)
{
struct snd_soc_codec *codec = mbhc->codec;
pr_debug("%s: enter\n", __func__);
WCD_MBHC_RSC_ASSERT_LOCKED(mbhc);
if (mbhc->mbhc_cb->hph_pull_down_ctrl)
mbhc->mbhc_cb->hph_pull_down_ctrl(codec, false);
WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_DETECTION_DONE, 0);
if (mbhc->mbhc_cb->mbhc_micbias_control) {
mbhc->mbhc_cb->mbhc_micbias_control(codec, MIC_BIAS_2,
MICB_ENABLE);
} else {
pr_err("%s: Mic Bias is not enabled\n", __func__);
return;
}
/* Re-initialize button press completion object */
reinit_completion(&mbhc->btn_press_compl);
wcd_schedule_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
pr_debug("%s: leave\n", __func__);
}
wcd_schedule_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);应该在保持挂起的中断上下文下调用
wcd_correct_swch_plug() 开始类型的检测
pr_debug("%s: enter\n", __func__);
mbhc = container_of(work, struct wcd_mbhc, correct_plug_swch);
codec = mbhc->codec;
WCD_MBHC_RSC_LOCK(mbhc);
/* Mask ADC COMPLETE interrupt */
wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, false);
WCD_MBHC_RSC_UNLOCK(mbhc);
/* Check for cross connection */
do {
cross_conn = wcd_check_cross_conn(mbhc);
try++;
} while (try < mbhc->swap_thr);
if (cross_conn > 0) {
plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
pr_debug("%s: cross connection found, Plug type %d\n",
__func__, plug_type);
goto correct_plug_type;
}
/* Find plug type */
output_mv = wcd_measure_adc_continuous(mbhc);
plug_type = wcd_mbhc_get_plug_from_adc(mbhc, output_mv);
/*
* Report plug type if it is either headset or headphone
* else start the 3 sec loop
*/
if ((plug_type == MBHC_PLUG_TYPE_HEADSET ||
plug_type == MBHC_PLUG_TYPE_HEADPHONE) &&
(!wcd_swch_level_remove(mbhc))) {
WCD_MBHC_RSC_LOCK(mbhc);
wcd_mbhc_find_plug_and_report(mbhc, plug_type);
WCD_MBHC_RSC_UNLOCK(mbhc);
}
先看下mic和gnd有没有接反,接着检测插头类型,不是耳机则开始3秒的循环,是则上报耳机类型事件。
wcd_mbhc_get_plug_from_adc() 通过输出电压的大小得到耳机类型。
static int wcd_mbhc_get_plug_from_adc(struct wcd_mbhc *mbhc, int adc_result)
{
enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_INVALID;
u32 hph_thr = 0, hs_thr = 0;
if (mbhc->hs_thr)
hs_thr = mbhc->hs_thr;
else
hs_thr = WCD_MBHC_ADC_HS_THRESHOLD_MV;
if (mbhc->hph_thr)
hph_thr = mbhc->hph_thr;
else
hph_thr = WCD_MBHC_ADC_HPH_THRESHOLD_MV;
if (adc_result < hph_thr)
plug_type = MBHC_PLUG_TYPE_HEADPHONE;
else if (adc_result > hs_thr)
plug_type = MBHC_PLUG_TYPE_HIGH_HPH;
else
plug_type = MBHC_PLUG_TYPE_HEADSET;
pr_debug("%s: plug type is %d found\n", __func__, plug_type);
return plug_type;
}
如果是高阻抗耳机,会一直往下走,直到最后上报耳机类型。
if (output_mv > WCD_MBHC_ADC_HS_THRESHOLD_MV) {
pr_debug("%s: cable is extension cable\n", __func__);
plug_type = MBHC_PLUG_TYPE_HIGH_HPH;
wrk_complete = true;
}
。。。
if (!wrk_complete) {
/*
* If plug_tye is headset, we might have already reported either
* in detect_plug-type or in above while loop, no need to report
* again
*/
if ((plug_type == MBHC_PLUG_TYPE_HEADSET) ||
(plug_type == MBHC_PLUG_TYPE_ANC_HEADPHONE)) {
pr_debug("%s: plug_type:0x%x already reported\n",
__func__, mbhc->current_plug);
WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_MODE, 0);
WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 0);
goto enable_supply;
}
}
if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH) {
if (wcd_is_special_headset(mbhc)) {
pr_debug("%s: Special headset found %d\n",
__func__, plug_type);
plug_type = MBHC_PLUG_TYPE_HEADSET;
} else {
WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_ISRC_EN, 1);
}
}
report:
if (wcd_swch_level_remove(mbhc)) {
pr_debug("%s: Switch level is low\n", __func__);
goto exit;
}
pr_debug("%s: Valid plug found, plug type %d wrk_cmpt %d btn_intr %d\n",
__func__, plug_type, wrk_complete,
mbhc->btn_press_intr);
WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_MODE, 0);
WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 0);
WCD_MBHC_RSC_LOCK(mbhc);
wcd_mbhc_find_plug_and_report(mbhc, plug_type);
WCD_MBHC_RSC_UNLOCK(mbhc);
至此,耳机类型判断完成。