高通平台的耳机检测(msm8909)

来源:互联网 发布:单片机和嵌入式 编辑:程序博客网 时间:2024/04/30 19:25

          记录高通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上,耳机检测脚是HS_DET。

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

这里设置不同的耳机插口类型(0或者1),简单的理解就是低电平检测还是高电平检测(可参考http://yunzhi.github.io/headset_knowledge)

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);
最终汇报上去的有效数据是5   2   1 (插头类型 耳机信号 值插入)

                                       0   0   0(同步)

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

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

则可以通过如下方法模拟耳机接入和拔出

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

或者拨出耳机

sendevent /dev/input/event2 5 2 0sendevent /dev/input/event2 0 0 0
这种方法只有成功检测到了耳机后才有效,可能还存在其他的机制。
关于线控耳机原理可参考http://www.guokr.com/post/615349/

0 0
原创粉丝点击