Xradio Sdk 的 Audio 驱动框架和 Linux 的 ASOC 驱动框架非常相似,只不过简化了很多。

驱动和芯片之间的关系图

下面的 SOC 表示的是 XR872 芯片,这里以 AC107 为例讲解,XR872 其实只有一路 I2S,这里画出了两路主要是便于后续的讲解。

从上图可以看出:

在软件上,一个完整的声卡,主要由 platform_driver 和 codec_driver 组成,它们的组成关系和相关配置信息由 snd_card_board_config 确定,系统根据此配置来构建出一张新的声卡。

在硬件上,XR872 通过 I2C 和 I2S 与 AC107 相连,I2C 接口主要是在 codec driver 中配置 codec ,如采样率、采样位深、数据传输格式等。而 I2S 主要是在 platform driver 中进行音频数据传输。

关键数据结构解析

在软件上,主要有四个核心的数据结构,它们之间的关系如下:

struct platform_driver:

是与平台相关的数字音频接口驱动,主要交互对象是 XR872 内部的  I2S 接口,如配置 I2S 主从关系, 数据传输格式,时钟频率,启动、停止 I2S 数据传输。一个 platform_driver 就代表 SOC 上的一个 I2S 控制器。

struct codec_driver:

则是与具体的 Codec 芯片相关的驱动,主要交互对象是 AC107 ,起到配置 AC107 相关寄存器的作用,具体配置会与 platfrom_driver 关联同步,如配置 I2S 主从关系,数据传输格式,时钟频率,启动、停止数据采集。一个 codec_driver 就代表一颗 Codec 芯片。

struct snd_card_board_config:

描述一张声卡的基本参数,指定由哪个 codec_driver 和 哪个 platform_driver 组成。相当于 Linux ASOC 中的 Machine 驱动角色。

struct snd_card:具体的声卡体现,一个 snd_card 对应一个声卡,由系统运行过程中动态创建。

声卡注册流程

一、Codec 驱动工作流程(以 AC107 为例:src\driver\chip\codec\ac107.c)

1. 构建一个 struct codec_driver 结构,并实现该结构中的相关回调接口。

/*** codec dai ops ****/
static const struct codec_dai_ops ac107_codec_dai_ops = {
.set_sysclk = ac107_dai_set_sysclk,
.set_fmt = ac107_dai_set_fmt,
.set_volume = ac107_dai_set_volume,
.set_route = ac107_dai_set_route,
.hw_params = ac107_dai_hw_params,
.hw_free = ac107_dai_hw_free,
}; /*** codec ops ****/
static const struct codec_ops ac107_codec_ops = {
.open = ac107_codec_open,
.close = ac107_codec_close,
.reg_read = ac107_codec_reg_read,
.reg_write = ac107_codec_reg_write,
.ioctl = ac107_codec_ioctl,
}; /*** codec driver ****/
static struct codec_driver ac107_codec_drv = {
.name = AC107_CODEC_NAME,
.codec_attr = XRADIO_CODEC_AC107,
.init = ac107_codec_init,
.deinit = ac107_codec_deinit,
.dai_ops = &ac107_codec_dai_ops,
.codec_ops = &ac107_codec_ops,
};

2. 提供一个注册和卸载接口,最终实现将 ac107_codec_drv 加入到 hal_snd_codec_list 链表。

HAL_Status ac107_codec_register(void)
{
// ...... // 初始化 I2C 控制器
if(HAL_I2C_Init(i, &i2c_param) != HAL_OK){
AC107_ERR("I2C-[%d] init Fail...\n",i);
} // ...... // 通过 I2C 读取芯片ID,用于确认芯片是否存在
AC107_I2C_READ(i2c_id, ac107_i2c_addr[i], CHIP_AUDIO_RST, I2C_MEMADDR_SIZE_8BIT, &chip_id, 1);
if(chip_id == 0x4B){
AC107_DBG("AC107-[0x%02x] on I2C-[%d] auto detect success\n",ac107_i2c_addr[i],i2c_id);
ac107_i2c_cfg_temp[detect_nums].i2c_id = i2c_id;
ac107_i2c_cfg_temp[detect_nums].i2c_addr = ac107_i2c_addr[i];
detect_nums++;
} // ...... // 将 ac107_codec_drv 添加到 hal_snd_codec_list 链表内
list_add(&ac107_codec_drv.node, &hal_snd_codec_list); return HAL_OK;
} HAL_Status ac107_codec_unregister(void)
{
// ...... // 遍历 hal_snd_codec_list 链表并将 ac107_codec_drv 移除
list_for_each_entry(codec_drv_ptr, &hal_snd_codec_list, node){
if(codec_drv_ptr == &ac107_codec_drv){
list_del(&ac107_codec_drv.node);
break;
}
} // ...... return HAL_OK;
} // src\driver\chip\hal_snd_card.c
HAL_Status HAL_SndCard_CodecRegisterAc107(void)
{
return ac107_codec_register();
} HAL_Status HAL_SndCard_CodecUnregisterAc107(void)
{
return ac107_codec_unregister();
}

二、platform 驱动工作流程(以 I2S 为例:src\driver\chip\hal_i2s.c)

1. 构建 struct platform_driver 数据结构,并实现该结构中的相关回调接口。

/*** platform dai ops ***/
static const struct platform_dai_ops xradio_i2s_dai_ops = {
.set_fmt = xradio_i2s_dai_set_fmt,
.set_clkdiv = xradio_i2s_set_clkdiv,
.hw_params = xradio_i2s_dai_hw_params,
.hw_free = xradio_i2s_dai_hw_free,
}; /*** platform ops ***/
static const struct platform_ops xradio_i2s_ops = {
.open = xradio_i2s_open,
.close = xradio_i2s_close,
.pcm_read = xradio_i2s_pcm_read,
.pcm_write = xradio_i2s_pcm_write,
.ioctl = xradio_i2s_ioctl,
}; /*** platform driver ***/
static struct platform_driver xradio_i2s_drv = {
.name = XRADIO_PLATFORM_I2S_NAME,
.platform_attr = XRADIO_PLATFORM_I2S,
.init = xradio_i2s_init,
.deinit = xradio_i2s_deinit,
.dai_ops = &xradio_i2s_dai_ops,
.platform_ops = &xradio_i2s_ops,
};

2. 提供一个注册和卸载接口,最终实现将 xradio_i2s_drv 加入到 hal_snd_platform_list 链表。


HAL_Status xradio_i2s_register(void)
{
// ...... // 将 xradio_i2s_drv 添加到 hal_snd_platform_list 链表内
list_add(&xradio_i2s_drv.node, &hal_snd_platform_list); return HAL_OK;
} HAL_Status xradio_i2s_unregister(void)
{
// ...... // 遍历 hal_snd_platform_list 链表并将 xradio_i2s_drv 移除
list_for_each_entry(i2s_drv_ptr, &hal_snd_platform_list, node){
if(i2s_drv_ptr == &xradio_i2s_drv){
list_del(&xradio_i2s_drv.node);
break;
}
} // ...... return HAL_OK;
} // src\driver\chip\hal_snd_card.c
HAL_Status HAL_SndCard_PlatformRegisterI2S(void)
{
return xradio_i2s_register();
} HAL_Status HAL_SndCard_PlatformUnregisterI2S(void)
{
return xradio_i2s_unregister();
}

三、在板级配置文件中,提供 snd_card_board_config  关联 Codec 和 Platform 并提供获取接口(以标案配置为例:project\common\board\xr872_evb_ai\board_config.c)

board_ioctl() --->  board_get_pinmux_info() ---> snd_cards_board_cfg[] ---> ac107_codec_snd_card

#if PRJCONF_AC107_SOUNDCARD_EN
__xip_rodata const static struct snd_card_board_config ac107_codec_snd_card = {
.card_num = SND_CARD_1,
.card_name = HAL_SND_CARD_NAME(AC107_CODEC_NAME, SND_CARD_SUFFIX),
.codec_link = XRADIO_CODEC_AC107,
.platform_link = XRADIO_PLATFORM_I2S,
.pa_switch_ctl = NULL,
.codec_sysclk_src = SYSCLK_SRC_MCLK,
.codec_pllclk_src = 0,
.codec_pll_freq_in = 0,
.i2s_fmt = DAIFMT_CBS_CFS | DAIFMT_I2S | DAIFMT_NB_NF,
};
#endif const static struct snd_card_board_config *snd_cards_board_cfg[] = {
// ...... #if PRJCONF_AC107_SOUNDCARD_EN
&ac107_codec_snd_card,
#endif // ......
}; static HAL_Status board_get_pinmux_info(uint32_t major, uint32_t minor, uint32_t param, struct board_pinmux_info info[])
{
// ......
switch (major) {
// ......
// 实现 HAL_DEV_MAJOR_AUDIO_CODEC 命令, 根据 card_num 号来返回对应的 snd_card_board_config
case HAL_DEV_MAJOR_AUDIO_CODEC:
for(i=0; i<HAL_ARRAY_SIZE(snd_cards_board_cfg); i++){
if(snd_cards_board_cfg[i]->card_num == minor){
if(snd_cards_board_cfg[i]->pa_switch_ctl){
info[0].pinmux = snd_cards_board_cfg[i]->pa_switch_ctl->pin_param;
info[0].count = snd_cards_board_cfg[i]->pa_switch_ctl->pin_param_cnt;
}
}
}
break; // ......
} return ret;
} HAL_Status board_ioctl(HAL_BoardIoctlReq req, uint32_t param0, uint32_t param1)
{
// ......
switch (req) {
case HAL_BIR_PINMUX_INIT:
case HAL_BIR_PINMUX_DEINIT:
memset(info, 0, sizeof(info));
major = HAL_DEV_MAJOR((HAL_Dev_t)param0);
minor = HAL_DEV_MINOR((HAL_Dev_t)param0);
ret = board_get_pinmux_info(major, minor, param1, info);
// ......
}
// ...... return ret;
}

四、触发声卡注册流程

1. 在 main.c 入口函数调用 platform_init()

__sram_text
void platform_init(void)
{
platform_init_level0();
platform_init_level1();
platform_init_level2(); // 进行二级初始化
platform_show_info();
} /* init extern platform hardware and services */
__weak void platform_init_level2(void)
{
// ...
#if PRJCONF_AUDIO_SNDCARD_EN
board_soundcard_init(); // 初始化声卡
audio_manager_init();
snd_pcm_init();
#if PRJCONF_AUDIO_CTRL_EN
audio_ctrl_init();
#endif
#endif
// ...
} #if PRJCONF_AUDIO_SNDCARD_EN
__weak HAL_Status board_soundcard_init(void)
{
/* Codec register */
#if PRJCONF_INTERNAL_SOUNDCARD_EN
HAL_SndCard_CodecRegisterInternal();
#endif
#if PRJCONF_AC107_SOUNDCARD_EN
HAL_SndCard_CodecRegisterAc107(); // 注册 AC107 即 ac107_codec_register()
#endif
#if PRJCONF_AC101_SOUNDCARD_EN
HAL_SndCard_CodecRegisterAc101();
#endif
//Add other codec register here /* Platform register */
#if PRJCONF_PLATFORM_I2S_EN
HAL_SndCard_PlatformRegisterI2S(); // 注册 I2S 即 xradio_i2s_register();
#endif
//Add other platform register here /* Snd Card register*/
HAL_SndCard_Register(); return HAL_OK;
} __weak HAL_Status board_soundcard_deinit(void)
{
/* Snd Card unregister*/
HAL_SndCard_Unregister(); /* Platform unregister */
#if PRJCONF_PLATFORM_I2S_EN
HAL_SndCard_PlatformUnregisterI2S();
#endif
//Add other platform unregister here /* Codec unregister */
#if PRJCONF_INTERNAL_SOUNDCARD_EN
HAL_SndCard_CodecUnregisterInternal();
#endif
#if PRJCONF_AC107_SOUNDCARD_EN
HAL_SndCard_CodecUnregisterAc107();
#endif
#if PRJCONF_AC101_SOUNDCARD_EN
HAL_SndCard_CodecUnregisterAc101();
#endif
//Add other codec unregister here return HAL_OK;
}
#endif

2. 声卡注册过程

uint8_t HAL_SndCard_Register(void)
{
// ...... // 通过遍历所有声卡号来找到所有的 snd_card_board_config
for(i=0; i<=SND_CARD_MAX; i++,snd_card_board_cfg = NULL)
{
// 获取 struct snd_card_board_config 配置信息
hal_status = HAL_BoardIoctl(HAL_BIR_GET_CFG, HAL_MKDEV(HAL_DEV_MAJOR_AUDIO_CODEC, i), (uint32_t)&snd_card_board_cfg);
if(hal_status != HAL_OK || snd_card_board_cfg == NULL){
continue;
} // 检查声卡号是否合法
if(snd_card_board_cfg->card_num > SND_CARD_MAX){
HAL_SND_CARD_ERROR("Invalid snd card number-[%d], while the max card number is %d!\n",snd_card_board_cfg->card_num,SND_CARD_MAX);
continue;
} // 遍历声卡链表, 若声卡硬件存在则不再继续
if(!list_empty(&hal_snd_card_list)){
found = 0;
list_for_each_entry(sound_card, &hal_snd_card_list, node){
if(sound_card->card_num == snd_card_board_cfg->card_num){
found = 1;
break;
}
}
if(found){
HAL_SND_CARD_ERROR("The snd card number-[%d] has registered\n",snd_card_board_cfg->card_num);
continue;
}
} // 遍历 hal_snd_codec_list 链表寻找 snd_card_board_config 指定的 codec_driver
if(snd_card_board_cfg->codec_link == XRADIO_CODEC_NULL){
codec_drv_ptr = NULL;
} else {
found = 0;
if(!list_empty(&hal_snd_codec_list)){
list_for_each_entry(codec_drv_ptr, &hal_snd_codec_list, node){
if(snd_card_board_cfg->codec_link == codec_drv_ptr->codec_attr){
found = 1;
break; //snd card match board config codec SUCCESS, break to continue register sound card
}
}
}
if(!found){
HAL_SND_CARD_ERROR("snd card-[%d] match board config codec Fail!\n",snd_card_board_cfg->card_num);
continue;
}
} // 遍历 hal_snd_platform_list 链表寻找 snd_card_board_config 指定的 platform_driver
if(snd_card_board_cfg->platform_link == XRADIO_PLATFORM_NULL){
platform_drv_ptr = NULL;
} else {
found = 0;
if(!list_empty(&hal_snd_platform_list)){
list_for_each_entry(platform_drv_ptr, &hal_snd_platform_list, node){
if(snd_card_board_cfg->platform_link == platform_drv_ptr->platform_attr){
found = 1;
break; //snd card match board config platform SUCCESS, break to continue register sound card
}
}
}
if(!found){
HAL_SND_CARD_ERROR("snd card-[%d] match board config platform Fail!\n",snd_card_board_cfg->card_num);
continue;
}
} // 如果都没有找到则继续下一个 snd_card_board_config
if(codec_drv_ptr == NULL && platform_drv_ptr == NULL){
HAL_SND_CARD_ERROR("snd card-[%d] must link one codec or platform at least!\n",snd_card_board_cfg->card_num);
continue;
} // 如果都找到了, 就申请内存开始创建一个新的声卡
sound_card = (struct snd_card *)HAL_Malloc(sizeof(struct snd_card));
if(!sound_card){
HAL_SND_CARD_ERROR("Malloc snd card-[%d] struct snd_card buffer Fail!\n",snd_card_board_cfg->card_num);
break;//continue;
}
HAL_Memset(sound_card, 0, sizeof(struct snd_card)); // 关联 codec driver 和 platform driver
sound_card->codec_drv = codec_drv_ptr;
sound_card->platform_drv = platform_drv_ptr; /* Card lock init */
if(sound_card->card_lock.handle == NULL){
hal_status = HAL_MutexInit(&sound_card->card_lock);
if(hal_status != HAL_OK){
HAL_SND_CARD_ERROR("snd card[%d] mutex init Fail\n", (uint8_t)sound_card->card_num);
HAL_Free(sound_card);
continue;
}
} // 拷贝 snd_card_board_config 配置信息
sound_card->card_num = snd_card_board_cfg->card_num;
sound_card->card_name = snd_card_board_cfg->card_name;
sound_card->codec_sysclk_src = snd_card_board_cfg->codec_sysclk_src;
sound_card->codec_pllclk_src = snd_card_board_cfg->codec_pllclk_src;
sound_card->codec_pll_freq_in = snd_card_board_cfg->codec_pll_freq_in;
sound_card->i2s_fmt = snd_card_board_cfg->i2s_fmt;
sound_card->pa_switch_ctl = snd_card_board_cfg->pa_switch_ctl; // 如果有指定功放控制引脚则根据配置信息设置功放控制引脚的状态
HAL_BoardIoctl(HAL_BIR_PINMUX_INIT, HAL_MKDEV(HAL_DEV_MAJOR_AUDIO_CODEC, (uint8_t)sound_card->card_num), 0);
if(sound_card->pa_switch_ctl){
//PA switch init
HAL_GPIO_WritePin(sound_card->pa_switch_ctl->pin_param->port,\
sound_card->pa_switch_ctl->pin_param->pin, !sound_card->pa_switch_ctl->on_state);
} // 调用 codec 的 init 回调,即调用的是 ac107_codec_drv.init = ac107_codec_init()
if(sound_card->codec_drv && sound_card->codec_drv->init){
hal_status = sound_card->codec_drv->init();
if(hal_status != HAL_OK){
HAL_SND_CARD_ERROR("snd card[%d] codec init Fail\n", (uint8_t)sound_card->card_num);
HAL_Free(sound_card);
continue;
}
} // 调用 codec 的 init 回调,即调用的是 xradio_i2s_drv.init = xradio_i2s_init()
if(sound_card->platform_drv && sound_card->platform_drv->init){
hal_status = sound_card->platform_drv->init();
if(hal_status != HAL_OK){
HAL_SND_CARD_ERROR("snd card[%d] platform-I2S init Fail\n", (uint8_t)sound_card->card_num);
HAL_Free(sound_card);
continue;
}
} // ...... // 如果 codec 和 platform 的 init() 都成功, 则正式将此声卡加入声卡链表
list_add(&sound_card->node, &hal_snd_card_list);
card_nums++; HAL_SND_CARD_DEBUG("/*** Register snd card[%d]->%s Success ***\\^_^\n\n",sound_card->card_num,sound_card->card_name);
} return card_nums;
}

五、记录到 audio_pcm_list 链表中,为 snd_pcm 类接口做准备

int snd_pcm_init(void)
{
char *pcm_priv_temp;
uint8_t *card_num, card_nums, i;
struct pcm_priv* audio_pcm_priv;
AUDIO_PCM_DEBUG("--->%s\n",__FUNCTION__); /* Get snd card nums */
card_nums = HAL_SndCard_GetCardNums();
if(!card_nums){
AUDIO_PCM_ERROR("card nums is 0!\n");
return -1;
} /* Malloc buffer and init */
pcm_priv_temp = (char *)pcm_zalloc(sizeof(struct pcm_priv) * card_nums);
card_num = (uint8_t *)pcm_zalloc(sizeof(uint8_t) * card_nums); if(!pcm_priv_temp || !card_num){
AUDIO_PCM_ERROR("Malloc audio pcm buffer Fail!\n");
pcm_free(pcm_priv_temp);
pcm_free(card_num);
return -1;
} HAL_SndCard_GetAllCardNum(card_num); /* Init struct pcm_priv and list add */
for(i=0; i<card_nums; i++){
//Init struct pcm_priv
audio_pcm_priv = (struct pcm_priv *)(pcm_priv_temp + sizeof(struct pcm_priv)*i);
audio_pcm_priv->card_num = (Snd_Card_Num)card_num[i];
pcm_lock_init(&audio_pcm_priv->play_lock);
pcm_lock_init(&audio_pcm_priv->cap_lock);
pcm_lock_init(&audio_pcm_priv->write_lock); //list add
list_add(&audio_pcm_priv->node, &audio_pcm_list);
} /* Free get card num buffer */
pcm_free(card_num); return 0;
}

六、汇总工作流程

按照 AC107 的 snd_card_board_config 配置(ac107_codec_snd_card )构建声卡的数据结构如下:

汇总整个声卡工作流程:

整个声卡注册都是为了着四个关键的链表做准备: hal_snd_codec_list、hal_snd_platform_list、hal_snd_card_list、audio_pcm_list

hal_snd_codec_list:保存着所有注册成功的 codec_driver,为 HAL_SndCard_Register() 做准备。

hal_snd_platform_list:保存着所有注册成功的 platform_driver ,为 HAL_SndCard_Register() 做准备。

hal_snd_card_list:保存着通过 snd_card_board_config 配置的信息,成功配对 codec_driver、platform_driver 而组合形成的声卡。

audio_pcm_list:保持着所有声卡的声卡号,为 snd_pcm_* 系列接口函数做准备(主要是建立和维护应用层的缓冲区)

应用层使用

一、应用示例

上面所有的这些,最终都是通过 API 接口的方式为上层应用的业务逻辑做准备,下面是最简单的一个录音程序。

#include <stdio.h>
#include <string.h>
#include "audio/pcm/audio_pcm.h"
#include "common/framework/platform_init.h" int main(void)
{
int ret;
void *data;
unsigned int len;
struct pcm_config config; // 平台初始化
platform_init(); // 配置音频参数
config.channels = 1;
config.format = PCM_FORMAT_S16_LE;
config.period_count = 2;
config.period_size = 1024;
config.rate = 16000; // 通过 SND_CARD_1 声卡号打开指定的声卡
ret = snd_pcm_open(SND_CARD_1, PCM_IN, &config);
if (ret) {
printf("snd_pcm_open fail.\n");
return ;
} // 申请缓冲区
len = config.channels * config.period_count * config.period_size;
data = malloc(len);
if (data == NULL) {
snd_pcm_close(AUDIO_SND_CARD_DEFAULT, PCM_IN);
return;
} while (1)
{
// 从声卡中读取数据
ret = snd_pcm_read(AUDIO_SND_CARD_DEFAULT, data, len);
if (ret != len) {
printf("snd_pcm_read fail.\n");
break;
}
// 处理音频数据
}
printf("record pcm over.\n");
free(data); // 关闭声卡
snd_pcm_close(AUDIO_SND_CARD_DEFAULT, PCM_IN); return;
}

二、打开声卡的工作流程

int snd_pcm_open(Snd_Card_Num card_num, Audio_Stream_Dir stream_dir, struct pcm_config *pcm_cfg)
{
uint32_t buf_size;
struct pcm_priv *audio_pcm_priv;
AUDIO_PCM_DEBUG("--->%s\n",__FUNCTION__); /* Get audio_pcm_priv */
audio_pcm_priv = card_num_to_pcm_priv(card_num);
if(audio_pcm_priv == NULL){
AUDIO_PCM_ERROR("Invalid sound card num [%d]!\n",(uint8_t)card_num);
return -1;
} /* Init audio_pcm_priv */
if (stream_dir == PCM_OUT) {
//play lock
if (pcm_lock(&audio_pcm_priv->play_lock) != OS_OK) {
AUDIO_PCM_ERROR("Obtain play lock err...\n");
return -1;
} // 申请播放缓冲区
buf_size = pcm_frames_to_bytes(pcm_cfg, pcm_config_to_frames(pcm_cfg));
audio_pcm_priv->play_priv.cache = pcm_zalloc(buf_size/2);
if (audio_pcm_priv->play_priv.cache == NULL) {
pcm_unlock(&audio_pcm_priv->play_lock);
AUDIO_PCM_ERROR("obtain play cache failed...\n");
return -1;
}
audio_pcm_priv->play_priv.length = 0;
audio_pcm_priv->play_priv.half_buf_size = buf_size/2;
} else {
//cap lock
if (pcm_lock(&audio_pcm_priv->cap_lock) != OS_OK) {
AUDIO_PCM_ERROR("obtain cap lock err...\n");
return -1;
} // 申请录音缓冲区
buf_size = pcm_frames_to_bytes(pcm_cfg, pcm_config_to_frames(pcm_cfg));
audio_pcm_priv->cap_priv.cache = pcm_zalloc(buf_size/2);
if (audio_pcm_priv->cap_priv.cache == NULL) {
pcm_unlock(&audio_pcm_priv->cap_lock);
AUDIO_PCM_ERROR("obtain cap cache failed...\n");
return -1;
}
audio_pcm_priv->cap_priv.length = 0;
audio_pcm_priv->cap_priv.half_buf_size = buf_size/2;
} // 打开声卡
if (HAL_SndCard_Open(card_num, stream_dir, pcm_cfg) != HAL_OK) {
AUDIO_PCM_ERROR("Sound card-[%d] open Fai!\n",card_num);
if (stream_dir == PCM_OUT) {
pcm_free(audio_pcm_priv->play_priv.cache);
memset(&(audio_pcm_priv->play_priv), 0, sizeof(struct play_priv));
pcm_unlock(&audio_pcm_priv->play_lock);
} else {
pcm_free(audio_pcm_priv->cap_priv.cache);
memset(&(audio_pcm_priv->cap_priv), 0, sizeof(struct cap_priv));
pcm_unlock(&audio_pcm_priv->cap_lock);
}
return -1;
} return 0;
}

HAL_Status HAL_SndCard_Open(Snd_Card_Num card_num, Audio_Stream_Dir stream_dir, struct pcm_config *pcm_cfg)
{
HAL_SND_CARD_DEBUG("--->%s\n",__FUNCTION__); uint32_t dma_buf_size;
HAL_Status hal_status; // 通过声卡卡号从 hal_snd_card_list 链表中找到对应的声卡
struct snd_card *sound_card = card_num_to_snd_card(card_num); // ...... HAL_MutexLock(&sound_card->card_lock, OS_WAIT_FOREVER); // 按照预设的参数先对该声卡所关联的 codec 进行配置(参数来自板级文件中的 snd_card_board_config、和应用程序的 pcm_cfg )
if(sound_card->codec_drv && sound_card->codec_drv->dai_ops){ // 设置系统时钟
if(sound_card->codec_drv->dai_ops->set_sysclk){
hal_status = sound_card->codec_drv->dai_ops->set_sysclk(sound_card->codec_sysclk_src,\
sound_card->codec_pllclk_src, sound_card->codec_pll_freq_in, pcm_cfg->rate);
if(hal_status != HAL_OK){
HAL_SND_CARD_ERROR("snd card[%d] codec set sysclk Fail!\n",(uint8_t)card_num);
goto codec_hw_free;
}
} // 设置 I2S 相关的参数
if(sound_card->codec_drv->dai_ops->set_fmt){
hal_status = sound_card->codec_drv->dai_ops->set_fmt(sound_card->i2s_fmt);
if(hal_status != HAL_OK){
HAL_SND_CARD_ERROR("snd card[%d] codec set fmt Fail!\n",(uint8_t)card_num);
goto codec_hw_free;
}
} // 配置硬件参数
if(sound_card->codec_drv->dai_ops->hw_params){
hal_status = sound_card->codec_drv->dai_ops->hw_params(stream_dir, pcm_cfg);
if(hal_status != HAL_OK){
HAL_SND_CARD_ERROR("snd card[%d] codec config hw params Fail!\n",(uint8_t)card_num);
goto codec_hw_free;
}
}
} // 如果实现了 codec_ops->open 回调就调用,让 codec 做好准备
if(sound_card->codec_drv && sound_card->codec_drv->codec_ops){
//codec ioctl
//related Codec Ioctl place here //codec open
if(sound_card->codec_drv->codec_ops->open){
hal_status = sound_card->codec_drv->codec_ops->open(stream_dir);
if(hal_status != HAL_OK){
HAL_SND_CARD_ERROR("snd card[%d] codec open Fail!\n",(uint8_t)card_num);
goto codec_hw_free;
}
}
} // 按照预设的参数先对该声卡所关联的 I2S 进行配置(参数来自板级文件中的 snd_card_board_config、和应用程序的 pcm_cfg )
if(sound_card->platform_drv && sound_card->platform_drv->dai_ops){ // 设置 I2S 相关的参数
if(sound_card->platform_drv->dai_ops->set_fmt){
hal_status = sound_card->platform_drv->dai_ops->set_fmt(sound_card->i2s_fmt);
if(hal_status != HAL_OK){
HAL_SND_CARD_ERROR("snd card[%d] platform set fmt Fail!\n",(uint8_t)card_num);
goto platform_hw_free;
}
} // 设置 I2S 时钟分频
if(sound_card->platform_drv->dai_ops->set_clkdiv){
hal_status = sound_card->platform_drv->dai_ops->set_clkdiv(pcm_cfg->rate);
if(hal_status != HAL_OK){
HAL_SND_CARD_ERROR("snd card[%d] platform set clkdiv Fail!\n",(uint8_t)card_num);
goto platform_hw_free;
}
} // 设置硬件参数
if(sound_card->platform_drv->dai_ops->hw_params){
hal_status = sound_card->platform_drv->dai_ops->hw_params(stream_dir, pcm_cfg);
if(hal_status != HAL_OK){
HAL_SND_CARD_ERROR("snd card[%d] platform config hw params Fail!\n",(uint8_t)card_num);
goto platform_hw_free;
}
}
} // 如果实现了 platform_ops->open 回调就调用,让 I2S 做好准备
if(sound_card->platform_drv && sound_card->platform_drv->platform_ops && sound_card->platform_drv->platform_ops->open){
hal_status = sound_card->platform_drv->platform_ops->open(stream_dir);
if(hal_status != HAL_OK){
HAL_SND_CARD_ERROR("snd card[%d] platform-I2S open Fail!\n",(uint8_t)card_num);
goto platform_hw_free;
}
} HAL_MutexUnlock(&sound_card->card_lock); // 如果是音频输出,则控制功放输出引脚,让功放工作
if(stream_dir == PCM_OUT){
if(sound_card->pa_switch_ctl){
if(sound_card->pa_switch_ctl->on_delay_before) HAL_MSleep(sound_card->pa_switch_ctl->on_delay_before);
HAL_GPIO_WritePin(sound_card->pa_switch_ctl->pin_param->port, sound_card->pa_switch_ctl->pin_param->pin, sound_card->pa_switch_ctl->on_state);
if(sound_card->pa_switch_ctl->on_delay_after) HAL_MSleep(sound_card->pa_switch_ctl->on_delay_after);
}
} return HAL_OK; platform_hw_free:
if(sound_card->platform_drv && sound_card->platform_drv->dai_ops && sound_card->platform_drv->dai_ops->hw_free){
hal_status = sound_card->platform_drv->dai_ops->hw_free(stream_dir);
if(hal_status != HAL_OK){
HAL_SND_CARD_ERROR("snd card[%d] platform hw free Fail!\n",(uint8_t)card_num);
}
} //codec_close:
if(sound_card->codec_drv && sound_card->codec_drv->codec_ops && sound_card->codec_drv->codec_ops->close){
hal_status = sound_card->codec_drv->codec_ops->close(stream_dir);
if(hal_status != HAL_OK){
HAL_SND_CARD_ERROR("snd card[%d] codec close Fail!\n",(uint8_t)card_num);
}
} codec_hw_free:
if(sound_card->codec_drv && sound_card->codec_drv->dai_ops && sound_card->codec_drv->dai_ops->hw_free){
hal_status = sound_card->codec_drv->dai_ops->hw_free(stream_dir);
if(hal_status != HAL_OK){
HAL_SND_CARD_ERROR("snd card[%d] codec hw free Fail!\n",(uint8_t)card_num);
}
} HAL_MutexUnlock(&sound_card->card_lock);
return HAL_ERROR;
}

三、读取音频数据流程

int snd_pcm_read(Snd_Card_Num card_num, void *data, uint32_t count)
{
// ...... 省略应用层的缓冲区处理流程 // 从声卡里面读取数据
if(read_remain >= half_buf_size){
hw_read = (read_remain / half_buf_size) * half_buf_size;
ret = HAL_SndCard_PcmRead(card_num, data_ptr, hw_read);
if(ret != hw_read){
AUDIO_PCM_ERROR("PCM read error!\n");
return ret ? count - read_remain + ret : count - read_remain;
}
data_ptr += hw_read;
read_remain -= hw_read;
if(!read_remain){
return count;
}
} // ...... 省略应用层的缓冲区处理流程 return count;
}
int HAL_SndCard_PcmRead(Snd_Card_Num card_num, uint8_t *buf, uint32_t size)
{
struct snd_card *sound_card = card_num_to_snd_card(card_num); // ...... // 如果 platform_driver 驱动有实现 platform_ops->pcm_read() 则优先从 pcm_read() 接口读取数据
if(sound_card->platform_drv && sound_card->platform_drv->platform_ops && sound_card->platform_drv->platform_ops->pcm_read){
return sound_card->platform_drv->platform_ops->pcm_read(buf, size);
} else if (sound_card->codec_drv && sound_card->codec_drv->codec_ops && sound_card->codec_drv->codec_ops->ioctl){
// 否则从 codec_driver 驱动的 codec_ops->ioctl 接口读取数据
uint32_t cmd_param[2] = {(uint32_t)buf, size};
return sound_card->codec_drv->codec_ops->ioctl(CODEC_IOCTL_PCM_READ, cmd_param, 2);
} return 0;
}

四、关闭声卡流程

HAL_Status HAL_SndCard_Close(Snd_Card_Num card_num, Audio_Stream_Dir stream_dir)
{
HAL_SND_CARD_DEBUG("--->%s\n",__FUNCTION__); HAL_Status hal_status;
struct snd_card *sound_card = card_num_to_snd_card(card_num); if(!sound_card){
HAL_SND_CARD_ERROR("Invalid sound card num [%d]!\n",(uint8_t)card_num);
return HAL_INVALID;
} // 先关闭功放
if(stream_dir == PCM_OUT){
if(sound_card->pa_switch_ctl){
HAL_GPIO_WritePin(sound_card->pa_switch_ctl->pin_param->port, sound_card->pa_switch_ctl->pin_param->pin, !sound_card->pa_switch_ctl->on_state);
}
} HAL_MutexLock(&sound_card->card_lock, OS_WAIT_FOREVER); // 关闭 I2S
if(sound_card->platform_drv && sound_card->platform_drv->platform_ops && sound_card->platform_drv->platform_ops->close){
hal_status = sound_card->platform_drv->platform_ops->close(stream_dir);
if(hal_status != HAL_OK){
HAL_SND_CARD_ERROR("snd card[%d] platform close Fail!\n",(uint8_t)card_num);
}
} // 关闭 Codec
if(sound_card->codec_drv && sound_card->codec_drv->codec_ops && sound_card->codec_drv->codec_ops->close){
hal_status = sound_card->codec_drv->codec_ops->close(stream_dir);
if(hal_status != HAL_OK){
HAL_SND_CARD_ERROR("snd card[%d] codec close Fail!\n",(uint8_t)card_num);
}
} // 释放 Codec 硬件配置
if(sound_card->codec_drv && sound_card->codec_drv->dai_ops && sound_card->codec_drv->dai_ops->hw_free){
hal_status = sound_card->codec_drv->dai_ops->hw_free(stream_dir);
if(hal_status != HAL_OK){
HAL_SND_CARD_ERROR("snd card[%d] codec hw free Fail!\n",(uint8_t)card_num);
}
} // 释放 I2S 硬件配置
if(sound_card->platform_drv && sound_card->platform_drv->dai_ops && sound_card->platform_drv->dai_ops->hw_free){
hal_status = sound_card->platform_drv->dai_ops->hw_free(stream_dir);
if(hal_status != HAL_OK){
HAL_SND_CARD_ERROR("snd card[%d] platform hw free Fail!\n",(uint8_t)card_num);
}
} HAL_MutexUnlock(&sound_card->card_lock); return HAL_OK;
}

【学习笔记】XR872 Audio 驱动框架分析的更多相关文章

  1. input子系统学习笔记六 按键驱动实例分析下【转】

    转自:http://blog.chinaunix.net/uid-20776117-id-3212095.html 本文接着input子系统学习笔记五 按键驱动实例分析上接续分析这个按键驱动实例! i ...

  2. Linux USB驱动框架分析 【转】

    转自:http://blog.chinaunix.net/uid-11848011-id-96188.html 初次接触与OS相关的设备驱动编写,感觉还挺有意思的,为了不至于忘掉看过的东西,笔记跟总结 ...

  3. linux驱动基础系列--linux spi驱动框架分析

    前言 主要是想对Linux 下spi驱动框架有一个整体的把控,因此会忽略某些细节,同时里面涉及到的一些驱动基础,比如平台驱动.设备模型等也不进行详细说明原理.如果有任何错误地方,请指出,谢谢! spi ...

  4. Linux USB驱动框架分析【转】

    转自:http://blog.csdn.net/jeffade/article/details/7701431 Linux USB驱动框架分析(一) 初次接触和OS相关的设备驱动编写,感觉还挺有意思的 ...

  5. uart驱动框架分析(二)uart_add_one_port

    作者:lizuobin (百问网论坛答疑助手) 原文: https://blog.csdn.net/lizuobin2/article/details/51801183 (所用开发板:mini2440 ...

  6. memcached学习笔记——存储命令源码分析下篇

    上一篇回顾:<memcached学习笔记——存储命令源码分析上篇>通过分析memcached的存储命令源码的过程,了解了memcached如何解析文本命令和mencached的内存管理机制 ...

  7. memcached学习笔记——存储命令源码分析上篇

    原创文章,转载请标明,谢谢. 上一篇分析过memcached的连接模型,了解memcached是如何高效处理客户端连接,这一篇分析memcached源码中的process_update_command ...

  8. ArcGIS案例学习笔记2_1_学校选址适宜性分析

    ArcGIS案例学习笔记2_1_学校选址适宜性分析 计划时间:第二天上午 目的:学校选址,适宜性分析 内容:栅格数据分析 教程:pdf page=323 数据:chapter8/ex1/教育,生活,土 ...

  9. Linux USB驱动框架分析(2)【转】

    转自:http://blog.chinaunix.net/uid-23046336-id-3243543.html   看了http://blog.chinaunix.net/uid-11848011 ...

  10. linux驱动基础系列--linux spi驱动框架分析(续)

    前言 这篇文章是对linux驱动基础系列--linux spi驱动框架分析的补充,主要是添加了最新的linux内核里设备树相关内容. spi设备树相关信息 如之前的文章里所述,控制器的device和s ...

随机推荐

  1. 夯实Java基础,一篇文章全解析线程问题

    1. 线程是什么 操作系统支持多个应用程序并发执行,每个应用程序至少对应一个进程 ,彼此之间的操作和数据不受干扰,彼此通信一般采用管道通信.消息队列.共享内存等方式.当一个进程需要磁盘IO的时候,CP ...

  2. 状态估计和KalmanFilter公式的推导与应用

    状态估计的概率解释 运动和观测方程: \[\left\lbrace \begin{array}{l} x_k = f(x_{k_1}, u_k) + w_k \\ z_k = h(y_j, x_k) ...

  3. 基于 .NET 7 的 QUIC 实现 Echo 服务

    前言 随着今年6月份的 HTTP/3 协议的正式发布,它背后的网络传输协议 QUIC,凭借其高效的传输效率和多路并发的能力,也大概率会取代我们熟悉的使用了几十年的 TCP,成为互联网的下一代标准传输协 ...

  4. nginx rewrite参数 以及 $1、$2参数解析(附有生产配置实例)

    在nginx的配置中,是否对rewrite的配置模糊不清,还有令人迷惑的$1.$2...参数,(其实$1.$2参数在shell脚本中经常用到,用来承接传递的参数).本篇从反向代理配置的角度帮助理解一下 ...

  5. 封装适用于CentOS7的MySQL离线包

    1 构建一个centos7.6.1810的docker镜像,用于下载MySQL+xtrabackup所需安装包 7.6.1810的docker镜像,低版本最小安装,会尽可能把所需的包拉齐. Docke ...

  6. PyQt5程序打包出错Failed to execute script

    出现这种报错一般有两种可能: 1. 该被引入的资源你没有放到 exe 的相对路径 这种错误一般是你直接引入图片或者图标, 而没有放到 打包后的exe的相对路径 2. 加参数 假设 main.py 为程 ...

  7. easyui combobox的级联设置

    <body> <input id="title" class="easyUI-combobox" //onSelect:在该combobox被 ...

  8. 《吐血整理》高级系列教程-吃透Fiddler抓包教程(35)-Fiddler如何抓取微信小程序的包-下篇

    1.简介 通过前边和宏哥的学习,我们了解到Android 7.0 之后增加了对第三方证书的限制,抓包工具(charles.fiddler等)提供的证书都无法通过校验,也就无法抓取HTTPS请求了,对测 ...

  9. 腾讯云数据库SaaS致力于构建数据库分布式云,为更多更广的用户提供服务

    大数据时代,数据库 SaaS 是企业实现降本增效和业务创新的重要抓手.在腾讯全球数字生态大会数据库 SaaS 专场上,腾讯云发布了多项数据库 SaaS 产品能力升级,并重点分享了其在上云.日常运维.数 ...

  10. Springboot 整合 SpringCache 使用 Redis 作为缓存

    一直以来对缓存都是一知半解,从没有正经的接触并使用一次,今天腾出时间研究一下缓存技术,开发环境为OpenJDK17与SpringBoot2.7.5 SpringCache基础概念 接口介绍 首先看看S ...