Audio
Audio驱动概述
多媒体系统是物联网设备开发中不可缺少的一部分,Audio作为其中重要的一个模块,Audio驱动模型的构建显得尤为重要。
本文主要介绍基于HDF(Hardware Driver Foundation)驱动框架开发的Audio驱动,包括Audio驱动的架构组成和功能部件。芯片厂商可以根据此驱动架构,进行各自驱动的开发及HAL层接口的调用。
Audio驱动框架介绍
驱动架构主要由以下几部分组成。
- HDI adapter:实现Audio HAL层驱动(HDI接口适配),给Audio服务(frameworks)提供所需的音频硬件驱动能力接口。包含 Audio Manager、Audio Adapter、Audio Control、Audio Capture、Audio Render等接口对象。
- Audio Interface Lib:配合内核中的Audio Driver Model使用,实现音频硬件的控制、录音数据的读取、播放数据的写入。它里面包括Stream_ctrl_common 通用层,主要是为了和上层的Audio HDI Adapter层进行对接。
- ADM(Audio Driver Model):音频驱动框架模型,向上服务于多媒体音频子系统,便于系统开发者能够更便捷的根据场景来开发应用。向下服务于具体的设备厂商,对于Codec和DSP设备厂商来说,可根据ADM模块提供的向下统一接口适配各自的驱动代码,就可以实现快速开发和适配OpenHarmony系统。
- Audio Control Dispatch: 接收lib层的控制指令并将控制指令分发到驱动层。
- Audio Stream Dispatch: 接收lib层的数据并将数据分发到驱动层。
- Card Manager: 多声卡管理模块。每个声卡含有Dai、Platform、Codec、Dsp、SAPM模块。
- Platform Drivers: 驱动适配层。
- SAPM(Smart Audio Power Manager):电源管理模块,对整个ADM电源进行功耗策略优化。
Audio驱动开发
以下将基于Audio驱动框架,并以Hi3516DV300平台为例,介绍相关驱动开发的具体步骤。
Audio ADM模块框架介绍
Audio驱动对HDI层提供三个服务hdf_audio_render、hdf_audio_capture、hdf_audio_control。开发板dev目录下驱动服务节点如下:
# ls -l hdf_audio*
crw-rw---- 1 system system 247, 6 1970-01-01 00:00 hdf_audio_capture // 音频数据录音流服务。
crw-rw---- 1 root root 247, 4 1970-01-01 00:00 hdf_audio_codec_primary_dev0 // 音频声卡设备0名称。
crw-rw---- 1 root root 247, 4 1970-01-01 00:00 hdf_audio_codec_primary_dev11 // 音频声卡设备1名称。
crw-rw---- 1 system system 247, 5 1970-01-01 00:00 hdf_audio_control // 音频控制流服务。
crw-rw---- 1 system system 247, 7 1970-01-01 00:00 hdf_audio_render // 音频数据播放流务。
shell
- 1
- 2
- 3
- 4
- 5
- 6
- 7
音频声卡设备包括的驱动服务:
hdf_audio_codec_primary_dev0
- dma_service_0 : dma服务
- dai_service : CPU dai服务
- codec_service_0 : codec服务(可以是smartPA)
- dsp_service_0 : dsp 服务(可选项)
hdf_audio_codec_primary_dev11
- dma_service_0 : dma服务
- dai_service : CPU dai服务
- codec_service_1 : codec服务(可以是smartPA)
- dsp_service_0 : dsp服务(可选项)
启动流程
- 系统启动时Audio模块的Platform、Codec、Dsp、Dai各个驱动首先被加载,各驱动从各自私有配置文件中获取配置信息,并将获取的配置信息保存到各驱动的Data数据结构中。
- 各驱动模块调用ADM注册接口将自己添加到各驱动模块的链表中。
- ADM模块读取hdf_audio_driver_0和hdf_audio_driver_1配置信息,加载各模块的具体设备。
- ADM模块调用各模块的初始化函数对各模块设备进行初始化。
- 将初始化成功的音频设备添加到cardManager链表。
播放流程
- 播放音频时,Interface Lib层通过播放流服务下发Render Open指令,Audio Stream Dispatch服务收到指令后分别调用各模块的函数接口对指令进行下发。
- Interface Lib层通过控制服务下发通路选择指令,Control Dispatch控制服务收到指令后调用Dai模块接口设置通路。
- Interface Lib层通过播放流服务下发硬件参数,Audio Stream Dispatch服务收到参数后分别调用各模块参数设置接口,对硬件参数进行设置。
- Interface Lib层通过播放流服务下发播放启动指令,Audio Stream Dispatch服务收到指令后分别调用各模块启动接口,对各模块进行启动设置。
- Interface Lib层通过播放流服务下发音频数据,Audio Stream Dispatch服务收到数据后调用Platform AudioPcmWrite接口将音频数据传给Dma。
- Interface Lib层通过播放流服务下发播放停止指令,Audio Stream Dispatch服务收到指令后分别调用各模块停止接口,对各模块进行停止设置。
- Interface Lib层通过播放流服务下发Render Close指令,Audio Stream Dispatch服务收到指令后调用Platform AudioRenderClose对已申请资源进行释放。
控制流程
- 设置音量,首先Interface Lib层通过控制服务下发获取音量范围指令,Control Dispatch控制服务收到指令后进行解析,并调用Codec模块Get函数,获取可设置音量的范围。
- Interface Lib层通过控制服务下发设置音量指令,Control Dispatch控制服务收到指令后进行解析,并调用Codec模块Set函数设置音量。
Audio驱动公共函数介绍
函数名 | 功能 |
---|---|
CodecDeviceReadReg | codec寄存器读函数 |
CodecDeviceWriteReg | codec寄存器写函数 |
CodecDaiRegI2cRead | codec dai通过I2C接口读寄存器函数 |
CodecDaiRegI2cWrite | codec dai通过I2C接口写寄存器函数 |
CodecDeviceRegI2cRead | codec通过I2C接口读寄存器函数 |
CodecDeviceRegI2cWrite | codec通过I2C接口写寄存器函数 |
CodecDeviceInitRegConfig | codec初始化函数 |
CodecDaiDeviceStartupRegConfig | codec启动函数 |
CodecSetCtlFunc | codec设置set和get接口实现函数 |
CodecSetConfigInfoOfControls | codec设置控制功能函数接口和寄存器信息的函数 |
CodecGetConfigInfo | codec获取HCS配置信息函数 |
CodecGetDaiName | codec获取HCS配置dai名称函数 |
CodecGetServiceName | codec获取HCS配置服务名称函数 |
DaiDeviceReadReg | dai读寄存器函数 |
DaiDeviceWriteReg | dai写寄存器函数 |
DaiSetConfigInfoOfControls | dai设置控制功能函数接口和寄存器信息的函数 |
DaiGetConfigInfo | dai获取HCS配置信息函数 |
Audio驱动开发步骤
已有平台开发
ADM适配已有平台(Hi3516DV300)Codec或Smart PA的驱动开发流程:
- 根据芯片说明将相关寄存器信息配置到Codec或Smart PA的私有HCS中。
- 如果新添加Codec或Smart PA和已适配Codec或Smart PA的工作流程相同则不需要实现Codec或Smart PA的操作函数集和配置编译文件。
- 进行编译调试验证。
新平台开发
ADM适配新平台Audio驱动开发流程:
Audio驱动需要将Audio相关的Codec(可选)、Dai、DMA、DSP(可选)、Smart PA(可选)驱动进行适配。
- 根据芯片说明将各模块驱动的寄存器信息配置到各模块的私有配置文件中。
- 实现各模块的操作函数集。
- 修改配置Audio模块编译文件。
- 进行编译调试验证。
Audio驱动开发实例
代码路径:device/board/hisilicon/hispark_taurus/audio_drivers
下面以Hi3516DV300为例,介绍Audio的Codec驱动、Dai驱动、Platform驱动开发步骤。
Codec驱动开发实例
代码路径:device/board/hisilicon/hispark_taurus/audio_drivers/codec/hi3516
codec驱动开发主要包含如下几个重要步骤:
- 定义填充一个具体的codec。
- 实现codec回调函数。
- 注册绑定到HDF框架。
- 配置HCS和Makefile。
Codec数据结构填充
Codec模块需要填充如下3个结构体:
- g_codecData:codec设备的操作函数集和私有数据集。
- g_codecDaiDeviceOps:codecDai的操作函数集,包括启动传输和参数配置等函数接口。
- g_codecDaiData:codec的数字音频接口的操作函数集和私有数据集。
struct CodecData g_codecData = {
.Init = CodecDeviceInit, // codec设备初始化(适配新平台需重新实现)
.Read = AudioDeviceReadReg, // 读寄存器(现有框架已实现可使用)
.Write = AudioDeviceWriteReg, // 写寄存器(现有框架已实现可使用)
};
struct AudioDaiOps g_codecDaiDeviceOps = {
.Startup = CodecDaiStartup, // 启动传输(适配新平台需重新实现)
.HwParams = CodecDaiHwParams, // 参数配置(适配新平台需重新实现)
};
struct DaiData g_codecDaiData = {
.DaiInit = CodecDaiDeviceInit, // codecdai设备初始化(适配新平台需重新实现)
.ops = &g_codecDaiDeviceOps, // codecdai操作函数
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
codecDevice和codecDai设备初始化
CodecDeviceInit将完成AIAO的设置、寄存器默认值初始化、g_audioControls插入到controls链、电源管理初始化、通路选择设置等。
int32_t CodecDeviceInit(struct AudioCard *audioCard, struct CodecDevice *codec)
{
...
/* hi3516平台AIAO的Set和Get注册 */
CodecSetCtlFunc(codec->devData, AudioCodecAiaoGetCtrlOps, AudioCodecAiaoSetCtrlOps)
...
/* hi3516平台codec寄存器IoRemap */
CodecHalSysInit();
...
/* hi3516平台codec寄存器默认值初始化 */
CodecRegDefaultInit(codec->devData->regCfgGroup);
...
/* hi3516平台g_audioControls挂到Control链表上 */
AudioAddControls(audioCard, codec->devData->controls, codec->devData->numControls);
...
/* hi3516平台codec加载到sapm */
AudioSapmNewComponents(audioCard, codec->devData->sapmComponents, codec->devData->numSapmComponent);
...
/* hi3516平台codec加挂到通路选择链表上 */
AudioSapmAddRoutes(audioCard, g_audioRoutes, HDF_ARRAY_SIZE(g_audioRoutes);
...
AudioSapmNewControls(audioCard);
...
/* hi3516平台codec电源管理 */
AudioSapmSleep(audioCard);
...
return HDF_SUCCESS;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
CodecDaiDeviceInit将完成codecDai侧初始化,hi3516此处未涉及,接口保留:
int32_t CodecDaiDeviceInit(struct AudioCard *card, const struct DaiDevice *device)
{
...
AUDIO_DRIVER_LOG_DEBUG("codec dai device name: %s\n", device->devDaiName);
(void)card;
- 1
- 2
- 3
- 4
- 5
- 6
评论记录:
回复评论: