Android 4.x下基於wm8994的mic檢測

        本文基於Android 4.4和4.2,檢測所用codec爲wm8994。

        Android和Kernel下的mic檢測是建立在headset檢測的基礎上的,具體過程如下:

        1)       kernel通過Jack檢測腳中斷檢測到有耳機插入

        2)       讀取codec寄存器判斷headset是否帶mic

        3)       通過InputEvent/UEvent機制通知Android上層

        詳情可以參看我的前一篇基於耳機插拔檢測的文章。本文基於UEvent機制來實現,即 switch driver的方式。

1.   mic檢測原理

        先看看帶mic的耳機和不帶mic的耳機的差別,如下圖,不帶mic的耳機爲3段,帶mic的耳機爲4段,比對一下實物可以看出兩者左右聲道段沒有差別,差別之處是不帶mic的耳機將GND和MIC兩段合併在一起。因而對於不帶mic的耳機來說,GND和MIC兩段是幾乎短路的(有一定電阻),而mic檢測就是基於這個原理。


        爲了實現錄音,需要在MIC段施加一定的偏置電壓,即micbias,對於沒有mic的耳機來說,由於MIC和GND合成爲一段,就相當於將micbias接地,因此會產生比較大的電流。一些codec支持電流檢測功能,當電流超過某個閾值時,會將相應的寄存器設置爲1,從而可以查詢得到結果。

2.   codec設置

        wm8994 codec支持電流檢測功能,要使用該功能,需要進行相應設置。

        具體的設置可以參考wm8994_mic_detect函數,但我在Linux 3.4.39中調用這個函數後會導致整個播放無聲,因此只能手動設置寄存器,以MICBIAS1爲例,需要手動設置寄存器代碼如下:


/*enable BIAS, MICBIAS1 and VMID in R01h*/
snd_soc_update_bits(codec,WM8994_POWER_MANAGEMENT_1,
                          WM8994_MICB1_ENA_MASK | WM8994_BIAS_ENA |WM8994_VMID_SEL_MASK,
                          WM8994_MICB1_ENA |WM8994_BIAS_ENA | 1 << WM8994_VMID_SEL_SHIFT);

 

/*enable MICBIAS Current Detect*/
snd_soc_update_bits(codec,WM8994_MICBIAS, WM8994_MICD_ENA, reg);

 

/* enable MICDETdeboune */
snd_soc_update_bits(codec,WM8994_IRQ_DEBOUNCE,
                          WM8994_MIC1_DET_DB_MASK,
                          WM8994_MIC1_DET_DB);
 

/* set MICDETthreshold */
snd_soc_update_bits(codec,WM8994_MICBIAS,
                          WM8994_MICD_THR_MASK,
                          0b100 <<WM8994_MICD_THR_SHIFT);
 

        由於有些寄存器和widget綁定導致dapm會在widget下電時將寄存器disable,因此還需要對codec原有代碼做相應修改:

        1) vmid_dereference中,對於MICBIAS和VMID的寄存器不應該設置回0,即取消如下行:

           snd_soc_update_bits(codec,WM8994_POWER_MANAGEMENT_1, WM8994_BIAS_ENA |WM8994_VMID_SEL_MASK, 0);

        2) 對於MICBIAS1對應的widget,將原有的其綁定的寄存器取消,MICBIAS對應widget在wm_hubs.c中,修改如下:

           SND_SOC_DAPM_SUPPLY("MICBIAS1",WM8993_POWER_MANAGEMENT_1, 4, 0, NULL, 0),

           -> SND_SOC_DAPM_SUPPLY("MICBIAS1", SND_SOC_NOPM, 4, 0, NULL, 0),

3.   switch driver中的實現

        switch driver的實現在前一篇文章中提到過,這裏基於已經實現的switch driver添加關於mic檢測的部分即可。

        前面提到,對於插拔檢測的中斷處理函數一般處理成delayed work,防止插拔過程中多次中斷,那麼在delayed work的回調函數中,流程如下:

        1) 讀取GPIO電平值,如果爲高(低)電平,則耳機插入。具體是高還是低與耳機檢測機制有關,大多數爲高電平插入。

        2) 如果檢測到耳機有插入,那麼讀取wm8994 codec上的寄存器進行進一步判斷,代碼如下: 


    mic = wm8994_reg_read(wm8994, WM8994_INTERRUPT_RAW_STATUS_2);
    mic = (mic & WM8994_MIC2_DET_STS_MASK) >> WM8994_MIC2_DET_STS_SHIFT;
    if(mic)
          switch_set_state(&data->sdev, 2);  //no mic
    else
          switch_set_state(&data->sdev, 1);  //with mic


            這裏的最大問題是,在switch driver中如何訪問wm8994的寄存器,即上面wm8994_reg_read的第一個參數wm8994從哪裏獲得?這裏介紹兩種方法:

 

  •   將switch device註冊爲wm8994-core.c的子設備

        這裏有必要先介紹一下wm8994驅動的結構。由於wm8994的控制部分(寄存器設置)走i2c,所以在linux kernel中,wm8994首先作爲一個mfd設備掛載在i2c總線下面,即爲wm8994 core(對應文件drivers/mfd/wm8994-core.c)。在wm8994 core下面,有若干個子設備(codec,gpio,regulator)實現不同的功能。

        其中codec(對應文件sound/soc/codecs/wm8994.c)就是wm8994 core的一個子設備,這個子設備又註冊到了ASOC中。而前面提到的其它子設備(gpio,regulator)也是wm8994 codec硬件的一些其它應用,如可以將codec當作gpio或regulator設備來使用,kernel爲這些特殊應用單獨實現了driver。

        瞭解了wm8994的大概結構,我這裏借鑑了wm8994-regulator.c和gpio-wm8994.c訪問wm8994寄存器的方法,將switch device掛載爲wm8994-core.c的一個子設備,實現代碼如下:


static struct mfd_cell wm8994_devs[] = {

          {

                   .name= "wm8994-codec",

                   .num_resources= ARRAY_SIZE(wm8994_codec_resources),

                   .resources= wm8994_codec_resources,

          },

 

          {

                   .name= "wm8994-gpio",

                   .num_resources= ARRAY_SIZE(wm8994_gpio_resources),

                   .resources= wm8994_gpio_resources,

                   .pm_runtime_no_callbacks= true,

          },

  
          /* hp detect switch driver*/
          {

                   .name= "xxx-hp-switch",

          },

};
 

          這樣,通過wm8994_device_init -> mfd_add_devices中就將switch device添加成了wm8994 core的一個子設備。在switch driver的probe函數中,通過如下語句:

          structwm8994 *wm8994 = dev_get_drvdata(pdev->dev.parent);

即可得到wm8994結構,從而調用wm8994_reg_read讀取寄存器。

 

  • Ÿ 在wm8994 codecdriver中註冊switch device

        這個方法不需要實現單獨的switch driver,整個檢測過程中集成在codec driver中,相當於將switch driver嵌入在了codec driver中,大致流程如下:

        1)在codec driver(sound/soc/codec/wm8994.c)的probe函數中:

             a)  調用switch_dev_register註冊一個switch device

             b)  申請GPIO中斷

         2)在中斷處理函數中讀取GPIO狀態和codec寄存器,判斷耳機插入和mic是否存在,通過switch_set_state設置當前狀態。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章