IMX6Q配置SSI为I2S接口,而不需要控制CODEC的方案实现

某项目需要通过IMX6Q外接音频解码芯片,但是IMX6Q仅通过I2S连接到CODEC,做业务输出,CODEC的控制由另外的芯片来做。大致架构如下图所示。
在这里插入图片描述
即IMX6Q仅仅输出I2S信号,完整的调试过程涉及到ALSA架构的修改,主要是下面几个方面。

1、 确认IMX6Q的IIS引脚

以我实际项目的代码为例

	MX6Q_PAD_CSI0_DAT4__AUDMUX_AUD3_TXC,	//I2S3_TXC
	MX6Q_PAD_CSI0_DAT6__AUDMUX_AUD3_TXFS,	//I2S3_TXFS
	MX6Q_PAD_CSI0_DAT5__AUDMUX_AUD3_TXD,	//I2S3_TXD
	MX6Q_PAD_CSI0_DAT7__AUDMUX_AUD3_RXD,	//I2S3_RXD

2、 audio初始化中添加下列结构体

好了,你们可以看到我使用的CODEC就是TEF6638,对行业熟悉的同志应该知道我做的是什么产品了。

	mxc_register_device(&mx6_sabresd_audio_tef6638_device,
                                   &tef6638_data);
   	imx6q_add_imx_ssi(1, &mx6_sabresd_ssi_pdata);
	mx6_sabresd_tef6638_init();
	mxc_register_device(&tef6638_codec,NULL);

这一步就是要做一个imx-tef6638的platform数据,表明使用的是SSI1以及对应的source和dest端口。同时还要做一个虚拟的tef6638_codec数据结构,该结构用来创建一个虚拟的codec,方便按照imx的asoc架构去完成注册。

static struct platform_device mx6_sabresd_audio_tef6638_device = {
	.name = "imx-tef6638",	//注册一个名为imx-tef6638的platfrom设备
};

static struct imx_ssi_platform_data mx6_sabresd_ssi_pdata = {
	.flags = IMX_SSI_DMA | IMX_SSI_SYN, //表明该SSI使用DMA传输,同步
};

static struct mxc_audio_platform_data tef6638_data = {
	.ssi_num = 1,	//使用SSI 1
	.src_port = 2,  //内部端口是 2
	.ext_port = 3,  //外部端口是3

	.hp_gpio = -1,
	.init = mx6_sabresd_tef6638_init,  //设置codec时钟
};

static struct platform_device tef6638_codec = {
	.name = "tef6638_codec", //虚拟的codec设备
};

3、 添加imx-tef6638.c文件

仿照其他imx架构的芯片添加imx-tef6638.c文件,在该文件中注册imx的驱动。该驱动重点构造了一个imx_tef6638_dai结构体,在该结构体中描述了整个音频接口。整个注册流程遵照ASOC的标准来做。在这里需要注意两个地方。
1、时钟输出的方向
由于IMX6Q并不控制CODEC,无法保证输出I2S音频的时候CODEC也能确保时钟,所以必须由IMX6Q来输出时钟信号。所以对应的时钟方向设置必须修改。

static int imx_audmux_config(int slave, int master)
{
	unsigned int ptcr, pdcr;
	slave = slave - 1;
	master = master - 1;

	ptcr =  MXC_AUDMUX_V2_PTCR_SYN;
     
     	pdcr = MXC_AUDMUX_V2_PDCR_RXDSEL(master);
     	mxc_audmux_v2_configure_port(slave, ptcr, pdcr);
     
     	ptcr =MXC_AUDMUX_V2_PTCR_SYN    |
      		MXC_AUDMUX_V2_PTCR_TFSDIR |    
      		MXC_AUDMUX_V2_PTCR_TFSEL(slave) |  
           	MXC_AUDMUX_V2_PTCR_TCLKDIR |
               MXC_AUDMUX_V2_PTCR_TCSEL(slave); //从slave口输出,也就是第一个参数口,默认为src_port
     	pdcr = MXC_AUDMUX_V2_PDCR_RXDSEL(slave);
     	mxc_audmux_v2_configure_port(master, ptcr, pdcr);
	return 0;
}

2、对应的soc侧的参数解析及配置

static int imx_tef6638_hw_params(struct snd_pcm_substream *substream,
	struct snd_pcm_hw_params *params)
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
	u32 dai_format;
	unsigned int sample_rate = 44100;
	unsigned int clk=0;
	int ret=0;
	unsigned int channels = params_channels(params);
	//解析左右声道,并设置TDM,如果不做设置将不会产生LRCK	
	switch (channels) {
	case 2:
		snd_soc_dai_set_tdm_slot(cpu_dai, 0xfffffffc, 0xfffffffc, 2, 32);
		break;
	case 1:
		snd_soc_dai_set_tdm_slot(cpu_dai, 0xfffffffe, 0xfffffffe, 2, 32);
		break;
	default:
		return -EINVAL;
	}
	//设置I2S格式
	dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF |
		SND_SOC_DAIFMT_CBS_CFS;
	ret = snd_soc_dai_set_fmt(cpu_dai, dai_format);
	//设置时基及分频系数
	ret = snd_soc_dai_set_clkdiv(cpu_dai, IMX_SSI_TX_DIV_PM, 4);
	if (ret < 0)
		return ret;
	//解析采样率
	sample_rate=params_rate(params);
	clk = sample_rate*64*4;
	//设置MCLK时钟
	ret=snd_soc_dai_set_sysclk(cpu_dai,IMX_SSP_SYS_CLK,clk,SND_SOC_CLOCK_OUT);
	if (ret < 0)
		return ret;
   
	return 0;
}

static struct snd_soc_ops imx_tef6638_hifi_ops = {
	.hw_params = imx_tef6638_hw_params,
};

完成上述配置后就可以从I2S引脚输出符合要求的音频信号了。

猜你喜欢

转载自blog.csdn.net/weixin_40983190/article/details/86518921