第一次写博客,而且也是职场小白,很多东西不懂,平时记东西还是用笔记本手写,作为程序员这是不能被容忍的,所以强制写个博客,记录一下工作的内容和过程。没有什么章法逻辑,高深的东西更没有,音频最后也没调试成功,姑且写出来,记录一下,以后若调出来再补充,若有音频大佬路过,还请多多指点一下,若本博客写的太糙影响大家的胃口,那就多提点意见,说不定你提了意见我也不改呢?哈哈,玩笑话…
音频驱动调试主要调试ac108,在A50 平台上,ac108芯片是旧的CA版本,从A64平台移植的ac108代码不能使用,向全志提问题,对方提供的ac108是AA版本的,后来通过对比A64与全志提供的代码的区别,最终在ac108中的ac108_i2c_probe函数中的一个宏失能之后可正常编译(修改使用的是A64的代码),但仍旧不能录音。
在ac108_i2c_probe函数中有一个ac108_write函数向寄存器00中写入12,将所有的寄存器值恢复到默认值。这一句中在底层传输的时候报错,没有完整传输,但用示波器抓波形之后发现可以将12写入。(后来发了ac108的驱动调试文档,里面说明了报错是正常的,因为在向ac108寄存器0x00中写入0x12时,包括i2c也会复位,从而导致此次i2c时序不完整报错,不影响正常功能的使用,文档很晚才发,导致因为i2c通信的问题搞了差不多两周,后来感觉很奇怪,搞不定就用之前从A64平台移植过来的ac108代码来做,改了一个宏)
sunxi_i2c_core_process:i2c控制器的中断服务程序,sunxi_i2c_handler调用了sunxi_i2c_core_process,i2c总线的实际传输控制也是在该函数中完成的。
流程:
a 读取i2c控制器的当前状态,twi_query_irq_status,保留在state中。
b 根据state的值进行分支跳转,控制i2c的工作状态。
c 传输完成,调用sunxi_i2c_xfer_complete,唤醒工作队列。
sunxi_i2c_xfer:每个i2c控制器设备,在驱动绑定之后都会创建一个i2c_adapter,用于描绘该控制器,i2c_adapter的建立与初始化是在驱动probe的时候完成的。每个i2c_adapter包含了一个i2c_algorithm结构体的指针,i2c_algorithm是用来对外提供操作i2c控制器的函数接口的,主要是master_xfer函数,函数功能是通知i2c_adapter需要对外设进行数据交换,需要交换的信息通过struct i2c_msg *msgs传入,sunxi_i2c_xfer实际上是调用sunxi_i2c_do_xfer进行传输的
因为i2c总线传输速率有限,sunxi_i2c_do_xfer启动i2c传输后,通过wait_event_timeout进入休眠,直到中断唤醒或超时,中断唤醒是由sunxi_i2c_xfer_complete完成的。
i2c通信函数调用流程:
ac108_write()
-> i2c_master_send() i2c/i2c-core.c
-> i2c_transfer()
-> __i2c_transfer()
-> ret = adap->algo->master_xfer()
->i2c_do_xfer()
wait_event_timeout() 休眠
i2c_xfer_complete() 唤醒/超时
include/linux/i2c.h struct i2c_adapter
-> struct algorithm->master_xfer()
-> drivers/i2c/busses/i2c_sunxi.c 文件中的 sunxi_i2c_xfer()
-> sunxi_i2c_do_xfer()
录音时 使用的是tinycap命令。在用adb shell命令连接到android 设备时,并不能找到并使用tinycap命令。这是因为在android编译的时候没有将其编译进内核,需手动编译并添加入android系统。
在使用tinycap命令录音之前要使用tinymix命令进行设置。(我是没有设置,不知道没有录出正确的数据是不是这里有问题)
设置命令格式:tinymix “RX3 MIX1 INP1” “RX1” (控件名 + 控件值)
录音命令格式:tinycap test.wav -D 1 -c 2 -b 24 -r 64000,使用ctrl+c停止录音
这时候在dev/snd目录下生成的设备节点只有pcm0,使用pcm0设备节点无法录音,被告知板子默认使用的是i2s1,要换成pcm1录音才可以,不知道两者有什么关联。
在make menuconfig中修改:(记得将ac108编译进内核)
Device Drivers-> Sound card support -> Advanced linux sound Architecture -> ALSA for soc audio support -> codec drivers -> Ac108 Audio Codec
Device Drivers-> Sound card support -> Advanced linux sound Architecture -> ALSA for soc audio support -> Allwinner soc AUdio support -> Allwinner Digital Audio Support
在/soc/sunxi/sunxi_snddaudio文件中修改sunxi_snddaudio_dai_link中的两项:(alsa框架中的machine部分,用来连接platform和codec)
.codec_dai_name = "ac108-pcm0"
.codec_name = "ac108.1-003b"
这时生成了pcm1的节点,编译烧录可录音,有数据。但数据是乱的,是0xff,修改文件中的时钟分频和时钟频率:
/* set platform clk source freq and set the mode as daudio or pcm */
ret = snd_soc_dai_set_sysclk(cpu_dai, 0, freq, 0);
if (ret < 0)
return ret;
/* set codec clk source freq and set the mode as daudio or pcm */
//ret = snd_soc_dai_set_sysclk(codec_dai, 0, freq, 0);
ret = snd_soc_dai_set_sysclk(codec_dai, 1, freq, 0); //1:选择pll为时钟源 0:选择mclk为时钟源
if (ret < 0)
dev_warn(card->dev, "codec_dai set sysclk failed\n");
/*set pll*/
ret = snd_soc_dai_set_pll(codec_dai, 0, 0, freq, freq);
if (ret < 0) {
pr_warn("[daudio0],the codec_dai set set_pll failed.\n");
return ret;
}
/* set codec dai fmt */
ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_LEFT_J |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_LEFT_J |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
dev_warn(card->dev, "codec dai set fmt failed\n");
/* set system clk div */
clk_div = freq / params_rate(params);
printk("wyc,clk_div = %d,rate = %d\n",clk_div,params_rate(params));
ret = snd_soc_dai_set_clkdiv(cpu_dai, 0, clk_div);
if (ret < 0)
return ret;
printk("wyc,cpu_dai->rate = %d,codec_dai->rate = %d\n",cpu_dai->rate,codec_dai->rate);
//ret = snd_soc_dai_set_clkdiv(codec_dai, 0, clk_div);
//if (ret < 0)
// dev_warn(card->dev, "codec_dai set clkdiv failed\n");
return 0;
修改之后发现可以录到数据,没有有序的通道号,乱码,也无法播放。录出来的数据用winhex打开发现数据杂乱没有章法,通道号只有4和3,目前是怀疑i2s的bclk和lrclk设置的不对导致音频驱动没有正常工作,仍在排查中…
最终,配置了寄存器 I2S/PCM_CLKD, I2S/PCM_FMT0, I2S/PCM_CTL之后可以正常录出数据,并可以正常播放。
修改:
~/work1/a50/lichee/linux-4.9$ git diff sound/soc/sunxi/sunxi-snddaudio.c
diff --git a/sound/soc/sunxi/sunxi-snddaudio.c b/sound/soc/sunxi/sunxi-snddaudio.c
index 676fcf6..ddcc12b 100644
--- a/sound/soc/sunxi/sunxi-snddaudio.c
+++ b/sound/soc/sunxi/sunxi-snddaudio.c
@@ -78,12 +78,12 @@ static int sunxi_snddaudio_hw_params(struct snd_pcm_substream *substream,
/* set codec clk source freq and set the mode as daudio or pcm */
//ret = snd_soc_dai_set_sysclk(codec_dai, 0, freq, 0);
- ret = snd_soc_dai_set_sysclk(codec_dai, 1, freq, 0);
+ ret = snd_soc_dai_set_sysclk(codec_dai, 1, 0, 0);
if (ret < 0)
dev_warn(card->dev, "codec_dai set sysclk failed\n");
/*set pll*/
-ret = snd_soc_dai_set_pll(codec_dai, 0, 0, freq, freq);
+ret = snd_soc_dai_set_pll(codec_dai, 1, 0, 3072000, freq);
if (ret < 0) {
pr_warn("[daudio0],the codec_dai set set_pll failed.\n");
return ret;
@@ -92,8 +92,6 @@ static int sunxi_snddaudio_hw_params(struct snd_pcm_substream *substream,
/* set codec dai fmt */
ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_LEFT_J |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
- ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_LEFT_J |
- SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
dev_warn(card->dev, "codec dai set fmt failed\n");
@@ -101,12 +99,11 @@ static int sunxi_snddaudio_hw_params(struct snd_pcm_substream *substream,
clk_div = freq / params_rate(params);
printk("wyc,clk_div = %d,rate = %d\n",clk_div,params_rate(params));
ret = snd_soc_dai_set_clkdiv(cpu_dai, 0, clk_div);
- if (ret < 0)
+ if (ret < 0) {
+ dev_warn(card->dev, "the cpu_dai set set_fmt failed.\n");
return ret;
+ }
- ret = snd_soc_dai_set_clkdiv(codec_dai, 0, clk_div);
- if (ret < 0)
- dev_warn(card->dev, "codec_dai set clkdiv failed\n");
return 0;
}
在调试过程中,在linux目录下误操作make distclean之后将 .config 文件删除了,从linux/arch/arm/configs/ 目录下重新拷贝了一个sun8iw15p1smp_android_defconfig之后编译烧录系统一直起不来,配置了make ARCH=arm menuconfig 之后一直不行,最后自行更改了device driver中的power的设置,具体选择了电源管理芯片为axp22x,烧录系统可以启动,但adb不能连接,相关的配置都有,在.config 和venus_a3.mk 文件中的adb都有配置,然而,到最后发现是.config 文件拷贝错了,实际上这些东西都不用自行配置的。。。
然后更改了寄存器就可以了。。