给联想乐蒙K3-Note编译一个完整功能的内核

本文介绍了如何为联想乐蒙K3-Note编译一个功能完整的内核。作者发现TFA9897音频芯片在i2C总线2的地址为0x34,通过将TFA_I2C_CHANNEL从2改为1解决了问题,恢复了音频功能。经过几天测试,所有功能正常。内核已更新至3.10.103并添加了MTK_I2C_EXTENSION支持。源代码已上传至GitHub。
摘要由CSDN通过智能技术生成
这是我早前在xda-developers.com上发的帖:
http://forum.xda-developers.com/k3-note/orig-development/kernel-dc-mtk-m1-kernel-vibeuiv3-51631-t3483573

有些朋友希望有的中文版,在那些手机论坛上,是无法发表比较详细的分析文章,因此,在这里再发布一次中文版:


联想内核开源码里面,缺少了tfa9897的驱动,现在大部分ROM只能用官方的内核。我花了一周多时间,编写了缺少的 tfa9897 驱动。
联想的内核开放源码: https://github.com/danielhk/android_kernel_lenovo_aio_otfp_m

经验分享:
当我们成功编译出 K3-Note 的内核,那份喜悦,只能维持一分钟!启动后,将会卡在第一屏。Log 和 kmsg 显示,找不到 /dev/tfa9897。如果我们把 /system/etc/tfa9897 改成其他名字,将会正常启动,除了没有声音,一切也工作正常。究竟是什么鬼原因?一个手机没有声音,是不可能吧!这些问题不断在困绕我。一番网上搜索后,大概明白了。大多数开发者都是卡在这里,被迫放弃。这让我非常气愤,一定要找出原因。越深入研究,越让我深信,这部分的代码,是被故意删除... 在一些旧有源码上,我们可以找到些蛛丝马迹,驱动本来是存在。此刻起,我决定要拨乱反正!我搜索 NXP 的网站,只能找到些芯片规格的 pdf。我搜索 github 和 Linux 驱动。能找到些 tfa98xx 的驱动、编码解码器和功放驱动。接着,我用 adb 查看官方1631固件的 sysfs:

shell@aio_otfp_m: $ cd /sys
shell@aio_otfp_m:/sys $ find -name tfa*
./bus/i2c/drivers/tfa9897
./bus/platform/devices/tfa9897.36
./bus/platform/drivers/tfa9897
./bus/platform/drivers/tfa9897/tfa9897.36
./devices/bus.2/tfa9897.36
./devices/virtual/misc/tfa9897
./class/misc/tfa9897
shell@aio_otfp_m: $ cd /sys/devices/bus.2/tfa9897.36
shell@aio_otfp_m:/sys/devices/bus.2/tfa9897.36 $ ls -l
lrwxrwxrwx root     root              2016-10-11 23:16 driver -> ../../../bus/platform/drivers/tfa9897
-r--r--r-- root     root         4096 2016-10-11 23:16 modalias
drwxr-xr-x root     root              2016-10-11 23:16 power
lrwxrwxrwx root     root              2016-10-11 23:16 subsystem -> ../../../bus/platform
-rw-r--r-- root     root         4096 2016-10-11 23:16 uevent
shell@aio_otfp_m:/sys/devices/bus.2/tfa9897.36 $ cat uevent
DRIVER=tfa9897
OF_NAME=tfa9897
OF_FULLNAME=/bus/tfa9897@0
OF_COMPATIBLE_0=mediatek,tfa9897
OF_COMPATIBLE_N=1
MODALIAS=of:Ntfa9897T<NULL>Cmediatek,tfa9897

我编译的第一个内核,更改 /system/etc/tfa98xx 名字后,得到以下
./bus/platform/devices/tfa9897.36
./devices/bus.2/tfa9897.36

之后用虚拟驱动,得到以下
./bus/platform/devices/tfa9897.36
./bus/platform/drivers/tfa9897
./devices/bus.2/tfa9897.36
./devices/virtual/misc/tfa9897
./class/misc/tfa9897
shell@aio_otfp_m:/sys/devices/bus.2/tfa9897.36 $ cat uevent
OF_NAME=tfa9897
OF_FULLNAME=/bus/tfa9897@0
OF_COMPATIBLE_0=mediatek,tfa9897
OF_COMPATIBLE_N=1
MODALIAS=of:Ntfa9897T<NULL>Cmediatek,tfa9897

缺少了一个 misc 驱动、一个 platform 驱动和一个 i2c 驱动!我也不知道为什么要那么复杂。我决定编写一个虚拟的驱动,去看看其操作原理是怎样。
虚拟驱动:

/* A dummy smartPA driver
* Written by: daniel_hk (https://github.com/danielhk)
*/
#include <asm/uaccess.h>
#include <linux/device.h>
#include <linux/input.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/types.h>

#define AudDrv_tfa9897_NAME    "MediaTek TFA9897 test Driver"
#define AUDDRV_AUTHOR        "daniel_hk"

static int AudDrv_tfa9897_probe(struct platform_device *dev)
{
printk("%s \n", __func__);
return 0;
}

static int AudDrv_tfa9897_open(struct inode *inode, struct file *fp)
{
return 0;
}

static long AudDrv_tfa9897_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
{
printk("%s: cmd = 0x%x arg = %lu\n",__func__, cmd, arg);
return 0;
}

static ssize_t AudDrv_tfa9897_write(struct file *fp, const char __user *data, size_t count, loff_t *offset)
{
printk("+%s, count=%d\n", __func__, (int)count);
    printk("-%s: data=%s\n", __func__, data);
return 0;
}

static ssize_t AudDrv_tfa9897_read(struct file *fp,  char __user *data, size_t count, loff_t *offset)
{
printk("+%s, count=%d\n", __func__, (int)count);
data = NULL;
return 0;
}

/**************************************************************************
*  The misc device and its file operations
**************************************************************************/

static struct file_operations AudDrv_tfa9897_fops =
{
.owner = THIS_MODULE,
.open = AudDrv_tfa9897_open,
.unlocked_ioctl = AudDrv_tfa9897_ioctl,
.write = AudDrv_tfa9897_write,
.read = AudDrv_tfa9897_read,
};

static struct miscdevice AudDrv_tfa9897_device =
{
.minor = MISC_DYNAMIC_MINOR,
.name = "tfa9897",
.fops = &AudDrv_tfa9897_fops,
};

#ifdef CONFIG_OF
static const struct of_device_id mtk_tfa9897_of_ids[] =
{
{ .compatible = "mediatek,tfa9897", },
{}
};
#endif

static struct platform_driver AudDrv_tfa9897 =
{
.probe = AudDrv_tfa9897_probe,
.driver = {
    .name = "tfa9897",
    .owner = THIS_MODULE,
#ifdef CONFIG_OF
    .of_match_table = mtk_tfa9897_of_ids,
#endif
},
};

static int AudDrv_tfa9897_mod_init(void)
{
int ret = 0;
printk("+%s\n", __func__);

// Register platform DRIVER
ret = platform_driver_register(&AudDrv_tfa9897);
if (ret)
{
    printk("%s: platform_driver_register Fail: %d \n", __func__, ret);
    return ret;
}

// register MISC device
if ((ret = misc_register(&AudDrv_tfa9897_device)))
{
    printk("%s: misc_register Fail:%d \n", __func__, ret);
    return ret;
}

printk("-%s\n", __func__);
return 0;
}
module_init(AudDrv_tfa9897_mod_init);

static void AudDrv_tfa9897_mod_exit(void)
{
}
module_exit(AudDrv_tfa9897_mod_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION(AudDrv_tfa9897_NAME);
MODULE_AUTHOR(AUDDRV_AUTHOR);

用虚拟驱动,我可以正常启动,然后,可能会死机。研究过 kmsg 后,得出以下结论:
1) 在 /system/etc/tfa9897 内的两个 .cnt 档,是相同
2) 启功时,libtfa9897_interface.so 会查看是什么芯片,然后,下载对应的 .cnt 档
3) 每次发送数据到 /dev/misc/tfa9897 前,会先呼叫 unlocked_ioctl
4) 我没有发现名为 tfa9897 或 tfa98xx 的编码解码器。
高通的驱动,都附带编码解码器。我又找到一些 mt6595 的功放驱动(忘记是那个品牌)。但,没有一个可以满足所有要求。最接近的是 tfa9800 的驱动,tfa9800 是 tfa9897 的旧版。
经过几次尝试,我终于把所有资料综合成一个驱动,可以产生和官方内核相同的 sysfs 架构。
1) 一个 misc 设备
2) 一个平台驱动
3) 在 probe 时,添加一个 I2C 驱动
以下就是驱动的初版,我把它放到 sound/soc/mediatek/nxp_tfa9897。既然是用在安桌的 Linux 内核,我补上 AOSP 的 GNU GPL 通用公共许可证。

/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Written by: daniel_hk (https://github.com/danielhk)
** 2016/10/12: initial release
*/

#include <asm/uaccess.h>
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <linux/input.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include "AudDrv_tfa9897.h"

#define TFA_I2C_CHANNEL (2)

#define ECODEC_SLAVE_ADDR_WRITE 0x68
#define ECODEC_SLAVE_ADDR_READ 0x69

#define I2C_MASTER_CLOCK 400
#define TFA9897_I2C_DEVNAME "tfa9897"

#define AudDrv_tfa9897_NAME "MediaTek TFA9897 SmartPA Driver"
#define AUDDRV_AUTHOR "daniel_hk"

//
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值