SGTL5000是Freescale推出的一款包含耳机的低功率立体声编解码器,可为需要线路输入、麦克风输入、线路输出、耳机输出和数字I/O的便携式产品提供全面的音频解决方案。Freescale官方的i.MX6Q SABRESD参考设计使用的Audio CODEC型号是Wolfson公司的WM8962,由于WM8962的引脚间距特别小,而且球也很小,所以不得不将其更换为SGTL5000的设计,笔者在调试SGTL5000的设备驱动程序中遇到了不少问题,在此分享给读者。
首先来讲一下硬件。此硬件基于Freescale i.MX6Q SABRESD参考设计,音频部分相对于参考设计作出的改动如下:
1. 参考设计CSI0部分引脚用于连接Audio CODEC,I2C1,UART1(调试接口),此硬件中用于连接视频解码器。
2. 参考设计使用WM8962作为Audio CODEC,此硬件使用SGTL5000。
不用多说,熟悉的读者一定知道需要更改I2S,I2C1及UART1接口的引脚复用定义。在经过了一番折腾,笔者成功地实现了I2S,I2C1及UART1的引脚功能,主要更改的文件就是
linux-3.0.35/arch/arm/mach-mx6/board-mx6q_sabresd.h,变更过后的部分文件内容如下:
/*Audio Codec*/
MX6Q_PAD_DISP0_DAT20__AUDMUX_AUD4_TXC,
MX6Q_PAD_DISP0_DAT21__AUDMUX_AUD4_TXD,
MX6Q_PAD_DISP0_DAT22__AUDMUX_AUD4_TXFS,
MX6Q_PAD_DISP0_DAT23__AUDMUX_AUD4_RXD,
/* I2C1, WM8958 */
MX6Q_PAD_EIM_D21__I2C1_SCL,
MX6Q_PAD_EIM_D28__I2C1_SDA,
/* UART1*/
MX6Q_PAD_SD3_DAT7__UART1_TXD,
MX6Q_PAD_SD3_DAT6__UART1_RXD,
接下来讲述漫长的音频驱动程序调试过程,当然本文只能记录要点部分。
1. 经过比较仔细的代码阅读,发现不同参考设计的主要区别就是1个.c文件和1个.h文件。例如,board-mx6q_sabresd.c和board-mx6q_sabresd.h就是与i.MX6Q SABRESD参考设计相关的文件,其中board-mx6q_sabresd.c定义了很多的结构体,外设初始化程序,外设注册程序等,board-mx6q_sabresd.h定义了引脚复用功能。
2. 无意间发现i.MX6Q SABRELITE中使用的就是SGTL5000作为音频CODEC,其相关的代码写在了board-mx6q_sabrelite.c中。对于笔者这种不熟悉驱动程序的工程师来说,有现成的代码当然要用。仔细研读board-mx6q_sabrelite.c代码,其中与SGTL5000部分相关的代码主要做了以下事情:
定义了14个结构体
mx6_sabrelite_audio_data,mx6_sabrelite_audio_device
sgtl5000_sabrelite_consumer_vdda,sgtl5000_sabrelite_vdda_reg_initdata,sgtl5000_sabrelite_vdda_reg_config,sgtl5000_sabrelite_vdda_reg_devices
sgtl5000_sabrelite_consumer_vddio,sgtl5000_sabrelite_vddio_reg_initdata,sgtl5000_sabrelite_vddio_reg_config,sgtl5000_sabrelite_vddio_reg_devices
sgtl5000_sabrelite_consumer_vddd,sgtl5000_sabrelite_vddd_reg_initdata,sgtl5000_sabrelite_vddd_reg_config,sgtl5000_sabrelite_vddd_reg_devices
定义了2个函数
mx6_sabrelite_sgtl5000_init,imx6q_init_audio
仿照board-mx6q_sabrelite.c,笔者将这些结构体及函数复制到board-mx6q_sabresd.c中。更改mx6_sabrelite_audio_data中的Audio端口为4,并将SGTL5000的I2C地址填写到mxc_i2c0_board_info[] __initdata结构体中。
3. 编译过程中出现了一些错误,不过都是很容易解决的,在此略过。
4. 将编译得到的Kernel及Rootfs下载至eMMC中,板子可以正常启动,但是未看到任何有关SGTL5000相关的Log,直觉告诉笔者,这一定是有问题的。经过了很长时间的代码阅读,终于发现在linux-3.0.35/sound/soc/imx/imx-sgtl5000.c
的代码中做了限制,笔者记不清原来的代码是怎样的,只记得更改后的代码如下:
if ( machine_is_mx6q_sabrelite() || machine_is_mx6q_sabresd())
imx_sgtl5000_dai[0].codec_name = "sgtl5000.0-000a";
else
imx_sgtl5000_dai[0].codec_name = "sgtl5000.1-000a";
按照原来的代码,imx_sgtl5000_dai[0].codec_name为sgtl5000.1-000a,这与board-mx6q_sabresd.c中的0-000a是不符的,所以一定行不通。
5. 更改过后的代码启动过程中可以打印出SGTL5000相关的Log,但是仍不能正常识别,报错信息如下:
sgtl5000 0-000a: Device with ID register 0 is not a sgtl5000
sgtl5000 0-000a: asoc: failed to probe CODEC sgtl5000.0-000a: –19
asoc: failed to instantiate card sgtl5000-audio: –1
查看imx-sgtl5000.c代码,发现是在注册Regulator设备时出错的。仔细查看硬件设计,发现SGTL5000的VDDD引脚电压仅为0.8V,这与Datasheet中要求的1.1~2.0V是不符合的,也就是说代码驱动程序想要将SGTL5000的内部LDO设置为1.2V,结果没成功,所以返回了这样的错误。回想当时做电路设计时,根据SGTL5000 Datasheet中的“This external VDDD power supply is required for new designs.”描述为将VDDD连接至了外部的LDO,而Freescale的设计中却并没有连接,那么问题应该就出在这里。将VDDD引脚的磁珠取下,再次启动板子,终于看到了正确的打印信息:
sgtl5000 0-000a: sgtl5000 revision 17
asoc: sgtl5000 <-> imx-ssi.1 mapping ok
asoc: mxc-hdmi-soc <-> imx-hdmi-soc-dai.0 mapping ok
ALSA device list:
#0: sgtl5000-audio
#1: imx-hdmi-soc
其实到这一步,笔者大概经历了3天的时间,总算是有了比较大的突破,笔者相信很多读者也会遇到这样的问题。
6. 然而,事情并没有这样完全OK。进入系统后,根据Freescale的官方文档,运行aplay命令,竟然提示找不到这个命令!在网上检索了一段时间,才终于得知,默认的编译选项没有选择alsa-lib及alsa-utils,于是运行./ltib –m config,在Packages List中选择了alsa-lib及alsa-utils。再次编译的Kernel及Roofts下载到板子中后,终于有了aplay,并且可以通过aplay -l命令看到当前的声卡设备,如下:
7. 此时使用aplay播放mp3,发现耳机中终于有声音了!哦,是噪音!通过网上搜索,得知aplay无法解码mp3,所以不得不使用madplay软件。madplay是笔者之前在OpenWRT平台上使用的音乐播放软件,而且这款软件刚好集成在LTIB中。再次运行./ltib –m config,在Packages List中选择了madplay,结果再次编译的Kernel及Roofts下载到板子中后仍然存在问题,提示找不到/dev/dsp设备,手动ls /dev目录,确实没有dsp设备,看来这个问题也需要解决。
8. 再次在网上寻找答案,得知ALSA这种结构默认不会在/dev下创建dsp,audio,mixer等常规设备,所以madplay无法正常工作。在编译时,打开配置Kernel的选项,在
---Device drivers
--<*>Sound card support---->
--<*>Advanced Linux Sound Architecture--->
中选择OSS Mixer API及OSS PCM (digital audio) API,如下图
madplay终于可以工作了!
9. 以下是madplay正常工作的截图,耳机中传来的音质还是相当不错的。