没有做过比这版更烂的开发,坑还在,开发还在继续,记一下笔记吧,或许找到一些灵感。
先用 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居然比主芯片贵好几倍,隐隐不安,果然这又开始另外一段悲伤的故事了。