记录高通msm8909耳机检测的机制,相关代码

kernel/arch/arm/boot/dts/qcom/msm8909-qrd-skuc.dtsi

sound {
        compatible = "qcom,msm8x16-audio-codec";
        qcom,model = "msm8909-skuc-snd-card";
        qcom,msm-snd-card-id = <0>;
        qcom,msm-codec-type = "internal";
        qcom,msm-ext-pa = "primary";
        qcom,msm-mclk-freq = <9600000>;
        qcom,msm-mbhc-hphl-swh = <0>;
        qcom,msm-mbhc-gnd-swh = <0>;
        qcom,msm-hs-micbias-type = "internal";
        qcom,msm-micbias1-ext-cap;
        qcom,msm-micbias2-ext-cap;
        qcom,audio-routing =
            "RX_BIAS", "MCLK",
            "SPK_RX_BIAS", "MCLK",
            "INT_LDO_H", "MCLK",
            "MIC BIAS Internal1", "Handset Mic",
            "MIC BIAS Internal2", "Headset Mic",
            "MIC BIAS Internal3", "Secondary Mic",
            "AMIC1", "MIC BIAS Internal1",
            "AMIC2", "MIC BIAS Internal2",
            "AMIC3", "MIC BIAS Internal3";
        pinctrl-names = "cdc_lines_act",
                "cdc_lines_sus";
        pinctrl-0 = <&cdc_pdm_lines_act>;
        pinctrl-1 = <&cdc_pdm_lines_sus>;
        asoc-platform = <&pcm0>, <&pcm1>, <&voip>, <&voice>,
                <&loopback>, <&compress>, <&hostless>,
                <&afe>, <&lsm>, <&routing>, <&lpa>,
                <&voice_svc>;
        asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1",
                "msm-voip-dsp", "msm-pcm-voice", "msm-pcm-loopback",
                "msm-compress-dsp", "msm-pcm-hostless", "msm-pcm-afe",
                "msm-lsm-client", "msm-pcm-routing", "msm-pcm-lpa",
                "msm-voice-svc";
        asoc-cpu = <&dai_pri_auxpcm>, <&dai_hdmi>,
                <&dai_mi2s0>, <&dai_mi2s1>, <&dai_mi2s2>, <&dai_mi2s3>,
                <&sb_0_rx>, <&sb_0_tx>, <&sb_1_rx>, <&sb_1_tx>,
                <&sb_3_rx>, <&sb_3_tx>, <&sb_4_rx>, <&sb_4_tx>,
                <&bt_sco_rx>, <&bt_sco_tx>, <&int_fm_rx>, <&int_fm_tx>,
                <&afe_pcm_rx>, <&afe_pcm_tx>, <&afe_proxy_rx>, <&afe_proxy_tx>,
                <&incall_record_rx>, <&incall_record_tx>, <&incall_music_rx>,
                <&incall_music_2_rx>;
        asoc-cpu-names = "msm-dai-q6-auxpcm.1", "msm-dai-q6-hdmi.8",
                "msm-dai-q6-mi2s.0", "msm-dai-q6-mi2s.1",
                "msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3",
                "msm-dai-q6-dev.16384", "msm-dai-q6-dev.16385",
                "msm-dai-q6-dev.16386", "msm-dai-q6-dev.16387",
                "msm-dai-q6-dev.16390", "msm-dai-q6-dev.16391",
                "msm-dai-q6-dev.16392", "msm-dai-q6-dev.16393",
                "msm-dai-q6-dev.12288", "msm-dai-q6-dev.12289",
                "msm-dai-q6-dev.12292", "msm-dai-q6-dev.12293",
                "msm-dai-q6-dev.224", "msm-dai-q6-dev.225",
                "msm-dai-q6-dev.241", "msm-dai-q6-dev.240",
                "msm-dai-q6-dev.32771", "msm-dai-q6-dev.32772",
                "msm-dai-q6-dev.32773", "msm-dai-q6-dev.32770";
        asoc-codec = <&stub_codec>, <&pm8909_conga_dig>;
        asoc-codec-names = "msm-stub-codec.1", "tombak_codec";
    };
kernel/arch/arm/boot/dts/qcom/msm-pm8909.dtsi

pm8909_conga_dig: 8909_wcd_codec@f000 {
            compatible = "qcom,msm8x16_wcd_codec";
            reg = <0xf000 0x100>;
            interrupt-parent = <&spmi_bus>;
            interrupts = <0x1 0xf0 0x0>,
                     <0x1 0xf0 0x1>,
                     <0x1 0xf0 0x2>,
                     <0x1 0xf0 0x3>,
                     <0x1 0xf0 0x4>,
                     <0x1 0xf0 0x5>,
                     <0x1 0xf0 0x6>,
                     <0x1 0xf0 0x7>;
            interrupt-names = "spk_cnp_int",
                      "spk_clip_int",
                      "spk_ocp_int",
                      "ins_rem_det1",
                      "but_rel_det",
                      "but_press_det",
                      "ins_rem_det",
                      "mbhc_int";
 
            cdc-vdda-cp-supply = <&pm8909_s2>;
            qcom,cdc-vdda-cp-voltage = <1800000 2200000>;
            qcom,cdc-vdda-cp-current = <500000>;
 
            cdc-vdda-h-supply = <&pm8909_l5>;
            qcom,cdc-vdda-h-voltage = <1800000 1800000>;
            qcom,cdc-vdda-h-current = <10000>;
 
            cdc-vdd-px-supply = <&pm8909_l5>;
            qcom,cdc-vdd-px-voltage = <1800000 1800000>;
            qcom,cdc-vdd-px-current = <5000>;
 
            cdc-vdd-pa-supply = <&pm8909_s2>;
            qcom,cdc-vdd-pa-voltage = <1800000 2200000>;
            qcom,cdc-vdd-pa-current = <260000>;
 
            cdc-vdd-mic-bias-supply = <&pm8909_l13>;
            qcom,cdc-vdd-mic-bias-voltage = <3075000 3075000>;
            qcom,cdc-vdd-mic-bias-current = <5000>;
 
            qcom,cdc-mclk-clk-rate = <9600000>;
 
            qcom,cdc-static-supplies = "cdc-vdda-h",
                           "cdc-vdd-px",
                           "cdc-vdd-pa",
                           "cdc-vdda-cp";
 
            qcom,cdc-on-demand-supplies = "cdc-vdd-mic-bias";
        };
 
        pm8909_conga_analog: 8909_wcd_codec@f100 {
            compatible = "qcom,msm8x16_wcd_codec";
            reg = <0xf100 0x100>;
            interrupt-parent = <&spmi_bus>;
            interrupts = <0x1 0xf1 0x0>,
                     <0x1 0xf1 0x1>,
                     <0x1 0xf1 0x2>,
                     <0x1 0xf1 0x3>,
                     <0x1 0xf1 0x4>,
                     <0x1 0xf1 0x5>;
            interrupt-names = "ear_ocp_int",
                      "hphr_ocp_int",
                      "hphl_ocp_det",
                      "ear_cnp_int",
                      "hphr_cnp_int",
                      "hphl_cnp_int";
        };

msm8909的耳机接口在pm8909上

耳机的基本知识http://yunzhi.github.io/headset_knowledge

HPH (耳机简写headphone)

MIC_BIAS (耳机麦克风偏置电压)

MICx_IN(耳机的mic输入口)

HPH_L(耳机左声道)

HPH_R(耳机右声道)

HS_DET(耳机检测脚)  headset_detect

HPH_REF(耳机参考地)     一些接耳机通道的外置PA,如果耳机地不接这里,接主板的地,可能通话过中会电流声

NC  常关耳机插座(hp_det和hph_l短路,插入耳机,hp_det和hph_l断路)

NO 常开耳机插座(这种类型常见,hp_det和hph_l断路,插入耳机,hph_l接了一个喇叭(小电阻)到地,相当有hp_det和hph_l短路)

耳机的左/右声道到地电阻大概10欧姆,mic到地大概1K欧姆

这里是no的耳机插座,设置

qcom,msm-mbhc-hphl-swh = <0>;

kernel/sound/soc/codecs/msm8x16-wcd.c

static const struct wcd_mbhc_intr intr_ids = {
    .mbhc_sw_intr =  MSM8X16_WCD_IRQ_MBHC_HS_DET,
    .mbhc_btn_press_intr = MSM8X16_WCD_IRQ_MBHC_PRESS,
    .mbhc_btn_release_intr = MSM8X16_WCD_IRQ_MBHC_RELEASE,
    .mbhc_hs_ins_intr = MSM8X16_WCD_IRQ_MBHC_INSREM_DET1,
    .mbhc_hs_rem_intr = MSM8X16_WCD_IRQ_MBHC_INSREM_DET,
    .hph_left_ocp = MSM8X16_WCD_IRQ_HPHL_OCP,
    .hph_right_ocp = MSM8X16_WCD_IRQ_HPHR_OCP,
};

static int msm8x16_wcd_codec_probe(struct snd_soc_codec *codec)
{
    struct msm8x16_wcd_priv *msm8x16_wcd_priv;
    struct msm8x16_wcd *msm8x16_wcd;
    int i, ret;
 
    dev_dbg(codec->dev, "%s()\n", __func__);
 
    msm8x16_wcd_priv = kzalloc(sizeof(struct msm8x16_wcd_priv), GFP_KERNEL);
    if (!msm8x16_wcd_priv) {
        dev_err(codec->dev, "Failed to allocate private data\n");
        return -ENOMEM;
    }
 
    for (i = 0; i < NUM_DECIMATORS; i++) {
        tx_hpf_work[i].msm8x16_wcd = msm8x16_wcd_priv;
        tx_hpf_work[i].decimator = i + 1;
        INIT_DELAYED_WORK(&tx_hpf_work[i].dwork,
            tx_hpf_corner_freq_callback);
    }
 
    codec->control_data = dev_get_drvdata(codec->dev);
    snd_soc_codec_set_drvdata(codec, msm8x16_wcd_priv);
    msm8x16_wcd_priv->codec = codec;
 
    /* codec resmgr module init */
    msm8x16_wcd = codec->control_data;
    msm8x16_wcd->dig_base = ioremap(MSM8X16_DIGITAL_CODEC_BASE_ADDR,
                  MSM8X16_DIGITAL_CODEC_REG_SIZE);
    if (msm8x16_wcd->dig_base == NULL) {
        dev_err(codec->dev, "%s ioremap failed\n", __func__);
        kfree(msm8x16_wcd_priv);
        return -ENOMEM;
    }
    msm8x16_wcd_priv->spkdrv_reg =
        wcd8x16_wcd_codec_find_regulator(codec->control_data,
                        MSM89XX_VDD_SPKDRV_NAME);
    msm8x16_wcd_priv->pmic_rev = snd_soc_read(codec,
                    MSM8X16_WCD_A_DIGITAL_REVISION1);
    msm8x16_wcd_priv->codec_version = snd_soc_read(codec,
                    MSM8X16_WCD_A_DIGITAL_PERPH_SUBTYPE);
    if (msm8x16_wcd_priv->codec_version == CONGA) {
        dev_dbg(codec->dev, "%s :Conga REV: %d\n", __func__,
                    msm8x16_wcd_priv->codec_version);
        msm8x16_wcd_priv->ext_spk_boost_set = true;
    } else {
        dev_dbg(codec->dev, "%s :PMIC REV: %d\n", __func__,
                    msm8x16_wcd_priv->pmic_rev);
    }
    /*
     * set to default boost option BOOST_SWITCH, user mixer path can change
     * it to BOOST_ALWAYS or BOOST_BYPASS based on solution chosen.
     */
    msm8x16_wcd_priv->boost_option = BOOST_SWITCH;
    msm8x16_wcd_dt_parse_boost_info(codec);
    msm8x16_wcd_set_boost_v(codec);
 
    snd_soc_add_codec_controls(codec, impedance_detect_controls,
                   ARRAY_SIZE(impedance_detect_controls));
 
    msm8x16_wcd_bringup(codec);
    msm8x16_wcd_codec_init_reg(codec);
    msm8x16_wcd_update_reg_defaults(codec);
 
    wcd9xxx_spmi_set_codec(codec);
 
    msm8x16_wcd_priv->on_demand_list[ON_DEMAND_MICBIAS].supply =
                wcd8x16_wcd_codec_find_regulator(
                codec->control_data,
                on_demand_supply_name[ON_DEMAND_MICBIAS]);
    atomic_set(&msm8x16_wcd_priv->on_demand_list[ON_DEMAND_MICBIAS].ref, 0);
 
    BLOCKING_INIT_NOTIFIER_HEAD(&msm8x16_wcd_priv->notifier);
 
    msm8x16_wcd_priv->fw_data = kzalloc(sizeof(*(msm8x16_wcd_priv->fw_data))
            , GFP_KERNEL);
    if (!msm8x16_wcd_priv->fw_data) {
        dev_err(codec->dev, "Failed to allocate fw_data\n");
        iounmap(msm8x16_wcd->dig_base);
        kfree(msm8x16_wcd_priv);
        return -ENOMEM;
    }
 
    set_bit(WCD9XXX_MBHC_CAL, msm8x16_wcd_priv->fw_data->cal_bit);
    ret = wcd_cal_create_hwdep(msm8x16_wcd_priv->fw_data,
            WCD9XXX_CODEC_HWDEP_NODE, codec);
    if (ret < 0) {
        dev_err(codec->dev, "%s hwdep failed %d\n", __func__, ret);
        iounmap(msm8x16_wcd->dig_base);
        kfree(msm8x16_wcd_priv->fw_data);
        kfree(msm8x16_wcd_priv);
        return ret;
    }
 
    wcd_mbhc_init(&msm8x16_wcd_priv->mbhc, codec, &mbhc_cb, &intr_ids,
            true);
 
    msm8x16_wcd_priv->mclk_enabled = false;
    msm8x16_wcd_priv->clock_active = false;
    msm8x16_wcd_priv->config_mode_active = false;
 
    /* Set initial MICBIAS voltage level */
    msm8x16_wcd_set_micb_v(codec);
 
    /* Set initial cap mode */
    msm8x16_wcd_configure_cap(codec, false, false);
    registered_codec = codec;
    modem_state_notifier =
        subsys_notif_register_notifier("modem",
                       &modem_state_notifier_block);
    if (!modem_state_notifier) {
        dev_err(codec->dev, "Failed to register modem state notifier\n"
            );
        iounmap(msm8x16_wcd->dig_base);
        kfree(msm8x16_wcd_priv->fw_data);
        kfree(msm8x16_wcd_priv);
        registered_codec = NULL;
        return -ENOMEM;
    }
    return 0;
}
kernel/sound/soc/codecs/wcd-mbhc-v2.c

int wcd_mbhc_init(struct wcd_mbhc *mbhc, struct snd_soc_codec *codec,
              const struct wcd_mbhc_cb *mbhc_cb,
              const struct wcd_mbhc_intr *mbhc_cdc_intr_ids,
              bool impedance_det_en)
{
    int ret = 0;
    int hph_swh = 0;
    int gnd_swh = 0;
    struct snd_soc_card *card = codec->card;
    const char *hph_switch = "qcom,msm-mbhc-hphl-swh";
    const char *gnd_switch = "qcom,msm-mbhc-gnd-swh";
 
    fb_notifier.notifier_call = fb_notifierier_callback;
    ret = fb_register_client(&fb_notifier);
    if (ret)
        printk( KERN_CRIT"%s: micbias Unable to register fb_notifier: %d\n", __func__,ret);
 
    ret = of_property_read_u32(card->dev->of_node, hph_switch, &hph_swh);
    if (ret) {
        dev_err(card->dev,
            "%s: missing %s in dt node\n", __func__, hph_switch);
        goto err;
    }
 
    ret = of_property_read_u32(card->dev->of_node, gnd_switch, &gnd_swh);
    if (ret) {
        dev_err(card->dev,
            "%s: missing %s in dt node\n", __func__, gnd_switch);
        goto err;
    }
 
    mbhc->in_swch_irq_handler = false;
    mbhc->current_plug = MBHC_PLUG_TYPE_NONE;
    mbhc->is_btn_press = false;
    mbhc->codec = codec;
    mbhc->intr_ids = mbhc_cdc_intr_ids;
    mbhc->impedance_detect = impedance_det_en;
    mbhc->hphl_swh = hph_swh;
    mbhc->gnd_swh = gnd_swh;
    mbhc->micbias_enable = false;
    mbhc->mbhc_cb = mbhc_cb;
    mbhc->btn_press_intr = false;
    mbhc->is_hs_recording = false;
    mbhc->is_extn_cable = false;
    mbhc->skip_imped_detection = false;
 
    if (mbhc->intr_ids == NULL) {
        pr_err("%s: Interrupt mapping not provided\n", __func__);
        return -EINVAL;
    }
 
    if (mbhc->headset_jack.jack == NULL) {
        ret = snd_soc_jack_new(codec, "Headset Jack",
                WCD_MBHC_JACK_MASK, &mbhc->headset_jack);
        if (ret) {
            pr_err("%s: Failed to create new jack\n", __func__);
            return ret;
        }
 
        ret = snd_soc_jack_new(codec, "Button Jack",
                       WCD_MBHC_JACK_BUTTON_MASK,
                       &mbhc->button_jack);
        if (ret) {
            pr_err("Failed to create new jack\n");
            return ret;
        }
 
        ret = snd_jack_set_key(mbhc->button_jack.jack,
                       SND_JACK_BTN_0,
                       KEY_MEDIA);
        if (ret) {
            pr_err("%s: Failed to set code for btn-0\n",
                __func__);
            return ret;
        }
        ret = snd_jack_set_key(mbhc->button_jack.jack,
                       SND_JACK_BTN_1,
                       KEY_VOICECOMMAND);
        if (ret) {
            pr_err("%s: Failed to set code for btn-1:%d\n",
                    __func__, ret);
            return ret;
        }
        ret = snd_jack_set_key(mbhc->button_jack.jack,
                       SND_JACK_BTN_2,
                       KEY_VOLUMEUP);
        if (ret) {
            pr_err("%s: Failed to set code for btn-2:%d\n",
                __func__, ret);
            return ret;
        }
        ret = snd_jack_set_key(mbhc->button_jack.jack,
                       SND_JACK_BTN_3,
                       KEY_VOLUMEDOWN);
        if (ret) {
            pr_err("%s: Failed to set code for btn-3:%d\n",
                __func__, ret);
            return ret;
        }
 
        INIT_DELAYED_WORK(&mbhc->mbhc_firmware_dwork,
                  wcd_mbhc_fw_read);
        INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd_btn_lpress_fn);
    }
 
    /* Register event notifier */
    mbhc->nblock.notifier_call = wcd_event_notify;
    ret = msm8x16_register_notifier(codec, &mbhc->nblock);
    if (ret) {
        pr_err("%s: Failed to register notifier %d\n", __func__, ret);
        return ret;
    }
 
    init_waitqueue_head(&mbhc->wait_btn_press);
    mutex_init(&mbhc->codec_resource_lock);
 
    ret = wcd9xxx_spmi_request_irq(mbhc->intr_ids->mbhc_sw_intr,
                  wcd_mbhc_mech_plug_detect_irq,
                  "mbhc sw intr", mbhc);
    if (ret) {
        pr_err("%s: Failed to request irq %d, ret = %d\n", __func__,
               mbhc->intr_ids->mbhc_sw_intr, ret);
        goto err_mbhc_sw_irq;
    }
 
    ret = wcd9xxx_spmi_request_irq(mbhc->intr_ids->mbhc_btn_press_intr,
                  wcd_mbhc_btn_press_handler,
                  "Button Press detect",
                  mbhc);
    if (ret) {
        pr_err("%s: Failed to request irq %d\n", __func__,
               mbhc->intr_ids->mbhc_btn_press_intr);
        goto err_btn_press_irq;
    }
 
    ret = wcd9xxx_spmi_request_irq(mbhc->intr_ids->mbhc_btn_release_intr,
                  wcd_mbhc_release_handler,
                  "Button Release detect", mbhc);
    if (ret) {
        pr_err("%s: Failed to request irq %d\n", __func__,
            mbhc->intr_ids->mbhc_btn_release_intr);
        goto err_btn_release_irq;
    }
 
    ret = wcd9xxx_spmi_request_irq(mbhc->intr_ids->mbhc_hs_ins_intr,
                  wcd_mbhc_hs_ins_irq,
                  "Elect Insert", mbhc);
    if (ret) {
        pr_err("%s: Failed to request irq %d\n", __func__,
               mbhc->intr_ids->mbhc_hs_ins_intr);
        goto err_mbhc_hs_ins_irq;
    }
    wcd9xxx_spmi_disable_irq(mbhc->intr_ids->mbhc_hs_ins_intr);
 
    ret = wcd9xxx_spmi_request_irq(mbhc->intr_ids->mbhc_hs_rem_intr,
                  wcd_mbhc_hs_rem_irq,
                  "Elect Remove", mbhc);
    if (ret) {
        pr_err("%s: Failed to request irq %d\n", __func__,
               mbhc->intr_ids->mbhc_hs_rem_intr);
        goto err_mbhc_hs_rem_irq;
    }
    wcd9xxx_spmi_disable_irq(mbhc->intr_ids->mbhc_hs_rem_intr);
 
    ret = wcd9xxx_spmi_request_irq(mbhc->intr_ids->hph_left_ocp,
                  wcd_mbhc_hphl_ocp_irq, "HPH_L OCP detect",
                  mbhc);
    if (ret) {
        pr_err("%s: Failed to request irq %d\n", __func__,
               mbhc->intr_ids->hph_left_ocp);
        goto err_hphl_ocp_irq;
    }
 
    ret = wcd9xxx_spmi_request_irq(mbhc->intr_ids->hph_right_ocp,
                  wcd_mbhc_hphr_ocp_irq, "HPH_R OCP detect",
                  mbhc);
    if (ret) {
        pr_err("%s: Failed to request irq %d\n", __func__,
               mbhc->intr_ids->hph_right_ocp);
        goto err_hphr_ocp_irq;
    }
 
    pr_debug("%s: leave ret %d\n", __func__, ret);
    return ret;
 
err_hphr_ocp_irq:
    wcd9xxx_spmi_free_irq(mbhc->intr_ids->hph_left_ocp, mbhc);
err_hphl_ocp_irq:
    wcd9xxx_spmi_free_irq(mbhc->intr_ids->mbhc_hs_rem_intr, mbhc);
err_mbhc_hs_rem_irq:
    wcd9xxx_spmi_free_irq(mbhc->intr_ids->mbhc_hs_ins_intr, mbhc);
err_mbhc_hs_ins_irq:
    wcd9xxx_spmi_free_irq(mbhc->intr_ids->mbhc_btn_release_intr, mbhc);
err_btn_release_irq:
    wcd9xxx_spmi_free_irq(mbhc->intr_ids->mbhc_btn_press_intr, mbhc);
err_btn_press_irq:
    wcd9xxx_spmi_free_irq(mbhc->intr_ids->mbhc_sw_intr, mbhc);
err_mbhc_sw_irq:
    msm8x16_unregister_notifier(codec, &mbhc->nblock);
    mutex_destroy(&mbhc->codec_resource_lock);
err:
    pr_debug("%s: leave ret %d\n", __func__, ret);
    return ret;
}
EXPORT_SYMBOL(wcd_mbhc_init);
耳机检测中断函数

static void wcd_mbhc_swch_irq_handler(struct wcd_mbhc *mbhc)
{
    bool detection_type;
    bool micbias1;
    struct snd_soc_codec *codec = mbhc->codec;
    pr_debug("%s: enter\n", __func__);
 
    WCD_MBHC_RSC_LOCK(mbhc);
 
    mbhc->in_swch_irq_handler = true;
 
    /* cancel pending button press */
    if (wcd_cancel_btn_work(mbhc))
        pr_debug("%s: button press is canceled\n", __func__);
 
    detection_type = (snd_soc_read(codec,
                MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_1)) & 0x20;
 
    /* Set the detection type appropriately */
    snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_1,
            0x20, (!detection_type << 5));
 
    pr_debug("%s: mbhc->current_plug: %d detection_type: %d\n", __func__,
            mbhc->current_plug, detection_type);
    wcd_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
 
    micbias1 = (snd_soc_read(codec, MSM8X16_WCD_A_ANALOG_MICB_1_EN) & 0x80);
    if ((mbhc->current_plug == MBHC_PLUG_TYPE_NONE) &&
        detection_type) {
        /* Make sure MASTER_BIAS_CTL is enabled */
        snd_soc_update_bits(codec,
                    MSM8X16_WCD_A_ANALOG_MASTER_BIAS_CTL,
                    0x30, 0x30);
        snd_soc_update_bits(codec,
                MSM8X16_WCD_A_ANALOG_MICB_1_EN,
                0x04, 0x04);
        if (!mbhc->mbhc_cfg->hs_ext_micbias)
            /* Enable Tx2 RBias if the headset
             * is using internal micbias*/
            snd_soc_update_bits(codec,
                    MSM8X16_WCD_A_ANALOG_MICB_1_INT_RBIAS,
                    0x10, 0x10);
        /* Remove pull down on MIC BIAS2 */
        snd_soc_update_bits(codec,
                 MSM8X16_WCD_A_ANALOG_MICB_2_EN,
                0x20, 0x00);
        /* Enable HW FSM */
        snd_soc_update_bits(codec,
                MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL,
                0x80, 0x80);
        /* Apply trim if needed on the device */
        if (mbhc->mbhc_cb && mbhc->mbhc_cb->trim_btn_reg)
            mbhc->mbhc_cb->trim_btn_reg(codec);
        /* Enable external voltage source to micbias if present */
        if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
            mbhc->mbhc_cb->enable_mb_source(codec, true);
        mbhc->btn_press_intr = false;
        wcd_mbhc_detect_plug_type(mbhc);
    } else if ((mbhc->current_plug != MBHC_PLUG_TYPE_NONE)
            && !detection_type) {
        /* Disable external voltage source to micbias if present */
        if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
            mbhc->mbhc_cb->enable_mb_source(codec, false);
        /* Disable HW FSM */
        snd_soc_update_bits(codec,
                MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL,
                0xB0, 0x00);
        snd_soc_update_bits(codec,
                MSM8X16_WCD_A_ANALOG_MICB_1_EN,
                0x04, 0x00);
        if (mbhc->mbhc_cb && mbhc->mbhc_cb->set_cap_mode)
            mbhc->mbhc_cb->set_cap_mode(codec, micbias1, false);
        mbhc->btn_press_intr = false;
        if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) {
            wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADPHONE);
        } else if (mbhc->current_plug == MBHC_PLUG_TYPE_GND_MIC_SWAP) {
            wcd_mbhc_report_plug(mbhc, 0, SND_JACK_UNSUPPORTED);
        } else if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET) {
            /* make sure to turn off Rbias */
            snd_soc_update_bits(codec,
                    MSM8X16_WCD_A_ANALOG_MICB_1_INT_RBIAS,
                    0x18, 0x08);
            snd_soc_update_bits(codec,
                    MSM8X16_WCD_A_ANALOG_MICB_2_EN,
                    0x20, 0x20);
            wcd9xxx_spmi_disable_irq(
                    mbhc->intr_ids->mbhc_hs_rem_intr);
            wcd9xxx_spmi_disable_irq(
                    mbhc->intr_ids->mbhc_hs_ins_intr);
            snd_soc_update_bits(codec,
                    MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_1,
                    0x01, 0x01);
            snd_soc_update_bits(codec,
                    MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_2,
                    0x06, 0x00);
            wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADSET);
        } else if (mbhc->current_plug == MBHC_PLUG_TYPE_HIGH_HPH) {
            mbhc->is_extn_cable = false;
            wcd9xxx_spmi_disable_irq(
                    mbhc->intr_ids->mbhc_hs_rem_intr);
            wcd9xxx_spmi_disable_irq(
                    mbhc->intr_ids->mbhc_hs_ins_intr);
            snd_soc_update_bits(codec,
                    MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_1,
                    0x01, 0x01);
            snd_soc_update_bits(codec,
                    MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_2,
                    0x06, 0x00);
            wcd_mbhc_report_plug(mbhc, 0, SND_JACK_LINEOUT);
        }
    } else if (!detection_type) {
        /* Disable external voltage source to micbias if present */
        if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
            mbhc->mbhc_cb->enable_mb_source(codec, false);
        /* Disable HW FSM */
        snd_soc_update_bits(codec,
                MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL,
                0xB0, 0x00);
    }
 
    mbhc->in_swch_irq_handler = false;
    WCD_MBHC_RSC_UNLOCK(mbhc);
    pr_debug("%s: leave\n", __func__);
}
 
static irqreturn_t wcd_mbhc_mech_plug_detect_irq(int irq, void *data)
{
    int r = IRQ_HANDLED;
    struct wcd_mbhc *mbhc = data;
 
    pr_debug("%s: enter\n", __func__);
    if (unlikely(wcd9xxx_spmi_lock_sleep() == false)) {
        pr_warn("%s: failed to hold suspend\n", __func__);
        r = IRQ_NONE;
    } else {
        /* Call handler */
        wcd_mbhc_swch_irq_handler(mbhc);
        wcd9xxx_spmi_unlock_sleep();
    }
    pr_debug("%s: leave %d\n", __func__, r);
    return r;
}
经过一系列的检测,判断是headset,headphones等,如果是headphones,最终通过wcd_mbhc_jack_report将数据汇报上去。

static void wcd_mbhc_jack_report(struct wcd_mbhc *mbhc,
                struct snd_soc_jack *jack, int status, int mask)
{
    snd_soc_jack_report_no_dapm(jack, status, mask);
}
内核通用的耳机汇报函数

void snd_soc_jack_report_no_dapm(struct snd_soc_jack *jack, int status,
                 int mask)
{
    jack->status &= ~mask;
    jack->status |= status & mask;
 
    snd_jack_report(jack->jack, jack->status);
}
EXPORT_SYMBOL_GPL(snd_soc_jack_report_no_dapm);

void snd_jack_report(struct snd_jack *jack, int status)
{
    int i;
 
    if (!jack)
        return;
 
    for (i = 0; i < ARRAY_SIZE(jack->key); i++) {
        int testbit = SND_JACK_BTN_0 >> i;
 
        if (jack->type & testbit)
            input_report_key(jack->input_dev, jack->key[i],
                     status & testbit);
    }
 
    for (i = 0; i < ARRAY_SIZE(jack_switch_types); i++) {
        int testbit = 1 << i;
        if (jack->type & testbit)
            input_report_switch(jack->input_dev,
                        jack_switch_types[i],
                        status & testbit);
    }//分别判断具体的位,有没有相应的事件
 
    input_sync(jack->input_dev);
}
EXPORT_SYMBOL(snd_jack_report);
static inline void input_report_switch(struct input_dev *dev, unsigned int code, int value)
{
    input_event(dev, EV_SW, code, !!value);
}
#define EV_SW            0x05
三段耳机最终汇报上去的有效数据是

5   2   1 (插头类型 耳机信号 插入/拔出)

0   0   0(同步)

static int jack_switch_types[] = {
    SW_HEADPHONE_INSERT,
    SW_MICROPHONE_INSERT,
    SW_LINEOUT_INSERT,
    SW_JACK_PHYSICAL_INSERT,
    SW_VIDEOOUT_INSERT,
    SW_LINEIN_INSERT,
    SW_HPHL_OVERCURRENT,
    SW_HPHR_OVERCURRENT,
    SW_UNSUPPORT_INSERT,
    SW_MICROPHONE2_INSERT,
};
enum wcd_mbhc_plug_type {
    MBHC_PLUG_TYPE_INVALID = -1,//无效设备
    MBHC_PLUG_TYPE_NONE,//未接入设备
    MBHC_PLUG_TYPE_HEADSET,//四段耳机
    MBHC_PLUG_TYPE_HEADPHONE,//三段耳机
    MBHC_PLUG_TYPE_HIGH_HPH,//高阻抗耳机
    MBHC_PLUG_TYPE_GND_MIC_SWAP,//欧美标标志位
};

当然也可以手工通过sendevent 发送事件,systemui就能显示耳机接入的图标

getevent -i获取Headset Jack对应的input,假设是/dev/input/event2,

则可以通过如下方法模拟耳机接入和拔出(getevent -i找到具体的eventn)

sendevent /dev/input/event2 5 2 1
sendevent /dev/input/event2 0 0 0
或者拨出耳机

sendevent /dev/input/event2 5 2 0
sendevent /dev/input/event2 0 0 0

关于线控耳机原理可参考http://www.guokr.com/post/615349/

高通平台的耳机检测(msm8909)相关推荐

  1. 高通平台耳机类型识别

    耳机类型 耳机类型一般有两种:3段式耳机和4段式耳机.其中4段式耳机又分为欧标和美标. 3段耳机:线序分别为,L,R,G,没有MIC端,所以三段耳机无法使用麦克风,只能接受声音,另外,三段耳机L,R线 ...

  2. 高通平台耳机知识记录

    一. 在高通平台中,默认使用内部codec的时候,耳机的输出及控制都是在内部codec中进行的,所以,可以想象得到,耳机的整个初始化起源过程,是在codec的初始化中.高通平台的machine驱动文件 ...

  3. 高通平台耳机知识记录(转载)

    一. 在高通平台中,默认使用内部codec的时候,耳机的输出及控制都是在内部codec中进行的,所以,可以想象得到,耳机的整个初始化起源过程,是在codec的初始化中.高通平台的machine驱动文件 ...

  4. 高通平台环境搭建,编译,系统引导流程分析 .

    1.高通平台android开发总结 1.1 搭建高通平台环境开发环境 在高通开发板上烧录文件系统 建立高通平台开发环境 高通平台,android和 modem 编译流程分析 高通平台 7620 启动流 ...

  5. 高通平台双卡槽网络模式支持国内所有运营商

    Part1 高通平台双卡网络模式通常是卡槽1支持4.3.2G网络,卡槽2一般会默认写为固定,在之前的android L及L以下,msm8909.msm8916平台上基本设置为GSM only,因为需求 ...

  6. 开机动画适配方案_高通平台刷机包定制方案适配-ROM定制开发入门到精通

    高通平台刷机包定制方案适配-ROM定制开发入门到精通 根据新老平台,高通平台线刷包至少要这样几个基本文件,但不一定全部都需要: 8x10_msimage.mbn----平台镜像,是个完整的磁盘,就是s ...

  7. [Linux Audio Driver] 高通平台内部MIC_BIAS简介

    #更新 2020.05.10 我觉得我这个标题取的不是很妥当,为了表达对技术的敬畏之心,我将原标题 <一文搞懂内部MIC_BIAS>修改为<高通平台内部MIC_BIAS简介> ...

  8. android 高通平台有前途吗,华为鸿蒙计划要适配高通平台了,可以告别安卓搭载鸿蒙OS了?...

    鸿蒙走出这一步是可以想象到的,看来华为打造这个系统希望的结果是万物皆可盘呀,所以一开始就提出了开源,也就意味着这次是高通,下次就可以是联发科,甚至更多的手机品牌也完全就可以搭载!早期我们一直在说国产手 ...

  9. 高通平台 /sys/module/lpm_levels/parameters/sleep_disabled节点

    高通平台 /sys/module/lpm_levels/parameters/sleep_disabled节点默认值为N,可通过修改init.rc文件来修改,最近遇到一个问题,就是两台设备韦根测试收发 ...

最新文章

  1. 管理员必备的Linux系统监控工具
  2. 性能测试(01)-jmeter元件-线程组、调试取样器
  3. c# 过滤HTML代码 源代码,案例 下载
  4. 完整机器学习项目的流程
  5. Spring Boot-使用JDBC连接并检索数据库(Mysql在Docker中)
  6. mysql6默认什么字符集_mysql默认字符集问题
  7. 小米平板android版本,小米平板2发布:分Android和Wind 10两个版本
  8. 【SQL】实验五 数据库的完整性
  9. W25Q256编程时需要关注的器件特性
  10. windows64位首次安装git
  11. 有时候你只是看起来很努力
  12. 南卡全新NEO系列骨传导耳机,主打轻运动风的南卡NEO测评!!!
  13. resultful接口开发
  14. [latex] 页码起始页设置
  15. 创业之路 - 魏杰:下一个 10 年,将造就一批新富翁
  16. Java编程环境搭建
  17. Qt6实战教程:媒体播放器示例
  18. 软考高级 真题 2012年上半年 信息系统项目管理师 案例分析
  19. Linux显示中文—韩文—日文-Zhcon使用方法
  20. 五年级信息技术上册教案计算机主机探秘,三年级信息技术第三课操作计算机教学设计...

热门文章

  1. 鲜枣课堂之深入浅出数字信号处理
  2. php 微信公众号开发,基于 PHP 的微信公众平台开发
  3. 中冠百年|怎样才能提高个人理财的执行力
  4. 2020阿里招聘岗位要求
  5. Opencv4Android的OpenCL的测试,使用Opencv的ocl封装库
  6. 分享一个很香的k8s.gcr.io Docker镜像拉取方法
  7. 谷歌浏览器 android 55,谷歌浏览器55稳定版|谷歌浏览器(Chrome 55稳定版)下载v55.0.2883.87官方正式版 - 欧普软件下载...
  8. 最新国产操作系统uos一小时使用体验
  9. Windows 10 build Error !include: could not find: ****StdUtils.nsh
  10. 知行合一ArduPilot | ArduPilot控制器框架简述与PID参数整定