Audio Jack 的耳机检测和按键检测

来源:互联网 发布:大数据 消费者洞察 编辑:程序博客网 时间:2024/05/20 00:38

转:http://blog.csdn.net/pillarbuaa/article/details/8900182


基于QCM 8960

二十七、Audio jack

1. https://wiki.sonyericsson.net/androiki/Huashan_legacy_feature:_Vibrator_%26_Audio_Jack
2. How to get debug info
    1 dump register before and after headset plug, use following command to dump register
    1.1 $ adb shell 'mount -t debugfs debugfs /sys/kernel/debug/'
    1.2 $ adb shell 'cat /sys/kernel/debug/asoc/msm8960-snd-card/tabla_codec/codec_reg'
    2 Log message during insertion please enable wcd9xxx-core.c +p, wcd9310.c +p, wcd9xxx-irq.c like following
    2.1 $ adb shell 'mount -t debugfs debugfs /sys/kernel/debug/'
    2.2 $ adb shell 'echo -n "file wcd9310.c +p" > /sys/kernel/debug/dynamic_debug/control'
    2.3 $ adb shell 'echo -n "file wcd9xxx-core.c +p" > /sys/kernel/debug/dynamic_debug/control'
    2.4 $ adb shell 'echo -n "file wcd9xxx-irq.c +p" > /sys/kernel/debug/dynamic_debug/control'

4. $ adb shell 'cat /sys/kernel/debug/gpio' 得到GPIO状态  gpio-189 = 151 + PMIC8921的38 Pin
    gpio-189 (--          ) in         lo 0x05 0x10 0x22 0x30 0x40 0x58
5. cat sys/module/snd_soc_msm8960/parameters/hs_detect_use_gpio; 可查看是否使用gpio中断检测headset insert/remove


二十八、如何检测 headset/headphone 插入或拔出 
0. 针对wcd9310不需要读取firmware.
1. 主要分析 /kernel/sound/soc/codecs/wcd9310.c, kernel/sound/soc/msm/msm8960.c
2. msm8960_audrx_init@msm8960.c中 全局变量hs_detect_use_gpio 设置是否用PMIC的GPIO检测耳机插入
    err = tabla_hs_detect(codec, &mbhc_cfg); //设置GPIO38的中断处理函数
       tabla_hs_detect->tabla_mbhc_init_and_calibrate 设置中断处理函数    tabla_mechanical_plug_detect_irq,
    中断pin是tabla->mbhc_cfg.gpio_irq; 
    @kernel/sound/soc/msm/msm8960.c中找到中断Pin的定义msm8960_audrx_init函数中 mbhc_cfg.gpio_irq = JACK_DETECT_INT; 也就是PM8921的第38Pin
    mbhc_cfg.gpio_level_insert=1;表示headset插入时,中断JACK_DETECT_INT的值为高
   如果不用GPIO中断检测耳机插入,则需要用到 TABLA_IRQ_MBHC_INSERTION 的内部中断
    wcd9xxx_enable_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION);     
3. tabla_mbhc_init_and_calibrate@wcd9310.c中设置中断回调函数tabla_mechanical_plug_detect_irq --> tabla_hs_gpio_handler        
    insert = (gpio_get_value_cansleep(tabla->mbhc_cfg.gpio) == tabla->mbhc_cfg.gpio_level_insert);
4. tabla_codec_detect_plug_type-->tabla_codec_get_plug_type@wcd9310.c 检测耳机类型
   其中plug_type_ptr->v_no_mic =73,
      plug_type_ptr->v_hs_max = 2850    
   重要结构static struct tabla_mbhc_config mbhc_cfg @kernel/sound/soc/msm/msm8960.c
    mbhc_cfg.gpio_level_insert = 1 //表示GPIO38为高时为耳机插入
   struct viskan_mbhc_data viskan_mbhc_data@kernel/arch/arm/mach-msm
    .v_hs_max = 2850,  //
   S(v_no_mic, 73);    
5. 支持的耳机jack顺序, MIC|GND|HPHR|HPHL, 在tabla_codec_get_plug_type@wcd9310.c中一共要读取4次电压
    1和2时,分别读取两次MIC和GND之间的电压
    3时,HPHR和GND都切换,读取两次MIC和GND之间的电压
    4时,只有MIC切换,读取两次MIC和GND之间的电压,同时会判断是否为PLUG_TYPE_GND_MIC_SWAP或PLUG_TYPE_INVALID,没搞清楚具体硬件原理
   如果 所有4次得到的MIC和GND之间的电压,均
    < v_no_mic 则是headphone (PLUG_TYPE_HEADPHONE)
    > v_hs_max 则是High impedance plug type,高阻态的耳机类型,线性输出(PLUG_TYPE_HIGH_HPH)
       > v_no_mic & < v_hs_max,则是headset (PLUG_TYPE_HEADSET)
   如果前后两次得到的耳机类型不一样,则说明该耳机不支持(PLUG_TYPE_INVALID)    
   支持的headset, 主要比较的是第一个电压mb_v[i]
    DCE #1, 003e, V 1638, scaled V 1638, GND 0, VDDIO 0, inval 0        
    DCE #2, 0071, V 1733, scaled V 1733, GND 0, VDDIO 0, inval 0
    DCE #3, ffa8, V 1360, scaled V 2092, GND 1, VDDIO 1, inval 1
    DCE #4, ffa8, V 1360, scaled V 2092, GND 0, VDDIO 1, inval 0
   不支持的headset
    DCE #1, fe30, V 661, scaled V 661, GND 0, VDDIO 0, inval 0     
    DCE #2, fe30, V 661, scaled V 661, GND 0, VDDIO 0, inval 0
    DCE #3, fd2f, V 183, scaled V 281, GND 1, VDDIO 1, inval 1
    DCE #4, fe17, V 615, scaled V 946, GND 0, VDDIO 1, inval 1
   支持的headphone
    DCE #1, fcd2, V 11, scaled V 11, GND 0, VDDIO 0, inval 0
    DCE #2, fcd2, V 11, scaled V 11, GND 0, VDDIO 0, inval 0
    DCE #3, fcd0, V 7, scaled V 10, GND 1, VDDIO 1, inval 0
    DCE #4, fcd0, V 7, scaled V 10, GND 0, VDDIO 1, inval 0
6. 根据测量audio jack中GND,MIC 脚与地之间的电压,来比较得出不同的耳机类型。目前只支持MIC|GND|HPHR|HPHL(CTIA)不支持(OMTP)
7. 耳机插入如何上报event
    tabla_codec_detect_plug_type
    -->tabla_codec_report_plug    (只有PLUG_TYPE_GND_MIC_SWAP,PLUG_TYPE_HEADPHONE,PLUG_TYPE_HEADSET,PLUG_TYPE_HIGH_HPH才上报)
    -->tabla_snd_soc_jack_report
    -->snd_soc_jack_report_no_dapm
    -->snd_jack_report@kernel/sound/core/jack.c
    -->input_report_switch@kernel/include/linux/input.h
    -->input_event(dev, EV_SW, code, !!value);
二十九,如何检测耳机的按键事件
1. tabla_hs_gpio_handler-->tabla_codec_detect_plug_type
    只有耳机类型为headset 才能触发tabla_codec_start_hs_polling, 设置各按键的 电压门限值;(mbhc_state == MBHC_STATE_POTENTIAL)
2. 设置中断处理函数 @tabla_codec_probe
    wcd9xxx_request_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL,tabla_dce_handler, "DC Estimation detect", tabla);
   其中 tabla_hs_detect->tabla_mbhc_init_and_calibrate-->tabla_mbhc_cal会enable 中断TABLA_IRQ_MBHC_POTENTIAL
    wcd9xxx_enable_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL); 
3. 当headset插入后,如果有按键则会触发 TABLA_IRQ_MBHC_POTENTIAL 中断,从而call tabla_dce_handler-->tabla_determine_button
    tabla_determine_button函数会通过读取的此时 micphone pin上的电压来决定是那个button key.
4. 而具体的按键是通过 
    @kernel/sound/soc/msm/msm8960.c中的函数snd_soc_jack_new(codec, "Button Jack",TABLA_JACK_BUTTON_MASK, &button_jack);注册一个jack
    -->snd_jack_new
    最终button_jack会接收按键信息
5. 根据测量micphone pin上的电压决定是那个按键,
    而各按键的电压定义在 def_tabla_mbhc_cal@kernel/sound/soc/msm/msm8960.c
    各个按键的结构定义为 struct tabla_mbhc_btn_detect_cfg@kernel/sound/soc/codecs/wcd9310.c
    目前定义了4个按键 TABLA_MBHC_DEF_BUTTONS = 4,电压区间如下
        btn_low[0] = -30;
        btn_high[0] = 73;
        btn_low[1] = 74;
        btn_high[1] = 336;
        btn_low[2] = 337;
        btn_high[2] = 680;
        btn_low[3] = 681;
        btn_high[3] = 1257;
6. 在tabla_dce_handler函数中得到 具体是那个button按键被按下,然后检测中断TABLA_IRQ_MBHC_RELEASE (该中断一直是enable),当按键释放后触发tabla_release_handler-->tabla_snd_soc_jack_report 然后上报该event
    -->snd_jack_report-->input_report_key@kernel/sound/core/jack.c
    -->input_event(dev, EV_KEY, code, !!value);
0 0