高通msm8909耳机调试

http://blog.csdn.net/mike8825/article/details/69489865?locationnum=3&fps=1

1、DTS相应修改:

DTS相关代码:kernel/arch/arm/boot/dts/qcom/msm8909-qrd-skuc.dtsi:

     sound {
compatible = "qcom,msm8x16-audio-codec";
qcom,model = "msm8909-skuc-snd-card";
qcom,msm-snd-card-id = <>;
qcom,msm-codec-type = "internal";
qcom,msm-ext-pa = "primary";
qcom,msm-mclk-freq = <>;
qcom,msm-mbhc-hphl-swh = <>;        //高通平台耳机驱动机制为MBHC,hphl为高通耳机平台的设置,简单的理解就是低电平检测还是高电平检测 
qcom,msm-mbhc-gnd-swh = <>;
qcom,msm-hs-micbias-type = "internal";
qcom,msm-micbias1-ext-cap;
qcom,msm-micbias2-ext-cap;
qcom,audio-routing =
"RX_BIAS", "MCLK",
"SPK_RX_BIAS", "MCLK",
"INT_LDO_H", "MCLK",
"MIC BIAS Internal1", "Handset Mic",
"MIC BIAS Internal2", "Headset Mic",
"MIC BIAS Internal3", "Secondary Mic",
"AMIC1", "MIC BIAS Internal1",
"AMIC2", "MIC BIAS Internal2",
"AMIC3", "MIC BIAS Internal3";
pinctrl-names = "cdc_lines_act",
"cdc_lines_sus";
pinctrl- = <&cdc_pdm_lines_act>;
pinctrl- = <&cdc_pdm_lines_sus>;
asoc-platform = <&pcm0>, <&pcm1>, <&voip>, <&voice>,
<&loopback>, <&compress>, <&hostless>,
<&afe>, <&lsm>, <&routing>, <&lpa>,
<&voice_svc>;
asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1",
"msm-voip-dsp", "msm-pcm-voice", "msm-pcm-loopback",
"msm-compress-dsp", "msm-pcm-hostless", "msm-pcm-afe",
"msm-lsm-client", "msm-pcm-routing", "msm-pcm-lpa",
"msm-voice-svc";
asoc-cpu = <&dai_pri_auxpcm>, <&dai_hdmi>,
<&dai_mi2s0>, <&dai_mi2s1>, <&dai_mi2s2>, <&dai_mi2s3>,
<&sb_0_rx>, <&sb_0_tx>, <&sb_1_rx>, <&sb_1_tx>,
<&sb_3_rx>, <&sb_3_tx>, <&sb_4_rx>, <&sb_4_tx>,
<&bt_sco_rx>, <&bt_sco_tx>, <&int_fm_rx>, <&int_fm_tx>,
<&afe_pcm_rx>, <&afe_pcm_tx>, <&afe_proxy_rx>, <&afe_proxy_tx>,
<&incall_record_rx>, <&incall_record_tx>, <&incall_music_rx>,
<&incall_music_2_rx>;
asoc-cpu-names = "msm-dai-q6-auxpcm.1", "msm-dai-q6-hdmi.8",
"msm-dai-q6-mi2s.0", "msm-dai-q6-mi2s.1",
"msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3",
"msm-dai-q6-dev.16384", "msm-dai-q6-dev.16385",
"msm-dai-q6-dev.16386", "msm-dai-q6-dev.16387",
"msm-dai-q6-dev.16390", "msm-dai-q6-dev.16391",
"msm-dai-q6-dev.16392", "msm-dai-q6-dev.16393",
"msm-dai-q6-dev.12288", "msm-dai-q6-dev.12289",
"msm-dai-q6-dev.12292", "msm-dai-q6-dev.12293",
"msm-dai-q6-dev.224", "msm-dai-q6-dev.225",
"msm-dai-q6-dev.241", "msm-dai-q6-dev.240",
"msm-dai-q6-dev.32771", "msm-dai-q6-dev.32772",
"msm-dai-q6-dev.32773", "msm-dai-q6-dev.32770";
asoc-codec = <&stub_codec>, <&pm8909_conga_dig>;      //在这里使用了pm8909电源管理芯片的codec芯片
asoc-codec-names = "msm-stub-codec.1", "tombak_codec";
};

通过find ./ -name "*8909*" | xargs grep -n "pm8909_conga_dig"来搜索关键字:

高通msm8909耳机调试

kernel/arch/arm/boot/dts/qcom/msm-pm8909.dtsi:

    pm8909_conga_dig: 8909_wcd_codec@f000 {
compatible = "qcom,msm8x16_wcd_codec";
reg = <0xf000 0x100>;
interrupt-parent = <&spmi_bus>;
interrupts = <0x1 0xf0 0x0>,
<0x1 0xf0 0x1>,
<0x1 0xf0 0x2>,
<0x1 0xf0 0x3>,
<0x1 0xf0 0x4>,
<0x1 0xf0 0x5>,
<0x1 0xf0 0x6>,
<0x1 0xf0 0x7>;
interrupt-names = "spk_cnp_int",
"spk_clip_int",
"spk_ocp_int",
"ins_rem_det1",
"but_rel_det",
"but_press_det",
"ins_rem_det",
"mbhc_int"; cdc-vdda-cp-supply = <&pm8909_s2>;
qcom,cdc-vdda-cp-voltage = < >;
qcom,cdc-vdda-cp-current = <>; cdc-vdda-h-supply = <&pm8909_l5>;
qcom,cdc-vdda-h-voltage = < >;
qcom,cdc-vdda-h-current = <>; cdc-vdd-px-supply = <&pm8909_l5>;
qcom,cdc-vdd-px-voltage = < >;
qcom,cdc-vdd-px-current = <>; cdc-vdd-pa-supply = <&pm8909_s2>;
qcom,cdc-vdd-pa-voltage = < >;
qcom,cdc-vdd-pa-current = <>; cdc-vdd-mic-bias-supply = <&pm8909_l13>;
qcom,cdc-vdd-mic-bias-voltage = < >;
qcom,cdc-vdd-mic-bias-current = <>; qcom,cdc-mclk-clk-rate = <>; qcom,cdc-static-supplies = "cdc-vdda-h",
"cdc-vdd-px",
"cdc-vdd-pa",
"cdc-vdda-cp"; qcom,cdc-on-demand-supplies = "cdc-vdd-mic-bias";
}; pm8909_conga_analog: 8909_wcd_codec@f100 {
compatible = "qcom,msm8x16_wcd_codec";
reg = <0xf100 0x100>;
interrupt-parent = <&spmi_bus>;
interrupts = <0x1 0xf1 0x0>,
<0x1 0xf1 0x1>,
<0x1 0xf1 0x2>,
<0x1 0xf1 0x3>,
<0x1 0xf1 0x4>,
<0x1 0xf1 0x5>;
interrupt-names = "ear_ocp_int",
"hphr_ocp_int",
"hphl_ocp_det",
"ear_cnp_int",
"hphr_cnp_int",
"hphl_cnp_int";
};

msm8909的耳机接口在pm8909上,耳机检测脚是HS_DET。

确定自己的插座是NO还是NC的,详情查看博客:http://www.cnblogs.com/linhaostudy/p/8260813.html

通过检查,判断JACK为NO状态;根据高通bring up 手册更改相应的DTSI:
高通msm8909耳机调试
 qcom,msm-mbhc-hphl-swh = <1>;    //由0改为1
 
 
 

2、kernel分析:

kernel/sound/soc/codecs/msm8x16-wcd.c

 static const struct wcd_mbhc_intr intr_ids = {
.mbhc_sw_intr = MSM8X16_WCD_IRQ_MBHC_HS_DET,
.mbhc_btn_press_intr = MSM8X16_WCD_IRQ_MBHC_PRESS,
.mbhc_btn_release_intr = MSM8X16_WCD_IRQ_MBHC_RELEASE,
.mbhc_hs_ins_intr = MSM8X16_WCD_IRQ_MBHC_INSREM_DET1,
.mbhc_hs_rem_intr = MSM8X16_WCD_IRQ_MBHC_INSREM_DET,
.hph_left_ocp = MSM8X16_WCD_IRQ_HPHL_OCP,
.hph_right_ocp = MSM8X16_WCD_IRQ_HPHR_OCP,
};

这个结构体描述了msm8909的wcdx系列的codec芯片的MBHC的信息;

 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, ret; dev_dbg(codec->dev, "%s()\n", __func__); msm8x16_wcd_priv = kzalloc(sizeof(struct msm8x16_wcd_priv), GFP_KERNEL);
if (!msm8x16_wcd_priv) {
dev_err(codec->dev, "Failed to allocate private data\n");
return -ENOMEM;
} for (i = ; i < NUM_DECIMATORS; i++) {
tx_hpf_work[i].msm8x16_wcd = msm8x16_wcd_priv;
tx_hpf_work[i].decimator = i + ;
INIT_DELAYED_WORK(&tx_hpf_work[i].dwork,
tx_hpf_corner_freq_callback);
} codec->control_data = dev_get_drvdata(codec->dev);
snd_soc_codec_set_drvdata(codec, msm8x16_wcd_priv);
msm8x16_wcd_priv->codec = codec; /* codec resmgr module init */
msm8x16_wcd = codec->control_data;
msm8x16_wcd->dig_base = ioremap(MSM8X16_DIGITAL_CODEC_BASE_ADDR,
MSM8X16_DIGITAL_CODEC_REG_SIZE);
if (msm8x16_wcd->dig_base == NULL) {
dev_err(codec->dev, "%s ioremap failed\n", __func__);
kfree(msm8x16_wcd_priv);
return -ENOMEM;
}
msm8x16_wcd_priv->spkdrv_reg =
wcd8x16_wcd_codec_find_regulator(codec->control_data,
MSM89XX_VDD_SPKDRV_NAME);
msm8x16_wcd_priv->pmic_rev = snd_soc_read(codec,
MSM8X16_WCD_A_DIGITAL_REVISION1);
msm8x16_wcd_priv->codec_version = snd_soc_read(codec,
MSM8X16_WCD_A_DIGITAL_PERPH_SUBTYPE);
if (msm8x16_wcd_priv->codec_version == CONGA) {
dev_dbg(codec->dev, "%s :Conga REV: %d\n", __func__,
msm8x16_wcd_priv->codec_version);
msm8x16_wcd_priv->ext_spk_boost_set = true;
} else {
dev_dbg(codec->dev, "%s :PMIC REV: %d\n", __func__,
msm8x16_wcd_priv->pmic_rev);
}
/*
* set to default boost option BOOST_SWITCH, user mixer path can change
* it to BOOST_ALWAYS or BOOST_BYPASS based on solution chosen.
*/
msm8x16_wcd_priv->boost_option = BOOST_SWITCH;
msm8x16_wcd_dt_parse_boost_info(codec);    //
msm8x16_wcd_set_boost_v(codec); snd_soc_add_codec_controls(codec, impedance_detect_controls,
ARRAY_SIZE(impedance_detect_controls)); msm8x16_wcd_bringup(codec);
msm8x16_wcd_codec_init_reg(codec);
msm8x16_wcd_update_reg_defaults(codec); wcd9xxx_spmi_set_codec(codec); msm8x16_wcd_priv->on_demand_list[ON_DEMAND_MICBIAS].supply =
wcd8x16_wcd_codec_find_regulator(
codec->control_data,
on_demand_supply_name[ON_DEMAND_MICBIAS]);
atomic_set(&msm8x16_wcd_priv->on_demand_list[ON_DEMAND_MICBIAS].ref, ); 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_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 < ) {
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);        //在这里处理相应耳机的相应信息 msm8x16_wcd_priv->mclk_enabled = false;
msm8x16_wcd_priv->clock_active = false;
msm8x16_wcd_priv->config_mode_active = false; /* Set initial MICBIAS voltage level */
msm8x16_wcd_set_micb_v(codec); /* Set initial cap mode */
msm8x16_wcd_configure_cap(codec, false, false);
registered_codec = codec;
modem_state_notifier =
subsys_notif_register_notifier("modem",
&modem_state_notifier_block);
if (!modem_state_notifier) {
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;
}
return ;
}

来到kernel/sound/soc/codecs/wcd-mbhc-v2.c:

 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,
bool impedance_det_en)
{
int ret = ;
int hph_swh = ;
int gnd_swh = ;
struct snd_soc_card *card = codec->card;
const char *hph_switch = "qcom,msm-mbhc-hphl-swh";
const char *gnd_switch = "qcom,msm-mbhc-gnd-swh";
const char *ext1_cap = "qcom,msm-micbias1-ext-cap";
const char *ext2_cap = "qcom,msm-micbias2-ext-cap"; pr_debug("%s: enter\n", __func__); ret = of_property_read_u32(card->dev->of_node, hph_switch, &hph_swh);    //在这里找到设备树信息,通过高低电平判断no或者nc的jack
if (ret) {
dev_err(card->dev,
"%s: missing %s in dt node\n", __func__, hph_switch);
goto err;
} ret = of_property_read_u32(card->dev->of_node, gnd_switch, &gnd_swh);
if (ret) {
dev_err(card->dev,
"%s: missing %s in dt node\n", __func__, gnd_switch);
goto err;
}
mbhc->micbias1_cap_mode =
(of_property_read_bool(card->dev->of_node, ext1_cap) ?
MICBIAS_EXT_BYP_CAP : MICBIAS_NO_EXT_BYP_CAP); mbhc->micbias2_cap_mode =
(of_property_read_bool(card->dev->of_node, ext2_cap) ?
MICBIAS_EXT_BYP_CAP : MICBIAS_NO_EXT_BYP_CAP); mbhc->in_swch_irq_handler = false;
mbhc->current_plug = MBHC_PLUG_TYPE_NONE;
mbhc->is_btn_press = false;
mbhc->codec = codec;
mbhc->intr_ids = mbhc_cdc_intr_ids;
mbhc->impedance_detect = impedance_det_en;
mbhc->hphl_swh = hph_swh;
mbhc->gnd_swh = gnd_swh;
mbhc->micbias_enable = false;
mbhc->mbhc_cb = mbhc_cb;
mbhc->btn_press_intr = false; if (mbhc->intr_ids == NULL) {
pr_err("%s: Interrupt mapping not provided\n", __func__);
return -EINVAL;
} if (mbhc->headset_jack.jack == NULL) {
ret = snd_soc_jack_new(codec, "Headset Jack",
WCD_MBHC_JACK_MASK, &mbhc->headset_jack);    //通过snd_soc_jack_new()函数新建一个新的jack,将该jack指向Machine对应的CODEC,使该jack与CODEC建立联系。snd_soc_jack_new -》snd_jack_new -》 snd_device_new - 》 snd_jack_dev_register -》

err = input_register_device(jack->input_dev); //最终注册input设备

58 if (ret) {

 59             pr_err("%s: Failed to create new jack\n", __func__);
60 return ret;
61 }
62
63 ret = snd_soc_jack_new(codec, "Button Jack",
64 WCD_MBHC_JACK_BUTTON_MASK,
65 &mbhc->button_jack);
66 if (ret) {
67 pr_err("Failed to create new jack\n");
68 return ret;
69 }
70
71 ret = snd_jack_set_key(mbhc->button_jack.jack,
72 SND_JACK_BTN_0,
73 KEY_MEDIA);
74 if (ret) {
75 pr_err("%s: Failed to set code for btn-0\n",
76 __func__);
77 return ret;
78 }
79
80 INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd_btn_lpress_fn);
81 }
82
83 /* Register event notifier */
84 mbhc->nblock.notifier_call = wcd_event_notify;
85 ret = msm8x16_register_notifier(codec, &mbhc->nblock);
86 if (ret) {
87 pr_err("%s: Failed to register notifier %d\n", __func__, ret);
88 return ret;
89 }
90
91 init_waitqueue_head(&mbhc->wait_btn_press);
92 mutex_init(&mbhc->codec_resource_lock);
93   //初测耳机插拔中断
94 ret = wcd9xxx_spmi_request_irq(mbhc->intr_ids->mbhc_sw_intr,
95 wcd_mbhc_mech_plug_detect_irq,
96 "mbhc sw intr", mbhc);
97 if (ret) {
98 pr_err("%s: Failed to request irq %d, ret = %d\n", __func__,
99 mbhc->intr_ids->mbhc_sw_intr, ret);
100 goto err_mbhc_sw_irq;
101 }
102   //申请耳机按下的中断
103 ret = wcd9xxx_spmi_request_irq(mbhc->intr_ids->mbhc_btn_press_intr,
104 wcd_mbhc_btn_press_handler,
105 "Button Press detect",
106 mbhc);
107 if (ret) {
108 pr_err("%s: Failed to request irq %d\n", __func__,
109 mbhc->intr_ids->mbhc_btn_press_intr);
110 goto err_btn_press_irq;
111 }
112
113 ret = wcd9xxx_spmi_request_irq(mbhc->intr_ids->mbhc_btn_release_intr,
114 wcd_mbhc_release_handler,
115 "Button Release detect", mbhc);
116 if (ret) {
117 pr_err("%s: Failed to request irq %d\n", __func__,
118 mbhc->intr_ids->mbhc_btn_release_intr);
119 goto err_btn_release_irq;
120 }
121 //这个应该是注册检测高阻抗的耳机延长线设备的插入中断
122 ret = wcd9xxx_spmi_request_irq(mbhc->intr_ids->mbhc_hs_ins_intr,
123 wcd_mbhc_hs_ins_irq,    //耳机插入监测函数
124 "Elect Insert", mbhc);
125 if (ret) {
126 pr_err("%s: Failed to request irq %d\n", __func__,
127 mbhc->intr_ids->mbhc_hs_ins_intr);
128 goto err_mbhc_hs_ins_irq;
129 }
130 wcd9xxx_spmi_disable_irq(mbhc->intr_ids->mbhc_hs_ins_intr);
131   //这个应该是注册检测高阻抗的耳机延长线设备的拔出中断
132 ret = wcd9xxx_spmi_request_irq(mbhc->intr_ids->mbhc_hs_rem_intr,
133 wcd_mbhc_hs_rem_irq,
134 "Elect Remove", mbhc);
135 if (ret) {
136 pr_err("%s: Failed to request irq %d\n", __func__,
137 mbhc->intr_ids->mbhc_hs_rem_intr);
138 goto err_mbhc_hs_rem_irq;
139 }
140 wcd9xxx_spmi_disable_irq(mbhc->intr_ids->mbhc_hs_rem_intr);
141
142 ret = wcd9xxx_spmi_request_irq(mbhc->intr_ids->hph_left_ocp,
143 wcd_mbhc_hphl_ocp_irq, "HPH_L OCP detect",
144 mbhc);                         
145 if (ret) {
146 pr_err("%s: Failed to request irq %d\n", __func__,
147 mbhc->intr_ids->hph_left_ocp);
148 goto err_hphl_ocp_irq;
149 }
150
151 ret = wcd9xxx_spmi_request_irq(mbhc->intr_ids->hph_right_ocp,
152 wcd_mbhc_hphr_ocp_irq, "HPH_R OCP detect",
153 mbhc);
154 if (ret) {
155 pr_err("%s: Failed to request irq %d\n", __func__,
156 mbhc->intr_ids->hph_right_ocp);
157 goto err_hphr_ocp_irq;
158 }
159
160 pr_debug("%s: leave ret %d\n", __func__, ret);
161 return ret;
162
163 err_hphr_ocp_irq:
164 wcd9xxx_spmi_free_irq(mbhc->intr_ids->hph_left_ocp, mbhc);
165 err_hphl_ocp_irq:
166 wcd9xxx_spmi_free_irq(mbhc->intr_ids->mbhc_hs_rem_intr, mbhc);
167 err_mbhc_hs_rem_irq:
168 wcd9xxx_spmi_free_irq(mbhc->intr_ids->mbhc_hs_ins_intr, mbhc);
169 err_mbhc_hs_ins_irq:
170 wcd9xxx_spmi_free_irq(mbhc->intr_ids->mbhc_btn_release_intr, mbhc);
171 err_btn_release_irq:
172 wcd9xxx_spmi_free_irq(mbhc->intr_ids->mbhc_btn_press_intr, mbhc);
173 err_btn_press_irq:
174 wcd9xxx_spmi_free_irq(mbhc->intr_ids->mbhc_sw_intr, mbhc);
175 err_mbhc_sw_irq:
176 msm8x16_unregister_notifier(codec, &mbhc->nblock);
177 mutex_destroy(&mbhc->codec_resource_lock);
178 err:
179 pr_debug("%s: leave ret %d\n", __func__, ret);
180 return ret;
181 }
 static irqreturn_t wcd_mbhc_mech_plug_detect_irq(int irq, void *data)
{
int r = IRQ_HANDLED;
struct wcd_mbhc *mbhc = data; pr_debug("%s: enter\n", __func__);
if (unlikely(wcd9xxx_spmi_lock_sleep() == false)) {
pr_warn("%s: failed to hold suspend\n", __func__);
r = IRQ_NONE;
} else {
/* Call handler */
wcd_mbhc_swch_irq_handler(mbhc);        //中断函数中的接口,负责处理相应的
wcd9xxx_spmi_unlock_sleep();
}
pr_debug("%s: leave %d\n", __func__, r);
return r;
}
//初始化函数,主要注册了耳机插拔和耳机按键的input设备,注册了耳机四个按键的键值,注册了一系列的中断,我们先看看其中比较重要的三个中断,耳机插入中断和按键按下松开中断。
 static void wcd_mbhc_swch_irq_handler(struct wcd_mbhc *mbhc)
{
bool detection_type;
bool micbias1;
struct snd_soc_codec *codec = mbhc->codec;
pr_debug("%s: enter\n", __func__); WCD_MBHC_RSC_LOCK(mbhc); mbhc->in_swch_irq_handler = true; /* cancel pending button press, 如果有耳机按键任务在运行,去掉掉该任务*/ */
if (wcd_cancel_btn_work(mbhc))
pr_debug("%s: button press is canceled\n", __func__);
  /*读取当前的检测类型,如果detection_type = 1, 是指当前为插入,检测插入耳机类型,如果为0,表示当前拔出*/
detection_type = (snd_soc_read(codec,
MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_1)) & 0x20; /* Set the detection type appropriately */
snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_1,
0x20, (!detection_type << )); pr_debug("%s: mbhc->current_plug: %d detection_type: %d\n", __func__,
mbhc->current_plug, detection_type);
wcd_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
  /*如果当前是检测耳机插入, 就进行耳机插入的检测*/
micbias1 = (snd_soc_read(codec, MSM8X16_WCD_A_ANALOG_MICB_1_EN) & 0x80);
if ((mbhc->current_plug == MBHC_PLUG_TYPE_NONE) &&
detection_type) {
/* Make sure MASTER_BIAS_CTL is enabled *//*下面是使能一系列的micbias相关的寄存器,把micbias2使能*/
snd_soc_update_bits(codec,
MSM8X16_WCD_A_ANALOG_MASTER_BIAS_CTL,
0x30, 0x30);
snd_soc_update_bits(codec,
MSM8X16_WCD_A_ANALOG_MICB_1_EN,
0x04, 0x04);
if (!mbhc->mbhc_cfg->hs_ext_micbias)
/* Enable Tx2 RBias if the headset
* is using internal micbias*/
snd_soc_update_bits(codec,
MSM8X16_WCD_A_ANALOG_MICB_1_INT_RBIAS,
0x10, 0x10);
/* Remove pull down on MIC BIAS2 */
snd_soc_update_bits(codec,
MSM8X16_WCD_A_ANALOG_MICB_2_EN,
0x20, 0x00);
/* Enable HW FSM */
snd_soc_update_bits(codec,
MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL,
0x80, 0x80);
/* Apply trim if needed on the device */
if (mbhc->mbhc_cb && mbhc->mbhc_cb->trim_btn_reg)
mbhc->mbhc_cb->trim_btn_reg(codec);
/* Enable external voltage source to micbias if present */ /*如果micbias电压是外供的,这里把它使能*/
if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
mbhc->mbhc_cb->enable_mb_source(codec, true);
mbhc->btn_press_intr = false;
wcd_mbhc_detect_plug_type(mbhc);
       /*下面是检测耳机拔出,耳机拔出后,关闭micbias电压, 上报耳机拔出事件*/
} else if ((mbhc->current_plug != MBHC_PLUG_TYPE_NONE)
&& !detection_type) {
/* Disable external voltage source to micbias if present */
if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
mbhc->mbhc_cb->enable_mb_source(codec, false);
/* Disable HW FSM */
snd_soc_update_bits(codec,
MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL,
0xB0, 0x00);
snd_soc_update_bits(codec,
MSM8X16_WCD_A_ANALOG_MICB_1_EN,
0x04, 0x00);
if (mbhc->mbhc_cb && mbhc->mbhc_cb->set_cap_mode)
mbhc->mbhc_cb->set_cap_mode(codec, micbias1, false);
mbhc->btn_press_intr = false;
if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) {        //如果判断是headphone,上报数据;
wcd_mbhc_report_plug(mbhc, , SND_JACK_HEADPHONE);
} else if (mbhc->current_plug == MBHC_PLUG_TYPE_GND_MIC_SWAP) {
wcd_mbhc_report_plug(mbhc, , SND_JACK_UNSUPPORTED);
} else if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET) {
/* make sure to turn off Rbias */
snd_soc_update_bits(codec,
MSM8X16_WCD_A_ANALOG_MICB_1_INT_RBIAS,
0x18, 0x08);
snd_soc_update_bits(codec,
MSM8X16_WCD_A_ANALOG_MICB_2_EN,
0x20, 0x20);
wcd9xxx_spmi_disable_irq(
mbhc->intr_ids->mbhc_hs_rem_intr);
wcd9xxx_spmi_disable_irq(
mbhc->intr_ids->mbhc_hs_ins_intr);
snd_soc_update_bits(codec,
MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_1,
0x01, 0x01);
snd_soc_update_bits(codec,
MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_2,
0x06, 0x00);
wcd_mbhc_report_plug(mbhc, , SND_JACK_HEADSET);
          /*如果当前是耳机延长线设备拔出,就关闭相关的中断检测,上报LINEOUT设备拔出事件*/
} else if (mbhc->current_plug == MBHC_PLUG_TYPE_HIGH_HPH) {
mbhc->is_extn_cable = false;
wcd9xxx_spmi_disable_irq(
mbhc->intr_ids->mbhc_hs_rem_intr);
wcd9xxx_spmi_disable_irq(
mbhc->intr_ids->mbhc_hs_ins_intr);
snd_soc_update_bits(codec,
MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_1,
0x01, 0x01);
snd_soc_update_bits(codec,
MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_2,
0x06, 0x00);
wcd_mbhc_report_plug(mbhc, , SND_JACK_LINEOUT);
}
} else if (!detection_type) {
/* Disable external voltage source to micbias if present */
if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
mbhc->mbhc_cb->enable_mb_source(codec, false);
/* Disable HW FSM */
snd_soc_update_bits(codec,
MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL,
0xB0, 0x00);
} mbhc->in_swch_irq_handler = false;
WCD_MBHC_RSC_UNLOCK(mbhc);
pr_debug("%s: leave\n", __func__);
}

经过一系列的检测,判断是headset,headphones等,如果是headphones,最终通过wcd_mbhc_jack_report将数据汇报上去。

 static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion,
enum snd_jack_types jack_type)
{
struct snd_soc_codec *codec = mbhc->codec;
WCD_MBHC_RSC_ASSERT_LOCKED(mbhc); pr_debug("%s: enter insertion %d hph_status %x\n",
__func__, insertion, mbhc->hph_status);
if (!insertion) {
/* Report removal */
mbhc->hph_status &= ~jack_type;
/*
* cancel possibly scheduled btn work and
* report release if we reported button press
*/
if (wcd_cancel_btn_work(mbhc)) {
pr_debug("%s: button press is canceled\n", __func__);
} else if (mbhc->buttons_pressed) {
pr_debug("%s: release of button press%d\n",
__func__, jack_type);
wcd_mbhc_jack_report(mbhc, &mbhc->button_jack, ,
mbhc->buttons_pressed);
mbhc->buttons_pressed &=
~WCD_MBHC_JACK_BUTTON_MASK;
} if (mbhc->micbias_enable)
mbhc->micbias_enable = false; mbhc->zl = mbhc->zr = ;
mbhc->is_hs_inserted = false;
pr_debug("%s: Reporting removal %d(%x)\n", __func__,
jack_type, mbhc->hph_status);
wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack,
mbhc->hph_status, WCD_MBHC_JACK_MASK);
wcd_mbhc_set_and_turnoff_hph_padac(mbhc);
hphrocp_off_report(mbhc, SND_JACK_OC_HPHR);
hphlocp_off_report(mbhc, SND_JACK_OC_HPHL);
mbhc->current_plug = MBHC_PLUG_TYPE_NONE;
} else {
/*
* Report removal of current jack type.
* Headphone to headset shouldn't report headphone
* removal.
*/
if (mbhc->mbhc_cfg->detect_extn_cable &&
(mbhc->current_plug == MBHC_PLUG_TYPE_HIGH_HPH ||
jack_type == SND_JACK_LINEOUT) &&
(mbhc->hph_status && mbhc->hph_status != jack_type)) { if (mbhc->micbias_enable)
mbhc->micbias_enable = false; mbhc->zl = mbhc->zr = ;
mbhc->is_hs_inserted = false;
pr_debug("%s: Reporting removal (%x)\n",
__func__, mbhc->hph_status);
wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack,
, WCD_MBHC_JACK_MASK); if (mbhc->hph_status == SND_JACK_LINEOUT) { pr_debug("%s: Enable micbias\n", __func__);
/* Disable current source and enable micbias */
snd_soc_update_bits(codec,
MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL,
0xB0, 0x80);
snd_soc_update_bits(codec,
MSM8X16_WCD_A_ANALOG_MICB_2_EN,
0x80, 0x80); pr_debug("%s: set up elec removal detection\n",
__func__);
snd_soc_update_bits(codec,
MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_1,
0x01, 0x00);
usleep_range(, );
wcd9xxx_spmi_enable_irq(
mbhc->intr_ids->mbhc_hs_rem_intr);
}
mbhc->hph_status &= ~(SND_JACK_HEADSET |
SND_JACK_LINEOUT |
SND_JACK_UNSUPPORTED);
} if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET &&
jack_type == SND_JACK_HEADPHONE)
mbhc->hph_status &= ~SND_JACK_HEADSET; /* Report insertion */
mbhc->hph_status |= jack_type; if (jack_type == SND_JACK_HEADPHONE)
mbhc->current_plug = MBHC_PLUG_TYPE_HEADPHONE;
else if (jack_type == SND_JACK_UNSUPPORTED)
mbhc->current_plug = MBHC_PLUG_TYPE_GND_MIC_SWAP;
else if (jack_type == SND_JACK_HEADSET) {
mbhc->current_plug = MBHC_PLUG_TYPE_HEADSET;
mbhc->jiffies_atreport = jiffies;
}
else if (jack_type == SND_JACK_LINEOUT)
mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH; if (mbhc->impedance_detect)
wcd_mbhc_calc_impedance(mbhc,
&mbhc->zl, &mbhc->zr);
mbhc->is_hs_inserted = true;
pr_debug("%s: Reporting insertion %d(%x)\n", __func__,
jack_type, mbhc->hph_status);
wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack,
mbhc->hph_status, WCD_MBHC_JACK_MASK);
wcd_mbhc_clr_and_turnon_hph_padac(mbhc);
}
pr_debug("%s: leave hph_status %x\n", __func__, mbhc->hph_status);
}
 static void wcd_mbhc_jack_report(struct wcd_mbhc *mbhc,
struct snd_soc_jack *jack, int status, int mask)
{
snd_soc_jack_report_no_dapm(jack, status, mask);
}
 void snd_soc_jack_report_no_dapm(struct snd_soc_jack *jack, int status,
int mask)
{
jack->status &= ~mask;
jack->status |= status & mask; snd_jack_report(jack->jack, jack->status);
}
EXPORT_SYMBOL_GPL(snd_soc_jack_report_no_dapm);
 void snd_jack_report(struct snd_jack *jack, int status)
{
int i; if (!jack)
return; for (i = ; i < ARRAY_SIZE(jack->key); i++) {
int testbit = SND_JACK_BTN_0 >> i; if (jack->type & testbit)
input_report_key(jack->input_dev, jack->key[i],
status & testbit);
} for (i = ; i < ARRAY_SIZE(jack_switch_types); i++) {
int testbit = << i;
if (jack->type & testbit)
input_report_switch(jack->input_dev,
jack_switch_types[i],
status & testbit);
} input_sync(jack->input_dev);      //上报键值
}
EXPORT_SYMBOL(snd_jack_report);

最终汇报上去的有效数据是5   6   1 (插头类型 耳机信号 值插入)

            0   0   0(同步)

插入的时候:

 高通msm8909耳机调试

拔出的时候:

高通msm8909耳机调试

然后也可以根据高通文档判断是否是handset还是handphone:

高通msm8909耳机调试

高通是通过申请为/dev/input/event事件来确定的;

handset插入事件:

高通msm8909耳机调试

handset拔出事件:

高通msm8909耳机调试

上一篇:memcpy函数用法


下一篇:OVER(PARTITION BY)函数用法