全志H2芯片外接tlv320adc3101声卡驱动开发记录

开发平台:OrangePi

CPU:全志 H2+

虚拟机:Ubuntu 12.04 64位

 

说明:

   tlv320adc3101声卡驱动主要使用了tlv320adc3101.csunxi-daudio0.csunxi-daudio0.csunxi-daudio0.csunxi-snddaudio0.c四个文件,其中tlv320adc3101.c是新加入的文件,其它文件为sunxi提供的linux-3.4内核自带的文件.

 

OrangePi-Kernel\linux-3.4\sound\soc\codecs

tlv320adc3101.ccodec文件,编译后生成snd-soc-tlv320adc3101.ko;

OrangePi-Kernel\linux-3.4\sound\soc\sunxi\daudio0

sunxi-daudio0.cplatform-dai(iis)文件,编译后生成sunxi-daudio0.ko

sunxi-daudiodma0.cplatform-dma文件,编译后生成sunxi-daudiodma0.ko

sunxi-snddaudio0.cmachine文件,编译后生成sunxi-snddaudio0.ko

snddaudio0.c是内核提供的codec文件,给未知的codec留了很多待补充的内容,这里不使用,makefile中将其注释

 

 

 

 

 

快速添加驱动方法:

 

一、给内核打补丁

 

1.前言

    声卡驱动开发涉及到文件较多,使用补丁方法可简便快速为内核添加新驱动.下面章节再讲述驱动开发的具体过程.

 

2.打补丁

    首先将补丁文件(h2_linux_adc3101.patch)拷贝到内核目录同文件夹OrangePi-Kernel,然后进入linux-3.4内核目录,运行patch -p1 < ../h2_linux_adc3101.patch 即可.

 

 

ps:制作补丁方法

1)进入OrangePi-Kernel目录,执行sudo ./build_linux_kernel.sh clean命令,清除一下编译结果,以防止生成的补丁文件过大.

2)linux-3.4文件夹重命名为linux-3.4-new

3)将原版内核源码linux-3.4解压到同一目录(OrangePi-Kernel),使用命令diff -rNu linux-3.4 linux-3.4-new > h2_linux_adc3101.patch就会得到h2_linux_adc3101.patch补丁文件.

 

 

二、其它文件更改

 

    除了内核相关文件,linux源码文件夹OrangePi-Kernel涉及到的其它文件有:OrangePi-Kernel/build_linux_kernel.sh(编译内核的批处理命令)OrangePi-Kernel/build/orange_zero_h2_snd_adc3101_config(内核配置文件)OrangePi-BuildLinux/params.sh(生成文件系统时传入的参数)orangepi_h2_linux-master/README(内核编译步骤)OrangePi-Kernel/chips/sun8iw7p1/bin/sys_config.fex(处理器各端口\外设资源配置)

 

 

 

 

 

声卡驱动开发具体过程:

 

一、在OrangePi-Kernel/linux-3.4/sound/soc/codecs中创建tlv320adc3101.ctlv320adc3101.h文件,此两文件以tlv320aic32x4.ctlv320aic32x4.h为模板,并进行相关修改:

 

1.aic32x4_i2c_probe()函数中添加一行:

i2c->dev.init_name = "adc3101_iic_24";//codec会使用此名称,名称中不能有-.,否则snd_soc_register_codec()->fmt_single_name()会不正确解析,在machine文件中与此名字进行匹配,而不是i2c_board_infoi2c_driver.id_table的名字。如果不加入此行代码,snd_soc_register_codec()->fmt_single_name()会解析得到adc3101_iic0.0-0018的字符串,即i2c_driver.id_table.name . i2c总线序号 - 416进制i2c设备地址。

 

2.aic32x4_modinit(void)函数外添加:

static struct i2c_board_info i2c_snd_devs[] = {

{ I2C_BOARD_INFO("adc3101_iic0", 24), },

{ I2C_BOARD_INFO("adc3101-iic1", 25), },

};

static struct i2c_client *adc3101_client_24;

 

3.创建i2c设备,在aic32x4_modinit(void)函数中添加:(之前在sun8i中使用i2c_register_board_info()函数注册i2c设备后,加载此模块后无法自动调用aic32x4_i2c_probe()函数,使用此种方式可以。)

i2c_adap = i2c_get_adapter(0);

adc3101_client_24 = i2c_new_device(i2c_adap, &i2c_snd_devs[0]);

i2c_put_adapter(i2c_adap);

 

4.aic32x4_exit(void)函数中添加:

i2c_unregister_device(adc3101_client_24);

 

5.static const struct i2c_device_id aic32x4_i2c_id[]中添加一项

{ "adc3101_iic0", 0 },

 

6.tlv320adc3101.h中添加如下代码(linux-3.4\include\sound\tlv320aic32x4.h中的部分)

//ADC3101_PDATA

#define ADC3101_PWR_MICBIAS_2075_LDOIN 0x00000001

#define ADC3101_PWR_AVDD_DVDD_WEAK_DISABLE 0x00000002

#define ADC3101_PWR_ADC3101_LDO_ENABLE 0x00000004

#define ADC3101_PWR_CMMODE_LDOIN_RANGE_18_36 0x00000008

#define ADC3101_PWR_CMMODE_HP_LDOIN_POWERED 0x00000010

 

#define ADC3101_MICPGA_ROUTE_LMIC_IN2R_10K 0x00000001

#define ADC3101_MICPGA_ROUTE_RMIC_IN1L_10K 0x00000002

 

7.tlv320adc3101.c中添加如下代码(linux-3.4\include\sound\tlv320aic32x4.h中的部分)

struct adc3101_pdata {

u32 power_cfg;

u32 micpga_routing;

bool swapdacs;

};

 

8.adc3101_hw_params()函数最后添加如下代码:

/* DOUT control */

data = snd_soc_read(codec, ADC3101_DOUTCTL);

data &= ~(0x10); //DOUT bus keeper enabled

printk("adc3101 ADC3101_DOUTCTL data = %d\n", data);

snd_soc_write(codec, ADC3101_DOUTCTL, data); //DOUT bus keeper enabled

 

snd_soc_write(codec, ADC3101_ADCSPB, 0x01); // Program  the  processing  block  to  be  used : PRB_P1 ,应该有助于消除噪音

 

printk("set ADC3101_MICBIAS = 0x50.\n");

snd_soc_write(codec, ADC3101_MICBIAS, 0x50); //MICBIAS1 is powered to 2.5V,MICBIAS2 is powered to 2.5V.

//printk("set ADC3101_MICBIAS = 0x28.\n");

//snd_soc_write(codec, ADC3101_MICBIAS, 0x28); //MICBIAS1 is powered to 2V,MICBIAS2 is powered to 2V.

 

snd_soc_write(codec, ADC3101_LMICPGAVOL, 0x0);  //Left Analog PGA   not mute,gain= 0x0 for 0 dB,0x50 for  40 dB

snd_soc_write(codec, ADC3101_RMICPGAVOL, 0x0);  //Right Analog PGA   not mute,gain=0x0 for 0 dB,0x50 for  40 dB

 

snd_soc_write(codec, ADC3101_LADCVOL, 0x16);  //Left Analog ADC volum   0x28:20 dB,max volum

snd_soc_write(codec, ADC3101_RADCVOL, 0x16);  //Right Analog ADC volum   0x28:20 dB,max volum

 

snd_soc_write(codec, ADC3101_LAGC1, 0x80);  //Left AGC enable,使能之后,mic灵敏度增加,噪音也同时加大

snd_soc_write(codec, ADC3101_RAGC1, 0x80);  //Right AGC enable,使能之后,mic灵敏度增加,噪音也同时加大

 

snd_soc_write(codec, ADC3101_LMICPGAPIN, 0xF3);  //AD转换声道选择,0x3F:IN2L(P)IN3L(M)作为差分信号输入

snd_soc_write(codec, ADC3101_RMICPGAPIN, 0xF3);  //AD转换声道选择,0x3F:IN2R(P)IN3R(M)作为差分信号输入

 

snd_soc_write(codec, ADC3101_ADCSETUP, 0xC2);//0xC2-Left&Right,0x42-Right,0x82-Left //Left&Right-channel ADC is powered up , ADC channel volume control soft-stepping is disabled.

snd_soc_write(codec, ADC3101_ADCFGA, 0x0);  //左右AD转换声道非静音,左右AD转换声道增益0dB.

9.static const struct snd_soc_dai_ops adc3101_ops结构体中添加一项:

.set_clkdiv = adc3101_set_dai_clkdiv,

并在本文件中以空函数实现之:

static int adc3101_set_dai_clkdiv(struct snd_soc_dai *codec_dai, int div_id, int div)

{

return 0;

}

如果不添加次函数,在编译时会报错:缺少这个函数.因为设置时钟的功能已经在adc3101_hw_params()函数中完成了,所以此函数为空即可.

 

10.分别修改此文件夹中的makefileKconfig.

 

 

 

 

 

二、修改OrangePi-Kernel\linux-3.4\sound\soc\sunxi\daudio0\sunxi-snddaudio0.c文件:

 

static struct snd_soc_dai_link sunxi_snddaudio_dai_link中修改如下两行

 

.codec_dai_name = "adc3101_iis",//.codec_dai_name = "snddaudio",

.codec_name = "adc3101_iic_24",//.codec_name = "sunxi-daudio-codec.0",

 

 

 

 

三、配置内核

 

1.cd xxxxx/orangepi_h2_linux-master/OrangePi-Kernel/linux-3.4

make ARCH=arm menuconfig

 

选择处理器

System Type  --->Select the wafer with arch sun8i->Allwinner Axx SOCs(sun8w7)

 Select the chip with wafer sun8iw7->Allwinner Axx chip(sun8w7)

 

2.选中I2C I2C_SUNXI

Device Drivers  --->

<*> I2C support  --->

 <*>   I2C device interface

 I2C Hardware Bus support  --->

<*> SUNXI I2C controller

 

3.选择声卡驱动

Device Drivers  --->

<*> Sound card support  --->

<*>   Advanced Linux Sound Architecture  --->

<*>   ALSA for SoC audio support  --->

<M>   SoC daudio0 tdm interface for SUNXI chips

<M>   Daudio0 Public Machine for SUNXI chips

<M>   TLV320ADC3101 Codec support

 

4.保存退出。

 

5..config保存为orange_zero_h2_snd_adc3101_config,build_linux_kernel.sh中会将其拷贝到arch/arm/configs/sun8iw7p1smp_linux_defconfig中作为默认的配置文件

使用命令:

cp .config ../build/orange_zero_h2_snd_adc3101_config

 

 

 

 

 

四、修改sys_config.fex

 

编译内核镜像时使用orangepi_h2_linux-master/OrangePi-Kernel/chips/sun8iw7p1/bin目录下的sys_config.fex.(注意:不要使用chips/sun8iw7p1/configs/dolphin-p2/sys_config.fex,经测试修改此文件无效)

 

:

[pcm0]

daudio_used = 0

:  //使能i2s0端口.使能后,在声卡cpu-dai程序中将获得此参数配置,cpu-dai被正常加载;

[pcm0]

daudio_used = 1

 

 

:

[twi1]

twi_used = 1

:  //禁用i2c1,解决i2c1i2s0端口冲突的问题.禁用后,驱动程序将不为i2c1申请端口资源,设备文件中不会生成/dev/i2c-1

[twi1]

twi_used = 0

 

 

 

 

 

 

、编译内核(编译内核最好使用64linux操作系统,如遇缺少相关包支持,还需使用apt-get下载支持包

 

1.修改OrangePi-Kernel/build_linux_kernel.sh47

原:

cp ../build/sun8iw7p1smp_android_defconfig arch/arm/configs/sun8iw7p1smp_linux_defconfig

修改为:

cp ../build/orange_zero_h2_snd_adc3101_config arch/arm/configs/sun8iw7p1smp_linux_defconfig

 

2OrangePi-BuildLinux/params.sh中修改:(可解决编译过程中报内核版本低的错误,虚拟机ubuntu内核也为precise (lsb_release -a 命令查看),难道这两个需要一致?)

# === Ubuntu ===

distro="precise"

#distro="xenial"

 

3.编译内核

cd OrangePi-Kernel

sudo ./build_uboot.sh zero    //compile uboot for OPI-zero

sudo ./build_linux_kernel.sh clean   //cleans the kernel tree before build

sudo ./build_linux_kernel.sh zero    //builds the uImage for OPI-zero                         

 

4.编译文件系统,打包,下载到tf

cd ../OrangePi-BuildLinux

//build file system

./create_image                                          

sudo ./image_from_dir linux-precise orangepi ext4 zero   //build zero image 此处要注意根据params.shdistro参数的设置进行修改。

sudo dd bs=4M if=orangepi.img of=/dev/sd*   //具体为sdasdbsdxx要通过dmesg | tail -10命令确认一下,不需要具体到sd*1sd*2,否则会造成烧写不成功。

 

 

 

 

 

 

 

六、启动,加载驱动模块:

 

1.连接准备

tf卡接入开发板;

TTLusb线连接电脑及开发板两端,打开超级终端,设置波特率:115200,数据位:8,奇偶校验:无,停止位:1,流控制:无;

 

2.启动

用户名:root

密码:orangepi

首次启动后要运行一下sudo fs_resize命令,否则会造成再次启动不成功。

 

3.加载驱动模块

板子启动后,可在/lib/modules/3.4.39_zero目录下找到编译好的内核模块,也可通过u盘或nfs方式加载模块,后者适用于驱动调试

ls /lib/modules/3.4.39_zero

insmod sunxi-daudio0.ko

insmod sunxi-daudiodma0.ko

insmod snd-soc-tlv320adc3101.ko

insmod sunxi-snddaudio0.ko

之后/dev/snd文件夹里会出现controlC0 \ pcmC0D0c节点

 

4.有时加载驱动模块会出现  error inserting 'xxxx.ko': -1 Invalid module format的错误,这种情况一般是更改内核配置文件将原驱动编译为模块,或修改内核其它部分配置项,再编译内核后发生。原因可能是由于内核模块不能比内核老,编辑一下内核模块源文件,但不改变其内容,再次编译得到的.ko文件拷贝进内核即可使用。

 

 

 

 

七、驱动调试

 

1.i2c接口调试

驱动中选择了<*> I2C device interface 以及<*> SUNXI I2C controller,系统启动后,/dev下会自动生成i2c-0设备节点。(更改sys_config.fex[twi1] twi_used = 0,系统将不会生成i2c-1设备节点,解决i2c1i2s0端口冲突的问题.)

 

i2cdetect检测有几组i2c总线在系统上,输入i2cdetect -l

i2cdetect检测挂载在i2c总线上器件,输入i2cdetect -r -y 0(检测i2c-0总线上的挂载情况)

i2cdump查看器件所有寄存器的值,输入i2cdump -f -y 0 0x18(查看i2c-0总线上设备地址为0x18的所有寄存器值)

设置单个寄存器值,输入 i2cset -f -y 0 0x18 0x77 0x3f (设置i2c-00x18器件的0x77寄存器值为0x3f

读取单个寄存器值,输入 i2cget -f -y 0 0x18 0x77      (读取i2c-00x18器件的0x77寄存器值)

 

如果文件系统中没有集成i2cdetect等程序,则首先将gcc-linaro-arm-linux-gnueabihf-4.9-2014.07_linux交叉编译工具添加进环境变量,然后下载i2c-tools-3.1.0源码,进入i2c-tools源码目录,修改makefile文件,CC ?= gcc 改为 CC := arm-linux-gnueabihf-gcc (:=是覆盖之前的值?= 是如果没有被赋值过就赋予等号后面的值+=是添加等号后面的值),然后i2c-tools源码目录输入make命令,编译完成后可在tools目录里得到i2cdetect等可执行文件,将其拷贝到板子上运行,在运行i2cdetect等程序前要加./

 

 

2.i2s接口调试

 

    i2s-0i2c-1端口复用,会在运行sunxi-daudio0.cdevm_pinctrl_get_select_defaul()时报pin already requested的错误。

    解决方法:sys_config.fex文件中将[twi1] twi_used =改为 twi_used = 0,这样在加载i2c-sunxi.ci2c-dev.c驱动时将不会申请i2c-1端口资源。

 

 

3.dma调试

 

cat /proc/interrupts

 

 

 

 

八、录音测试

    注意:编译h2应用程序需使用gcc-linaro-arm-linux-gnueabihf-4.9-2014.07_linux交叉编译工具,而不能使用OrangePi-Kernel\brandy\gcc-linaro里的arm-linux-gnueabi交叉编译工具

 

 

1.编译alsa-lib

alsa编译安装路径默认为/usr,如果编译时指定路径--prefix = xxx,则安装到板子上也必须为此路径,则编译时可使用两种方法:

 

1) 编译时不指定路径,使用默认路径/usr,以免破坏板子的文件系统,但会改变ubuntu系统中/usr目录名称,如果操作不当会损坏ubuntu系统。

且本ubuntu系统更改环境变量后找不到arm-linux-gnueabihf-gcc编译器,所以这里推荐使用此方法。

cd /

mv /usr /usr_bk

echo $PATH

得到环境变量为:

/usr/lib/lightdm/lightdm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/opt/gcc-linaro-arm-linux-gnueabihf-4.9-2014.07_linux/bin

 

修改环境变量:

export PATH=/usr_bk/lib/lightdm/lightdm:/usr_bk/local/sbin:/usr_bk/local/bin:/usr_bk/sbin:/usr_bk/bin:/sbin:/bin:/usr_bk/games:/opt/gcc-linaro-arm-linux-gnueabihf-4.9-2014.07_linux/bin

 

./configure --host=arm-linux-gnueabihf --enable-shared --disable-python

 

make

sudo mkdir /usr

sudo chown book:book /usr

make install

sudo cp -rf /usr /work/projects/alsa/

sudo rm -rf /usr

sudo mv /usr_bak /usr

恢复环境变量:

export PATH=/usr/lib/lightdm/lightdm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/opt/gcc-linaro-arm-linux-gnueabihf-4.9-2014.07_linux/bin

 

/*

把头文件和库复制进交叉工具链里

cd /work/projects/alsa/usr/include

sudo cp * -rfd /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/usr/include

 

cd /work/projects/alsa/usr/lib

sudo cp * -rfd /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/armv4t/lib

*/

 

把库复制到根文件系统的lib目录下

 

 

2)选定/home/alsa目录为存放alsa库编译结果的目录

首先将alsa-lib解压,将其目录里的所有文件拷贝到/home目录里。如果选择其它目录,在下面make时会抱错smixer-sbase.la libtool: link: only absolute run-paths are allowed,网络上没有找到好的解决办法,猜想是路径的问题,使用此方法得以解决。

./configure --host=arm-linux-gnueabihf --prefix=$PWD/alsa --enable-shared --disable-python --with-configdir=$PWD/alsa/alsa_lib/share --with-plugindir=$PWD/alsa/alsa_lib/lib

make

make install

 

 

2.编译alsa-utils

 

./configure --host=arm-linux-gnueabihf --prefix=$PWD/alsa CFLAGS="-I/home/alsa/include" LDFLAGS="-L/home/alsa/lib -lasound" --disable-alsamixer --disable-xmlto --disable-nls

这里,如果不指定--disable-nls,会报cannot stat 't-ja.gmo'的错。

make

make install

 

 

3.拷贝到板子上,运行

1)编译完成后,会在/home/alsa目录里得到alsa可执行文件、库文件、头文件。

2)alsa目录拷贝到板子/home文件夹下

3)cp alsa/lib/* -rf /usr/lib

4)运行

cd /home/alsa/bin  

./arecord -d 7 -f dat test.wav   //录音

 

 

 

九、驱动模块调试命令集合(使用u盘作为驱动模块传递载体)

 

dmesg | tail -10

mount /dev/sda1 /mnt/

cd /mnt/

insmod snd-soc-tlv320adc3101.ko

insmod sunxi-daudio0.ko

insmod sunxi-daudiodma0.ko

insmod sunxi-snddaudio0.ko

ls /dev/snd/

 

cd /home/alsa/bin

./arecord -d 7 -f dat test.wav

cp test.wav /mnt

sync

umount /mnt/

 

 

录音:

./arecord -d 7 -f dat test.wav  //-d 7 :录制7秒钟;  -f dat :16 bit little endian, 48000 ,stereo.

./arecord -d 7 -f S16_LE -r 48000 test.wav //此种方式为单声道录音,这时只录声卡左声道的声音(MIC1MIC3),右声道无论接不接,都不会录

./arecord -d 10 -f cd test.wav  //CD质量录制test.wav文件10秒钟.

 

调节音量:

./amixer controls

./amixer cget numid=4      //查看ADC Level Volume设定值

./amixer cset numid=4 40    //ADC Level Volume调至最大

./amixer cset numid=5 80    //PGA Level Volume调至最大

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值