一些笔记:关于mt7628 openwrt 音频调试

没有做过比这版更烂的开发,坑还在,开发还在继续,记一下笔记吧,或许找到一些灵感。

先用 mtk-openwrt-sdk-20160324-8f8e4f1e.tar.bz2 这版官方SDK(从官方网站上看这是最新的一版openwrt SDK,2016年的,是的,你没看错),然后i2c都巨艰难的调试出来,因为makefile里dev-i2c.o都被屏蔽了,坑。再接着发现虽说kernel 是 linux-3.10.14,但驱动写法居然是2.6,连dts设备树都没有。

换开源的lede-17.01,有设备树,熟悉的感觉。复制target\linux\ramips\dts\MT7628.dts到自己创建的设备树文件,修改target/linux/ramips/image/mt7628.mk 增加自己的产品平台。在根节点增加 :

	sound {
		compatible = "simple-audio-card";
		simple-audio-card,name = "Audio-I2S";
		simple-audio-card,format = "i2s";
		simple-audio-card,bitclock-master = <&dailink0_master>;
		simple-audio-card,frame-master = <&dailink0_master>;
		simple-audio-card,widgets = 
			"Microphone", "Microphone Jack",
			"Headphone", "Headphone Jack";
		simple-audio-card,routing =
			"LINPUT1", "Microphone Jack",
			"RINPUT1", "Microphone Jack",
			"Headphone Jack", "HP_L",
			"Headphone Jack", "HP_R";
		simple-audio-card,mclk-fs = <256>;

		simple-audio-card,cpu {
			sound-dai = <&i2s>;
		};

		dailink0_master: simple-audio-card,codec {
			sound-dai = <&codec>;
		};
	};

enable i2c i2s,并在i2c下挂上 wm8960:

&i2c {
	status = "okay";
	
	codec: wm8960@1a {
		#sound-dai-cells = <0>;
		compatible = "wlf,wm8960";
		reg = <0x1a>;
		wlf,shared-lrclk;
	};
};

&i2s {
	status = "okay";
	
	#sound-dai-cells = <0>;
	status = "okay";
	pinctrl-names = "default";
	pinctrl-0 = <&wm8960_mclk_pins>;
};

接着配置pinctrl了:

	i2s_pins: i2s {
		i2s {
			ralink,group = "i2s";
			ralink,function = "i2s";
		};
	};
		
	wm8960_mclk_pins: wm8960_mclk {
		wm8960_mclk {
			ralink,group = "refclk";
			ralink,function = "refclk";
		};
	};

结果是未找到声卡,怀疑是pinctrl配置和硬件有差异,待查验。同时由于wifi驱动比较难在这版SDK上移植过来,遂放弃这版SDK。

找到官方SDK MediaTek_APSoC_SDK5030_20170331.tar.bz2, 看了下代码,跟官方openwrt差不多,在3.10kernel上用2.6的写法。由于其他开发组需要很多应用是基于openwrt的,官方SDK只做参考,不做开发了。

突然听闻有人在开源分支chaos_calmer上配置7688音频成功了。下载编译调试,如果7628也是配置成功的,那就找到配置的关键点了,但最终结果是未找到声卡。这版SDK更奇葩一点,部分硬件资源放在dts上,部分写在C代码里。因为未配置7628音频成功,不继续下去了,专心弄官方的openwrt SDK。

小道消息知道,江湖上还流传着 mtk-openwrt-sdk-20170518-1443366e.tar.xz 这版SDK。遂专心在这版上开发调试,i2c,i2s,codec的节点都生成出来了,但还是功亏一篑。下面记录下这版开发的一些笔记吧。

1. 修改uboot和kernel波特率为115200:

uboot/src/include/configs/rt2880.h
#define CONFIG_BAUDRATE        115200

linux-3.10.14/arch/mips/ralink/cmdline.c
char rt2880_cmdline[]="console=ttyS1,115200n8 root=/dev/mtdblock5";

2.  GPIO的配置方式。

调试外设第一步应该知道GPIO是如何配置的。找到MTK 的gpio 驱动文件 drivers/char/ralink_gpio.c,并不是按照linux的gpio子系统那套写法,这年头见到这种写法也是比较难得的,够奇葩。里面注册字符设备和配置GPIO复用。

int __init ralink_gpio_init(void)
	register_chrdev(ralink_gpio_major, RALINK_GPIO_DEVNAME,&ralink_gpio_fops);  --- 注册字符设备
	gpiomode = le32_to_cpu(*(volatile u32 *)(RALINK_REG_GPIOMODE));
	gpiomode |= RALINK_GPIOMODE_DFT;					    --- 配置GPIO复用方式
	*(volatile u32 *)(RALINK_REG_GPIOMODE) = cpu_to_le32(gpiomode);		    --- 写到寄存器

在 arch/mips/include/asm/mach-ralink/rt_mmap.h 里定义了基地址:

#define RALINK_SYSCTL_BASE		0xB0000000
#define RALINK_PIO_BASE			0xB0000600
#define RALINK_I2C_BASE			0xB0000900
#define RALINK_I2S_BASE			0xB0000A00

在 drivers/char/ralink_gpio.h定义了GPIO相关头文件,特别是RALINK_GPIOMODE_DFT, 里面意识猜测是 UART2/UART3/SPI_CS1/WDT均作为普通GPIO 用。

#elif defined (CONFIG_RALINK_MT7628)
#define RALINK_GPIOMODE_GPIO		0x1
#define RALINK_GPIOMODE_SPI_CS1		0x10
#define RALINK_GPIOMODE_I2S		0x40
#define RALINK_GPIOMODE_WDT		0x4000
#define RALINK_GPIOMODE_REFCLK		0x40000
#define RALINK_GPIOMODE_I2C		0x100000
#define RALINK_GPIOMODE_UART2		0x1000000
#define RALINK_GPIOMODE_UART3		0x4000000

#elif defined (CONFIG_RALINK_MT7628)
#define RALINK_GPIOMODE_DFT		(RALINK_GPIOMODE_UART2 | RALINK_GPIOMODE_UART3) | (RALINK_GPIOMODE_SPI_CS1) | (RALINK_GPIOMODE_WDT)  

GPIO驱动不按照标准gpio子系统写法,那么在/sys/class/下就没有GPIO节点了,那么如何操作GPIO呢?放心,用package\ramips\applications\gpio\src\gpio.c编了一个应用程序通过ioctl来跟内核通信,内核ralink_gpio.c也提供了file_operation函数ralink_gpio_ioctl,比如说设置gpio方向,不用函数封装了,直接暴力操作寄存器:

	case RALINK_GPIO_SET_DIR:
		*(volatile u32 *)(RALINK_REG_PIODIR) = cpu_to_le32(arg);
		break;

3. 配置I2C

刚开始发现好几个与i2c相关的文件,在这里依旧不是主流i2c写法。在内核里,i2c 驱动框架大概分为两层,adapter 驱动 和 设备驱动,adapter 驱动 就是i2c-mtk.c, 设备驱动就是 wm8960.c。简单说下以下文件作用

arch/mips/ralink/i2c-slave.c    --- 注册设备资源,I2C_BOARD_INFO
drivers/char/i2c_drv.c          --- 创建/dev/i2cM0,配合 i2ccmd.c 即 i2ctest 命令
drivers/i2c/busses/i2c-mtk.c    --- 适配器驱动,创建/dev/i2c-0,适配器名字mt-i2c,driver和device均在这一个文件
drivers/i2c/i2c-dev.c           --- 封装了soc的i2c控制器操作,并向应用层提供ioctl操作接口,非主流做法

然后MTK 提供了一个i2ctest的命令,源码在package\ramips\applications\i2ctest\src\i2ccmd.c,生成i2ctest,当然需要先修改ramips\modules.mk,屏蔽DEPENDS,才能在menuconfig里面选到。

但是这个工具不咋好用,移植i2ctools 吧。创建package\utils\i2c-tools\makefile:

include $(TOPDIR)/rules.mk

PKG_NAME:=i2c-tools
PKG_VERSION:=3.1.2
PKG_RELEASE:=1

PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://mirrors.edge.kernel.org/pub/software/utils/i2c-tools/
PKG_MD5SUM:=afe041581b8f01666e353bec20917c85

include $(INCLUDE_DIR)/package.mk

define Package/i2c-tools
  SECTION:=utils
  CATEGORY:=Utilities
  TITLE:=i2c tools
  URL:=https://i2c.wiki.kernel.org/index.php/I2C_Tools
endef

define Build/Compile
	$(MAKE) -C $(PKG_BUILD_DIR) \
		$(TARGET_CONFIGURE_OPTS) \
		CFLAGS="$(TARGET_CFLAGS) $(TARGET_CPPFLAGS) -Wall" \
		all
endef

define Package/i2c-tools/install	
	$(INSTALL_DIR) $(1)/usr/sbin
	$(INSTALL_BIN) $(PKG_BUILD_DIR)/tools/i2cdetect  $(1)/usr/sbin/
	$(INSTALL_BIN) $(PKG_BUILD_DIR)/tools/i2cdump  $(1)/usr/sbin/
	$(INSTALL_BIN) $(PKG_BUILD_DIR)/tools/i2cget  $(1)/usr/sbin/
	$(INSTALL_BIN) $(PKG_BUILD_DIR)/tools/i2cset  $(1)/usr/sbin/
endef

$(eval $(call BuildPackage,i2c-tools))

如果dl目录里下载失败后,直接手动下载吧。完成后就可以使用这个工具了:

列举i2c总线
i2cdetect -l

列举 I2C bus i2c-0 上面连接的所有i2c设备
i2cdetect -y 0

读取I2C 设备(地址为0x33)寄存器0x55的值,W表示读取长度为一个word,默认为一个字节
i2cget -y -f 0 0x34 0x55 w
0x7667

设置 I2C 设备(地址为0x33)寄存器0x03的值为0x05
# i2cset -y -f 1 0x33 0x03 0x05

dump I2C 读取外设(地址为0x33)的所有寄存器值
i2cdump -y -f 0 0x34

4. I2S 控制接口

I2S控制驱动文件:char/i2s/core/i2s_ctrl.c。为了配合i2stest命令,最好去掉这些限制LINUX_VERSION_CODE > KERNEL_VERSION(2,6,35),这样可以用device_create创建设备文件:

static const struct file_operations i2s_fops = 
	mmap		: i2s_mmap,
	open		: i2s_open,
	unlocked_ioctl:     i2s_ioctl,

int __init i2s_mod_init(void)
	register_chrdev(i2sdrv_major, I2SDRV_DEVNAME, &i2s_fops);		--- i2sdrv_major= 191;I2SDRV_DEVNAME"i2s0";
	i2smodule_class=class_create(THIS_MODULE, I2SDRV_DEVNAME);
	device_create(i2smodule_class, NULL, MKDEV(i2sdrv_major, 0), NULL, I2SDRV_DEVNAME);

在i2s_ioctl里有很多关于i2s寄存器的操作,音频未成功可能跟其配置有关,后面需要仔细跟进看看:

long i2s_ioctl (struct file *filp, unsigned int cmd, unsigned long arg)
	case I2S_RESET:
	case I2S_SRATE:
	case I2S_TX_VOL:
	case I2S_RX_VOL:
	case I2S_WORD_LEN:
	case I2S_ENDIAN_FMT:
	case I2S_TX_ENABLE:
	case I2S_RX_ENABLE:
	case I2S_PUT_AUDIO:
	case I2S_GET_AUDIO:

i2stest命令由package\ramips\applications\i2stest\src\i2scmd.c生成,可以用来播放录音,当然跟i2ctest一样需要修改ramips\modules.mk,屏蔽DEPENDS。当然也跟i2ctest一样不咋好用。

5. machine驱动

好了,重头戏来了。ASoC被分为 Machine、Platform 和 Code c三大部分,其中的Machine驱动负责 Platform 和 Codec 之间的耦合以及部分和设备或板子特定的代码。machine驱动文件在sound\soc\mtk\mt76xx_machine.c。

static struct snd_soc_card mtk_audio_card = 
	.name = "MTK APSoC I2S",                 --- 声卡名字,可以用aplay -l查询到
	.dai_link = &mtk_audio_dai,//I2S/Codec	 --- 连接 platform 和 codec 的

static struct snd_soc_dai_link mtk_audio_dai = 	--- 指明到底用那个 codec,那个 platfrom	----重要结构体
	.name = "mtk_dai",
	.cpu_dai_name	= "mt76xx-i2s",		--- 指定 cpu侧的dai 名字,也就是所谓的cpu侧的数字音频接口,一般都是i2S接口
	.codec_dai_name	= "wm8960-hifi",	--- 用于 codec侧的dai 名字,不可以省略
	.codec_name	= CODEC_NAME,		
        //--- 用于指定codec芯片,不可以省略. wm8960.0-001a,wm8960:codec driver 名字, 0:i2c总线号 ,001a: codec 设备从机地址
	.platform_name	= "mt76xx-pcm",	        --- 用于指定 cpu侧平台驱动,通常都是DMA驱动,用于传输
	.ignore_pmdown_time = true,
	.init = mt76xx_codec_init,		--- 空函数?
	.ops = &mtk_audio_ops,		        --- audio 的相关操作函数集合

static struct snd_soc_ops mtk_audio_ops = 
	.hw_params = mt76xx_codec_clock_hwparams,
	.startup = mt76xx_codec_startup,	--- 空函数?

static int __init mt76xx_machine_init(void)	--- 从这里开始
	mt76xx_audio_device = platform_device_alloc("soc-audio",-1);		
        //--- 分配出 soc-audio 平台设备, 对应 soc-core.c 中 soc_driver.
	platform_set_drvdata(mt76xx_audio_device, &mtk_audio_card);
	platform_device_add(mt76xx_audio_device);

经过Machine的驱动的注册,Machine会根据注册以 "soc-audio" 为名字的平台设备,然后在同名的平台的驱动的 probe 函数中,会根据 snd_soc_dai_link 结构体中的name,进行匹配查找相应的 codec, codec_dai,platform, cpu_dai。找到之后将这些值全部放入结构体 snd_soc_pcm_runtime 的相应位置,然后注册 card,依次调用 codec platform,cpu_dai侧相应的 probe 函数进行初始化,接着创建pcm设备,注册card到系统中。


6. codec配置

在代码里找到两个codec文件,drivers/char/i2s/codec/i2c_wm8960.c和sound/soc/codecs/wm8960.c,跟进代码后发现wm8960.c才是真的codec驱动,根据上面.codec_name=wm8960.0-001a也知道那个是真的codec驱动。

static struct i2c_board_info __initdata mt_audio_i2c_devs_info[] = 
        I2C_BOARD_INFO("wm8960", (0x34>>1))              --- 在i2c总线上地址为 0x34
		
static struct i2c_driver wm8960_i2c_driver = 
	.driver = 
	.name = "wm8960",                                ---codec驱动名字为 wm8960
	.probe =    wm8960_i2c_probe,
	
static struct snd_soc_codec_driver soc_codec_dev_wm8960 = 
	.probe =	wm8960_probe,
	.read = wm8960_codec_read,
        .write = wm8960_codec_write,
	
static struct snd_soc_dai_driver wm8960_dai = 
	.name = "wm8960-hifi",                            --- 与上面codec_dai_name一致
	.playback = 
	.stream_name = "Playback",
	.capture = 
	.stream_name = "Capture",
	.ops = &wm8960_dai_ops,

static const struct snd_kcontrol_new wm8960_snd_controls[] =     --- 控件,录音和放音
SOC_DOUBLE_R_TLV("Capture Volume", WM8960_LINVOL, WM8960_RINVOL,
		 0, 63, 0, adc_tlv),
SOC_DOUBLE_R_TLV("Playback Volume", WM8960_LDAC, WM8960_RDAC,
		 0, 255, 0, dac_tlv),

static int __init wm8960_i2c_init(void)            --- 从这里开始
	i2c_register_board_info(1, mt_audio_i2c_devs_info, ARRAY_SIZE(mt_audio_i2c_devs_info));	--- 添加 i2c 硬件
	i2c_add_driver(&wm8960_i2c_driver);        --- 增加i2c设备驱动,与i2c适配器驱动联系起来
	
static int wm8960_i2c_probe(struct i2c_client *i2c,const struct i2c_device_id *id)
	wm8960 = devm_kzalloc(&i2c->dev, sizeof(struct wm8960_priv),GFP_KERNEL);
	i2c_set_clientdata(i2c, wm8960);
	snd_soc_register_codec(&i2c->dev,&soc_codec_dev_wm8960, &wm8960_dai, 1);
        //---1. 注册 codec,同时传入了 snd_soc_codec_driver 和 snd_soc_dai_driver 结构。
	
static int wm8960_probe(struct snd_soc_codec *codec)
	snd_soc_add_codec_controls(codec, wm8960_snd_controls,ARRAY_SIZE(wm8960_snd_controls));	--- 添加 控件

1:
sound/soc/soc-core.c
int snd_soc_register_codec(...)
	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);    --- 分配一个 snd_soc_codec 结构
	fixup_codec_formats(&dai_drv[i].playback);		--- 根据 snd_soc_dai_driver 中的参数设置Format
	fixup_codec_formats(&dai_drv[i].capture);
	snd_soc_register_dais(dev, dai_drv, num_dai);		--- 注册dai,传入参数有dai的驱动,以及dai的参数
	
static int snd_soc_register_dais(...)
	dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL);
	dai->name = fmt_multiple_name(dev, &dai_drv[i]);
	dai->dev = dev;
	dai->driver = &dai_drv[i];
	dai->codec = codec;
	list_add(&dai->list, &dai_list);	--- 此 dai 加入到 dai_list 中。
通过调用 snd_soc_register_codec 函数之后,分配的codec最终放入了codec_list链表中,codec下的component组件全部放入component_list链表中,codec 下的dai全部存放入 code->component.dai_list 中。 可以在 Machine 中看到,machine 匹配 codec_dai 和 cpu_dai 也是从 code->component.dai_list 中获取的。


7. mt76xx_i2s.c

static struct platform_driver mt76xx_i2s_driver = 
	.probe  = mt76xx_i2s_drv_probe,
	.driver = 
	.name  = "mt76xx-i2s",			-----------------------------------**
		
const struct snd_soc_component_driver mt76xx_i2s_component = 
	.name		= "mt76xx-i2s",
	
struct snd_soc_dai_driver mt76xx_i2s_dai = 	--- cpu侧的dai驱动,其中包括dai的配置(音频格式,clock,音量等)。
	.playback = {
	},
	.capture = {
	},
	.ops = &mt76xx_i2s_dai_ops,
	
static struct snd_soc_dai_ops mt76xx_i2s_dai_ops = 
	.startup   = mt76xx_i2s_startup,
	.hw_params = mt76xx_i2s_hw_params,
	.hw_free   = mt76xx_i2s_hw_free,
	.prepare   = mt76xx_i2s_prepare,


mt76xx_i2s_init
	platform_driver_register(&mt76xx_i2s_driver);

static int mt76xx_i2s_drv_probe(struct platform_device *pdev)
	snd_soc_register_component(&pdev->dev, &mt76xx_i2s_component,&mt76xx_i2s_dai, 1);--- 注册一个 component 组件
	
int snd_soc_register_component(...)			--- 注册dai,最终将注册的dai放入到 component->dai_list 中
	snd_soc_register_dais(dev, dai_drv, num_dai);
	list_add(&cmpnt->list, &component_list);

8. mt76xx_pcm.c

static struct platform_driver mt76xx_pcm_driver = 
	.driver = 
	.name = "mt76xx-pcm",				---- 与 machine 中 platform_name 对应
	.probe = mt76xx_platform_drv_probe,
	
struct snd_soc_platform_driver mt76xx_soc_platform = 
	.ops		= &mt76xx_pcm_ops,
	.pcm_new	= mt76xx_pcm_new,
	.pcm_free	= mt76xx_pcm_free,

static struct snd_pcm_ops mt76xx_pcm_ops = 
	.open = 	mt76xx_pcm_open,
	.ioctl = 	snd_pcm_lib_ioctl,
	.hw_params = 	mt76xx_pcm_hw_params,
	.hw_free = 	mt76xx_pcm_hw_free,
	.trigger =	mt76xx_pcm_trigger,
	.prepare = 	mt76xx_pcm_prepare,
	.pointer = 	mt76xx_pcm_pointer,
	.close = 	mt76xx_pcm_close,
	.mmap = mt76xx_pcm_mmap,

static int __init mt76xx_pcm_init(void)
	platform_driver_register(&mt76xx_pcm_driver);

static int mt76xx_platform_drv_probe(struct platform_device *pdev)
	snd_soc_register_platform(&pdev->dev, &mt76xx_soc_platform);
	
int snd_soc_register_platform(..)
	snd_soc_add_platform(dev, platform, platform_drv);
	
int snd_soc_add_platform(..)
	list_add(&platform->list, &platform_list);

9 .移植aplay和arecord

在package\utils目录下 增加alsa-lib和alsa-utils,添加对应makefile. menuconfig选择对应配置后编译即可。系统中就有 aplay和arecord等命令。

10. 节点测试

root@OpenWrt:~# i2cdetect -l        【i2c适配器驱动成功】
i2c-0   i2c             mt-i2c                                  I2C adapter

root@OpenWrt:~# i2cdetect -y 0        【0x34地址上有挂载设备,codec设备挂载成功】
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 
10: 10 11 12 13 14 15 16 17 18 19 UU 1b 1c 1d 1e 1f 
20: 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 
30: 30 31 32 33 UU 35 36 37 38 39 3a 3b 3c 3d 3e 3f

root@OpenWrt:~# ls /dev/snd
controlC0  pcmC0D0c   pcmC0D0p   timer

root@OpenWrt:~# ls /sys/class/sound/
card0      controlC0  pcmC0D0c   pcmC0D0p   timer

root@OpenWrt:/proc/asound# ls
I2S      card0    cards    devices  oss      pcm      seq      timers   version
root@OpenWrt:/proc/asound# cat cards 
 0 [I2S            ]: MTK_APSoC_I2S - MTK APSoC I2S
                      MTK APSoC I2S 
root@OpenWrt:/proc/asound# aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: I2S [MTK APSoC I2S], device 0: WMserious PCM wm8960-hifi-0 []
  Subdevices: 1/1
  Subdevice #0: subdevice #0
root@OpenWrt:/proc/asound# cd /sys/kernel/debug/asoc
root@OpenWrt:/sys/kernel/debug/asoc# ls
MTK APSoC I2S  codecs         dais           platforms
root@OpenWrt:/sys/kernel/debug/asoc# cat platforms 
mt76xx-pcm                                                ---cpu侧平台驱动
snd-soc-dummy
root@OpenWrt:/sys/kernel/debug/asoc# cat dais 
mt76xx-i2s                                                ---cpu侧的dai名字
wm8960-hifi
snd-soc-dummy-dai
root@OpenWrt:/sys/kernel/debug/asoc# cat codecs 
wm8960.0-001a                                             ---codec_name
snd-soc-dummy

从文件节点来看,一切好像正常,但是....

11.功能测试

用官方的工具 i2stest 测试录音,i2stest    1 48000 1 1024 24 1,对应参数意思是:cmd    record srate    size        wordlen    little,然后报错:

This is Ralink I2S Command Program...
record size=1024
mmap fdm failed
i2scmd ...quit

用i2stest  播放:i2stest 0 48000 1 24 1 < ./record.snd,回报一个Segmentation fault。

用arecord录音: arecord -Dhw:0,0 -r8000 -f cd hjb.wmv ,系统卡死:

[ 5608.596000] I2S reset complete!!
[ 5608.600000] MMAP[0]=0x83350000, i2s_mmap_addr[0]=0x03350000
[ 5608.604000] MMAP[1]=0x83350C00, i2s_mmap_addr[1]=0x03350c00
[ 5608.612000] MMAP[2]=0x83351800, i2s_mmap_addr[2]=0x03351800
[ 5608.616000] MMAP[3]=0x83352400, i2s_mmap_addr[3]=0x03352400
[ 5608.624000] MMAP[4]=0x83353000, i2s_mmap_addr[4]=0x03353000
[ 5608.628000] MMAP[5]=0x83353C00, i2s_mmap_addr[5]=0x03353c00
[ 5608.636000] MMAP[6]=0x83354800, i2s_mmap_addr[6]=0x03354800
[ 5608.640000] MMAP[7]=0x83355400, i2s_mmap_addr[7]=0x03355400
Recording WAVE 'hjb.wmv' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
[ 5608.952000] WM8960 is in master mode
[ 5608.968000] ****** wm8960_set_dai_fmt ******
[ 5608.992000] ****** wm8960_hw_params ******
[ 5609.204000] This SoC is in Slave mode


[ 5619.224000] capture write error (DMA or IRQ trouble?)
arecord: pcm_read:2031: read error: Input/output error
从“capture write error”来自sound/core/pcm_lib.c:1941,SNDRV_PCM_STATE_DISCONNECTED,意思是  hardware is disconnected。

用tftp导入一个文件,用 aplay test.wav 播放,会有杂音。

用i2stest命令,测试时MCLK有信号,但是I2S四个引脚全部无信号。

【2018.5.28更新】:

经过无比艰难的调试,终于找到原因了,软件硬件都有问题。

1.硬件设计有问题,有个充电芯片挂在i2c上,跟codec地址有冲突。

2.I2C驱动问题,是的,MTK官方的i2c驱动有问题,那么去github上更新I2C驱动就好了。

当移除所有其他I2C设备做实验时,软件驱动问题存在;当用gpio模拟i2c或者打上正确补丁后,驱动没问题时,硬件又没有移除其他I2C设备。这真是一个悲伤的故事。后来我们换ak7755 codec了,codec居然比主芯片贵好几倍,隐隐不安,果然这又开始另外一段悲伤的故事了。

你可能感兴趣的:(外设驱动)