天天看點

高通平台耳機知識記錄

一.

在高通平台中,預設使用内部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;

}

如果想調試按鍵的門檻值:

  1. 分别配置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

  1. 按下耳機按鍵,分别測量兩個模式下的耳機mic上的電壓值,把測量的值分别填入高通提供的表格,或者按照80-NK808-15的Table3-3和Table3-4計算出最後的閥值。

(轉自 https://www.cnblogs.com/wulizhi/p/8289905.html#undefined)