linux驱动由浅入深系列:ALSA框架详解 音频子系统之二

linux驱动由浅入深系列:ALSA框架详解 音频子系统之二

2018年06月26日 15:16:10

阅读数:86

转载自   https://blog.csdn.net/RadianceBlau/article/details/79432661

linux驱动由浅入深系列:tinyalsa(tinymix/tinycap/tinyplay/tinypcminfo)音频子系统之一
linux驱动由浅入深系列:ALSA框架详解 音频子系统之二

本文以高通平台为例,介绍一下android下的音频结构。android使用的是tinyALSA作为音频系统,使用方法和基本框架与linux中常用的ALSA音频子系统是一致的。

ALSA音频框架

ALSA(Advanced Linux Sound Architecture)是一个开源项目(http://www.alsa-project.org/) ,在Kernel 2.6正式版本被引入,它提供了一整套音频解决方案,涵盖了用户空间和内核空间对音频设备的操作接口。
在传统的Linux平台,用户空间可以调用alsa-lib提供的应用来调用内核空间的alsa-soc接口, 实现对底层音频设备的控制。

android平台的音频框架如下:

Android平台音频系统使用的是tinyALSA,主要的改变在于用户空间的音频接口不依赖alsa-lib库, 改用精简的libtinyalsa库,但是在内核中仍然使用ALSA框架的驱动框架。android中HAL层相关模块使用如下图所示:

声卡的主要功能:
1,播放声音(playback)
2,录音(capture)
3,声音控制(control)
音频设备节点

音频设备节点位于/dev/snd目录下,分为三类:
1,形如 pcmC0D0p   以‘p’结尾的表示播放节点,playback
2,形如 pcmC0D0c   以‘c’结尾的表示录音节点,capture
3,形如controlC0      以“control”开头的表示控制节点,控制音量等
其中pcmC0D0p与pcmC0D0c组成一个pcm设备,其中C0代表0号声卡Card,D0代表0号设备device。一个声卡可以有多个设备,每个设备代表该声卡的一个音频通路。

  

生成pcmC0D0p的代码分析如下,分析一下各个pcm节点的含义,如pcmC0D1c:
在kernel/sound/core/pcm.c中组织了pcmC0D0p字符串,其中D0的数字0来自于pcm->device,pcm由参数传递而来。

以高通msm8952平台为例:
D后面的数字为msm8952_dai结构体成员的个数
/kernel/sound/soc/msm/msm8952.c

android平台HAL层音频框架

音频数据流框图如下

HAL层音频框架接口
1) audio.primary.msm8952.so
该文件是高通平台音频HAL层代码库,负责与framework交换音频数据和控制参数。
代码路径: hardware/qcom/audio/hal
LOCAL_SRC_FILES := \
audio_hw.c \
voice.c \
platform_info.c \
$(AUDIO_PLATFORM)/platform.c
2) libtinyalsa.so
该文件是tinyALSA的应用库,由HAL层调用,负责对接kernel的ALSA驱动接口。
代码路径: external/tinyalsa
LOCAL_SRC_FILES:= mixer.c pcm.c
3) libaudioroute.so
该文件用于解析用户空间的音频通道配置文件(mixer_path_XXX.xml),由ALSA来配置音频通道。
代码路径: system/media/audio_route

LOCAL_SRC_FILES:= audio_route.c

kernel层ASoC框架

在内核中, ALSA依赖ASoC(ALSA System on Chip)驱动模型。ASoC是嵌入式系统使用的音频框架,它从硬件架构的角度来将功能相对独立的硬件单元细分出来,在驱动设备上分为machine、 platform/CPU和CODEC三个模块。

可以这么理解:一套嵌入式硬件平台(Machine)包含了平台AP(Platform)和音频CODEC芯片(Codec),对应ASoC的三个设备驱动。这三个设备分别注册各自功能的dev设备,但都是以内核platform设备模型来创建。 ASoC主要代码位于kernel/sound/soc下。 下面分别来介绍一下:

MAchine设备驱动

Machine设备可以看成是一块嵌入式主板(Board) 或者一块声卡。machine设备驱动是ASoC驱动框架的入口, 主要功能是负责platform/cpu和codec之间的连接和控制,或者响应独立于Platform功能和Codec功能之外的特殊音频事件,如平台GPIO控制外置功放等,这些属于machine本身的特定操作代码,都会放到machine驱动里。
Machine设备驱动的主要功能是定义各种DAI(Digital Audio Interface) links,它的作用是把platform/cpu和codec设备驱动连接起来,形成完整的音频通路。
在内核设备树中,平台会定义一个声卡设备,如MSM8976平台在msm8976.dtsi中定义了一个名为“msm8976-tasha-snd-card” 的声卡,它就是ASoC框架里的machine设备。 machine设备的初始化是整个ASoC驱动的入口。 machine设备的probe()会调用snd_soc_register_card()去注册声卡,然后在snd_soc_instantiate_card()里实例化声卡设备的时候,调用Platform和Codec设备各自的probe(),完成这两个设备的初始化。如果没有错误,那么声卡会注册成功,我们在/dev/snd下可以看到多个音频设备。
高通平台的machine设备驱动放在如下路径:
kernel/sound/soc/msm/<Chipset>
msm8976.dtsi定义如下:

msm9952-slimbus.c中代码中是machine的probe:

platform设备驱动

Platform设备可看作是平台AP(SoC主控,或CPU)。 它负责提供嵌入式平台端的音频功能, 如播放、录音、 Voice通话等。
Platform设备驱动主要有两个作用:
(1) transfer:负责平台AP端的audio/voice数据流(stream)和DSP之间的传输;
(2) routing:将stream数据流按照特定的路线对应到其他音频模块中。
ASoC会注册多个Platform设备来负责不同功能的音频模块:
(1) msm-pcm-dsp: 负责audio回放/录音
代码路径: kernel/sound/msm/msm-pcm-q6.c
(2) msm-pcm-voice: 负责voice通话
代码路径: kernel/sound/msm/msm-pcm-voice-v2.c
(3) msm-voip-dsp: 负责VoIP通话
代码路径: kernel/sound/msm/msm-pcm-voip-v2.c
(4) msm-compress-dsp: 负责压缩格式的audio播放
代码路径: kernel/sound/msm/msm-compressed-q6-v2.c
(5) msm-pcm-routing:负责stream数据流的路线指定
代码路径: kernel/sound/msm/msm-pcm-routing-v2.c
msm-pcm-routing-v2.dtsi定义如下:

在msm-pcm-routing-v2.c里定义了很多kcontrol、 widget和route。
(1) struct snd_kcontrol_new
kcontrol代表一个控制单元, 描述了控件自身的属性和功能。 如mixer多路混合器、 mux多路选择器、 gain增益设置、 mute开关等。
(2) struct snd_soc_dapm_widget
widget是kcontrol的封装,能够用path来连接多widget,形成一条音频路径。
(3) struct snd_soc_dapm_route
route用来描述相互连接的widget。

CPU设备驱动

高通平台将CPU设备独立于Platform设备,其实他们是紧密结合的关系, CPU设备驱动定义了平台能够支持的各种stream。
高通平台用单独的文件来定义CPU设备。
(1)在msm-dai-fe.c里定义了FE CPU DAI 

(2)在msm-dai-q6-v2.c里定义了BE CPU DAI 

CODEC设备驱动

对于一块嵌入式设备的主板来说,一般会集成一颗音频CODEC芯片。 ASoC架构下的CODEC设备功能和物理CODEC对应, 其在machine的控制下和platform设备连接,负责音频的实际处理,如声音播放(D/A)、声音采集(A/D) 和各种音频control控件的设置。
平台一般会集成一个CODEC单元,也会有添加外部独立CODEC芯片,已达到更好的音质。如Wolfson的WM8998芯片,它是一颗独立的CODEC,基于I2S接口从平台获取音频数据,在其内部经过DAC输出到耳机或speaker。高通有自己的外置CODEC芯片,如WCD9326/9335等,和平台AP的音频数据接口叫Slimbus,其实是和I2S复用的GPIO口。
CODEC芯片可能需要I2C或SPI控制。
COCEC设备支持耳机插拔及按键检测功能。 
CODEC设备驱动中定义了大量的mixer、 mux和各种开关的kcontrol,以及DAPM使用的widget和route。 
kernel/sound/soc/codec/wcd9335.c

实例:打开MIC录音

CODEC设备驱动中定义了大量的mixer、 mux和各种开关的kcontrol,以及DAPM使用的widget和route。 

音频模块调试

对于使用平台内置CODEC或者高通外部CODEC的常规项目,把audio功能调通所需要修改的内容不多,如果是添加额外的SmartPA、 CODEC或者DAC芯片,那么会比较麻烦。我们在项目中可能会有以下工作:
(1)主、副mic调试
部分高通平台的原始代码使用的是DMIC,而我们项目通常使用AMIC,这个需要修改DTSI的声卡节点中“qcom,audio-routing” 值。
部分平台代码的machine driver中缺少副mic的control,需要手动添加。
工模中需要对副mic单独测试,但是高通默认没有这样的usecase来单独使用mic,我们需要在audio hal层修改input设备的选择。
(2)耳机按键
在machine driver里有一个耳机按键阀值的数组,我们需要根据高通文档计算出不同按键阻抗的阀值,填进数组。
(3)普通音频PA
现在多数项目都会添加speaker PA,如果是单个gpio口控制开关的普通的PA,则需要在machine driver里添加控制逻辑。
(4)第三方CODEC调试
第三方CODEC通过I2S接口和AP传输音频数据,然后控制speaker或headphone发声,一般需要I2C或者SPI来控制芯片。我们需要把I2C或SPI调通,获得CODEC芯片的寄存器数据;调通平台端的master I2S输出;综合调试。
(5) SmartPA调试
SmartPA包含了CODEC和PA,芯片寄存器配置上比CODEC简单,但是硬件接口仍然会是I2S和I2C,我们要做的事情和第三方CODEC差不多。
(6)第三方音效移植
从第三方获取移植包,导入即可。依据音效算法运行的位置,我们需要配置平台端的音乐播放模式。如果算法跑在AP侧,我们需要关闭平台端offload播放模式,仍采用传统的deep_buffer模式。
(7)音频loopback测试
高通平台在audioFT里提供了loopback模式功能,我们需要针对不同的项目,修改ftm_loopback_config配置文件,满足工厂的测试要求。
(8)更新音频参数

调试中遇到的问题

在实际项目中, audio的问题种类会很多。 Audio涉及Android几乎所有的层,遇到声音问题时我们需要明确界定问题所处的位置。
(1) 无声问题
一个新项目前期如果遇到无声问题,首先检查声卡驱动是否挂载成功。如果声卡没挂上,那么检查驱动;如果声卡OK,那么用QXDM抓取音频数据检查DSP是否无声。如果DSP无声,需要往framework上面dump数据找原因;如果DSP正常,一般问题出在kernel里,我们先试一下耳机是否有声音。如果耳机有声音speaker无声音,那就重点检查speaker这块,是否新加的PA有问题;如果耳机无声音,那么dump一下CODEC的寄存器,或者检查mixer_paths是否配置错误。等等。。
(2)播放杂音问题
杂音可能有多种原因。驱动里PA的开关时序不合适会造成POP音;上层framework性能问题会造成音频流断续,也会导致POP音;音频参数没调好也会导致音质差,等等。。
(3)其他问题

调试工具

(1) QXDM + QCAT
QXDM工具用来抓取DSP部分的log,包含音频数据,然后用QCAT解析log,能够还原出DSP内部各个节点的音频数据。我们借此可以判断声音从AP进入DSP和从DSP出来前是否异常。

(2)使用QACT确认音频参数和调用的input/output设备的Device

(3)打开logcat和kmsg的各种log
对于上层,在音频相关文件的开头打开#define LOG_NDEBUG 0

对于kernel,如果内核的dynamic_debug可用,我们可以直接打开相关文件的调试信息;如果不可用,那需要在文件的开头添加#define DEBUG 

(4)使用tinymix确认音频控件开关
在shell里可以使用tinymix把ASoC所有的control导出来,我们可以检查开关异常的control。

版权

猜你喜欢

转载自blog.csdn.net/weixin_42082222/article/details/81207209