i.MX 6ULL 驱动开发 二十四:多点触摸屏

一、多点触摸(MT)协议

Linux 驱动开发 六十六:多点触控(MT)协议_lqonlylove的博客-CSDN博客

二、FT5426原理

见官方手册。

三、Linux 下电容触摸屏驱动框架

1、驱动总线

FT5426 使用 I2C 接口进行通信。

2、触摸信息上报机制

触摸点信息使用 input 子系统完成数据上报。

3、ABS_MT 事件

#define ABS_MT_SLOT		0x2f		/* MT slot being modified */
#define ABS_MT_TOUCH_MAJOR	0x30	/* Major axis of touching ellipse */
#define ABS_MT_TOUCH_MINOR	0x31	/* Minor axis (omit if circular) */
#define ABS_MT_WIDTH_MAJOR	0x32	/* Major axis of approaching ellipse */
#define ABS_MT_WIDTH_MINOR	0x33	/* Minor axis (omit if circular) */
#define ABS_MT_ORIENTATION	0x34	/* Ellipse orientation */
#define ABS_MT_POSITION_X	0x35	/* Center X touch position */
#define ABS_MT_POSITION_Y	0x36	/* Center Y touch position */
#define ABS_MT_TOOL_TYPE	0x37	/* Type of touching device */
#define ABS_MT_BLOB_ID		0x38	/* Group a set of packets as a blob */
#define ABS_MT_TRACKING_ID	0x39	/* Unique ID of initiated contact */
#define ABS_MT_PRESSURE		0x3a	/* Pressure on contact area */
#define ABS_MT_DISTANCE		0x3b	/* Contact hover distance */
#define ABS_MT_TOOL_X		0x3c	/* Center X tool position */
#define ABS_MT_TOOL_Y		0x3d	/* Center Y tool position */

ABS_MT 事件定义在:include\uapi\linux\input.h 文件中。

四、Linux 下 API 接口

1、隔离触摸点信息

/*
 * dev:具体的 input_dev 设备。
 */
static inline void input_mt_sync(struct input_dev *dev)
{
	input_event(dev, EV_SYN, SYN_MT_REPORT, 0);
}

2、上报事件槽

/*
 * dev:具体的 input_dev 设备。
 * slot:触摸槽信息
 */
static inline void input_mt_slot(struct input_dev *dev, int slot)
{
	input_event(dev, EV_ABS, ABS_MT_SLOT, slot);
}

3、初始化事件槽

/*
 * dev:具体的 input_dev 设备。
 * num_slots:SLOT 数量(触摸点数量)
 * flags:标志位
 */
int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots,unsigned int flags);

4、为 slot 关联一个 ID

/*
 * dev:具体的 input_dev 设备。
 * tool_type:触摸类型
 * active:ture(连续触摸)、flase(触摸点抬起)
 */
void input_mt_report_slot_state(struct input_dev *dev,unsigned int tool_type, bool active)

5、上报触摸点坐标

/*
 * dev:具体的 input_dev 设备。
 * code:上报数据类型:ABS_MT_POSITION_X(x轴坐标信息)、ABS_MT_POSITION_Y(y轴坐标信息)
 * value:坐标值
 */
static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)
{
	input_event(dev, EV_ABS, code, value);
}

6、上报触摸点数量

/*
 * dev:具体的 input_dev 设备。
 * use_count:触摸点数量
 */
void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count);

7、Type A 触摸点信息上报时序

ABS_MT_POSITION_X x[0] 	# 使用 input_report_abs 上传 x 轴坐标
ABS_MT_POSITION_Y y[0]	# 使用 input_report_abs 上传 y 轴坐标
SYN_MT_REPORT			# 使用 input_mt_sync 完成
ABS_MT_POSITION_X x[1]
ABS_MT_POSITION_Y y[1]
SYN_MT_REPORT
SYN_REPORT				# 使用 input_sync 实现

8、Type B 触摸点信息上报时序

ABS_MT_SLOT 0			# 使用 input_mt_slot 上报事件槽信息
ABS_MT_TRACKING_ID 45	# 使用  input_mt_report_slot_state 添加触摸点
ABS_MT_POSITION_X x[0]	# 使用 input_report_abs 上传 x 轴坐标
ABS_MT_POSITION_Y y[0]	# 使用 input_report_abs 上传 y 轴坐标
ABS_MT_SLOT 1
ABS_MT_TRACKING_ID 46
ABS_MT_POSITION_X x[1]
ABS_MT_POSITION_Y y[1]
SYN_REPORT				# 使用 input_sync 实现

五、驱动编写思路

1、搭建 I2C 驱动框架

#include "linux/init.h"
#include "linux/module.h"
#include "linux/printk.h"
#include "linux/i2c.h"
#include "linux/miscdevice.h"
#include "linux/fs.h"
#include "linux/delay.h"
#include "asm-generic/uaccess.h"


typedef struct{
}newchrdev_t;

newchrdev_t newchrdev;



 /*
  * @description     : i2c驱动的probe函数,当驱动与
  *                    设备匹配以后此函数就会执行
  * @param - client  : i2c设备
  * @param - id      : i2c设备ID
  * @return          : 0,成功;其他负值,失败
  */
static int ft5426_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    int ret = 0;
    printk("ft5426_probe!\r\n");
    return ret;
}

/*
 * @description     : i2c驱动的remove函数,移除i2c驱动的时候此函数会执行
 * @param - client 	: i2c设备
 * @return          : 0,成功;其他负值,失败
 */
static int ft5426_remove(struct i2c_client *client)
{
    int ret = 0;
    printk("ft5426_remove!\r\n");
    return ret;
}

/* 传统匹配方式ID列表 */
static const struct i2c_device_id ft5426_id[] = {
	{"focaltech,ft5426", 0},  
	{}
};

/* 设备树匹配列表 */
static const struct of_device_id ft5426_of_match[] = {
	{ .compatible = "focaltech,ft5426" },
	{ /* Sentinel */ }
};

/* i2c驱动结构体 */	
static struct i2c_driver ft5426_driver = {
    .probe = ft5426_probe,
    .remove = ft5426_remove,
    .driver = {
        .owner = THIS_MODULE,
            .name = "ft5426",
            .of_match_table = ft5426_of_match, 
           },
    .id_table = ft5426_id,
};

/*
 * @description	: 驱动入口函数
 * @param 		: 无
 * @return 		: 无
 */
static int __init ft5426_init(void)
{
    int ret = 0;
    ret = i2c_add_driver(&ft5426_driver);
    return ret;
}

/*
 * @description	: 驱动出口函数
 * @param 		: 无
 * @return 		: 无
 */
static void __exit ft5426_exit(void)
{
    i2c_del_driver(&ft5426_driver);
}

/* module_i2c_driver(ft5426_driver) */

module_init(ft5426_init);
module_exit(ft5426_exit);
MODULE_LICENSE("GPL");

2、添加中断处理

#include "linux/init.h"
#include "linux/module.h"
#include "linux/printk.h"
#include "linux/i2c.h"
#include "linux/miscdevice.h"
#include "linux/fs.h"
#include "linux/delay.h"
#include "asm-generic/uaccess.h"
#include "linux/of_gpio.h"
#include "linux/gpio.h"
#include "linux/interrupt.h"


typedef struct{
    struct i2c_client *client;      /* i2c设备 */
    int irq_pin,reset_pin;          /* 中断引脚和复位引脚 */
    int irqnum;                     /* 中断号 */
}newchrdev_t;

newchrdev_t newchrdev;

/*
 * @description     : 复位FT5X06
 * @param - client 	: 要操作的i2c
 * @param - multidev: 自定义的multitouch设备
 * @return          : 0,成功;其他负值,失败
 */
static int ft5x06_reset(struct i2c_client *client, newchrdev_t *dev)
{
	int ret = 0;

	if (gpio_is_valid(dev->reset_pin)) {  		/* 检查IO是否有效 */
		/* 申请复位IO,并且默认输出低电平 */
		ret = devm_gpio_request_one(&client->dev,dev->reset_pin, GPIOF_OUT_INIT_LOW,"ft5x06 reset");
		if (ret) {
			return ret;
		}

		msleep(5);
		gpio_set_value(dev->reset_pin, 1);	/* 输出高电平,停止复位 */
		msleep(300);
	}

	return 0;
}

/*
 * @description     : FT5X06中断服务函数
 * @param - irq 	: 中断号 
 * @param - dev_id	: 设备结构。
 * @return 			: 中断执行结果
 */
static irqreturn_t ft5x06_handler(int irq, void *dev_id)
{
    return 0;
}

/*
 * @description     : FT5x06中断初始化
 * @param - client 	: 要操作的i2c
 * @param - multidev: 自定义的multitouch设备
 * @return          : 0,成功;其他负值,失败
 */
static int ft5x06_ts_irq(struct i2c_client *client, newchrdev_t *dev)
{
	int ret = 0;

	/* 1,申请中断GPIO */
	if (gpio_is_valid(dev->irq_pin)) {
		ret = devm_gpio_request_one(&client->dev, dev->irq_pin,GPIOF_IN, "ft5x06 irq");
		if (ret) {
			dev_err(&client->dev,
				"Failed to request GPIO %d, error %d\n",
				dev->irq_pin, ret);
			return ret;
		}
	}

	/* 2,申请中断,client->irq就是IO中断, */
	ret = devm_request_threaded_irq(&client->dev, client->irq, NULL,
					ft5x06_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
					client->name, &newchrdev);
	if (ret) {
		dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
		return ret;
	}

	return 0;
}

 /*
  * @description     : i2c驱动的probe函数,当驱动与
  *                    设备匹配以后此函数就会执行
  * @param - client  : i2c设备
  * @param - id      : i2c设备ID
  * @return          : 0,成功;其他负值,失败
  */
static int ft5426_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    int ret = 0;
    newchrdev.client = client;

    printk("ft5426_probe!\r\n");
    /* 1、获取设备树中的中断和复位引脚*/
    newchrdev.irq_pin = of_get_named_gpio(client->dev.of_node, "interrupt-gpios", 0);
    newchrdev.reset_pin = of_get_named_gpio(client->dev.of_node, "reset-gpios", 0);
    /* 2、初始化 ft5426 复位引脚 */
    ret = ft5x06_reset(newchrdev.client,&newchrdev);
    if(ret < 0) {
        goto fail;
    }
    /* 3、初始化中断 */
    ft5x06_ts_irq(newchrdev.client,&newchrdev);

    return 0;
fail:
    return ret;
}

/*
 * @description     : i2c驱动的remove函数,移除i2c驱动的时候此函数会执行
 * @param - client 	: i2c设备
 * @return          : 0,成功;其他负值,失败
 */
static int ft5426_remove(struct i2c_client *client)
{
    int ret = 0;
    printk("ft5426_remove!\r\n");
    return ret;
}

/* 传统匹配方式ID列表 */
static const struct i2c_device_id ft5426_id[] = {
	{"focaltech,ft5426", 0},  
	{}
};

/* 设备树匹配列表 */
static const struct of_device_id ft5426_of_match[] = {
	{ .compatible = "focaltech,ft5426" },
	{ /* Sentinel */ }
};

/* i2c驱动结构体 */	
static struct i2c_driver ft5426_driver = {
    .probe = ft5426_probe,
    .remove = ft5426_remove,
    .driver = {
        .owner = THIS_MODULE,
            .name = "ft5426",
            .of_match_table = ft5426_of_match, 
           },
    .id_table = ft5426_id,
};

/*
 * @description	: 驱动入口函数
 * @param 		: 无
 * @return 		: 无
 */
static int __init ft5426_init(void)
{
    int ret = 0;
    ret = i2c_add_driver(&ft5426_driver);
    return ret;
}

/*
 * @description	: 驱动出口函数
 * @param 		: 无
 * @return 		: 无
 */
static void __exit ft5426_exit(void)
{
    i2c_del_driver(&ft5426_driver);
}

/* module_i2c_driver(ft5426_driver) */

module_init(ft5426_init);
module_exit(ft5426_exit);
MODULE_LICENSE("GPL");

3、添加 input 子系统框架

#include "linux/init.h"
#include "linux/module.h"
#include "linux/printk.h"
#include "linux/i2c.h"
#include "linux/miscdevice.h"
#include "linux/fs.h"
#include "linux/delay.h"
#include "asm-generic/uaccess.h"
#include "linux/of_gpio.h"
#include "linux/gpio.h"
#include "linux/interrupt.h"
#include "linux/input.h"
#include "linux/input/mt.h"


#define MAX_SUPPORT_POINTS		5			/* 5点触摸 	*/

typedef struct{
    struct i2c_client *client;      /* i2c设备 */
    int irq_pin,reset_pin;          /* 中断引脚和复位引脚 */
    int irqnum;                     /* 中断号 */
    struct input_dev *input;        /* input 输入结构体 */
}newchrdev_t;

newchrdev_t newchrdev;

/*
 * @description     : 复位FT5X06
 * @param - client 	: 要操作的i2c
 * @param - multidev: 自定义的multitouch设备
 * @return          : 0,成功;其他负值,失败
 */
static int ft5x06_reset(struct i2c_client *client, newchrdev_t *dev)
{
	int ret = 0;

	if (gpio_is_valid(dev->reset_pin)) {  		/* 检查IO是否有效 */
		/* 申请复位IO,并且默认输出低电平 */
		ret = devm_gpio_request_one(&client->dev,dev->reset_pin, GPIOF_OUT_INIT_LOW,"ft5x06 reset");
		if (ret) {
			return ret;
		}

		msleep(5);
		gpio_set_value(dev->reset_pin, 1);	/* 输出高电平,停止复位 */
		msleep(300);
	}

	return 0;
}

/*
 * @description     : FT5X06中断服务函数
 * @param - irq 	: 中断号 
 * @param - dev_id	: 设备结构。
 * @return 			: 中断执行结果
 */
static irqreturn_t ft5x06_handler(int irq, void *dev_id)
{
    return 0;
}

/*
 * @description     : FT5x06中断初始化
 * @param - client 	: 要操作的i2c
 * @param - multidev: 自定义的multitouch设备
 * @return          : 0,成功;其他负值,失败
 */
static int ft5x06_irq(struct i2c_client *client, newchrdev_t *dev)
{
	int ret = 0;

	/* 1,申请中断GPIO */
	if (gpio_is_valid(dev->irq_pin)) {
		ret = devm_gpio_request_one(&client->dev, dev->irq_pin,GPIOF_IN, "ft5x06 irq");
		if (ret) {
			dev_err(&client->dev,
				"Failed to request GPIO %d, error %d\n",
				dev->irq_pin, ret);
			return ret;
		}
	}

	/* 2,申请中断,client->irq就是IO中断, */
	ret = devm_request_threaded_irq(&client->dev, client->irq, NULL,
					ft5x06_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
					client->name, &newchrdev);
	if (ret) {
		dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
		return ret;
	}

	return 0;
}

 /*
  * @description     : i2c驱动的probe函数,当驱动与
  *                    设备匹配以后此函数就会执行
  * @param - client  : i2c设备
  * @param - id      : i2c设备ID
  * @return          : 0,成功;其他负值,失败
  */
static int ft5426_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    int ret = 0;
    newchrdev.client = client;

    printk("ft5426_probe!\r\n");
    /* 1、获取设备树中的中断和复位引脚*/
    newchrdev.irq_pin = of_get_named_gpio(client->dev.of_node, "interrupt-gpios", 0);
    newchrdev.reset_pin = of_get_named_gpio(client->dev.of_node, "reset-gpios", 0);
    /* 2、初始化 ft5426 复位引脚 */
    ret = ft5x06_reset(newchrdev.client,&newchrdev);
    if(ret < 0) {
        goto fail;
    }
    /* 3、初始化中断 */
    ft5x06_irq(newchrdev.client,&newchrdev);
    /* 4、初始化input子系统 */
    newchrdev.input = devm_input_allocate_device(&client->dev);
	if (!newchrdev.input) {
		ret = -ENOMEM;
		goto fail;
	}
	newchrdev.input->name = client->name;
	newchrdev.input->id.bustype = BUS_I2C;
	newchrdev.input->dev.parent = &client->dev;

	__set_bit(EV_KEY, newchrdev.input->evbit);
	__set_bit(EV_ABS, newchrdev.input->evbit);
	__set_bit(BTN_TOUCH, newchrdev.input->keybit);

	input_set_abs_params(newchrdev.input, ABS_X, 0, 1024, 0, 0);
	input_set_abs_params(newchrdev.input, ABS_Y, 0, 600, 0, 0);
	input_set_abs_params(newchrdev.input, ABS_MT_POSITION_X,0, 1024, 0, 0);
	input_set_abs_params(newchrdev.input, ABS_MT_POSITION_Y,0, 600, 0, 0);
	ret = input_mt_init_slots(newchrdev.input, MAX_SUPPORT_POINTS, 0);
	if (ret) {
		goto fail;
	}

	ret = input_register_device(newchrdev.input);
	if (ret)
		goto fail;

    return 0;
fail:
    return ret;
}

/*
 * @description     : i2c驱动的remove函数,移除i2c驱动的时候此函数会执行
 * @param - client 	: i2c设备
 * @return          : 0,成功;其他负值,失败
 */
static int ft5426_remove(struct i2c_client *client)
{
    int ret = 0;
    printk("ft5426_remove!\r\n");
    return ret;
}

/* 传统匹配方式ID列表 */
static const struct i2c_device_id ft5426_id[] = {
	{"focaltech,ft5426", 0},  
	{}
};

/* 设备树匹配列表 */
static const struct of_device_id ft5426_of_match[] = {
	{ .compatible = "focaltech,ft5426" },
	{ /* Sentinel */ }
};

/* i2c驱动结构体 */	
static struct i2c_driver ft5426_driver = {
    .probe = ft5426_probe,
    .remove = ft5426_remove,
    .driver = {
        .owner = THIS_MODULE,
            .name = "ft5426",
            .of_match_table = ft5426_of_match, 
           },
    .id_table = ft5426_id,
};

/*
 * @description	: 驱动入口函数
 * @param 		: 无
 * @return 		: 无
 */
static int __init ft5426_init(void)
{
    int ret = 0;
    ret = i2c_add_driver(&ft5426_driver);
    return ret;
}

/*
 * @description	: 驱动出口函数
 * @param 		: 无
 * @return 		: 无
 */
static void __exit ft5426_exit(void)
{
    i2c_del_driver(&ft5426_driver);
}

/* module_i2c_driver(ft5426_driver) */

module_init(ft5426_init);
module_exit(ft5426_exit);
MODULE_LICENSE("GPL");

4、初始化 FT5426

5、上报触摸点信息

六、添加设备树

1、确定FT5426使用的 I2C 引脚

通信接口使用 I2C2

INTGPIO1_IO09

RSTGPIO5_IO09

2、添加pinctrl子系统相关配置

pinctrl_tsc: tscgrp {
	fsl,pins = <
		/* 7寸RGB屏幕,FT5426 */
		MX6UL_PAD_GPIO1_IO09__GPIO1_IO09	0xF080 		/* TSC_INT */	
	>;
};
pinctrl_tsc_reset: tsc_reset {
	fsl,pins = <
		MX6ULL_PAD_SNVS_TAMPER9__GPIO5_IO09 0x10B0
	>;
};

3、添加FT5426相关配置

&i2c2 {
	clock_frequency = <100000>;
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_i2c2>;
	status = "okay";

	……

	ft5426: ft5426@38 {
		compatible = "focaltech,ft5426";
		reg = <0x38>;
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_tsc
					&pinctrl_tsc_reset >; 
		interrupt-parent = <&gpio1>; 
		interrupts = <9 0>; 
		reset-gpios = <&gpio5 9 GPIO_ACTIVE_LOW>;  
		interrupt-gpios = <&gpio1 9 GPIO_ACTIVE_LOW>;
		status = "okay";
	};
	
	……
};

4、确定引脚是否冲突

5、编译

onlylove@ubuntu:~/my/linux/linux-imx-4.1.15$ make dtbs
  CHK     include/config/kernel.release
  CHK     include/generated/uapi/linux/version.h
  CHK     include/generated/utsrelease.h
make[1]: 'include/generated/mach-types.h' is up to date.
  CHK     include/generated/bounds.h
  CHK     include/generated/asm-offsets.h
  CALL    scripts/checksyscalls.sh
  DTC     arch/arm/boot/dts/imx6ull-alientek-emmc.dtb
  DTC     arch/arm/boot/dts/imx6ull-alientek-nand.dtb
onlylove@ubuntu:~/my/linux/linux-imx-4.1.15$

6、测试

# pwd
/proc/device-tree/soc/aips-bus@02100000
#
# ls
#address-cells       lcdif@021c8000       serial@021f0000
#size-cells          mmdc@021b0000        serial@021f4000
adc@02198000         name                 serial@021fc000
compatible           ocotp-ctrl@021bc000  usb@02184000
csi@021c4000         pxp@021cc000         usb@02184200
csu@021c0000         qspi@021e0000        usbmisc@02184800
ethernet@02188000    ranges               usdhc@02190000
i2c@021a0000         reg                  usdhc@02194000
i2c@021a4000         romcp@021ac000       weim@021b8000
i2c@021a8000         serial@021e8000
i2c@021f8000         serial@021ec000
#
# cd i2c@021a4000
#
# ls
#address-cells   compatible       name             reg
#size-cells      ft5426@38        ov5640@3c        status
clock_frequency  gt9147@14        pinctrl-0        wm8960@1a
clocks           interrupts       pinctrl-names
#
# cd ft5426@38/
#
# ls
compatible        interrupts        pinctrl-names     status
interrupt-gpios   name              reg
interrupt-parent  pinctrl-0         reset-gpios
#
# cat compatible
focaltech,ft5426#
#

七、驱动编写

1、驱动源码

#include "linux/init.h"
#include "linux/module.h"
#include "linux/printk.h"
#include "linux/i2c.h"
#include "linux/miscdevice.h"
#include "linux/fs.h"
#include "linux/delay.h"
#include "asm-generic/uaccess.h"
#include "linux/of_gpio.h"
#include "linux/gpio.h"
#include "linux/interrupt.h"
#include "linux/input.h"
#include "linux/input/mt.h"


#define MAX_SUPPORT_POINTS		5			/* 5点触摸 	*/
#define TOUCH_EVENT_DOWN		0x00		/* 按下 	*/
#define TOUCH_EVENT_UP			0x01		/* 抬起 	*/
#define TOUCH_EVENT_ON			0x02		/* 接触 	*/
#define TOUCH_EVENT_RESERVED	0x03		/* 保留 	*/

/* FT5X06寄存器相关宏定义 */
#define FT5X06_TD_STATUS_REG	0X02		/*	状态寄存器地址 		*/
#define FT5x06_DEVICE_MODE_REG	0X00 		/* 模式寄存器 			*/
#define FT5426_IDG_MODE_REG		0XA4		/* 中断模式				*/
#define FT5X06_READLEN			29			/* 要读取的寄存器个数 	*/

typedef struct{
    struct i2c_client *client;      /* i2c设备 */
    int irq_pin,reset_pin;          /* 中断引脚和复位引脚 */
    int irqnum;                     /* 中断号 */
    struct input_dev *input;        /* input 输入结构体 */
}newchrdev_t;

newchrdev_t newchrdev;

/*
 * @description     : 复位FT5X06
 * @param - client 	: 要操作的i2c
 * @param - multidev: 自定义的multitouch设备
 * @return          : 0,成功;其他负值,失败
 */
static int ft5x06_reset(struct i2c_client *client, newchrdev_t *dev)
{
	int ret = 0;

	if (gpio_is_valid(dev->reset_pin)) {  		/* 检查IO是否有效 */
		/* 申请复位IO,并且默认输出低电平 */
		ret = devm_gpio_request_one(&client->dev,dev->reset_pin, GPIOF_OUT_INIT_LOW,"ft5x06 reset");
		if (ret) {
			return ret;
		}

		msleep(5);
		gpio_set_value(dev->reset_pin, 1);	/* 输出高电平,停止复位 */
		msleep(300);
	}

	return 0;
}

/*
 * @description	: 向ft5x06多个寄存器写入数据
 * @param - dev:  ft5x06设备
 * @param - reg:  要写入的寄存器首地址
 * @param - val:  要写入的数据缓冲区
 * @param - len:  要写入的数据长度
 * @return 	  :   操作结果
 */
static int ft5x06_write_regs(newchrdev_t *dev, unsigned char reg, unsigned char *buf, unsigned char len)
{
	unsigned char b[256];
	struct i2c_msg msg;
	struct i2c_client *client = (struct i2c_client *)dev->client;
	
	b[0] = reg;					/* 寄存器首地址 */
	memcpy(&b[1],buf,len);		/* 将要写入的数据拷贝到数组b里面 */
		
	msg.addr = client->addr;	/* ft5x06地址 */
	msg.flags = 0;				/* 标记为写数据 */

	msg.buf = b;				/* 要写入的数据缓冲区 */
	msg.len = len + 1;			/* 要写入的数据长度 */

	return i2c_transfer(client->adapter, &msg, 1);
}

/*
 * @description	: 向ft5x06指定寄存器写入指定的值,写一个寄存器
 * @param - dev:  ft5x06设备
 * @param - reg:  要写的寄存器
 * @param - data: 要写入的值
 * @return   :    无
 */
static void ft5x06_write_reg(newchrdev_t *dev, unsigned char reg, unsigned char data)
{
	unsigned char buf = 0;
	buf = data;
	ft5x06_write_regs(dev, reg, &buf, 1);
}

/*
 * @description	: 从FT5X06读取多个寄存器数据
 * @param - dev:  ft5x06设备
 * @param - reg:  要读取的寄存器首地址
 * @param - val:  读取到的数据
 * @param - len:  要读取的数据长度
 * @return 		: 操作结果
 */
static int ft5x06_read_regs(newchrdev_t *dev, unsigned char reg, void *val, int len)
{
	int ret;
	struct i2c_msg msg[2];
	struct i2c_client *client = (struct i2c_client *)dev->client;

	/* msg[0]为发送要读取的首地址 */
	msg[0].addr = client->addr;			/* ft5x06地址 */
	msg[0].flags = 0;					/* 标记为发送数据 */
	msg[0].buf = &reg;					/* 读取的首地址 */
	msg[0].len = 1;						/* reg长度*/

	/* msg[1]读取数据 */
	msg[1].addr = client->addr;			/* ft5x06地址 */
	msg[1].flags = I2C_M_RD;			/* 标记为读取数据*/
	msg[1].buf = val;					/* 读取数据缓冲区 */
	msg[1].len = len;					/* 要读取的数据长度*/

	ret = i2c_transfer(client->adapter, msg, 2);
	if(ret == 2) {
		ret = 0;
	} else {
		ret = -EREMOTEIO;
	}
	return ret;
}

/*
 * @description     : FT5X06中断服务函数
 * @param - irq 	: 中断号 
 * @param - dev_id	: 设备结构。
 * @return 			: 中断执行结果
 */
static irqreturn_t ft5x06_handler(int irq, void *dev_id)
{
	newchrdev_t *multidata = dev_id;

	u8 rdbuf[29];
	int i, type, x, y, id;
	int offset, tplen;
	int ret;
	bool down;

	offset = 1; 	/* 偏移1,也就是0X02+1=0x03,从0X03开始是触摸值 */
	tplen = 6;		/* 一个触摸点有6个寄存器来保存触摸值 */

	memset(rdbuf, 0, sizeof(rdbuf));		/* 清除 */

	/* 读取FT5X06触摸点坐标从0X02寄存器开始,连续读取29个寄存器 */
	ret = ft5x06_read_regs(multidata, FT5X06_TD_STATUS_REG, rdbuf, FT5X06_READLEN);
	if (ret) {
		goto fail;
	}

	/* 上报每一个触摸点坐标 */
	for (i = 0; i < MAX_SUPPORT_POINTS; i++) {
		u8 *buf = &rdbuf[i * tplen + offset];

		/* 以第一个触摸点为例,寄存器TOUCH1_XH(地址0X03),各位描述如下:
		 * bit7:6  Event flag  0:按下 1:释放 2:接触 3:没有事件
		 * bit5:4  保留
		 * bit3:0  X轴触摸点的11~8位。
		 */
		type = buf[0] >> 6;     /* 获取触摸类型 */
		if (type == TOUCH_EVENT_RESERVED)
			continue;
 
		/* 我们所使用的触摸屏和FT5X06是反过来的 */
		x = ((buf[2] << 8) | buf[3]) & 0x0fff;
		y = ((buf[0] << 8) | buf[1]) & 0x0fff;
		
		/* 以第一个触摸点为例,寄存器TOUCH1_YH(地址0X05),各位描述如下:
		 * bit7:4  Touch ID  触摸ID,表示是哪个触摸点
		 * bit3:0  Y轴触摸点的11~8位。
		 */
		id = (buf[2] >> 4) & 0x0f;
		down = type != TOUCH_EVENT_UP;

		input_mt_slot(multidata->input, id);
		input_mt_report_slot_state(multidata->input, MT_TOOL_FINGER, down);

		if (!down)
			continue;

		input_report_abs(multidata->input, ABS_MT_POSITION_X, x);
		input_report_abs(multidata->input, ABS_MT_POSITION_Y, y);
	}

	input_mt_report_pointer_emulation(multidata->input, true);
	input_sync(multidata->input);

fail:
	return IRQ_HANDLED;

}

/*
 * @description     : FT5x06中断初始化
 * @param - client 	: 要操作的i2c
 * @param - multidev: 自定义的multitouch设备
 * @return          : 0,成功;其他负值,失败
 */
static int ft5x06_irq(struct i2c_client *client, newchrdev_t *dev)
{
	int ret = 0;

	/* 1,申请中断GPIO */
	if (gpio_is_valid(dev->irq_pin)) {
		ret = devm_gpio_request_one(&client->dev, dev->irq_pin,GPIOF_IN, "ft5x06 irq");
		if (ret) {
			dev_err(&client->dev,
				"Failed to request GPIO %d, error %d\n",
				dev->irq_pin, ret);
			return ret;
		}
	}

	/* 2,申请中断,client->irq就是IO中断, */
	ret = devm_request_threaded_irq(&client->dev, client->irq, NULL,
					ft5x06_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
					client->name, &newchrdev);
	if (ret) {
		dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
		return ret;
	}

	return 0;
}

 /*
  * @description     : i2c驱动的probe函数,当驱动与
  *                    设备匹配以后此函数就会执行
  * @param - client  : i2c设备
  * @param - id      : i2c设备ID
  * @return          : 0,成功;其他负值,失败
  */
static int ft5426_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    int ret = 0;
    newchrdev.client = client;

    printk("ft5426_probe!\r\n");
    /* 1、获取设备树中的中断和复位引脚*/
    newchrdev.irq_pin = of_get_named_gpio(client->dev.of_node, "interrupt-gpios", 0);
    newchrdev.reset_pin = of_get_named_gpio(client->dev.of_node, "reset-gpios", 0);
    /* 2、初始化 ft5426 复位引脚 */
    ret = ft5x06_reset(newchrdev.client,&newchrdev);
    if(ret < 0) {
        goto fail;
    }
    /* 3、初始化中断 */
    ft5x06_irq(newchrdev.client,&newchrdev);
    /* 4、初始化input子系统 */
    newchrdev.input = devm_input_allocate_device(&client->dev);
	if (!newchrdev.input) {
		ret = -ENOMEM;
		goto fail;
	}
	newchrdev.input->name = client->name;
	newchrdev.input->id.bustype = BUS_I2C;
	newchrdev.input->dev.parent = &client->dev;

	__set_bit(EV_KEY, newchrdev.input->evbit);
	__set_bit(EV_ABS, newchrdev.input->evbit);
	__set_bit(BTN_TOUCH, newchrdev.input->keybit);

	input_set_abs_params(newchrdev.input, ABS_X, 0, 1024, 0, 0);
	input_set_abs_params(newchrdev.input, ABS_Y, 0, 600, 0, 0);
	input_set_abs_params(newchrdev.input, ABS_MT_POSITION_X,0, 1024, 0, 0);
	input_set_abs_params(newchrdev.input, ABS_MT_POSITION_Y,0, 600, 0, 0);
	ret = input_mt_init_slots(newchrdev.input, MAX_SUPPORT_POINTS, 0);
	if (ret) {
		goto fail;
	}

	ret = input_register_device(newchrdev.input);
	if (ret)
		goto fail;

    /* 5、初始化 ft5426 */
    ft5x06_write_reg(&newchrdev, FT5x06_DEVICE_MODE_REG, 0); 	/* 进入正常模式 	*/
	ft5x06_write_reg(&newchrdev, FT5426_IDG_MODE_REG, 1); 		/* FT5426中断模式	*/

    return 0;
fail:
    return ret;
}

/*
 * @description     : i2c驱动的remove函数,移除i2c驱动的时候此函数会执行
 * @param - client 	: i2c设备
 * @return          : 0,成功;其他负值,失败
 */
static int ft5426_remove(struct i2c_client *client)
{
    int ret = 0;
    printk("ft5426_remove!\r\n");
    return ret;
}

/* 传统匹配方式ID列表 */
static const struct i2c_device_id ft5426_id[] = {
	{"focaltech,ft5426", 0},  
	{}
};

/* 设备树匹配列表 */
static const struct of_device_id ft5426_of_match[] = {
	{ .compatible = "focaltech,ft5426" },
	{ /* Sentinel */ }
};

/* i2c驱动结构体 */	
static struct i2c_driver ft5426_driver = {
    .probe = ft5426_probe,
    .remove = ft5426_remove,
    .driver = {
        .owner = THIS_MODULE,
            .name = "ft5426",
            .of_match_table = ft5426_of_match, 
           },
    .id_table = ft5426_id,
};

/*
 * @description	: 驱动入口函数
 * @param 		: 无
 * @return 		: 无
 */
static int __init ft5426_init(void)
{
    int ret = 0;
    ret = i2c_add_driver(&ft5426_driver);
    return ret;
}

/*
 * @description	: 驱动出口函数
 * @param 		: 无
 * @return 		: 无
 */
static void __exit ft5426_exit(void)
{
    i2c_del_driver(&ft5426_driver);
}

/* module_i2c_driver(ft5426_driver) */

module_init(ft5426_init);
module_exit(ft5426_exit);
MODULE_LICENSE("GPL");

2、测试

# ls
ft5426.ko
#
# ls -l /dev/input/
total 0
crw-r-----    1 root     root       13,  64 Jan  1 00:00 event0
crw-r-----    1 root     root       13,  65 Jan  1 00:00 event1
crw-r-----    1 root     root       13,  63 Jan  1 00:00 mice
#
# insmod ft5426.ko
ft5426_probe!
input: ft5426 as /devices/platform/soc/2100000.aips-bus/21a4000.i2c/i2c-1/1-0038/input/input2
#
# rmmod ft5426.ko
ft5426_remove!
#
# insmod ft5426.ko
ft5426_probe!
input: ft5426 as /devices/platform/soc/2100000.aips-bus/21a4000.i2c/i2c-1/1-0038/input/input3
#
# ls -l /dev/input/
total 0
crw-r-----    1 root     root       13,  64 Jan  1 00:00 event0
crw-r-----    1 root     root       13,  65 Jan  1 00:00 event1
crw-r-----    1 root     root       13,  66 Jan  1 01:20 event2
crw-r-----    1 root     root       13,  63 Jan  1 00:00 mice
crw-r-----    1 root     root       13,  32 Jan  1 01:20 mouse0
#
# hexdump /dev/input/random: nonblocking pool is initialized
# hexdump /dev/input/event3
hexdump: /dev/input/event3: No such file or directory
# hexdump /dev/input/event2
0000000 130e 0000 165c 0004 0003 0039 0000 0000
0000010 130e 0000 165c 0004 0003 0035 01bd 0000
0000020 130e 0000 165c 0004 0003 0036 0195 0000
0000030 130e 0000 165c 0004 0001 014a 0001 0000
0000040 130e 0000 165c 0004 0003 0000 01bd 0000
0000050 130e 0000 165c 0004 0003 0001 0195 0000
0000060 130e 0000 165c 0004 0000 0000 0000 0000
0000070 130e 0000 de2f 0004 0003 0039 ffff ffff
0000080 130e 0000 de2f 0004 0001 014a 0000 0000
0000090 130e 0000 de2f 0004 0000 0000 0000 0000
00000a0 130f 0000 bb6e 0004 0003 0039 0001 0000
00000b0 130f 0000 bb6e 0004 0003 0035 01be 0000
00000c0 130f 0000 bb6e 0004 0003 0036 019b 0000
00000d0 130f 0000 bb6e 0004 0001 014a 0001 0000
00000e0 130f 0000 bb6e 0004 0003 0000 01be 0000
00000f0 130f 0000 bb6e 0004 0003 0001 019b 0000
0000100 130f 0000 bb6e 0004 0000 0000 0000 0000
0000110 130f 0000 ff0b 0005 0003 0039 ffff ffff
0000120 130f 0000 ff0b 0005 0001 014a 0000 0000
0000130 130f 0000 ff0b 0005 0000 0000 0000 0000
^C
#
# rmmod ft5426.ko
ft5426_remove!
#

八、tslib 移植

九、使用内核自带的ft5x06驱动

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值