高通msm8909耳机调试
时间:2022-05-03
本文章向大家介绍高通msm8909耳机调试,主要内容包括1、DTS相应修改:、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。
1、DTS相应修改:
DTS相关代码:kernel/arch/arm/boot/dts/qcom/msm8909-qrd-skuc.dtsi:
1 sound {
2 compatible = "qcom,msm8x16-audio-codec";
3 qcom,model = "msm8909-skuc-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 = <0>; //高通平台耳机驱动机制为MBHC,hphl为高通耳机平台的设置
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,audio-routing =
14 "RX_BIAS", "MCLK",
15 "SPK_RX_BIAS", "MCLK",
16 "INT_LDO_H", "MCLK",
17 "MIC BIAS Internal1", "Handset Mic",
18 "MIC BIAS Internal2", "Headset Mic",
19 "MIC BIAS Internal3", "Secondary Mic",
20 "AMIC1", "MIC BIAS Internal1",
21 "AMIC2", "MIC BIAS Internal2",
22 "AMIC3", "MIC BIAS Internal3";
23 pinctrl-names = "cdc_lines_act",
24 "cdc_lines_sus";
25 pinctrl-0 = <&cdc_pdm_lines_act>;
26 pinctrl-1 = <&cdc_pdm_lines_sus>;
27 asoc-platform = <&pcm0>, <&pcm1>, <&voip>, <&voice>,
28 <&loopback>, <&compress>, <&hostless>,
29 <&afe>, <&lsm>, <&routing>, <&lpa>,
30 <&voice_svc>;
31 asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1",
32 "msm-voip-dsp", "msm-pcm-voice", "msm-pcm-loopback",
33 "msm-compress-dsp", "msm-pcm-hostless", "msm-pcm-afe",
34 "msm-lsm-client", "msm-pcm-routing", "msm-pcm-lpa",
35 "msm-voice-svc";
36 asoc-cpu = <&dai_pri_auxpcm>, <&dai_hdmi>,
37 <&dai_mi2s0>, <&dai_mi2s1>, <&dai_mi2s2>, <&dai_mi2s3>,
38 <&sb_0_rx>, <&sb_0_tx>, <&sb_1_rx>, <&sb_1_tx>,
39 <&sb_3_rx>, <&sb_3_tx>, <&sb_4_rx>, <&sb_4_tx>,
40 <&bt_sco_rx>, <&bt_sco_tx>, <&int_fm_rx>, <&int_fm_tx>,
41 <&afe_pcm_rx>, <&afe_pcm_tx>, <&afe_proxy_rx>, <&afe_proxy_tx>,
42 <&incall_record_rx>, <&incall_record_tx>, <&incall_music_rx>,
43 <&incall_music_2_rx>;
44 asoc-cpu-names = "msm-dai-q6-auxpcm.1", "msm-dai-q6-hdmi.8",
45 "msm-dai-q6-mi2s.0", "msm-dai-q6-mi2s.1",
46 "msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3",
47 "msm-dai-q6-dev.16384", "msm-dai-q6-dev.16385",
48 "msm-dai-q6-dev.16386", "msm-dai-q6-dev.16387",
49 "msm-dai-q6-dev.16390", "msm-dai-q6-dev.16391",
50 "msm-dai-q6-dev.16392", "msm-dai-q6-dev.16393",
51 "msm-dai-q6-dev.12288", "msm-dai-q6-dev.12289",
52 "msm-dai-q6-dev.12292", "msm-dai-q6-dev.12293",
53 "msm-dai-q6-dev.224", "msm-dai-q6-dev.225",
54 "msm-dai-q6-dev.241", "msm-dai-q6-dev.240",
55 "msm-dai-q6-dev.32771", "msm-dai-q6-dev.32772",
56 "msm-dai-q6-dev.32773", "msm-dai-q6-dev.32770";
57 asoc-codec = <&stub_codec>, <&pm8909_conga_dig>; //在这里使用了pm8909电源管理芯片的codec芯片
58 asoc-codec-names = "msm-stub-codec.1", "tombak_codec";
59 };
通过find ./ -name "*8909*" | xargs grep -n "pm8909_conga_dig"来搜索关键字:
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。
确定自己的插座是NO还是NC的,详情查看博客:http://www.cnblogs.com/linhaostudy/p/8260813.html
通过检查,判断JACK为NO状态;根据高通bring up 手册更改相应的DTSI:
1 qcom,msm-mbhc-hphl-swh = <1>; //由0改为1
2、kernel分析:
kernel/sound/soc/codecs/msm8x16-wcd.c
1 static const struct wcd_mbhc_intr intr_ids = {
2 .mbhc_sw_intr = MSM8X16_WCD_IRQ_MBHC_HS_DET,
3 .mbhc_btn_press_intr = MSM8X16_WCD_IRQ_MBHC_PRESS,
4 .mbhc_btn_release_intr = MSM8X16_WCD_IRQ_MBHC_RELEASE,
5 .mbhc_hs_ins_intr = MSM8X16_WCD_IRQ_MBHC_INSREM_DET1,
6 .mbhc_hs_rem_intr = MSM8X16_WCD_IRQ_MBHC_INSREM_DET,
7 .hph_left_ocp = MSM8X16_WCD_IRQ_HPHL_OCP,
8 .hph_right_ocp = MSM8X16_WCD_IRQ_HPHR_OCP,
9 };
这个结构体描述了msm8909的wcdx系列的codec芯片的MBHC的信息;
1 static int msm8x16_wcd_codec_probe(struct snd_soc_codec *codec)
2 {
3 struct msm8x16_wcd_priv *msm8x16_wcd_priv;
4 struct msm8x16_wcd *msm8x16_wcd;
5 int i, ret;
6
7 dev_dbg(codec->dev, "%s()n", __func__);
8
9 msm8x16_wcd_priv = kzalloc(sizeof(struct msm8x16_wcd_priv), GFP_KERNEL);
10 if (!msm8x16_wcd_priv) {
11 dev_err(codec->dev, "Failed to allocate private datan");
12 return -ENOMEM;
13 }
14
15 for (i = 0; i < NUM_DECIMATORS; i++) {
16 tx_hpf_work[i].msm8x16_wcd = msm8x16_wcd_priv;
17 tx_hpf_work[i].decimator = i + 1;
18 INIT_DELAYED_WORK(&tx_hpf_work[i].dwork,
19 tx_hpf_corner_freq_callback);
20 }
21
22 codec->control_data = dev_get_drvdata(codec->dev);
23 snd_soc_codec_set_drvdata(codec, msm8x16_wcd_priv);
24 msm8x16_wcd_priv->codec = codec;
25
26 /* codec resmgr module init */
27 msm8x16_wcd = codec->control_data;
28 msm8x16_wcd->dig_base = ioremap(MSM8X16_DIGITAL_CODEC_BASE_ADDR,
29 MSM8X16_DIGITAL_CODEC_REG_SIZE);
30 if (msm8x16_wcd->dig_base == NULL) {
31 dev_err(codec->dev, "%s ioremap failedn", __func__);
32 kfree(msm8x16_wcd_priv);
33 return -ENOMEM;
34 }
35 msm8x16_wcd_priv->spkdrv_reg =
36 wcd8x16_wcd_codec_find_regulator(codec->control_data,
37 MSM89XX_VDD_SPKDRV_NAME);
38 msm8x16_wcd_priv->pmic_rev = snd_soc_read(codec,
39 MSM8X16_WCD_A_DIGITAL_REVISION1);
40 msm8x16_wcd_priv->codec_version = snd_soc_read(codec,
41 MSM8X16_WCD_A_DIGITAL_PERPH_SUBTYPE);
42 if (msm8x16_wcd_priv->codec_version == CONGA) {
43 dev_dbg(codec->dev, "%s :Conga REV: %dn", __func__,
44 msm8x16_wcd_priv->codec_version);
45 msm8x16_wcd_priv->ext_spk_boost_set = true;
46 } else {
47 dev_dbg(codec->dev, "%s :PMIC REV: %dn", __func__,
48 msm8x16_wcd_priv->pmic_rev);
49 }
50 /*
51 * set to default boost option BOOST_SWITCH, user mixer path can change
52 * it to BOOST_ALWAYS or BOOST_BYPASS based on solution chosen.
53 */
54 msm8x16_wcd_priv->boost_option = BOOST_SWITCH;
55 msm8x16_wcd_dt_parse_boost_info(codec); //
56 msm8x16_wcd_set_boost_v(codec);
57
58 snd_soc_add_codec_controls(codec, impedance_detect_controls,
59 ARRAY_SIZE(impedance_detect_controls));
60
61 msm8x16_wcd_bringup(codec);
62 msm8x16_wcd_codec_init_reg(codec);
63 msm8x16_wcd_update_reg_defaults(codec);
64
65 wcd9xxx_spmi_set_codec(codec);
66
67 msm8x16_wcd_priv->on_demand_list[ON_DEMAND_MICBIAS].supply =
68 wcd8x16_wcd_codec_find_regulator(
69 codec->control_data,
70 on_demand_supply_name[ON_DEMAND_MICBIAS]);
71 atomic_set(&msm8x16_wcd_priv->on_demand_list[ON_DEMAND_MICBIAS].ref, 0);
72
73 BLOCKING_INIT_NOTIFIER_HEAD(&msm8x16_wcd_priv->notifier);
74
75 msm8x16_wcd_priv->fw_data = kzalloc(sizeof(*(msm8x16_wcd_priv->fw_data))
76 , GFP_KERNEL);
77 if (!msm8x16_wcd_priv->fw_data) {
78 dev_err(codec->dev, "Failed to allocate fw_datan");
79 iounmap(msm8x16_wcd->dig_base);
80 kfree(msm8x16_wcd_priv);
81 return -ENOMEM;
82 }
83
84 set_bit(WCD9XXX_MBHC_CAL, msm8x16_wcd_priv->fw_data->cal_bit);
85 ret = wcd_cal_create_hwdep(msm8x16_wcd_priv->fw_data,
86 WCD9XXX_CODEC_HWDEP_NODE, codec);
87 if (ret < 0) {
88 dev_err(codec->dev, "%s hwdep failed %dn", __func__, ret);
89 iounmap(msm8x16_wcd->dig_base);
90 kfree(msm8x16_wcd_priv->fw_data);
91 kfree(msm8x16_wcd_priv);
92 return ret;
93 }
94
95 wcd_mbhc_init(&msm8x16_wcd_priv->mbhc, codec, &mbhc_cb, &intr_ids,
96 true); //在这里获取到了相应的设备树信息
97
98 msm8x16_wcd_priv->mclk_enabled = false;
99 msm8x16_wcd_priv->clock_active = false;
100 msm8x16_wcd_priv->config_mode_active = false;
101
102 /* Set initial MICBIAS voltage level */
103 msm8x16_wcd_set_micb_v(codec);
104
105 /* Set initial cap mode */
106 msm8x16_wcd_configure_cap(codec, false, false);
107 registered_codec = codec;
108 modem_state_notifier =
109 subsys_notif_register_notifier("modem",
110 &modem_state_notifier_block);
111 if (!modem_state_notifier) {
112 dev_err(codec->dev, "Failed to register modem state notifiern"
113 );
114 iounmap(msm8x16_wcd->dig_base);
115 kfree(msm8x16_wcd_priv->fw_data);
116 kfree(msm8x16_wcd_priv);
117 registered_codec = NULL;
118 return -ENOMEM;
119 }
120 return 0;
121 }
kernel/sound/soc/codecs/wcd-mbhc-v2.c:
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 int ret = 0;
7 int hph_swh = 0;
8 int gnd_swh = 0;
9 struct snd_soc_card *card = codec->card;
10 const char *hph_switch = "qcom,msm-mbhc-hphl-swh";
11 const char *gnd_switch = "qcom,msm-mbhc-gnd-swh";
12 const char *ext1_cap = "qcom,msm-micbias1-ext-cap";
13 const char *ext2_cap = "qcom,msm-micbias2-ext-cap";
14
15 pr_debug("%s: entern", __func__);
16
17 ret = of_property_read_u32(card->dev->of_node, hph_switch, &hph_swh); //在这里找到设备树信息,通过高低电平判断no或者nc的jack
18 if (ret) {
19 dev_err(card->dev,
20 "%s: missing %s in dt noden", __func__, hph_switch);
21 goto err;
22 }
23
24 ret = of_property_read_u32(card->dev->of_node, gnd_switch, &gnd_swh);
25 if (ret) {
26 dev_err(card->dev,
27 "%s: missing %s in dt noden", __func__, gnd_switch);
28 goto err;
29 }
30 mbhc->micbias1_cap_mode =
31 (of_property_read_bool(card->dev->of_node, ext1_cap) ?
32 MICBIAS_EXT_BYP_CAP : MICBIAS_NO_EXT_BYP_CAP);
33
34 mbhc->micbias2_cap_mode =
35 (of_property_read_bool(card->dev->of_node, ext2_cap) ?
36 MICBIAS_EXT_BYP_CAP : MICBIAS_NO_EXT_BYP_CAP);
37
38 mbhc->in_swch_irq_handler = false;
39 mbhc->current_plug = MBHC_PLUG_TYPE_NONE;
40 mbhc->is_btn_press = false;
41 mbhc->codec = codec;
42 mbhc->intr_ids = mbhc_cdc_intr_ids;
43 mbhc->impedance_detect = impedance_det_en;
44 mbhc->hphl_swh = hph_swh;
45 mbhc->gnd_swh = gnd_swh;
46 mbhc->micbias_enable = false;
47 mbhc->mbhc_cb = mbhc_cb;
48 mbhc->btn_press_intr = false;
49
50 if (mbhc->intr_ids == NULL) {
51 pr_err("%s: Interrupt mapping not providedn", __func__);
52 return -EINVAL;
53 }
54
55 if (mbhc->headset_jack.jack == NULL) {
56 ret = snd_soc_jack_new(codec, "Headset Jack",
57 WCD_MBHC_JACK_MASK, &mbhc->headset_jack);
58 if (ret) {
59 pr_err("%s: Failed to create new jackn", __func__);
60 return ret;
61 }
62
63 ret = snd_soc_jack_new(codec, "Button Jack",
64 WCD_MBHC_JACK_BUTTON_MASK,
65 &mbhc->button_jack);
66 if (ret) {
67 pr_err("Failed to create new jackn");
68 return ret;
69 }
70
71 ret = snd_jack_set_key(mbhc->button_jack.jack,
72 SND_JACK_BTN_0,
73 KEY_MEDIA);
74 if (ret) {
75 pr_err("%s: Failed to set code for btn-0n",
76 __func__);
77 return ret;
78 }
79
80 INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd_btn_lpress_fn);
81 }
82
83 /* Register event notifier */
84 mbhc->nblock.notifier_call = wcd_event_notify;
85 ret = msm8x16_register_notifier(codec, &mbhc->nblock);
86 if (ret) {
87 pr_err("%s: Failed to register notifier %dn", __func__, ret);
88 return ret;
89 }
90
91 init_waitqueue_head(&mbhc->wait_btn_press);
92 mutex_init(&mbhc->codec_resource_lock);
93
94 ret = wcd9xxx_spmi_request_irq(mbhc->intr_ids->mbhc_sw_intr,
95 wcd_mbhc_mech_plug_detect_irq,
96 "mbhc sw intr", mbhc);
97 if (ret) {
98 pr_err("%s: Failed to request irq %d, ret = %dn", __func__,
99 mbhc->intr_ids->mbhc_sw_intr, ret);
100 goto err_mbhc_sw_irq;
101 }
102
103 ret = wcd9xxx_spmi_request_irq(mbhc->intr_ids->mbhc_btn_press_intr,
104 wcd_mbhc_btn_press_handler,
105 "Button Press detect",
106 mbhc);
107 if (ret) {
108 pr_err("%s: Failed to request irq %dn", __func__,
109 mbhc->intr_ids->mbhc_btn_press_intr);
110 goto err_btn_press_irq;
111 }
112
113 ret = wcd9xxx_spmi_request_irq(mbhc->intr_ids->mbhc_btn_release_intr,
114 wcd_mbhc_release_handler,
115 "Button Release detect", mbhc);
116 if (ret) {
117 pr_err("%s: Failed to request irq %dn", __func__,
118 mbhc->intr_ids->mbhc_btn_release_intr);
119 goto err_btn_release_irq;
120 }
121
122 ret = wcd9xxx_spmi_request_irq(mbhc->intr_ids->mbhc_hs_ins_intr,
123 wcd_mbhc_hs_ins_irq,
124 "Elect Insert", mbhc);
125 if (ret) {
126 pr_err("%s: Failed to request irq %dn", __func__,
127 mbhc->intr_ids->mbhc_hs_ins_intr);
128 goto err_mbhc_hs_ins_irq;
129 }
130 wcd9xxx_spmi_disable_irq(mbhc->intr_ids->mbhc_hs_ins_intr);
131
132 ret = wcd9xxx_spmi_request_irq(mbhc->intr_ids->mbhc_hs_rem_intr,
133 wcd_mbhc_hs_rem_irq,
134 "Elect Remove", mbhc);
135 if (ret) {
136 pr_err("%s: Failed to request irq %dn", __func__,
137 mbhc->intr_ids->mbhc_hs_rem_intr);
138 goto err_mbhc_hs_rem_irq;
139 }
140 wcd9xxx_spmi_disable_irq(mbhc->intr_ids->mbhc_hs_rem_intr);
141
142 ret = wcd9xxx_spmi_request_irq(mbhc->intr_ids->hph_left_ocp,
143 wcd_mbhc_hphl_ocp_irq, "HPH_L OCP detect",
144 mbhc); //在这里申请对detect管脚的判断中断
145 if (ret) {
146 pr_err("%s: Failed to request irq %dn", __func__,
147 mbhc->intr_ids->hph_left_ocp);
148 goto err_hphl_ocp_irq;
149 }
150
151 ret = wcd9xxx_spmi_request_irq(mbhc->intr_ids->hph_right_ocp,
152 wcd_mbhc_hphr_ocp_irq, "HPH_R OCP detect",
153 mbhc);
154 if (ret) {
155 pr_err("%s: Failed to request irq %dn", __func__,
156 mbhc->intr_ids->hph_right_ocp);
157 goto err_hphr_ocp_irq;
158 }
159
160 pr_debug("%s: leave ret %dn", __func__, ret);
161 return ret;
162
163 err_hphr_ocp_irq:
164 wcd9xxx_spmi_free_irq(mbhc->intr_ids->hph_left_ocp, mbhc);
165 err_hphl_ocp_irq:
166 wcd9xxx_spmi_free_irq(mbhc->intr_ids->mbhc_hs_rem_intr, mbhc);
167 err_mbhc_hs_rem_irq:
168 wcd9xxx_spmi_free_irq(mbhc->intr_ids->mbhc_hs_ins_intr, mbhc);
169 err_mbhc_hs_ins_irq:
170 wcd9xxx_spmi_free_irq(mbhc->intr_ids->mbhc_btn_release_intr, mbhc);
171 err_btn_release_irq:
172 wcd9xxx_spmi_free_irq(mbhc->intr_ids->mbhc_btn_press_intr, mbhc);
173 err_btn_press_irq:
174 wcd9xxx_spmi_free_irq(mbhc->intr_ids->mbhc_sw_intr, mbhc);
175 err_mbhc_sw_irq:
176 msm8x16_unregister_notifier(codec, &mbhc->nblock);
177 mutex_destroy(&mbhc->codec_resource_lock);
178 err:
179 pr_debug("%s: leave ret %dn", __func__, ret);
180 return ret;
181 }
1 static irqreturn_t wcd_mbhc_mech_plug_detect_irq(int irq, void *data)
2 {
3 int r = IRQ_HANDLED;
4 struct wcd_mbhc *mbhc = data;
5
6 pr_debug("%s: entern", __func__);
7 if (unlikely(wcd9xxx_spmi_lock_sleep() == false)) {
8 pr_warn("%s: failed to hold suspendn", __func__);
9 r = IRQ_NONE;
10 } else {
11 /* Call handler */
12 wcd_mbhc_swch_irq_handler(mbhc); //中断函数中的接口,负责处理相应的
13 wcd9xxx_spmi_unlock_sleep();
14 }
15 pr_debug("%s: leave %dn", __func__, r);
16 return r;
17 }
1 static void wcd_mbhc_swch_irq_handler(struct wcd_mbhc *mbhc)
2 {
3 bool detection_type;
4 bool micbias1;
5 struct snd_soc_codec *codec = mbhc->codec;
6 pr_debug("%s: entern", __func__);
7
8 WCD_MBHC_RSC_LOCK(mbhc);
9
10 mbhc->in_swch_irq_handler = true;
11
12 /* cancel pending button press */
13 if (wcd_cancel_btn_work(mbhc))
14 pr_debug("%s: button press is canceledn", __func__);
15
16 detection_type = (snd_soc_read(codec,
17 MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_1)) & 0x20;
18
19 /* Set the detection type appropriately */
20 snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_1,
21 0x20, (!detection_type << 5));
22
23 pr_debug("%s: mbhc->current_plug: %d detection_type: %dn", __func__,
24 mbhc->current_plug, detection_type);
25 wcd_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
26
27 micbias1 = (snd_soc_read(codec, MSM8X16_WCD_A_ANALOG_MICB_1_EN) & 0x80);
28 if ((mbhc->current_plug == MBHC_PLUG_TYPE_NONE) &&
29 detection_type) {
30 /* Make sure MASTER_BIAS_CTL is enabled */
31 snd_soc_update_bits(codec,
32 MSM8X16_WCD_A_ANALOG_MASTER_BIAS_CTL,
33 0x30, 0x30);
34 snd_soc_update_bits(codec,
35 MSM8X16_WCD_A_ANALOG_MICB_1_EN,
36 0x04, 0x04);
37 if (!mbhc->mbhc_cfg->hs_ext_micbias)
38 /* Enable Tx2 RBias if the headset
39 * is using internal micbias*/
40 snd_soc_update_bits(codec,
41 MSM8X16_WCD_A_ANALOG_MICB_1_INT_RBIAS,
42 0x10, 0x10);
43 /* Remove pull down on MIC BIAS2 */
44 snd_soc_update_bits(codec,
45 MSM8X16_WCD_A_ANALOG_MICB_2_EN,
46 0x20, 0x00);
47 /* Enable HW FSM */
48 snd_soc_update_bits(codec,
49 MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL,
50 0x80, 0x80);
51 /* Apply trim if needed on the device */
52 if (mbhc->mbhc_cb && mbhc->mbhc_cb->trim_btn_reg)
53 mbhc->mbhc_cb->trim_btn_reg(codec);
54 /* Enable external voltage source to micbias if present */
55 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
56 mbhc->mbhc_cb->enable_mb_source(codec, true);
57 mbhc->btn_press_intr = false;
58 wcd_mbhc_detect_plug_type(mbhc);
59 } else if ((mbhc->current_plug != MBHC_PLUG_TYPE_NONE)
60 && !detection_type) {
61 /* Disable external voltage source to micbias if present */
62 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
63 mbhc->mbhc_cb->enable_mb_source(codec, false);
64 /* Disable HW FSM */
65 snd_soc_update_bits(codec,
66 MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL,
67 0xB0, 0x00);
68 snd_soc_update_bits(codec,
69 MSM8X16_WCD_A_ANALOG_MICB_1_EN,
70 0x04, 0x00);
71 if (mbhc->mbhc_cb && mbhc->mbhc_cb->set_cap_mode)
72 mbhc->mbhc_cb->set_cap_mode(codec, micbias1, false);
73 mbhc->btn_press_intr = false;
74 if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) { //如果判断是headphone,上报数据;
75 wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADPHONE);
76 } else if (mbhc->current_plug == MBHC_PLUG_TYPE_GND_MIC_SWAP) {
77 wcd_mbhc_report_plug(mbhc, 0, SND_JACK_UNSUPPORTED);
78 } else if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET) {
79 /* make sure to turn off Rbias */
80 snd_soc_update_bits(codec,
81 MSM8X16_WCD_A_ANALOG_MICB_1_INT_RBIAS,
82 0x18, 0x08);
83 snd_soc_update_bits(codec,
84 MSM8X16_WCD_A_ANALOG_MICB_2_EN,
85 0x20, 0x20);
86 wcd9xxx_spmi_disable_irq(
87 mbhc->intr_ids->mbhc_hs_rem_intr);
88 wcd9xxx_spmi_disable_irq(
89 mbhc->intr_ids->mbhc_hs_ins_intr);
90 snd_soc_update_bits(codec,
91 MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_1,
92 0x01, 0x01);
93 snd_soc_update_bits(codec,
94 MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_2,
95 0x06, 0x00);
96 wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADSET);
97 } else if (mbhc->current_plug == MBHC_PLUG_TYPE_HIGH_HPH) {
98 mbhc->is_extn_cable = false;
99 wcd9xxx_spmi_disable_irq(
100 mbhc->intr_ids->mbhc_hs_rem_intr);
101 wcd9xxx_spmi_disable_irq(
102 mbhc->intr_ids->mbhc_hs_ins_intr);
103 snd_soc_update_bits(codec,
104 MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_1,
105 0x01, 0x01);
106 snd_soc_update_bits(codec,
107 MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_2,
108 0x06, 0x00);
109 wcd_mbhc_report_plug(mbhc, 0, SND_JACK_LINEOUT);
110 }
111 } else if (!detection_type) {
112 /* Disable external voltage source to micbias if present */
113 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
114 mbhc->mbhc_cb->enable_mb_source(codec, false);
115 /* Disable HW FSM */
116 snd_soc_update_bits(codec,
117 MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL,
118 0xB0, 0x00);
119 }
120
121 mbhc->in_swch_irq_handler = false;
122 WCD_MBHC_RSC_UNLOCK(mbhc);
123 pr_debug("%s: leaven", __func__);
124 }
经过一系列的检测,判断是headset,headphones等,如果是headphones,最终通过wcd_mbhc_jack_report将数据汇报上去。
1 static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion,
2 enum snd_jack_types jack_type)
3 {
4 struct snd_soc_codec *codec = mbhc->codec;
5 WCD_MBHC_RSC_ASSERT_LOCKED(mbhc);
6
7 pr_debug("%s: enter insertion %d hph_status %xn",
8 __func__, insertion, mbhc->hph_status);
9 if (!insertion) {
10 /* Report removal */
11 mbhc->hph_status &= ~jack_type;
12 /*
13 * cancel possibly scheduled btn work and
14 * report release if we reported button press
15 */
16 if (wcd_cancel_btn_work(mbhc)) {
17 pr_debug("%s: button press is canceledn", __func__);
18 } else if (mbhc->buttons_pressed) {
19 pr_debug("%s: release of button press%dn",
20 __func__, jack_type);
21 wcd_mbhc_jack_report(mbhc, &mbhc->button_jack, 0,
22 mbhc->buttons_pressed);
23 mbhc->buttons_pressed &=
24 ~WCD_MBHC_JACK_BUTTON_MASK;
25 }
26
27 if (mbhc->micbias_enable)
28 mbhc->micbias_enable = false;
29
30 mbhc->zl = mbhc->zr = 0;
31 mbhc->is_hs_inserted = false;
32 pr_debug("%s: Reporting removal %d(%x)n", __func__,
33 jack_type, mbhc->hph_status);
34 wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack,
35 mbhc->hph_status, WCD_MBHC_JACK_MASK);
36 wcd_mbhc_set_and_turnoff_hph_padac(mbhc);
37 hphrocp_off_report(mbhc, SND_JACK_OC_HPHR);
38 hphlocp_off_report(mbhc, SND_JACK_OC_HPHL);
39 mbhc->current_plug = MBHC_PLUG_TYPE_NONE;
40 } else {
41 /*
42 * Report removal of current jack type.
43 * Headphone to headset shouldn't report headphone
44 * removal.
45 */
46 if (mbhc->mbhc_cfg->detect_extn_cable &&
47 (mbhc->current_plug == MBHC_PLUG_TYPE_HIGH_HPH ||
48 jack_type == SND_JACK_LINEOUT) &&
49 (mbhc->hph_status && mbhc->hph_status != jack_type)) {
50
51 if (mbhc->micbias_enable)
52 mbhc->micbias_enable = false;
53
54 mbhc->zl = mbhc->zr = 0;
55 mbhc->is_hs_inserted = false;
56 pr_debug("%s: Reporting removal (%x)n",
57 __func__, mbhc->hph_status);
58 wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack,
59 0, WCD_MBHC_JACK_MASK);
60
61 if (mbhc->hph_status == SND_JACK_LINEOUT) {
62
63 pr_debug("%s: Enable micbiasn", __func__);
64 /* Disable current source and enable micbias */
65 snd_soc_update_bits(codec,
66 MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL,
67 0xB0, 0x80);
68 snd_soc_update_bits(codec,
69 MSM8X16_WCD_A_ANALOG_MICB_2_EN,
70 0x80, 0x80);
71
72 pr_debug("%s: set up elec removal detectionn",
73 __func__);
74 snd_soc_update_bits(codec,
75 MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_1,
76 0x01, 0x00);
77 usleep_range(200, 210);
78 wcd9xxx_spmi_enable_irq(
79 mbhc->intr_ids->mbhc_hs_rem_intr);
80 }
81 mbhc->hph_status &= ~(SND_JACK_HEADSET |
82 SND_JACK_LINEOUT |
83 SND_JACK_UNSUPPORTED);
84 }
85
86 if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET &&
87 jack_type == SND_JACK_HEADPHONE)
88 mbhc->hph_status &= ~SND_JACK_HEADSET;
89
90 /* Report insertion */
91 mbhc->hph_status |= jack_type;
92
93 if (jack_type == SND_JACK_HEADPHONE)
94 mbhc->current_plug = MBHC_PLUG_TYPE_HEADPHONE;
95 else if (jack_type == SND_JACK_UNSUPPORTED)
96 mbhc->current_plug = MBHC_PLUG_TYPE_GND_MIC_SWAP;
97 else if (jack_type == SND_JACK_HEADSET) {
98 mbhc->current_plug = MBHC_PLUG_TYPE_HEADSET;
99 mbhc->jiffies_atreport = jiffies;
100 }
101 else if (jack_type == SND_JACK_LINEOUT)
102 mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH;
103
104 if (mbhc->impedance_detect)
105 wcd_mbhc_calc_impedance(mbhc,
106 &mbhc->zl, &mbhc->zr);
107 mbhc->is_hs_inserted = true;
108 pr_debug("%s: Reporting insertion %d(%x)n", __func__,
109 jack_type, mbhc->hph_status);
110 wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack,
111 mbhc->hph_status, WCD_MBHC_JACK_MASK);
112 wcd_mbhc_clr_and_turnon_hph_padac(mbhc);
113 }
114 pr_debug("%s: leave hph_status %xn", __func__, mbhc->hph_status);
115 }
1 static void wcd_mbhc_jack_report(struct wcd_mbhc *mbhc,
2 struct snd_soc_jack *jack, int status, int mask)
3 {
4 snd_soc_jack_report_no_dapm(jack, status, mask);
5 }
1 void snd_soc_jack_report_no_dapm(struct snd_soc_jack *jack, int status,
2 int mask)
3 {
4 jack->status &= ~mask;
5 jack->status |= status & mask;
6
7 snd_jack_report(jack->jack, jack->status);
8 }
9 EXPORT_SYMBOL_GPL(snd_soc_jack_report_no_dapm);
1 void snd_jack_report(struct snd_jack *jack, int status)
2 {
3 int i;
4
5 if (!jack)
6 return;
7
8 for (i = 0; i < ARRAY_SIZE(jack->key); i++) {
9 int testbit = SND_JACK_BTN_0 >> i;
10
11 if (jack->type & testbit)
12 input_report_key(jack->input_dev, jack->key[i],
13 status & testbit);
14 }
15
16 for (i = 0; i < ARRAY_SIZE(jack_switch_types); i++) {
17 int testbit = 1 << i;
18 if (jack->type & testbit)
19 input_report_switch(jack->input_dev,
20 jack_switch_types[i],
21 status & testbit);
22 }
23
24 input_sync(jack->input_dev); //上报键值
25 }
26 EXPORT_SYMBOL(snd_jack_report);
最终汇报上去的有效数据是5 6 1 (插头类型 耳机信号 值插入)
0 0 0(同步)
插入的时候:
拔出的时候:
然后也可以根据高通文档判断是否是handset还是handphone:
高通是通过申请为/dev/input/event事件来确定的;
handset插入事件:
handset拔出事件:
- Git常用命令
- 手把手教你搭建SpringMVC——最小化配置
- Mysql-16-缓存的配置和使用
- 重叠(Overlapped)IO模型
- Mysql-15-mysql分布式应用
- 基于Spring Mvc实现的Excel文件上传下载
- Java程序员的日常—— Arrays工具类的使用
- Mysql-14-mysql的日志管理
- ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)
- Java程序员的日常 —— static的用法讲解实践
- WSAEventSelect模型 ---应用实例,重写TCP服务器实例
- Mysql-13mysql的复制
- 蓝牙门禁系统
- Java程序员的日常 —— 工作一天的收获
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 2017年天梯赛大区赛题集 7-1 出生年
- 2017年天梯赛大区赛题集 7-9 人以群分
- 浙大版《C语言程序设计(第3版)》题目集 练习2-1 Programming in C is fun!
- 浙大版《C语言程序设计(第3版)》题目集 练习2-3 输出倒三角图案
- 这款网络排查工具,堪称神器!
- 浙大版《C语言程序设计(第3版)》题目集 练习2-4 温度转换
- 浙大版《C语言程序设计(第3版)》题目集 练习2-6 计算物体自由下落的距离
- 浙大版《C语言程序设计(第3版)》题目集 练习2-8 计算摄氏温度
- 浙大版《C语言程序设计(第3版)》题目集 练习2-9 整数四则运算
- 浙大版《C语言程序设计(第3版)》题目集 练习2-10 计算分段函数[1]
- 浙大版《C语言程序设计(第3版)》题目集 练习2-11 计算分段函数[2]
- SAP Spartacus产品页面的normalizer
- Nodejs中使用net-snmp库读取智慧站房的温湿度、空调等实时数据
- 生气!面试官你过来,我给你手写一个Spring Aop实现!
- 浙大版《C语言程序设计(第3版)》题目集 练习2-12 输出华氏-摄氏温度转换表