MT6853 android11 TP fts_ts 驱动框架解析

MT6853 android11 TP fts_ts 驱动框架解析
环境:         
      MT6853 Android11 focaltech_touch HD720 5寸tp
一、TP代码分析
1、硬件参数设置
在dts文件中,对照硬件接口,修改对应GPIO设置,还有TP的虚拟键坐标、分辨率等参数信息
//cust_mt6853_touch_1080x1920.dtsi
&touch {
tpd-resolution = <720 1280>;
lcm-resolution = <720 1280>;
use-tpd-button = <0>;
tpd-key-num = <3>;
tpd-key-local = <139 172 158 0>;
tpd-key-dim-local = <90 883 100 40 230 883 100 40 370 883 100 40 0 0 0 0>;
tpd-max-touch-num = <5>;
tpd-filter-enable = <1>;
tpd-filter-pixel-density = <146>;
tpd-filter-custom-prameters = <0 0 0 0 0 0 0 0 0 0 0 0>;
tpd-filter-custom-speed = <0 0 0>;
pinctrl-names = "default", "state_eint_as_int",
"state_eint_output0", "state_eint_output1",
"state_rst_output0", "state_rst_output1";
pinctrl-0 = <&ctp_pins_default>;
pinctrl-1 = <&ctp_ts_eint_as_int>;
pinctrl-2 = <&ctp_ts_eint_output0>;
pinctrl-3 = <&ctp_ts_eint_output1>;
pinctrl-4 = <&ctp_ts_rst_output0>;
pinctrl-5 = <&ctp_ts_rst_output1>;
status = "okay";
};
..........
2、TP设备驱动
文件mtk_tpd.c是TP设备驱动的入口,可以通过分析本文件简单捋一下代码结构。
​ 首先,是通过platform_driver_register注册一个设备驱动:
//mtk_tpd.c
static void tpd_init_work_callback(struct work_struct *work)
{
TPD_DEBUG("MediaTek touch panel driver init\n");
if (platform_driver_register(&tpd_driver) != 0)
TPD_DMESG("unable to register touch panel driver.\n");
}
static int __init tpd_device_init(void)
{
int res = 0;
tpd_init_workqueue = create_singlethread_workqueue("mtk-tpd");
INIT_WORK(&tpd_init_work, tpd_init_work_callback);
res = queue_work(tpd_init_workqueue, &tpd_init_work);
if (!res)
pr_info("tpd : touch device init failed res:%d\n", res);
return 0;
}
//设备驱动结构体
static struct platform_driver tpd_driver = {
.remove = tpd_remove,
.shutdown = NULL,
.probe = tpd_probe,
.driver = {
.name = TPD_DEVICE,
.pm = &tpd_pm_ops,
.owner = THIS_MODULE,
.of_match_table = touch_of_match,
},
};
我们看一下这个设备驱动的probe函数:
//mtk_tpd.c
static int tpd_probe(struct platform_device *pdev)
{
......
//首先,注册了一个杂项设备
if (misc_register(&tpd_misc_device))
{
pr_err("mtk_tpd: tpd_misc_device register failedn");
}
//读取dts中相关gpio等设置
tpd_get_gpio_info(pdev);
//在内存中为输入设备结构体分配一个空间,并对结构体成员进行初始化
tpd = kmalloc(sizeof(struct tpd_device), GFP_KERNEL);
if (tpd == NULL)
return -ENOMEM;
memset(tpd, 0, sizeof(struct tpd_device));
tpd->dev = input_allocate_device();
if (tpd->dev == NULL) {
kfree(tpd);
return -ENOMEM;
}
//看下是否存在横屏竖用,TP也要跟着LCD进行横竖转换
#ifdef CONFIG_MTK_LCM_PHYSICAL_ROTATION
if (0 == strncmp(CONFIG_MTK_LCM_PHYSICAL_ROTATION, "90", 2) || 0 == strncmp(CONFIG_MTK_LCM_PHYSICAL_ROTATION, "270", 3)) 
{
#ifdef CONFIG_MTK_FB /*Fix build errors,as some projects  cannot support these apis while bring up*/
TPD_RES_Y = DISP_GetScreenWidth();
TPD_RES_X = DISP_GetScreenHeight();
#endif
else
#endif
{
#ifdef CONFIG_CUSTOM_LCM_X
#ifndef CONFIG_MTK_FPGA
#ifdef CONFIG_MTK_FB /*Fix build errors,as some projects  cannot support these apis while bring up*/
TPD_RES_X = DISP_GetScreenWidth();
TPD_RES_Y = DISP_GetScreenHeight();
#endif
#endif
#else
#ifdef CONFIG_LCM_WIDTH
ret = kstrtoul(CONFIG_LCM_WIDTH, 0, &tpd_res_x);
if (ret < 0) {
pr_err("Touch down get lcm_x failed");
return ret;
}
TPD_RES_X = tpd_res_x;
ret = kstrtoul(CONFIG_LCM_HEIGHT, 0, &tpd_res_x);*/
if (ret < 0) {
pr_err("Touch down get lcm_y failed");
return ret;
}
TPD_RES_Y = tpd_res_y;
#endif
#endif
}
//设置输入设备参数,包括时间类型等;
tpd_mode = TPD_MODE_NORMAL;
tpd_mode_axis = 0;
tpd_mode_min = TPD_RES_Y / 2;
tpd_mode_max = TPD_RES_Y;
tpd_mode_keypad_tolerance = TPD_RES_X * TPD_RES_X / 1600;
//初始化输入设备结构
tpd->dev->name = TPD_DEVICE;
set_bit(EV_ABS, tpd->dev->evbit);
set_bit(EV_KEY, tpd->dev->evbit);
set_bit(ABS_X, tpd->dev->absbit);
set_bit(ABS_Y, tpd->dev->absbit);
set_bit(ABS_PRESSURE, tpd->dev->absbit);
#if !defined(CONFIG_MTK_S3320) && !defined(CONFIG_MTK_S3320_47)
&& !defined(CONFIG_MTK_S3320_50) && !defined(CONFIG_MTK_MIT200) 
&& !defined(CONFIG_TOUCHSCREEN_SYNAPTICS_S3528) && !defined(CONFIG_MTK_S7020)
set_bit(BTN_TOUCH, tpd->dev->keybit);
#endif /* CONFIG_MTK_S3320 */
set_bit(INPUT_PROP_DIRECT, tpd->dev->propbit);
//将注册到的platform_device保存到创建的输入设备中;
tpd->tpd_dev = &pdev->dev;
//遍历tpd_driver_list,找到当前在用TP;
for (i = 1; i < TP_DRV_MAX_COUNT; i++) 
{
if (tpd_driver_list[i].tpd_device_name != NULL) 
{
tpd_driver_list[i].tpd_local_init();
if (tpd_load_status == 1) 
{
TPD_DMESG("[mtk-tpd]tpd_probe, tpd_driver_name=%sn",tpd_driver_list[i].tpd_device_name);
g_tpd_drv = &tpd_driver_list[i];
break;
}
}
}
//如果tpd_driver_list中所有TP都初始化失败,强制使用index=0的TP;
if (g_tpd_drv == NULL) 
{
if (tpd_driver_list[0].tpd_device_name != NULL) 
{
g_tpd_drv = &tpd_driver_list[0];
/* touch_type:0: r-touch, 1: C-touch */
touch_type = 0;
g_tpd_drv->tpd_local_init();
TPD_DMESG("[mtk-tpd]Generic touch panel drivern");
} else {
TPD_DMESG("[mtk-tpd]cap touch and Generic touch both are not loaded!!n");
return 0;
}
}
//为TP工作创建一个工作队列;
touch_resume_workqueue = create_singlethread_workqueue("touch_resume");
INIT_WORK(&touch_resume_work, touch_resume_workqueue_callback);
//设置背光通知的回调函数,以便在亮屏时唤醒TP,或者息屏后关闭TP;
tpd_fb_notifier.notifier_call = tpd_fb_notifier_callback;
if (fb_register_client(&tpd_fb_notifier))
TPD_DMESG("register fb_notifier fail!n");
//设置TP相关参数;
if (touch_type == 1) 
{
set_bit(ABS_MT_TRACKING_ID, tpd->dev->absbit);
set_bit(ABS_MT_TOUCH_MAJOR, tpd->dev->absbit);
set_bit(ABS_MT_TOUCH_MINOR, tpd->dev->absbit);
set_bit(ABS_MT_POSITION_X, tpd->dev->absbit);
set_bit(ABS_MT_POSITION_Y, tpd->dev->absbit);
input_set_abs_params(tpd->dev, ABS_MT_POSITION_X, 0, TPD_RES_X, 0, 0);
input_set_abs_params(tpd->dev, ABS_MT_POSITION_Y, 0, TPD_RES_Y, 0, 0);
#if defined(CONFIG_MTK_S3320) || defined(CONFIG_MTK_S3320_47) 
|| defined(CONFIG_MTK_S3320_50) || defined(CONFIG_MTK_MIT200) 
|| defined(CONFIG_TOUCHSCREEN_SYNAPTICS_S3528) || defined(CONFIG_MTK_S7020)
input_set_abs_params(tpd->dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
input_set_abs_params(tpd->dev, ABS_MT_WIDTH_MAJOR, 0, 15, 0, 0);
input_set_abs_params(tpd->dev, ABS_MT_WIDTH_MINOR, 0, 15, 0, 0);
input_mt_init_slots(tpd->dev, 10, 0);
#else
input_set_abs_params(tpd->dev, ABS_MT_TOUCH_MAJOR, 0, 100, 0, 0);
input_set_abs_params(tpd->dev, ABS_MT_TOUCH_MINOR, 0, 100, 0, 0);
#endif /* CONFIG_MTK_S3320 */
TPD_DMESG("Cap touch panel drivern");
}
input_set_abs_params(tpd->dev, ABS_X, 0, TPD_RES_X, 0, 0);
input_set_abs_params(tpd->dev, ABS_Y, 0, TPD_RES_Y, 0, 0);
input_abs_set_res(tpd->dev, ABS_X, TPD_RES_X);
input_abs_set_res(tpd->dev, ABS_Y, TPD_RES_Y);
input_set_abs_params(tpd->dev, ABS_PRESSURE, 0, 255, 0, 0);
input_set_abs_params(tpd->dev, ABS_MT_TRACKING_ID, 0, 10, 0, 0);
//注册输入设备
if (input_register_device(tpd->dev))
TPD_DMESG("input_register_device failed.(tpd)n");
else
tpd_register_flag = 1;
//如果TP支持虚拟按键,还要进行虚拟按键的初始化;
if (g_tpd_drv->tpd_have_button)
tpd_button_init();
//创建用于调试用的节点文件
if (g_tpd_drv->attrs.num)
tpd_create_attributes(&pdev->dev, &g_tpd_drv->attrs);
return 0;
}
执行完probe函数,TP的设备节点就创建好了。
​ 在probe中,查找当前使用TP是通过遍历一个支持列表—tpd_driver_list实现的,这个列表的元素是TP操作的具体函数组成的结构体,定义在:
//tpd.h
struct tpd_driver_t {
char *tpd_device_name; //TP名字
int (*tpd_local_init)(void); //TP初始化
void (*suspend)(struct device *h);//TP休眠
void (*resume)(struct device *h);//TP唤醒
int tpd_have_button;//是否有虚拟按键
struct tpd_attrs attrs;//创建节点文件
};
不同的TP都是要实现这个结构体中的内容,完成TP应有的工作。
​ tpd_driver_list这个列表时通过调用函数tpd_driver_add来添加元素的:
//mtk_tpd.c
int tpd_driver_add(struct tpd_driver_t *tpd_drv)
{
......
tpd_drv->tpd_have_button = tpd_dts_data.use_tpd_button;
//如果是电阻屏,强制放到列表index=0位置;
if (strcmp(tpd_drv->tpd_device_name, "generic") == 0) {
tpd_driver_list[0].tpd_device_name = tpd_drv->tpd_device_name;
tpd_driver_list[0].tpd_local_init = tpd_drv->tpd_local_init;
tpd_driver_list[0].suspend = tpd_drv->suspend;
tpd_driver_list[0].resume = tpd_drv->resume;
tpd_driver_list[0].tpd_have_button = tpd_drv->tpd_have_button;
return 0;
}
    //如果是电容屏,放到列表尾;
for (i = 1; i < TP_DRV_MAX_COUNT; i++) {
if (tpd_driver_list[i].tpd_device_name == NULL) {
tpd_driver_list[i].tpd_device_name = tpd_drv->tpd_device_name;
tpd_driver_list[i].tpd_local_init = tpd_drv->tpd_local_init;
tpd_driver_list[i].suspend = tpd_drv->suspend;
tpd_driver_list[i].resume = tpd_drv->resume;
tpd_driver_list[i].tpd_have_button = tpd_drv->tpd_have_button;
tpd_driver_list[i].attrs = tpd_drv->attrs;
break;
}
        //若列表中已经有了这个TP,不再重复添加;
if (strcmp(tpd_driver_list[i].tpd_device_name, tpd_drv->tpd_device_name) == 0)
return 1;
}
return 0;
}
​ 另外,probe函数中还注册了一个背光通知回调函数,用于控制TP随背光变化调整工作方式,回调函数如下:
//mtk_tpd.c
static int tpd_fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data)
{
......
switch (blank) {
    //背光被点亮通知
case FB_BLANK_UNBLANK:
TPD_DMESG("LCD ON Notifyn");
if (g_tpd_drv && tpd_suspend_flag) {
            //通过工作队列方式,调用TP的唤醒函数
err = queue_work(touch_resume_workqueue, &touch_resume_work);
if (!err) {
TPD_DMESG("start touch_resume_workqueue failedn");
return err;
}
}
break;
    //背光被熄灭通知        
case FB_BLANK_POWERDOWN:
TPD_DMESG("LCD OFF Notifyn");
if (g_tpd_drv)
        {
            //先取消工作队列,避免逻辑混乱
err = cancel_work_sync(&touch_resume_work);
if (!err)
TPD_DMESG("cancel touch_resume_workqueue err = %dn", err);  
            //直接调用TP的休眠函数
g_tpd_drv->suspend(NULL);
tpd_suspend_flag = 1;
        }
break;
default:
break;
}
return 0;
}
​ TP的设备驱动基本结构就是这些。
3、TP模组驱动
TP设备驱动搭好了TP驱动的架构,不同的TP模组按照这个架构,主要是实现结构体 tpd_driver_t 中内容,然后通过函数 tpd_driver_add 加入到驱动列表中。下面就以一个实例—focaltech_touch,看一下TP模组的驱动代码。
​ 首先,通过 module_init 和 module_exit 实现模组的初始化和退出:
//focaltech_core.c
static int __init tpd_driver_init(void)
{
FTS_FUNC_ENTER();
FTS_INFO("Driver version: %s", FTS_DRIVER_VERSION);
tpd_get_dts_info();
if (tpd_dts_data.touch_max_num < 2)
tpd_dts_data.touch_max_num = 2;
else if (tpd_dts_data.touch_max_num > FTS_MAX_POINTS_SUPPORT)
tpd_dts_data.touch_max_num = FTS_MAX_POINTS_SUPPORT;
FTS_INFO("tpd max touch num:%d", tpd_dts_data.touch_max_num);
#if FTS_PSENSOR_EN
fts_proximity_init();
#endif
if (tpd_driver_add(&tpd_device_driver) < 0)
FTS_ERROR("[TPD]: Add FTS Touch driver failed!!");
FTS_FUNC_EXIT();
return 0;
}
static void __exit tpd_driver_exit(void)
{
FTS_FUNC_ENTER();
tpd_driver_remove(&tpd_device_driver);
FTS_FUNC_EXIT();
}
module_init(tpd_driver_init);
module_exit(tpd_driver_exit);
结构体 tpd_driver_t 的具体实现为:
static struct tpd_driver_t tpd_device_driver = {
.tpd_device_name = FTS_DRIVER_NAME,
.tpd_local_init = tpd_local_init,
.suspend = tpd_suspend,
.resume = tpd_resume,
};
其中,tpd_local_init 源码如下:
//focaltech_core.c
static int fts_ts_driver_init(void)
{
//添加一个I2C驱动,用于和TP通信
return i2c_add_driver(&fts_ts_driver);
}
static int tpd_local_init(void)
{
int ret = 0;
FTS_FUNC_ENTER();
ret = fts_ts_driver_init();
...................
//电容屏
tpd_type_cap = 1;
FTS_FUNC_EXIT();
return 0;
}
​ 可以看出,添加I2C驱动的操作,是 tpd_local_init 的重点,fts_ts_driver实现如下:
//focaltech_core.c
static const struct i2c_device_id fts_ts_id[] = {
{FTS_DRIVER_NAME, 0}, {},
};
static const struct of_device_id fts_dt_match[] = {
{.compatible = "mediatek,cap_touch"}, {},
};
MODULE_DEVICE_TABLE(of, fts_dt_match);
static struct i2c_driver fts_ts_driver = {
.probe = fts_ts_probe,
.remove = fts_ts_remove,
.driver = {
.name = FTS_DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(fts_dt_match),
},
.id_table = fts_ts_id,
.detect = fts_ts_detect,
};
还是要读一下probe函数:
//focaltech_core.c
static int fts_ts_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
   ......
//赋值I2C地址
if (client->addr != FTS_I2C_SLAVE_ADDR) {
FTS_INFO("[TPD]Change i2c addr 0x%02x to %x", client->addr,
 FTS_I2C_SLAVE_ADDR);
client->addr = FTS_I2C_SLAVE_ADDR;
FTS_INFO("[TPD]i2c addr=0x%x\n", client->addr);
}
ret = fts_input_init(ts_data);
if (ret) {
FTS_ERROR("input initialize fail");
goto err_input_init;
}
//配置GPIO
ret = fts_gpio_configure(ts_data);
if (ret) {
FTS_ERROR("configure the gpios fail");
goto err_gpio_config;
}
//配置上电
#if FTS_POWER_SOURCE_CUST_EN
ret = fts_power_source_init(ts_data);
if (ret) {
FTS_ERROR("fail to get power(regulator)");
goto err_power_init;
}
#endif
//控制reset
#if (!FTS_CHIP_IDC)
fts_reset_proc(200);
#endif
//定义与上层交互的读写接口
ret = fts_create_apk_debug_channel(ts_data);
if (ret)
FTS_ERROR("create apk debug node fail");
 //通过I2C读取TP数据、解析TP数据、上报TP数据
#if FTS_POINT_REPORT_CHECK_EN
ret = fts_point_report_check_init(ts_data);
if (ret)
FTS_ERROR("init point report check fail");
#endif
//注册中断
ret = fts_irq_registration(ts_data);
if (ret) {
FTS_ERROR("request irq failed");
goto err_irq_req;
}
tpd_load_status = 1;
}
读一下fts_input_init函数
//注册输入设备
static int fts_input_init(struct fts_ts_data *ts_data)
{
int ret = 0;
int key_num = 0;
struct fts_ts_platform_data *pdata = ts_data->pdata;
struct input_dev *input_dev;
FTS_FUNC_ENTER();
//申请一个输入设备
input_dev = input_allocate_device();
if (!input_dev) {
FTS_ERROR("Failed to allocate memory for input device");
return -ENOMEM;
}
//初始化、注册输入设备
input_dev->name = FTS_DRIVER_NAME;
input_dev->id.bustype = BUS_I2C;
input_dev->dev.parent = ts_data->dev;
input_set_drvdata(input_dev, ts_data);
//设置TP相关参数;
__set_bit(EV_SYN, input_dev->evbit);
__set_bit(EV_ABS, input_dev->evbit);
__set_bit(EV_KEY, input_dev->evbit);
__set_bit(BTN_TOUCH, input_dev->keybit);
__set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
if (pdata->have_key) {
FTS_INFO("set key capabilities");
for (key_num = 0; key_num < pdata->key_number; key_num++)
input_set_capability(input_dev, EV_KEY,
     pdata->keys[key_num]);
}
#if FTS_MT_PROTOCOL_B_EN
input_mt_init_slots(input_dev, pdata->max_touch_number,
    INPUT_MT_DIRECT);
#else
input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, 0x0F, 0, 0);
#endif
input_set_abs_params(input_dev, ABS_MT_POSITION_X, pdata->x_min,
     pdata->x_max, 0, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_Y, pdata->y_min,
     pdata->y_max, 0, 0);
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 0xFF, 0, 0);
#if FTS_REPORT_PRESSURE_EN
input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 0xFF, 0, 0);
#endif
//注册输入设备
ret = input_register_device(input_dev);
if (ret) {
FTS_ERROR("Input device registration failed");
input_set_drvdata(input_dev, NULL);
input_free_device(input_dev);
input_dev = NULL;
return ret;
}
ts_data->input_dev = input_dev;
FTS_FUNC_EXIT();
return 0;
}
//拉高复位脚
static int fts_gpio_configure(struct fts_ts_data *ts_data)
{
tpd_gpio_output(ts_data->pdata->reset_gpio, 1);
return 0;
}
  //给TP上电
static int fts_power_source_init(struct fts_ts_data *ts_data)
{
int ret = 0;
FTS_FUNC_ENTER();
ts_data->vdd = regulator_get(tpd->tpd_dev, "vtouch");
if (IS_ERR_OR_NULL(ts_data->vdd)) {
ret = PTR_ERR(ts_data->vdd);
FTS_ERROR("get vdd regulator failed,ret=%d", ret);
return ret;
}
if (regulator_count_voltages(ts_data->vdd) > 0) {
ret = regulator_set_voltage(ts_data->vdd, FTS_VTG_MIN_UV,
    FTS_VTG_MAX_UV);
if (ret) {
FTS_ERROR("vdd regulator set_vtg failed ret=%d", ret);
regulator_put(ts_data->vdd);
return ret;
}
}
ts_data->power_disabled = true;
ret = fts_power_source_ctrl(ts_data, ENABLE);
if (ret)
FTS_ERROR("fail to enable power(regulator)");
FTS_FUNC_EXIT();
return ret;
}
//控制RESET脚,使TP复位一下
int fts_reset_proc(int hdelayms)
{
FTS_DEBUG("tp reset");
tpd_gpio_output(fts_data->pdata->reset_gpio, 0);
msleep(20);
tpd_gpio_output(fts_data->pdata->reset_gpio, 1);
if (hdelayms)
msleep(hdelayms);
return 0;
}
//定义与上层交互的读写接口
static const struct file_operations fts_proc_fops = {
.owner = THIS_MODULE, .read = fts_debug_read, .write = fts_debug_write,
};
int fts_create_apk_debug_channel(struct fts_ts_data *ts_data)
{
struct ftxxxx_proc *proc = &ts_data->proc;
#if 1
proc->proc_entry = proc_create(PROC_NAME, 0777, NULL, &fts_proc_fops);
if (proc->proc_entry == NULL) {
FTS_ERROR("create proc entry fail");
return -ENOMEM;
}
#else
proc->proc_entry = create_proc_entry(PROC_NAME, 0777, NULL);
if (proc->proc_entry == NULL) {
FTS_ERROR("create proc entry fail");
return -ENOMEM;
}
proc->proc_entry->write_proc = fts_debug_write;
proc->proc_entry->read_proc = fts_debug_read;
#endif
FTS_INFO("Create proc entry success!");
return 0;
}
int fts_point_report_check_init(struct fts_ts_data *ts_data)
{
FTS_FUNC_ENTER();
if (ts_data->ts_workqueue) {
INIT_DELAYED_WORK(&ts_data->prc_work, fts_prc_func);
} else {
FTS_ERROR(
"fts workqueue is NULL, can't run point report check function");
return -EINVAL;
}
FTS_FUNC_EXIT();
return 0;
}
int fts_point_report_check_init(struct fts_ts_data *ts_data)
{
FTS_FUNC_ENTER();
if (ts_data->ts_workqueue) {
INIT_DELAYED_WORK(&ts_data->prc_work, fts_prc_func);
} else {
FTS_ERROR(
"fts workqueue is NULL, can't run point report check function");
return -EINVAL;
}
FTS_FUNC_EXIT();
return 0;
}
int fts_point_report_check_init(struct fts_ts_data *ts_data)
{
FTS_FUNC_ENTER();
if (ts_data->ts_workqueue) {
INIT_DELAYED_WORK(&ts_data->prc_work, fts_prc_func);
} else {
FTS_ERROR(
"fts workqueue is NULL, can't run point report check function");
return -EINVAL;
}
FTS_FUNC_EXIT();
return 0;
}
int fts_point_report_check_init(struct fts_ts_data *ts_data)
{
FTS_FUNC_ENTER();
if (ts_data->ts_workqueue) {
INIT_DELAYED_WORK(&ts_data->prc_work, fts_prc_func);
} else {
FTS_ERROR(
"fts workqueue is NULL, can't run point report check function");
return -EINVAL;
}
FTS_FUNC_EXIT();
return 0;
}
//work处理函数,上报数据
static void fts_prc_func(struct work_struct *work)
{
struct fts_ts_data *ts_data =
container_of(work, struct fts_ts_data, prc_work.work);
struct input_dev *input_dev = ts_data->input_dev;
#if FTS_MT_PROTOCOL_B_EN
u32 finger_count = 0;
u32 max_touches = fts_data->pdata->max_touch_number;
#endif
FTS_FUNC_ENTER();
mutex_lock(&ts_data->report_mutex);
#if FTS_MT_PROTOCOL_B_EN
for (finger_count = 0; finger_count < max_touches; finger_count++) {
input_mt_slot(input_dev, finger_count);
input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false);
}
#else
input_mt_sync(input_dev);
#endif
input_report_key(input_dev, BTN_TOUCH, 0);
input_sync(input_dev);
mutex_unlock(&ts_data->report_mutex);
FTS_FUNC_EXIT();
}
int fts_point_report_check_init(struct fts_ts_data *ts_data)
{
FTS_FUNC_ENTER();
//init work
if (ts_data->ts_workqueue) {
INIT_DELAYED_WORK(&ts_data->prc_work, fts_prc_func);
} else {
FTS_ERROR(
"fts workqueue is NULL, can't run point report check function");
return -EINVAL;
}
FTS_FUNC_EXIT();
return 0;
}
//中断注册
static int fts_irq_registration(struct fts_ts_data *ts_data)
{
int ret = 0;
struct device_node *node = NULL;
//查找指定节点,获取相关参数
node = of_find_matching_node(node, touch_of_match);
if (node == NULL) {
FTS_ERROR("Can not find touch eint device node!");
return -ENODATA;
}
//运行一个线程
ts_data->thread_tpd = kthread_run(touch_event_handler, 0, TPD_DEVICE);
if (IS_ERR_OR_NULL(ts_data->thread_tpd)) {
ret = PTR_ERR(ts_data->thread_tpd);
FTS_ERROR("create kernel thread_tpd fail,ret:%d", ret);
ts_data->thread_tpd = NULL;
return ret;
}
 //设置中断,包括中断号、中断回调、中断触发方式等;
tpd_gpio_as_int(ts_data->pdata->irq_gpio);
ts_data->irq = irq_of_parse_and_map(node, 0);
ts_data->pdata->irq_gpio_flags = IRQF_TRIGGER_FALLING;
FTS_INFO("irq:%d, flag:%x", ts_data->irq,
 ts_data->pdata->irq_gpio_flags);
ret = request_irq(ts_data->irq, fts_irq_handler,
  ts_data->pdata->irq_gpio_flags, FTS_DRIVER_NAME,
  ts_data);
return ret;
}
看下中断回调函数:
static irqreturn_t fts_irq_handler(int irq, void *data)
{
tpd_flag = 1;
wake_up_interruptible(&waiter);//唤醒等待队列
return IRQ_HANDLED;
}
可以看到,中断回调只是EINT中断的上半部,上半部只是唤醒了一个等待队列。fts_irq_registration函数中创建了一个线程,ENIT中断的下半部放在这个线程中执行:
static DECLARE_WAIT_QUEUE_HEAD(waiter); //声明并创建一个等待队列
static int touch_event_handler(void *unused)
{
struct sched_param param = {.sched_priority = RTPM_PRIO_TPD};
//设置线程调度策略为:时间片轮转实时调度;
sched_setscheduler(current, SCHED_RR, &param);
do {
set_current_state(TASK_INTERRUPTIBLE);
        //等待等待队列被唤醒;
wait_event_interruptible(waiter, tpd_flag != 0);
tpd_flag = 0;
set_current_state(TASK_RUNNING);
#if FTS_PSENSOR_EN
if (fts_proximity_readdata(fts_data) == 0)
continue;
#endif
 //通过I2C读取TP数据、解析TP数据、上报TP数据
fts_irq_read_report();
} while (!kthread_should_stop());
return 0;
}
 //通过I2C读取TP数据、解析TP数据、上报TP数据
static void fts_irq_read_report(void)
{
int ret = 0;
struct fts_ts_data *ts_data = fts_data;
#if FTS_ESDCHECK_EN
fts_esdcheck_set_intr(1);
#endif
#if FTS_POINT_REPORT_CHECK_EN
fts_prc_queue_work(ts_data);
#endif
//读取解析TP数据
ret = fts_read_parse_touchdata(ts_data);
if (ret == 0) {
mutex_lock(&ts_data->report_mutex);
#if FTS_MT_PROTOCOL_B_EN
fts_input_report_b(ts_data);//上报TP数据
#else
fts_input_report_a(ts_data);
#endif
mutex_unlock(&ts_data->report_mutex);
}
#if FTS_ESDCHECK_EN
fts_esdcheck_set_intr(0);
#endif
}
看到这里,TP通过中断上报数据的流程就清晰了。
​ 最后,我们再看看TP的休眠和唤醒都做了什么:
focaltech_core.c
//休眠函数
static void tpd_suspend(struct device *dev)
{
int ret = 0;
struct fts_ts_data *ts_data = fts_data;
FTS_FUNC_ENTER();
if (ts_data->suspended) {
FTS_INFO("Already in suspend state");
return;
}
if (ts_data->fw_loading) {
FTS_INFO("fw upgrade in process, can't suspend");
return;
}
#if FTS_PSENSOR_EN
if (fts_proximity_suspend() == 0) {
fts_release_all_finger();
ts_data->suspended = true;
return;
}
#endif
#if FTS_ESDCHECK_EN
fts_esdcheck_suspend();
#endif
if (ts_data->gesture_mode) {
fts_gesture_suspend(ts_data);
} else {
fts_irq_disable();
// //通过I2C,控制TP进入休眠模式
ret = fts_write_reg(FTS_REG_POWER_MODE,
    FTS_REG_POWER_MODE_SLEEP);
if (ret < 0)
FTS_ERROR("set TP to sleep mode fail, ret=%d", ret);
if (!ts_data->ic_info.is_incell) {
#if FTS_POWER_SOURCE_CUST_EN
ret = fts_power_source_suspend(ts_data);
if (ret < 0)
FTS_ERROR("power enter suspend fail");
#endif
}
}
fts_release_all_finger();
ts_data->suspended = true;
FTS_FUNC_EXIT();
}
//唤醒函数
static void tpd_resume(struct device *dev)
{
struct fts_ts_data *ts_data = fts_data;
FTS_FUNC_ENTER();
if (!ts_data->suspended) {
FTS_DEBUG("Already in awake state");
return;
}
#if FTS_PSENSOR_EN
if (fts_proximity_resume() == 0) {
ts_data->suspended = false;
return;
}
#endif
fts_release_all_finger();
if (!ts_data->ic_info.is_incell) {
#if FTS_POWER_SOURCE_CUST_EN
fts_power_source_resume(ts_data);
#endif
fts_reset_proc(200); //控制RESET脚复位TP
}
fts_wait_tp_to_valid();
fts_ex_mode_recovery(ts_data);
#if FTS_ESDCHECK_EN
fts_esdcheck_resume();
#endif
if (ts_data->gesture_mode)
fts_gesture_resume(ts_data);
else
fts_irq_enable(); //使能EINT中断
ts_data->suspended = false;
FTS_FUNC_EXIT();
}
​ 至此,TP驱动的核心代码就很清晰了。
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zeropoint127

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值