高通平台耳机知识记录

一.

在高通平台中,默认使用内部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平台。

扫描二维码关注公众号,回复: 6238850 查看本文章

在设备树文件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-= <&cdc_pdm_lines_act &vdd_spkdrv_act>;

pinctrl-= <&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

猜你喜欢

转载自blog.csdn.net/qianxuedegushi/article/details/81565408