ALSA子系统(十七)------支持Type-C耳机驱动

你好!这里是风筝的博客,

欢迎和我一起交流。


之前讲解了耳机驱动:
ALSA子系统(十六)------虚拟耳机驱动
Android音频子系统(四)------耳机拔插流程

那么必然少不了现在市场上较多的Type-C耳机。
TYPEC

常见的TYPE-C耳机有两种:

  • 一种是通过TYPE-C转3.5mm的转接线接3.5mm耳机,这种本质就是模拟耳机,信号还是通过HPOUT。
  • 一种是真正的TYPE-C耳机,一体式的,本质是数字耳机,耳机内部有DAC将数字信号转换为模拟信号(有的TYPE-C耳机也是一体式的,无需转接头,但是它是伪造的TYPE-C耳机,里面并没有DAC解码芯片,相当于里面封装了一个TYPE-C转3.5mm+模拟耳机)

一、通过3.5mm的转接线的TYPE-C模拟耳机

单纯要HPOUT转USB(TYPE-C)还是比较简单的,加一个转换芯片就行,我们看下硬件实现:

USB_SWITCH
其中USB0-DP-USBC和USB0-DM-USBC接到TYPE-C座子。
这样只要按照真值表,拉高EN和拉低SEL,通路为:
APB <-> DAC/ADC <-> HPOUTL/R <-> USB0-DP/M-USBC <-> TYPE-C接口
这样插入typec耳机,播音走HPOUT的话,耳机就能听到声音了,所以,理论上,无需任何TYPE-C芯片驱动,配置IO拉高EN,拉低SEL,即可使用TYPE-C模拟耳机进行播音,啥驱动都不用写,非常简单~

但是!!!!
kedaya
我们如何探知typec耳机的插入和拔出呢?
所以实际上,我们会使用一个TYPE-C控制芯片,用来检测TYPE-C耳机何时插入,用来识别是什么设备插入。
这里我们用的是wusb3801 typec 控制芯片。
typec
电路还是比较简单的,驱动代码在GitHub上可以搜得到,搜索wusb3801即可,这里我就不贴代码了。
插入耳机会触发INT中断,代码里面会判断插入的是啥耳机,进而控制USB/AUDIO SWITCH芯片的EN和SEL脚进行功能选择。

二、一体式TYPE-C数字耳机

对于真正的TYPE-C耳机,实际上是数字耳机,走的是USB声卡,选择的是USB通路。(需要确认内核中 CONFIG_SND_USB_AUDIO 是打开的
插入TYPE-C耳机之后,通过wusb3801芯片的USB-ID脚,USB会切换成Host模式,此时插入TYPE-C的话会被注册成一个USB声卡。
这里我插入华为TYPE-C耳机之后,就会发现多了一个声卡:

console:/ # cat /proc/asound/cards
0 [HEADSET ]: USB-Audio - HUAWEI USB-C HEADSET
bestechnic HUAWEI USB-C HEADSET at usb-sunxi-ohci-1, full speed

然后往这个华为耳机声卡写数据,就可以在耳机听到声音~

最后实验的是时候还发现个问题!
理论上Type-C接口没有正反面之分的,两面都一样,但是实际上使用Type-C耳机发现,正插(A面)Type-C耳机,没问题,翻个面过来反插(B面)Type-C耳机,就会出现左右声道数据串扰的问题!

这个主要是因为加了MIC/GND SWTICH芯片造成的。
mic
SBU1、SBU2分别接到了Type-C座子的SBU1、SBU2。
typec

默认情况下SEL是低电平,此时HPOUTFB接到了SBU1,HS_MIC接到了SBU2。
正(A面)插时,SBU1对应Type-C的SBU1,SBU2对应Type-C的SBU2,这是没问题的。
反(B面)插时,就会导致SBU1对应Type-C的SBU2,SBU2对应Type-C的SBU1,所以出现了耳机左右声道串扰问题。

所以,理论上,反(B面)插时,我们要拉高MIC/GND SWTICH芯片的SEL脚,这样才对。

那么我们如何判断是正插还是反插呢?
靠mic!

这篇博客写的挺好的:Android 4.x下基于wm8994的mic检测

先看看带mic的耳机和不带mic的耳机的差别,如下图,不带mic的耳机为3段,带mic的耳机为4段,比对一下实物可以看出两者左右声道段没有差别,差别之处是不带mic的耳机将GND和MIC两段合并在一起。因而对于不带mic的耳机来说,GND和MIC两段是几乎短路的(有一定电阻),而mic检测就是基于这个原理。

headset

为了实现录音,需要在MIC段施加一定的偏置电压,即micbias,对于没有mic的耳机来说,由于MIC和GND合成为一段,就相当于将micbias接地,因此会产生比较大的电流。一些codec支持电流检测功能,当电流超过某个阈值时,会将相应的寄存器设置为1,从而可以查询得到结果。

我们需要识别哪个是MIC,依靠MIC ADC值去判断是正面还是反面,驱动里会尝试控制SEL那个pin去切换。

static void wait_and_detect_mic_gnd_state(struct sound_card_priv *priv)
{
    
    
	int reg_val;
	int threshold = 0x8;

	gpio_set_value(priv->mic_gnd_sw_gpio, 0);//尝试先拉低SEL
	reg_val = snd_soc_read(priv->codec, SUNXI_HMIC_STS);//读取ADC的值
	reg_val = (reg_val >> HMIC_DATA) & 0x1f;
	if (reg_val >= threshold) {
    
    //大于阈值,是mic
		return;//是mic,则直接返回,保留SEL为低电平
	} else {
    
    
		gpio_set_value(priv->mic_gnd_sw_gpio, 1);//否则拉高SEL
	}

	return;
}

猜你喜欢

转载自blog.csdn.net/Guet_Kite/article/details/117529642