一.
在高通平台中,預設使用内部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: [email protected] {
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)