在全志A50平台调试音频驱动的过程

在全志A50平台调试音频驱动的过程

  • 前言
  • 正文
    • 插播关于i2c的一些知识点:
    • 录音命令的解决方法
    • 更新
      • .config相关操作

前言

第一次写博客,而且也是职场小白,很多东西不懂,平时记东西还是用笔记本手写,作为程序员这是不能被容忍的,所以强制写个博客,记录一下工作的内容和过程。没有什么章法逻辑,高深的东西更没有,音频最后也没调试成功,姑且写出来,记录一下,以后若调出来再补充,若有音频大佬路过,还请多多指点一下,若本博客写的太糙影响大家的胃口,那就多提点意见,说不定你提了意见我也不改呢?哈哈,玩笑话…

正文

音频驱动调试主要调试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代码来做,改了一个宏)


插播关于i2c的一些知识点:

  1. 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,唤醒工作队列。

  2. 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进行传输的

  3. 因为i2c总线传输速率有限,sunxi_i2c_do_xfer启动i2c传输后,通过wait_event_timeout进入休眠,直到中断唤醒或超时,中断唤醒是由sunxi_i2c_xfer_complete完成的。

  4. 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() 唤醒/超时
  1. i2c通信算法的实现流程:
	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系统。

  1. 首先确保android源码能够正常编译通过。
  2. 进入android目录,执行source build/envsetup.sh,确保能使用mmm编译。
  3. 执行mmm external/tinyalsa,成功之后会生成四个命令和一个动态库,tinycap,tinyplay,tinymix,tinypcminfo,libtinyalsa.so
  4. 使用adb push命令将命令和库分别推到android设备的system/bin和system/lib中,就可以使用命令了。

在使用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;
 }

.config相关操作

在调试过程中,在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 文件拷贝错了,实际上这些东西都不用自行配置的。。。

然后更改了寄存器就可以了。。

你可能感兴趣的:(驱动,linux,音频驱动,ac108,i2c)