一.
在高通平台中,默认使用内部codec的时候,耳机的输出及控制都是在内部codec中进行的,所以,可以想象得到,耳机的整个初始化起源过程,是在codec的初始化中。高通平台的machine驱动文件一般都是平台名字开头的,例如8974的是msm8974.c, 8998的是msm8998.c,8909的是msm8x16.c。可以通过cat proc/asound/cards找到声卡的名字,根据名字可以找到该平台的machine驱动文件。同时可以根据machine驱动的compatible的名字,找到dts文件中,sound相关的信息。如qcom,msm8x16-audio-codec, 在arch/arm/boot/dts/NX505J/msm8974.dtsi文件中,可以找到sound相关信息。

1 sound {
2 compatible = "qcom,msm8x16-audio-codec";
3 qcom,model = "msm8909-skue-snd-card";
4 qcom,msm-snd-card-id = <0>;
5 qcom,msm-codec-type = "internal";
6 qcom,msm-ext-pa = "primary";
7 qcom,msm-mclk-freq = <9600000>;
8 qcom,msm-mbhc-hphl-swh = <1>;
9 qcom,msm-mbhc-gnd-swh = <0>;
10 qcom,msm-hs-micbias-type = "internal";
11 qcom,msm-micbias1-ext-cap;
12 qcom,msm-micbias2-ext-cap;
13 ...

其中 qcom,model = "msm8909-skue-snd-card", 即注册的声卡名字。
然后找到系统注册进去的dai_link的地方,在后端的dai_link中,可以找到primary mi2s playback那路dai_link,在高通平台中,这primary_mi2s这一路i2s,都是留给内部codec用的,所以,这路的
dai_link上的codec_name和codec_dai_name,就是对应着内部codec的信息:

1 {
2 .name = LPASS_BE_PRI_MI2S_RX,
3 .stream_name = "Primary MI2S Playback",
4 .cpu_dai_name = "msm-dai-q6-mi2s.0",
5 .platform_name = "msm-pcm-routing",
6 .codec_name = "tombak_codec",
7 .codec_dai_name = "msm8x16_wcd_i2s_rx1",
8 .no_pcm = 1,
9 .be_id = MSM_BACKEND_DAI_PRI_MI2S_RX,
10 .init = &msm_audrx_init,
11 .be_hw_params_fixup = msm_pri_rx_be_hw_params_fixup,
12 .ops = &msm8x16_mi2s_be_ops,
13 .ignore_suspend = 1,
14 },

由此我们可以找到高通平台默认的codec驱动文件msm8x16-wcd.c,在该文件中,注册了codec_dai_driver : msm8x16_wcd_i2s_rx1。
那这里就要谈论一个问题,在初始化的时候,如何凭借dai_link中的codec信息找到对应的codec,答案是codec_name。但注意,这里并不是通过这个名字直接寻找的,例如8909平台。
在设备树文件msm8909-mtp.dtsi中,sound节点下有如下信息:
1 asoc-codec = <&stub_codec>, <&pm8909_conga_dig>; 2 asoc-codec-names = "msm-stub-codec.1", "tombak_codec";
在初始化的时候,dai_link中的codec_name会跟这里的asoc-codec-names进行匹配,进而获取上面asoc-codec中的codec_node :

1 pm8909_conga_dig: 8909_wcd_codec@f000 {
2 compatible = "qcom,msm8x16_wcd_codec";
3 reg = <0xf000 0x100>;
4 interrupt-parent = <&spmi_bus>;
5 ...
6 }

而这个node节点正式codec驱动的设备树节点。在soc_bind_dai_link()函数中,会做出如下处理:

1 /*注册codec的时候,会将所有注册的codec链接到codec_list中*/
2 list_for_each_entry(codec, &codec_list, list) {
3 if (dai_link->codec_of_node) {
4 /*根据设备数节点句柄进行匹配*/
5 if (codec->dev->of_node != dai_link->codec_of_node)
6 continue;
7 } else {
8 /*如果句柄为空,根据,codec_name进行匹配,在这里不会走这里,其实codec_name是 wcd-spmi-core.1*/
9 if (strcmp(codec->name, dai_link->codec_name))
10 continue;
11 }
12
13 rtd->codec = codec;
14
15 /*找到codec之后,根据codec_dai的名字找到对应的codec_dai*/
16 list_for_each_entry(codec_dai, &dai_list, list) {
17 if (codec->dev == codec_dai->dev &&
18 !strcmp(codec_dai->name,
19 dai_link->codec_dai_name)) {
20
21 rtd->codec_dai = codec_dai;
22 }
23 }
24 }

所以,我们可以根据dai_link中的codec_dai的名字或者codec名字来找到对应的codec驱动。
二.
耳机部分的初始化是在codec_driver的probe函数中完成的:
调用wcd_mbhc_init(&msm8x16_wcd_priv->mbhc, codec, &mbhc_cb, &intr_ids, true); 进行初始化
调用msm8x16_wcd_set_micb_v(codec); 设置micbias电压
调用msm8x16_wcd_configure_cap(codec, false, false); 根据外部有没有接电容来初始化电容模式
后面两个个处理比较简单:
1). msm8x16_wcd_set_micb_v(codec); 设置Micbias电压:
有时候一些大阻抗的耳机,比如苹果耳机,它的mic工作的时候要求的电压要高些,比如2.7v,而一般高通平台是把micbias电压设置成1.8v,所以,这里就需要更改来满足要求。
如果设备树中定义了属性qcom,cdc-micbias-cfilt-mv, 就更改该属性的值,如果没有就采用默认值MICBIAS_DEFAULT_VAL。
所以,可以通过修改 #define MICBIAS_DEFAULT_VAL 1800000 或者更改 qcom,cdc-micbias-cfilt-mv = <2700>; 来修改micbias电压值
2). msm8x16_wcd_configure_cap()这个函数是根据micbias1和micbias2外部有没有接电容,来配置设备树中的qcom,msm-micbias2-ext-cap属性,见下图,在micbias1和micbias2上面都外接了一个电容,所以,需要在dtsi文件中配置qcom,msm-micbias2-ext-cap; 和qcom,msm-micbias1-ext-cap

3). 初始化函数wcd_mbhc_init():

1 int wcd_mbhc_init(struct wcd_mbhc *mbhc, struct snd_soc_codec *codec,
2 const struct wcd_mbhc_cb *mbhc_cb,
3 const struct wcd_mbhc_intr *mbhc_cdc_intr_ids,
4 bool impedance_det_en)
5 {
6 /*注册耳机插拔和按键的input设备,设置耳机按键值,*/
7 if (mbhc->headset_jack.jack == NULL) {
8 ret = snd_soc_jack_new(codec, "Headset Jack",
9 WCD_MBHC_JACK_MASK, &mbhc->headset_jack);
10
11 ret = snd_soc_jack_new(codec, "Button Jack",
12 WCD_MBHC_JACK_BUTTON_MASK,
13 &mbhc->button_jack);
14
15 ret = snd_jack_set_key(mbhc->button_jack.jack,
16 SND_JACK_BTN_0,
17 KEY_MEDIA);
18
19 ret = snd_jack_set_key(mbhc->button_jack.jack,
20 SND_JACK_BTN_1,
21 KEY_VOICECOMMAND);
22
23 ret = snd_jack_set_key(mbhc->button_jack.jack,
24 SND_JACK_BTN_2,
25 KEY_VOLUMEUP);
26
27 ret = snd_jack_set_key(mbhc->button_jack.jack,
28 SND_JACK_BTN_3,
29 KEY_VOLUMEDOWN);
30
31 INIT_DELAYED_WORK(&mbhc->mbhc_firmware_dwork,
32 wcd_mbhc_fw_read);
33 INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd_btn_lpress_fn);
34 }
35
36 /* Register event notifier */
37 mbhc->nblock.notifier_call = wcd_event_notify;
38 ret = msm8x16_register_notifier(codec, &mbhc->nblock);
39 if (ret) {
40 pr_err("%s: Failed to register notifier %d\n", __func__, ret);
41 return ret;
42 }
43
44 init_waitqueue_head(&mbhc->wait_btn_press);
45 mutex_init(&mbhc->codec_resource_lock);
46
47 /*申请初测耳机插拔中断*/
48 ret = wcd9xxx_spmi_request_irq(mbhc->intr_ids->mbhc_sw_intr,
49 wcd_mbhc_mech_plug_detect_irq,
50 "mbhc sw intr", mbhc);
51 if (ret) {
52 pr_err("%s: Failed to request irq %d, ret = %d\n", __func__,
53 mbhc->intr_ids->mbhc_sw_intr, ret);
54 goto err_mbhc_sw_irq;
55 }
56
57 /*申请注册耳机按键按下的中断*/
58 ret = wcd9xxx_spmi_request_irq(mbhc->intr_ids->mbhc_btn_press_intr,
59 wcd_mbhc_btn_press_handler,
60 "Button Press detect",
61 mbhc);
62 if (ret) {
63 pr_err("%s: Failed to request irq %d\n", __func__,
64 mbhc->intr_ids->mbhc_btn_press_intr);
65 goto err_btn_press_irq;
66 }
67
68 /*申请注册耳机按键松开的中断*/
69 ret = wcd9xxx_spmi_request_irq(mbhc->intr_ids->mbhc_btn_release_intr,
70 wcd_mbhc_release_handler,
71 "Button Release detect", mbhc);
72 if (ret) {
73 pr_err("%s: Failed to request irq %d\n", __func__,
74 mbhc->intr_ids->mbhc_btn_release_intr);
75 goto err_btn_release_irq;
76 }
77
78 /*这个应该是注册检测高阻抗的耳机延长线设备的插入中断*/
79 ret = wcd9xxx_spmi_request_irq(mbhc->intr_ids->mbhc_hs_ins_intr,
80 wcd_mbhc_hs_ins_irq,
81 "Elect Insert", mbhc);
82 if (ret) {
83 pr_err("%s: Failed to request irq %d\n", __func__,
84 mbhc->intr_ids->mbhc_hs_ins_intr);
85 goto err_mbhc_hs_ins_irq;
86 }
87 wcd9xxx_spmi_disable_irq(mbhc->intr_ids->mbhc_hs_ins_intr);
88
89 /*这个应该是注册检测高阻抗的耳机延长线设备的拔出中断*/
90 ret = wcd9xxx_spmi_request_irq(mbhc->intr_ids->mbhc_hs_rem_intr,
91 wcd_mbhc_hs_rem_irq,
92 "Elect Remove", mbhc);
93 if (ret) {
94 pr_err("%s: Failed to request irq %d\n", __func__,
95 mbhc->intr_ids->mbhc_hs_rem_intr);
96 goto err_mbhc_hs_rem_irq;
97 }
98 wcd9xxx_spmi_disable_irq(mbhc->intr_ids->mbhc_hs_rem_intr);
99
100 ret = wcd9xxx_spmi_request_irq(mbhc->intr_ids->hph_left_ocp,
101 wcd_mbhc_hphl_ocp_irq, "HPH_L OCP detect",
102 mbhc);
103 if (ret) {
104 pr_err("%s: Failed to request irq %d\n", __func__,
105 mbhc->intr_ids->hph_left_ocp);
106 goto err_hphl_ocp_irq;
107 }
108
109 ret = wcd9xxx_spmi_request_irq(mbhc->intr_ids->hph_right_ocp,
110 wcd_mbhc_hphr_ocp_irq, "HPH_R OCP detect",
111 mbhc);
112 if (ret) {
113 pr_err("%s: Failed to request irq %d\n", __func__,
114 mbhc->intr_ids->hph_right_ocp);
115 goto err_hphr_ocp_irq;
116 }
117
118 }

初始化函数,主要注册了耳机插拔和耳机按键的input设备,注册了耳机四个按键的键值,注册了一系列的中断,我们先看看其中比较重要的三个中断,耳机插入中断和按键按下松开中断。
可能一般项目要求我们耳机按键只支持media键就好了,而要求我们去掉其他的耳机按键,可以在这里进行更改.
我们看看耳机插拔的中断处理函数:wcd_mbhc_mech_plug_detect_irq()

1 static irqreturn_t wcd_mbhc_mech_plug_detect_irq(int irq, void *data)
2 {
3 wcd_mbhc_swch_irq_handler(mbhc);
4 }
5
6
7 static void wcd_mbhc_swch_irq_handler(struct wcd_mbhc *mbhc)
8 {
9 bool detection_type;
10 bool micbias1;
11 struct snd_soc_codec *codec = mbhc->codec;
12 pr_debug("%s: enter\n", __func__);
13
14 WCD_MBHC_RSC_LOCK(mbhc);
15
16 mbhc->in_swch_irq_handler = true;
17
18 /*如果有耳机按键任务在运行,去掉掉该任务*/
19 if (wcd_cancel_btn_work(mbhc))
20 pr_debug("%s: button press is canceled\n", __func__);
21
22 /*读取当前的检测类型,如果detection_type = 1, 是指当前为插入,检测插入耳机类型,如果为0,表示当前拔出*/
23 detection_type = (snd_soc_read(codec,
24 MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_1)) & 0x20;
25
26 /* Set the detection type appropriately */
27 snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_1,
28 0x20, (!detection_type << 5));
29
30 pr_debug("%s: mbhc->current_plug: %d detection_type: %d\n", __func__,
31 mbhc->current_plug, detection_type);
32 wcd_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
33
34 /*如果当前是检测耳机插入, 就进行耳机插入的检测*/
35 micbias1 = (snd_soc_read(codec, MSM8X16_WCD_A_ANALOG_MICB_1_EN) & 0x80);
36 if ((mbhc->current_plug == MBHC_PLUG_TYPE_NONE) &&
37 detection_type) {
38 /*下面是使能一系列的micbias相关的寄存器,把micbias2使能*/
39 /* Make sure MASTER_BIAS_CTL is enabled */
40 snd_soc_update_bits(codec,
41 MSM8X16_WCD_A_ANALOG_MASTER_BIAS_CTL,
42 0x30, 0x30);
43 snd_soc_update_bits(codec,
44 MSM8X16_WCD_A_ANALOG_MICB_1_EN,
45 0x04, 0x04);
46 if (!mbhc->mbhc_cfg->hs_ext_micbias)
47 /* Enable Tx2 RBias if the headset
48 * is using internal micbias*/
49 snd_soc_update_bits(codec,
50 MSM8X16_WCD_A_ANALOG_MICB_1_INT_RBIAS,
51 0x10, 0x10);
52 /* Remove pull down on MIC BIAS2 */
53 snd_soc_update_bits(codec,
54 MSM8X16_WCD_A_ANALOG_MICB_2_EN,
55 0x20, 0x00);
56 /* Enable HW FSM */
57 snd_soc_update_bits(codec,
58 MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL,
59 0x80, 0x80);
60 /* Apply trim if needed on the device */
61 if (mbhc->mbhc_cb && mbhc->mbhc_cb->trim_btn_reg)
62 mbhc->mbhc_cb->trim_btn_reg(codec);
63 /*如果micbias电压是外供的,这里把它使能*/
64 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
65 mbhc->mbhc_cb->enable_mb_source(codec, true);
66 mbhc->btn_press_intr = false;
67 /*开始检测插入耳机类型*/
68 wcd_mbhc_detect_plug_type(mbhc);
69 }
70 /*下面是检测耳机拔出,耳机拔出后,关闭micbias电压, 上报耳机拔出事件*/
71 else if ((mbhc->current_plug != MBHC_PLUG_TYPE_NONE)
72 && !detection_type) {
73 /* Disable external voltage source to micbias if present */
74 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
75 mbhc->mbhc_cb->enable_mb_source(codec, false);
76 /* Disable HW FSM */
77 snd_soc_update_bits(codec,
78 MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL,
79 0xB0, 0x00);
80 snd_soc_update_bits(codec,
81 MSM8X16_WCD_A_ANALOG_MICB_1_EN,
82 0x04, 0x00);
83 if (mbhc->mbhc_cb && mbhc->mbhc_cb->set_cap_mode)
84 mbhc->mbhc_cb->set_cap_mode(codec, micbias1, false);
85 mbhc->btn_press_intr = false;
86 if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) {
87 wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADPHONE);
88 } else if (mbhc->current_plug == MBHC_PLUG_TYPE_GND_MIC_SWAP) {
89 wcd_mbhc_report_plug(mbhc, 0, SND_JACK_UNSUPPORTED);
90 } else if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET) {
91 /* make sure to turn off Rbias */
92 snd_soc_update_bits(codec,
93 MSM8X16_WCD_A_ANALOG_MICB_1_INT_RBIAS,
94 0x18, 0x08);
95 snd_soc_update_bits(codec,
96 MSM8X16_WCD_A_ANALOG_MICB_2_EN,
97 0x20, 0x20);
98 wcd9xxx_spmi_disable_irq(
99 mbhc->intr_ids->mbhc_hs_rem_intr);
100 wcd9xxx_spmi_disable_irq(
101 mbhc->intr_ids->mbhc_hs_ins_intr);
102 snd_soc_update_bits(codec,
103 MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_1,
104 0x01, 0x01);
105 snd_soc_update_bits(codec,
106 MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_2,
107 0x06, 0x00);
108 wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADSET);
109 }
110 /*如果当前是耳机延长线设备拔出,就关闭相关的中断检测,上报LINEOUT设备拔出事件*/
111 else if (mbhc->current_plug == MBHC_PLUG_TYPE_HIGH_HPH) {
112 mbhc->is_extn_cable = false;
113 wcd9xxx_spmi_disable_irq(
114 mbhc->intr_ids->mbhc_hs_rem_intr);
115 wcd9xxx_spmi_disable_irq(
116 mbhc->intr_ids->mbhc_hs_ins_intr);
117 snd_soc_update_bits(codec,
118 MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_1,
119 0x01, 0x01);
120 snd_soc_update_bits(codec,
121 MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_2,
122 0x06, 0x00);
123 wcd_mbhc_report_plug(mbhc, 0, SND_JACK_LINEOUT);
124 }
125 } else if (!detection_type) {
126 /* Disable external voltage source to micbias if present */
127 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
128 mbhc->mbhc_cb->enable_mb_source(codec, false);
129 /* Disable HW FSM */
130 snd_soc_update_bits(codec,
131 MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL,
132 0xB0, 0x00);
133 }
134
135 mbhc->in_swch_irq_handler = false;
136 WCD_MBHC_RSC_UNLOCK(mbhc);
137 pr_debug("%s: leave\n", __func__);
138 }

耳机插拔中断处理函数中的处理可以分为三部分:
1). 检测到耳机插入, 打开micbias电压,进行耳机类型的检测
2). 检测到耳机拔出,关闭micbias电压,上报耳机拔出事件
3). 检测到耳机延长线设备拔出,上报耳机延长线设备拔出事件
我们再看看检测插入耳机类型的处理函数wcd_mbhc_detect_plug_type()

1 static void wcd_mbhc_detect_plug_type(struct wcd_mbhc *mbhc)
2 {
3 timeout_result = wait_event_interruptible_timeout(mbhc->wait_btn_press,
4 mbhc->is_btn_press, timeout);
5
6 WCD_MBHC_RSC_LOCK(mbhc);
7 result1 = snd_soc_read(codec, MSM8X16_WCD_A_ANALOG_MBHC_BTN_RESULT);
8 result2 = snd_soc_read(codec,
9 MSM8X16_WCD_A_ANALOG_MBHC_ZDET_ELECT_RESULT);
10
11 /*没有按键按下, 检测耳机类型*/
12 if (!timeout_result) {
13 pr_debug("%s No btn press interrupt\n", __func__);
14 /*
15 * Check if there is any cross connection,
16 * Micbias and schmitt trigger (HPHL-HPHR)
17 * needs to be enabled.
18 */
19 pr_debug("%s: result1 %x, result2 %x\n", __func__,
20 result1, result2);
21 if (!(result2 & 0x01)) {
22 /*
23 * Cross connection result is not reliable
24 * so do check for it for 4 times to conclude
25 * cross connection occured or not.
26 */
27 /*检测耳机的各段有没有接反,例如欧标美标耳机mic和gnd是反的,
28 例如耳机只插入三截,导致耳机的左声道跟插孔的检测脚,左声道,右声道短接到一起,耳机的右声道跟插孔的地或者Mic短接到一起*/
29 do {
30 cross_conn = wcd_check_cross_conn(mbhc);
31 try++;
32 } while (try < GND_MIC_SWAP_THRESHOLD);
33 /*如果检测到有插反的动作,直接跳到后面调度任务去校准耳机插入类型*/
34 if (cross_conn) {
35 pr_debug("%s: cross con found, start polling\n",
36 __func__);
37 plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
38 goto exit;
39 }
40 }
41
42 /* Read back result1 and result2 value again to reconfirm*/
43 result1 = snd_soc_read(codec,
44 MSM8X16_WCD_A_ANALOG_MBHC_BTN_RESULT);
45 result2 = snd_soc_read(codec,
46 MSM8X16_WCD_A_ANALOG_MBHC_ZDET_ELECT_RESULT);
47
48 /*如果没有插反,根据结果,设置不同的耳机类型*/
49 if (!result1 && !(result2 & 0x01))
50 plug_type = MBHC_PLUG_TYPE_HEADSET;
51 else if (!result1 && (result2 & 0x01))
52 plug_type = MBHC_PLUG_TYPE_HIGH_HPH;
53 else {
54 plug_type = MBHC_PLUG_TYPE_INVALID;
55 goto exit;
56 }
57 } else {
58 /*根据结果设置耳机类型是三段耳机或者无效耳机*/
59 if (!result1 && !(result2 & 0x01))
60 plug_type = MBHC_PLUG_TYPE_HEADPHONE;
61 else {
62 plug_type = MBHC_PLUG_TYPE_INVALID;
63 goto exit;
64 }
65 }
66 exit:
67 pr_debug("%s: Valid plug found, plug type is %d\n",
68 __func__, plug_type);
69 /*退出的时候,如果是四段耳机或者三段耳机,上报耳机类型,如果其他情况,调度任务去校准耳机类型*/
70 if (plug_type == MBHC_PLUG_TYPE_HEADSET ||
71 plug_type == MBHC_PLUG_TYPE_HEADPHONE) {
72 wcd_mbhc_find_plug_and_report(mbhc, plug_type);
73 wcd_schedule_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
74 } else {
75 wcd_schedule_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
76 }
77 pr_debug("%s: leave\n", __func__);
78 }

耳机类型检测函数做的事情可以概括为:初步检测下耳机的插入状态,如果是三段四段耳机就上报耳机类型,然后,都调度任务进行进一步的耳机类型的检测。
我们大概可以想象的到校准耳机类型的函数做了哪些事情,设置各种状态寄存器,进一步去细分耳机类型,再上报。函数为wcd_correct_swch_plug();

1 static void wcd_correct_swch_plug(struct work_struct *work)
2 {
3 struct wcd_mbhc *mbhc;
4 struct snd_soc_codec *codec;
5 enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_INVALID;
6 unsigned long timeout;
7 u16 result1, result2;
8 bool wrk_complete = false;
9 int pt_gnd_mic_swap_cnt = 0;
10 int no_gnd_mic_swap_cnt = 0;
11 bool is_pa_on;
12 bool micbias2;
13 bool micbias1;
14
15 pr_debug("%s: enter\n", __func__);
16
17 mbhc = container_of(work, struct wcd_mbhc, correct_plug_swch);
18 codec = mbhc->codec;
19
20 /* Enable micbias for detection in correct work*/
21 wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
22 /*设置耳机检测超时时间,这里是3s*/
23 timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
24 while (!time_after(jiffies, timeout)) {
25 if (mbhc->hs_detect_work_stop) {
26 pr_debug("%s: stop requested: %d\n", __func__,
27 mbhc->hs_detect_work_stop);
28 wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_NONE);
29 goto exit;
30 }
31 mbhc->btn_press_intr = false;
32 /* Toggle FSM */
33 snd_soc_update_bits(codec,
34 MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL,
35 0x80, 0x00);
36 snd_soc_update_bits(codec,
37 MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL,
38 0x80, 0x80);
39 /* allow sometime and re-check stop requested again */
40 msleep(20);
41 if (mbhc->hs_detect_work_stop) {
42 pr_debug("%s: stop requested: %d\n", __func__,
43 mbhc->hs_detect_work_stop);
44 wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_NONE);
45 goto exit;
46 }
47 result1 = snd_soc_read(codec,
48 MSM8X16_WCD_A_ANALOG_MBHC_BTN_RESULT);
49 result2 = snd_soc_read(codec,
50 MSM8X16_WCD_A_ANALOG_MBHC_ZDET_ELECT_RESULT);
51 pr_debug("%s: result2 = %x\n", __func__, result2);
52
53 is_pa_on = snd_soc_read(codec,
54 MSM8X16_WCD_A_ANALOG_RX_HPH_CNP_EN) &
55 0x30;
56
57 /*
58 * instead of hogging system by contineous polling, wait for
59 * sometime and re-check stop request again.
60 */
61 msleep(180);
62 if ((!(result2 & 0x01)) && (!is_pa_on)) {
63 /*再次检测耳机脚插入,有没有错位,如果检测次数小于4次,就一直检测,
64 如果大于四次,就判定当前类型为MBHC_PLUG_TYPE_GND_MIC_SWAP反了,
65 如果等于四次,如果外面有接兼容欧标美标耳机的电路,就调接口函数去校准结果,兼容欧标美标耳机
66 */
67 if (wcd_check_cross_conn(mbhc)) {
68 pt_gnd_mic_swap_cnt++;
69 no_gnd_mic_swap_cnt = 0;
70 if (pt_gnd_mic_swap_cnt <
71 GND_MIC_SWAP_THRESHOLD) {
72 continue;
73 } else if (pt_gnd_mic_swap_cnt >
74 GND_MIC_SWAP_THRESHOLD) {
75 /*
76 * This is due to GND/MIC switch didn't
77 * work, Report unsupported plug.
78 */
79 pr_debug("%s: switch didnt work\n",
80 __func__);
81 plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
82 goto report;
83 } else {
84 plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
85 }
86 } else { /*如果没有错位现象,就将耳机类型设置为MBHC_PLUG_TYPE_HEADSET*/
87 no_gnd_mic_swap_cnt++;
88 pt_gnd_mic_swap_cnt = 0;
89 plug_type = MBHC_PLUG_TYPE_HEADSET;
90 if (no_gnd_mic_swap_cnt <
91 GND_MIC_SWAP_THRESHOLD) {
92 continue;
93 } else {
94 no_gnd_mic_swap_cnt = 0;
95 }
96 }
97 }
98 /*如果判定为gnd和mic反了,调用接口函数去兼容欧标美标耳机*/
99 if ((pt_gnd_mic_swap_cnt == GND_MIC_SWAP_THRESHOLD) &&
100 (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP)) {
101 /*
102 * if switch is toggled, check again,
103 * otherwise report unsupported plug
104 */
105 if (mbhc->mbhc_cfg->swap_gnd_mic &&
106 mbhc->mbhc_cfg->swap_gnd_mic(codec)) {
107 pr_debug("%s: US_EU gpio present,flip switch\n"
108 , __func__);
109 continue;
110 }
111 }
112
113 /*判定为高阻抗耳机*/
114 if (result2 == 1) {
115 pr_debug("%s: cable is extension cable\n", __func__);
116 plug_type = MBHC_PLUG_TYPE_HIGH_HPH;
117 wrk_complete = true;
118 } else {
119 /*重新判定当前耳机类型是四段耳机,如果之前判定的不是四段的,这里就去上报四段键值*/
120 pr_debug("%s: cable might be headset: %d\n", __func__,
121 plug_type);
122 if (!(plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP)) {
123 plug_type = MBHC_PLUG_TYPE_HEADSET;
124 /*
125 * Report headset only if not already reported
126 * and if there is not button press without
127 * release
128 */
129 if (mbhc->current_plug !=
130 MBHC_PLUG_TYPE_HEADSET &&
131 !mbhc->btn_press_intr) {
132 pr_debug("%s: cable is headset\n",
133 __func__);
134 goto report;
135 }
136 }
137 wrk_complete = false;
138 }
139 }
140 /*判定为三段耳机*/
141 if (!wrk_complete && mbhc->btn_press_intr) {
142 pr_debug("%s: Can be slow insertion of headphone\n", __func__);
143 wcd_cancel_btn_work(mbhc);
144 plug_type = MBHC_PLUG_TYPE_HEADPHONE;
145 }
146 /*
147 * If plug_tye is headset, we might have already reported either in
148 * detect_plug-type or in above while loop, no need to report again
149 */
150 if (!wrk_complete && plug_type == MBHC_PLUG_TYPE_HEADSET) {
151 pr_debug("%s: Headset already reported\n", __func__);
152 goto enable_supply;
153 }
154
155 if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH &&
156 (!det_extn_cable_en)) {
157 if (wcd_is_special_headset(mbhc)) {
158 pr_debug("%s: Special headset found %d\n",
159 __func__, plug_type);
160 plug_type = MBHC_PLUG_TYPE_HEADSET;
161 goto report;
162 }
163 }
164
165 report:
166 pr_debug("%s: Valid plug found, plug type %d wrk_cmpt %d btn_intr %d\n",
167 __func__, plug_type, wrk_complete,
168 mbhc->btn_press_intr);
169 WCD_MBHC_RSC_LOCK(mbhc);
170 wcd_mbhc_find_plug_and_report(mbhc, plug_type);
171 WCD_MBHC_RSC_UNLOCK(mbhc);
172 enable_supply:
173 /*根据耳机的类型,设置micbias电压的状态*/
174 wcd_enable_mbhc_supply(mbhc, plug_type);
175 exit:
176 micbias1 = (snd_soc_read(codec, MSM8X16_WCD_A_ANALOG_MICB_1_EN) & 0x80);
177 micbias2 = (snd_soc_read(codec, MSM8X16_WCD_A_ANALOG_MICB_2_EN) & 0x80);
178 if (mbhc->mbhc_cb && mbhc->mbhc_cb->set_cap_mode)
179 mbhc->mbhc_cb->set_cap_mode(codec, micbias1, micbias2);
180 wcd9xxx_spmi_unlock_sleep();
181 pr_debug("%s: leave\n", __func__);
182 }

wcd_correct_swch_plug()校准耳机类型函数:继续检测耳机插入是否有错位,当检测达到四次,调度接口函数,去兼容欧标美标耳机,大于四次,判定耳机插反,设置类型为MBHC_PLUG_TYPE_GND_MIC_SWAP, 检测耳机类型是否为高阻抗耳机MBHC_PLUG_TYPE_HIGH_HPH,检测耳机类型是否为三段耳机MBHC_PLUG_TYPE_HEADPHONE,最后上报响应的input事件,设置micbias电压的状态。 这里micbias电压是通过wcd_enable_mbhc_supply()设置的:

1 static void wcd_enable_mbhc_supply(struct wcd_mbhc *mbhc,
2 enum wcd_mbhc_plug_type plug_type)
3 {
4 /*
5 * Do not disable micbias if recording is going on or
6 * headset is inserted on the other side of the extn
7 * cable. If headset has been detected current source
8 * needs to be kept enabled for button detection to work.
9 * If the accessory type is invalid or unsupported, we
10 * dont need to enable either of them.
11 */
12 if (det_extn_cable_en && mbhc->is_extn_cable) {
13 if (plug_type == MBHC_PLUG_TYPE_HEADPHONE ||
14 plug_type == MBHC_PLUG_TYPE_HEADSET)
15 wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
16 } else {
17 if (plug_type == MBHC_PLUG_TYPE_HEADSET) {
18 if (mbhc->is_hs_recording)
19 wcd_enable_curr_micbias(mbhc,
20 WCD_MBHC_EN_MB);
21 else if ((test_bit(WCD_MBHC_EVENT_PA_HPHL,
22 &mbhc->event_state)) ||
23 (test_bit(WCD_MBHC_EVENT_PA_HPHR,
24 &mbhc->event_state)))
25 wcd_enable_curr_micbias(mbhc,
26 WCD_MBHC_EN_PULLUP);
27 else
28 wcd_enable_curr_micbias(mbhc,
29 WCD_MBHC_EN_CS);
30 } else if (plug_type == MBHC_PLUG_TYPE_HEADPHONE) {
31 wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_CS);
32 } else {
33 wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_NONE);
34 }
35 }
36 }

micbias电压有四种状态:
WCD_MBHC_EN_CS: 关闭micbias2电压
WCD_MBHC_EN_MB: 打开micbias电压
WCD_MBHC_EN_PULLUP: 打开micbias电压,并设置成PULL_UP状态
WCD_MBHC_EN_NONE: 关闭micbias电压
如果micbias外部接有电容,耳机类型是三段和四段耳机,设置micbias状态为WCD_MBHC_EN_MB
如果没有外接电容,耳机类型是四段耳机的时候,如果正在录音,设置micbias状态为WCD_MBHC_EN_MB, 如果左右声道PA在工作,即正在播放音乐,设置为WCD_MBHC_EN_PULLUP,
否则,设置状态为WCD_MBHC_EN_CS。
如果没有外接电容,耳机类型是三段耳机, 设置Micbias电压状态为WCD_MBHC_EN_CS
其他状态设置为WCD_MBHC_EN_NONE
三。以上过程,可以引用高通文档中的流程图:

上面有一个地方可以注意下,可以通过调节VREF的值,来兼容阻抗大一点的耳机:
调节kernel/sound/soc/codecs/wcd-mbhc-v2.c #define HS_VREF_MIN_VAL 1400
1.4v,最大只能识别7700欧阻抗的耳机, 这个阻抗指的是mic对地的阻抗,耳机的后两节之间的阻抗
1.5v,最大能识别11k
1.6v,最大能识别17.6k
1.7v,最大能识别37.4k
再高的阻抗,可以尝试提高micbias电压,来进行兼容。
我们总结下,一般在调试高通的耳机功能的时候,我们需要更改的东西:
1). 耳机的插拔检测:
qcom,msm-mbhc-hphl-swh = <1>; //0是NC,1是NO
NO是指耳机的accdet脚默认上拉到1.8v,插入耳机后,accdet脚跟左声道短接到一块,电平拉低。而NC是指耳机的accdet脚默认和左声道短接到一块,为低电平,插入耳机后,accdet脚与左声道断开,accdet脚变为高电平。如下图所示:

2). 耳机mic的micbias电压是外部接过去的还是内部接过去的,如上图micbias就是从外部接过去的
如果micbias电压是内部接过去的:
qcom,msm-hs-micbias-type = "internal";
"MIC BIAS Internal2", "Headset Mic",
"AMIC2", "MIC BIAS Internal2",
如果micbias电压是外部接过去的:
qcom,msm-hs-micbias-type = "external";
"MIC BIAS External2", "Headset Mic",
"AMIC2", "MIC BIAS External2",
3). 耳机所使用的micbias输出上是否接有外部电容,如果接有外部电容,需要添加:
qcom,msm-micbias2-ext-cap
4). 耳机是否支持欧标/美标的检测的兼容,如下图:

美标耳机的顺序为左/右/地/麦,欧标的顺序为左/右/麦/地,三段耳机是指没有麦的,从外观看的话,一般情况下,美标耳机的绝缘环是黑色的,欧标耳机的绝缘环是白色的。
如果要设备支持欧标/美标耳机的兼容检测的话,需要外加专用于检测的电路或者IC,所以一般情况下我们是没有添加兼容的功能的,如下图所示:

如果支持欧标/美标兼容检测,注意修改如下pinctrl的GPIO口。
cross-conn-det {
qcom,pins = <&gp 97>;
qcom,num-grp-pins = <1>;
qcom,pin-func = <0>;
label = "cross-conn-det-sw";
cross_conn_det_act: lines_on {
drive-strength = <8>;
output-low;
bias-pull-down;
};
cross_conn_det_sus: lines_off {
drive-strength = <2>;
bias-disable;
};
};
如果不支持欧标/美标的兼容检测,也记得从设备树中删除对以上pinctrl的引用,例如8909平台上删除:
pinctrl-names = "cdc_lines_act",
"cdc_lines_sus",
//"cross_conn_det_act",
//"cross_conn_det_sus",
"vdd_spkdrv_act",
"vdd_spkdrv_sus";
pinctrl-0 = <&cdc_pdm_lines_act &vdd_spkdrv_act>;
pinctrl-1 = <&cdc_pdm_lines_sus &vdd_spkdrv_sus>;
// pinctrl-2 = <&cross_conn_det_act>;
// pinctrl-3 = <&cross_conn_det_sus>;
// qcom,cdc-us-euro-gpios = <&msm_gpio 97 0>;
5).更改micbias电压:
类似于苹果耳机,它的mic的工作电压是大于1.8v,所以为了能正常使用苹果耳机,需要增加micbias电压:
qcom,cdc-micbias-cfilt-mv = <2700>;
或者更改代码: kernel/sound/soc/codecs/msm8x16-wcd.c #define MICBIAS_DEFAULT_VAL 2700000
6).有时候,插入了一些大阻抗的耳机,需要我们支持,可以按照如下进行修改:
修改:
kernel/sound/soc/codecs/wcd-mbhc-v2.c #define HS_VREF_MIN_VAL 1400
1.4v,最大只能识别7700欧阻抗的耳机, 这个阻抗指的是mic对地的阻抗,耳机的后两节之间的阻抗
1.5v,最大能识别11k
1.6v,最大能识别17.6k
1.7v,最大能识别37.4k
7).耳机对lineout设备的识别,有时候需要要求设备支持LINEOUT设备,类如接到耳机插孔上的音箱,高通平台上, 对应LINEOUT设备是上报成SND_JACK_LINEOUT设备,但android层是不支持LINEOUT设备的,它不会对此事件做响应,所以,插入之后无法识别。可以按照如下方式更改:
kernel/sound/soc/msm/msm8x16.c .linein_th = 5000,改为 .linein_th = 0,
这样设备就能将LINEOUT设备识别成三段耳机了
8). 如果外部没有接兼容欧标美标耳机的电路,但是又想兼容的话,可以播放音乐,但是无法通话,可以做出如下修改:

1 void wcd_correct_swch_plug()
2 {
3 report:
4 if (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP)
5 plug_type = MBHC_PLUG_TYPE_HEADPHONE;
6 }

9). 对耳机按键的调试
耳机上报的键值定义:
.key_code[0] = KEY_MEDIA,
.key_code[1] = KEY_VOICECOMMAND,
.key_code[2] = KEY_VOLUMEUP,
.key_code[3] = KEY_VOLUMEDOWN,
.key_code[4] = 0,
.key_code[5] = 0,
.key_code[6] = 0,
.key_code[7] = 0,
耳机按键的数量定义在:
#define WCD_MBHC_DEF_BUTTONS 4
耳机按键的阈值定义在:
static void *def_msm8x16_wcd_mbhc_cal(void)
{
btn_low[0] = 12.5;
btn_high[0] = 12.5;
btn_low[1] = 37.5;
btn_high[1] = 37.5;
btn_low[2] = 75;
btn_high[2] = 62.5;
btn_low[3] = 100;
btn_high[3] = 100;
btn_low[4] = 125;
btn_high[4] = 125;
}
所以如果想修改耳机支持的按键数目,按照如下方式修改:
.key_code[0] = KEY_MEDIA,
.key_code[1] = 0,
.key_code[2] = 0,
.key_code[3] = 0,
.key_code[4] = 0,
.key_code[5] = 0,
.key_code[6] = 0,
.key_code[7] = 0,
#define WCD_MBHC_DEF_BUTTONS 1
static void *def_msm8x16_wcd_mbhc_cal(void)
{
btn_low[0] = 12.5;
btn_high[0] = 12.5;
}
如果想调试按键的阈值:
- 分别配置MBHC为CS(Current Source)和MB(MIC BIAS)模式:
CS mode : 0x144 = 0x00, 0x151 = 0xB0
MB mode : 0x144 = 0x80, 0x151 = 0x80
adb root
cd sys/kernel/debug/soc/<snd-card>/msm8x16_wcd_codec/
echo “<Register Address><value>” > codec_reg
类如: echo “0x121 0xA0” > codec_reg
- 按下耳机按键,分别测量两个模式下的耳机mic上的电压值,把测量的值分别填入高通提供的表格,或者按照80-NK808-15的Table3-3和Table3-4计算出最后的阀值。
(转自 https://www.cnblogs.com/wulizhi/p/8289905.html#undefined)
1263

被折叠的 条评论
为什么被折叠?



