typec模拟耳机在某些高端手机上已经很常见了,但拿到的代码默认不支持typec模拟耳机(在某些平台上默认就是typec模拟耳机),那只能自己修改了。
这里用的版本为Android11,kernel4.19。
由于默认的耳机检测驱动mt6357-accdet.c已换成ko的形式(非ko方式可忽略该修改),导致无法调用该驱动里的函数,先把该驱动编译进内核。
defconfig加入CONFIG_SND_SOC_MT6357_ACCDET=y
BoardConfig.mk
@@ -53,8 +53,7 @@ BOARD_FLASH_BLOCK_SIZE := 4096
KERNEL_OUT ?= $(OUT_DIR)/target/project/$(TARGET_DEVICE)/obj/KERNEL_OBJ
# in-tree kernel modules installed to vendor
# For Common
-BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_OUT)/sound/soc/codecs/mt6357-accdet.ko \
- $(KERNEL_OUT)/kernel/trace/trace_mmstat.ko \
+BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_OUT)/kernel/trace/trace_mmstat.ko \
然后将CC脚检测到的耳机信号发出来
--- a/kernel-4.19/drivers/misc/mediatek/typec/tcpc/rt_pd_manager.c
+++ b/kernel-4.19/drivers/misc/mediatek/typec/tcpc/rt_pd_manager.c
@@ -52,7 +52,7 @@ void __attribute__((weak)) usb_dpdm_pulldown(bool enable)
{
pr_notice("%s is not defined\n", __func__);
}
-
+extern void typec_headphone_irq_handler(int state);
static int pd_tcp_notifier_call(struct notifier_block *nb,
unsigned long event, void *data)
{
@@ -91,10 +91,12 @@ static int pd_tcp_notifier_call(struct notifier_block *nb,
if (noti->typec_state.old_state == TYPEC_UNATTACHED &&
noti->typec_state.new_state == TYPEC_ATTACHED_AUDIO) {
/* AUDIO plug in */
+ typec_headphone_irq_handler(1);
pr_info("%s audio plug in\n", __func__);
} else if (noti->typec_state.old_state == TYPEC_ATTACHED_AUDIO
&& noti->typec_state.new_state == TYPEC_UNATTACHED) {
/* AUDIO plug out */
+ typec_headphone_irq_handler(0);
pr_info("%s audio plug out\n", __func__);
}
break;
在mt6357-accdet.c加入如下修改
+void typec_headphone_irq_handler(int state)
+{
+ if (accdet->cur_eint_state == EINT_PLUG_IN) {
+ accdet->cur_eint_state = EINT_PLUG_OUT;
+ } else {
+ accdet->cur_eint_state = EINT_PLUG_IN;
+ mod_timer(&micbias_timer,jiffies + MICBIAS_DISABLE_TIMER);
+ }
+ queue_work(accdet->eint_workqueue, &accdet->eint_work);
+}
+EXPORT_SYMBOL(typec_headphone_irq_handler);
这时,系统已经能检测到耳机了,播放音乐能正常听到音乐。
但检测到都是3段耳机,理论上正插和反插有一次是对的,不需要进行mic和gnd的切换。
这时测量micbias电压(有将micbias关闭的地方屏蔽),妥妥的0v。
跟mtk沟通,说要屏蔽住这里
ret = of_property_read_u32(node,
"headset-eint-num", &tmp);
if (ret)
tmp = 0;
if (tmp == 0)
accdet->data->caps |= ACCDET_PMIC_EINT0;
else if (tmp == 1)
accdet->data->caps |= ACCDET_PMIC_EINT1;
else if (tmp == 2)
accdet->data->caps |= ACCDET_PMIC_BI_EINT;
同时加入如下修改
&pmic_accdet{
+ headset-use-ap-eint=<1>;
}
屏蔽后,正插能识别到4段耳机,反插识别为3段耳机。猜测PMIC_EINT的作用是accdet中断来了,才能打开micbias,但由于触发不了accdet中断,导致micbias无法开启。
如果像小米一样采用自动切换mic和gnd的ic FSA4480(https://github.com/MiCode/Xiaomi_Kernel_OpenSource/blob/cezanne-q-oss/drivers/misc/mediatek/accdet/mt6359/accdet.c),这里的工作已经是完成了。
但这里采用切换ic是需要手工识别的,还需判断mic引脚的电压,如果电压偏低,需要进行切换。
用dct工具配置切换ic的引脚为普通gpio模式,同时确认该引脚没有被占用
+&pmic_accdet{
+ pinctrl-names ="mic_state1","mic_state2";
+ pinctrl-0 = <&mic_state1>;
+ pinctrl-1 = <&mic_state2>;
+};
+&pio {
+ mic_state1:mic_state1{
+ pins_cmd_dat {
+ pinmux = <PINMUX_GPIO155__FUNC_GPIO155>;
+ slew-rate = <1>; //1输出 0输入
+ bias-disable;
+ output-low;
+ };
+ };
+ mic_state2:mic_state2{
+ pins_cmd_dat {
+ pinmux = <PINMUX_GPIO155__FUNC_GPIO155>;
+ slew-rate = <1>;
+ bias-disable;
+ output-high;
+ };
+ };
+};
在probe中加入如下函数mic_gpio_get(pdev);
static inline int mic_gpio_get(struct platform_device *platform_device)
{
int ret = 0;
accdet->pinctrl = devm_pinctrl_get(&platform_device->dev);
if (IS_ERR(accdet->pinctrl)) {
ret = PTR_ERR(accdet->pinctrl);
return ret;
}
accdet->mic1 = pinctrl_lookup_state(accdet->pinctrl,
"mic_state1");
if (IS_ERR(accdet->mic1)) {
ret = PTR_ERR(accdet->mic1);
return ret;
}
accdet->mic2 = pinctrl_lookup_state(accdet->pinctrl,
"mic_state2");
if (IS_ERR(accdet->mic2)) {
ret = PTR_ERR(accdet->mic2);
return ret;
}
return 0;
}
--- a/kernel-4.19/sound/soc/codecs/mt6357-accdet.c
+++ b/kernel-4.19/sound/soc/codecs/mt6357-accdet.c
@@ -99,6 +99,8 @@ struct mt63xx_accdet_data {
/* when caps include ACCDET_AP_GPIO_EINT */
struct pinctrl *pinctrl;
struct pinctrl_state *pins_eint;
+ struct pinctrl_state *mic1;
+ struct pinctrl_state *mic2;
u32 gpiopin;
u32 gpio_hp_deb;
u32 gpioirq;
这里的600对应0.6v,实际该值改为0.4v,应该也是可以的。