本文介绍的代码是在CPU(imx6)和Codec(wm8960)上做的调试并且Codec做主模式,在“概述篇”中提到Codec部分、
Platform部分代码由设备原厂提供和系统提供,所以我们在声卡调试中需要做的只有Machine部分,其他代码在需要的情况下略做微
调即可,调试过程中还要注意在BSP文件里配置好I2S(SSI和AUD复用)。
1、imx_hifi_hw_params函数介绍
static int imx_hifi_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params){/* 设置Codec与CPU音频数据通信格式为I2S,BLCK不反转,Codec为主模式*/dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |SND_SOC_DAIFMT_CBM_CFM;/* set codec DAI configuration */ret = snd_soc_dai_set_fmt(codec_dai, dai_format); if( ret < 0 ){ printk( "%s: Codec DAI configuration error, %d\n", __func__, ret ); return ret; }/* set i.MX active slot mask */snd_soc_dai_set_tdm_slot(cpu_dai,channels == 1 ? 0xfffffffe : 0xfffffffc,channels == 1 ? 0xfffffffe : 0xfffffffc,2, 32);/* 设置CPU与Codec音频数据通信格式为I2S,BLCK不反转,CPU为从模式*/dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |SND_SOC_DAIFMT_CBM_CFM;/* set cpu DAI configuration */ret = snd_soc_dai_set_fmt(cpu_dai, dai_format); if( ret < 0 ){ printk( "%s: AP DAI configuration error, %d\n", __func__, ret ); return ret; }sample_rate = params_rate(params);switch (sample_rate) { case 44100: if (channels == 1) { // 根据通道数配置相关时钟频率(1:代表单通道) /* Mono mode */ dacdiv = WM8960_DAC_DIV_2; /* 22.05 KHz */ bclk = WM8960_BCLK_DIV_16; } else { /* Stereo Mode */ dacdiv =WM8960_DAC_DIV_1; // 44.1 KHz,为设置采样频率(LRCK),配置Codec寄存器的值 bclk = WM8960_BCLK_DIV_4; // 为设置BCLK频率,配置Codec寄存器的值dclk = WM8960_DCLK_DIV_16;// 为设置D类放大器时钟频率,配置Codec寄存器的值} sysclk_div = WM8960_SYSCLK_DIV_2; // 为设置Codec的系统时钟频率,配置Codec寄存器的值 pll_out = 11289600; // 设定PLL的频率值 break; default: printk("do not support this sample frequency"); return -EINVAL; } /* 设置Codec的PLL频率:11289600*/ret=snd_soc_dai_set_pll(codec_dai,1,0,priv->sysclk/2,pll_out); if( ret < 0 ){ printk( "%s: AP codec pll error, %d\n", __func__, ret ); return ret; }/* 设置Codec的系统时钟:11289600*/ ret = snd_soc_dai_set_clkdiv( codec_dai, WM8960_SYSCLKDIV, sysclk_div ); if( ret < 0 ){ printk( "%s: Codec SYSCLKDIV setting error, %d\n", __func__, ret ); return ret; }/* 设置采样频率(LRCK)的时钟,lrckclk= sysclk/1*256=44100 */ ret = snd_soc_dai_set_clkdiv( codec_dai, WM8960_DACDIV, dacdiv); if( ret < 0 ){ printk( "%s: Codec DACDIV setting error, %d\n", __func__, ret ); return ret; }/* 设置D类放大器时钟频率:dclk= sysclk/16 */ret = snd_soc_dai_set_clkdiv( codec_dai, WM8960_DCLKDIV,dclk);if( ret < 0 ){printk( "%s: Codec DCLKDIV setting error, %d\n", __func__, ret );return ret;}/* 设置BCLK类放大器时钟频率:bitclk=sysclk/4 = 2822400 */ret = snd_soc_dai_set_clkdiv( codec_dai, WM8960_BCLKDIV, bclk); if( ret < 0 ){printk( "%s: Codec WM8960_BCLKDIV setting error, %d\n", __func__, ret );return ret; } return 0;}
2、snd_soc_dai_link结构体介绍
platform 连接 codec 与 cpu 的驱动时,需要通过内核函数结构体snd_soc_dai_link来设置两边的接口。
static struct snd_soc_dai_link imx_dai[] = {{.name = "HiFi",.stream_name = "HiFi",.codec_dai_name= "wm8960-hifi",.codec_name= "wm8960.1-001a", /* wm8960.1-001a代表的是用I2C1来控制codec,其I2C地址为0x1a;I2CX, X代表的是0,1,2,开始 */.cpu_dai_name= "imx-ssi.1", /* * 代表的是同一个声卡中的第一个设备与SSI1相连;I2SX X代表0,1,2;ssi.1代表SSI1。 * IMX6中的SSI接口有3对可以接着路I2S;三路SSI可以每路SSI 单独作为一个声卡, * 也可以是一个声卡包含多个设备; */.platform_name= "imx-pcm-audio.1",/* 代表的连接接口与SSI来对应,用哪路SSI,其后面的数字就是几。 */.init= imx_wm8960_init,.ops= &imx_hifi_ops,},};
Platform部分代码由设备原厂提供和系统提供,所以我们在声卡调试中需要做的只有Machine部分,其他代码在需要的情况下略做微
调即可,调试过程中还要注意在BSP文件里配置好I2S(SSI和AUD复用)。
1、imx_hifi_hw_params函数介绍
static int imx_hifi_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params){/* 设置Codec与CPU音频数据通信格式为I2S,BLCK不反转,Codec为主模式*/dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |SND_SOC_DAIFMT_CBM_CFM;/* set codec DAI configuration */ret = snd_soc_dai_set_fmt(codec_dai, dai_format); if( ret < 0 ){ printk( "%s: Codec DAI configuration error, %d\n", __func__, ret ); return ret; }/* set i.MX active slot mask */snd_soc_dai_set_tdm_slot(cpu_dai,channels == 1 ? 0xfffffffe : 0xfffffffc,channels == 1 ? 0xfffffffe : 0xfffffffc,2, 32);/* 设置CPU与Codec音频数据通信格式为I2S,BLCK不反转,CPU为从模式*/dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |SND_SOC_DAIFMT_CBM_CFM;/* set cpu DAI configuration */ret = snd_soc_dai_set_fmt(cpu_dai, dai_format); if( ret < 0 ){ printk( "%s: AP DAI configuration error, %d\n", __func__, ret ); return ret; }sample_rate = params_rate(params);switch (sample_rate) { case 44100: if (channels == 1) { // 根据通道数配置相关时钟频率(1:代表单通道) /* Mono mode */ dacdiv = WM8960_DAC_DIV_2; /* 22.05 KHz */ bclk = WM8960_BCLK_DIV_16; } else { /* Stereo Mode */ dacdiv =WM8960_DAC_DIV_1; // 44.1 KHz,为设置采样频率(LRCK),配置Codec寄存器的值 bclk = WM8960_BCLK_DIV_4; // 为设置BCLK频率,配置Codec寄存器的值dclk = WM8960_DCLK_DIV_16;// 为设置D类放大器时钟频率,配置Codec寄存器的值} sysclk_div = WM8960_SYSCLK_DIV_2; // 为设置Codec的系统时钟频率,配置Codec寄存器的值 pll_out = 11289600; // 设定PLL的频率值 break; default: printk("do not support this sample frequency"); return -EINVAL; } /* 设置Codec的PLL频率:11289600*/ret=snd_soc_dai_set_pll(codec_dai,1,0,priv->sysclk/2,pll_out); if( ret < 0 ){ printk( "%s: AP codec pll error, %d\n", __func__, ret ); return ret; }/* 设置Codec的系统时钟:11289600*/ ret = snd_soc_dai_set_clkdiv( codec_dai, WM8960_SYSCLKDIV, sysclk_div ); if( ret < 0 ){ printk( "%s: Codec SYSCLKDIV setting error, %d\n", __func__, ret ); return ret; }/* 设置采样频率(LRCK)的时钟,lrckclk= sysclk/1*256=44100 */ ret = snd_soc_dai_set_clkdiv( codec_dai, WM8960_DACDIV, dacdiv); if( ret < 0 ){ printk( "%s: Codec DACDIV setting error, %d\n", __func__, ret ); return ret; }/* 设置D类放大器时钟频率:dclk= sysclk/16 */ret = snd_soc_dai_set_clkdiv( codec_dai, WM8960_DCLKDIV,dclk);if( ret < 0 ){printk( "%s: Codec DCLKDIV setting error, %d\n", __func__, ret );return ret;}/* 设置BCLK类放大器时钟频率:bitclk=sysclk/4 = 2822400 */ret = snd_soc_dai_set_clkdiv( codec_dai, WM8960_BCLKDIV, bclk); if( ret < 0 ){printk( "%s: Codec WM8960_BCLKDIV setting error, %d\n", __func__, ret );return ret; } return 0;}
2、snd_soc_dai_link结构体介绍
platform 连接 codec 与 cpu 的驱动时,需要通过内核函数结构体snd_soc_dai_link来设置两边的接口。
static struct snd_soc_dai_link imx_dai[] = {{.name = "HiFi",.stream_name = "HiFi",.codec_dai_name= "wm8960-hifi",.codec_name= "wm8960.1-001a", /* wm8960.1-001a代表的是用I2C1来控制codec,其I2C地址为0x1a;I2CX, X代表的是0,1,2,开始 */.cpu_dai_name= "imx-ssi.1", /* * 代表的是同一个声卡中的第一个设备与SSI1相连;I2SX X代表0,1,2;ssi.1代表SSI1。 * IMX6中的SSI接口有3对可以接着路I2S;三路SSI可以每路SSI 单独作为一个声卡, * 也可以是一个声卡包含多个设备; */.platform_name= "imx-pcm-audio.1",/* 代表的连接接口与SSI来对应,用哪路SSI,其后面的数字就是几。 */.init= imx_wm8960_init,.ops= &imx_hifi_ops,},};