【学习笔记】XR872 Audio 驱动框架分析
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.cHAL_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.cHAL_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,};#endifconst 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_configcase HAL_DEV_MAJOR_AUDIO_CODEC:for(i=0; icard_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_textvoid 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_ENboard_soundcard_init(); // 初始化声卡audio_manager_init();snd_pcm_init(); #if PRJCONF_AUDIO_CTRL_ENaudio_ctrl_init(); #endif#endif // ... }#if PRJCONF_AUDIO_SNDCARD_EN__weak HAL_Status board_soundcard_init(void){/* Codec register */#if PRJCONF_INTERNAL_SOUNDCARD_ENHAL_SndCard_CodecRegisterInternal();#endif#if PRJCONF_AC107_SOUNDCARD_ENHAL_SndCard_CodecRegisterAc107(); // 注册 AC107 即 ac107_codec_register()#endif#if PRJCONF_AC101_SOUNDCARD_ENHAL_SndCard_CodecRegisterAc101();#endif//Add other codec register here/* Platform register */#if PRJCONF_PLATFORM_I2S_ENHAL_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_ENHAL_SndCard_PlatformUnregisterI2S();#endif//Add other platform unregister here/* Codec unregister */#if PRJCONF_INTERNAL_SOUNDCARD_ENHAL_SndCard_CodecUnregisterInternal();#endif#if PRJCONF_AC107_SOUNDCARD_ENHAL_SndCard_CodecUnregisterAc107();#endif#if PRJCONF_AC101_SOUNDCARD_ENHAL_SndCard_CodecUnregisterAc101();#endif//Add other codec unregister herereturn HAL_OK;}#endif
2. 声卡注册过程
uint8_t HAL_SndCard_Register(void){// ......// 通过遍历所有声卡号来找到所有的 snd_card_board_config for(i=0; icard_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_configif(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 driversound_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 initHAL_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; icard_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 addlist_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 #include #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 lockif (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 lockif (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 openif(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);}}// 关闭 Codecif(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;}