【学习笔记】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.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;
}

 

posted @   Love_梦想  阅读(107)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示