1580 lines · 41.0 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* *  Driver for Goodix Touchscreens * *  Copyright (c) 2014 Red Hat Inc. *  Copyright (c) 2015 K. Merker <merker@debian.org> * *  This code is based on gt9xx.c authored by andrew@goodix.com: * *  2010 - 2012 Goodix Technology. */  #include <linux/kernel.h>#include <linux/dmi.h>#include <linux/firmware.h>#include <linux/module.h>#include <linux/delay.h>#include <linux/irq.h>#include <linux/interrupt.h>#include <linux/platform_data/x86/soc.h>#include <linux/slab.h>#include <linux/acpi.h>#include <linux/of.h>#include <linux/unaligned.h>#include "goodix.h" #define GOODIX_GPIO_INT_NAME		"irq"#define GOODIX_GPIO_RST_NAME		"reset" #define GOODIX_MAX_HEIGHT		4096#define GOODIX_MAX_WIDTH		4096#define GOODIX_INT_TRIGGER		1#define GOODIX_CONTACT_SIZE		8#define GOODIX_MAX_CONTACT_SIZE		9#define GOODIX_MAX_CONTACTS		10 #define GOODIX_CONFIG_MIN_LENGTH	186#define GOODIX_CONFIG_911_LENGTH	186#define GOODIX_CONFIG_967_LENGTH	228#define GOODIX_CONFIG_GT9X_LENGTH	240 #define GOODIX_BUFFER_STATUS_READY	BIT(7)#define GOODIX_HAVE_KEY			BIT(4)#define GOODIX_BUFFER_STATUS_TIMEOUT	20 #define RESOLUTION_LOC			1#define MAX_CONTACTS_LOC		5#define TRIGGER_LOC			6 #define GOODIX_POLL_INTERVAL_MS		17	/* 17ms = 60fps */ /* Our special handling for GPIO accesses through ACPI is x86 specific */#if defined CONFIG_X86 && defined CONFIG_ACPI#define ACPI_GPIO_SUPPORT#endif struct goodix_chip_id {	const char *id;	const struct goodix_chip_data *data;}; static int goodix_check_cfg_8(struct goodix_ts_data *ts,			      const u8 *cfg, int len);static int goodix_check_cfg_16(struct goodix_ts_data *ts,			       const u8 *cfg, int len);static void goodix_calc_cfg_checksum_8(struct goodix_ts_data *ts);static void goodix_calc_cfg_checksum_16(struct goodix_ts_data *ts); static const struct goodix_chip_data gt1x_chip_data = {	.config_addr		= GOODIX_GT1X_REG_CONFIG_DATA,	.config_len		= GOODIX_CONFIG_GT9X_LENGTH,	.check_config		= goodix_check_cfg_16,	.calc_config_checksum	= goodix_calc_cfg_checksum_16,}; static const struct goodix_chip_data gt911_chip_data = {	.config_addr		= GOODIX_GT9X_REG_CONFIG_DATA,	.config_len		= GOODIX_CONFIG_911_LENGTH,	.check_config		= goodix_check_cfg_8,	.calc_config_checksum	= goodix_calc_cfg_checksum_8,}; static const struct goodix_chip_data gt967_chip_data = {	.config_addr		= GOODIX_GT9X_REG_CONFIG_DATA,	.config_len		= GOODIX_CONFIG_967_LENGTH,	.check_config		= goodix_check_cfg_8,	.calc_config_checksum	= goodix_calc_cfg_checksum_8,}; static const struct goodix_chip_data gt9x_chip_data = {	.config_addr		= GOODIX_GT9X_REG_CONFIG_DATA,	.config_len		= GOODIX_CONFIG_GT9X_LENGTH,	.check_config		= goodix_check_cfg_8,	.calc_config_checksum	= goodix_calc_cfg_checksum_8,}; static const struct goodix_chip_id goodix_chip_ids[] = {	{ .id = "1151", .data = &gt1x_chip_data },	{ .id = "1158", .data = &gt1x_chip_data },	{ .id = "5663", .data = &gt1x_chip_data },	{ .id = "5688", .data = &gt1x_chip_data },	{ .id = "917S", .data = &gt1x_chip_data },	{ .id = "9286", .data = &gt1x_chip_data }, 	{ .id = "911", .data = &gt911_chip_data },	{ .id = "9271", .data = &gt911_chip_data },	{ .id = "9110", .data = &gt911_chip_data },	{ .id = "9111", .data = &gt911_chip_data },	{ .id = "927", .data = &gt911_chip_data },	{ .id = "928", .data = &gt911_chip_data }, 	{ .id = "912", .data = &gt967_chip_data },	{ .id = "9147", .data = &gt967_chip_data },	{ .id = "967", .data = &gt967_chip_data },	{ }}; static const unsigned long goodix_irq_flags[] = {	IRQ_TYPE_EDGE_RISING,	IRQ_TYPE_EDGE_FALLING,	IRQ_TYPE_LEVEL_LOW,	IRQ_TYPE_LEVEL_HIGH,}; static const struct dmi_system_id nine_bytes_report[] = {#if defined(CONFIG_DMI) && defined(CONFIG_X86)	{		/* Lenovo Yoga Book X90F / X90L */		.matches = {			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "CHERRYVIEW D1 PLATFORM"),			DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "YETI-11"),		}	},	{		/* Lenovo Yoga Book X91F / X91L */		.matches = {			/* Non exact match to match F + L versions */			DMI_MATCH(DMI_PRODUCT_NAME, "Lenovo YB1-X91"),		}	},#endif	{}}; /* * Those tablets have their x coordinate inverted */static const struct dmi_system_id inverted_x_screen[] = {#if defined(CONFIG_DMI) && defined(CONFIG_X86)	{		.ident = "Cube I15-TC",		.matches = {			DMI_MATCH(DMI_SYS_VENDOR, "Cube"),			DMI_MATCH(DMI_PRODUCT_NAME, "I15-TC")		},	},#endif	{}}; /** * goodix_i2c_read - read data from a register of the i2c slave device. * * @client: i2c device. * @reg: the register to read from. * @buf: raw write data buffer. * @len: length of the buffer to write */int goodix_i2c_read(struct i2c_client *client, u16 reg, u8 *buf, int len){	struct i2c_msg msgs[2];	__be16 wbuf = cpu_to_be16(reg);	int ret; 	msgs[0].flags = 0;	msgs[0].addr  = client->addr;	msgs[0].len   = 2;	msgs[0].buf   = (u8 *)&wbuf; 	msgs[1].flags = I2C_M_RD;	msgs[1].addr  = client->addr;	msgs[1].len   = len;	msgs[1].buf   = buf; 	ret = i2c_transfer(client->adapter, msgs, 2);	if (ret >= 0)		ret = (ret == ARRAY_SIZE(msgs) ? 0 : -EIO); 	if (ret)		dev_err(&client->dev, "Error reading %d bytes from 0x%04x: %d\n",			len, reg, ret);	return ret;} /** * goodix_i2c_write - write data to a register of the i2c slave device. * * @client: i2c device. * @reg: the register to write to. * @buf: raw data buffer to write. * @len: length of the buffer to write */int goodix_i2c_write(struct i2c_client *client, u16 reg, const u8 *buf, int len){	u8 *addr_buf;	struct i2c_msg msg;	int ret; 	addr_buf = kmalloc(len + 2, GFP_KERNEL);	if (!addr_buf)		return -ENOMEM; 	addr_buf[0] = reg >> 8;	addr_buf[1] = reg & 0xFF;	memcpy(&addr_buf[2], buf, len); 	msg.flags = 0;	msg.addr = client->addr;	msg.buf = addr_buf;	msg.len = len + 2; 	ret = i2c_transfer(client->adapter, &msg, 1);	if (ret >= 0)		ret = (ret == 1 ? 0 : -EIO); 	kfree(addr_buf); 	if (ret)		dev_err(&client->dev, "Error writing %d bytes to 0x%04x: %d\n",			len, reg, ret);	return ret;} int goodix_i2c_write_u8(struct i2c_client *client, u16 reg, u8 value){	return goodix_i2c_write(client, reg, &value, sizeof(value));} static const struct goodix_chip_data *goodix_get_chip_data(const char *id){	unsigned int i; 	for (i = 0; goodix_chip_ids[i].id; i++) {		if (!strcmp(goodix_chip_ids[i].id, id))			return goodix_chip_ids[i].data;	} 	return &gt9x_chip_data;} static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data){	unsigned long max_timeout;	int touch_num;	int error;	u16 addr = GOODIX_READ_COOR_ADDR;	/*	 * We are going to read 1-byte header,	 * ts->contact_size * max(1, touch_num) bytes of coordinates	 * and 1-byte footer which contains the touch-key code.	 */	const int header_contact_keycode_size = 1 + ts->contact_size + 1; 	/*	 * The 'buffer status' bit, which indicates that the data is valid, is	 * not set as soon as the interrupt is raised, but slightly after.	 * This takes around 10 ms to happen, so we poll for 20 ms.	 */	max_timeout = jiffies + msecs_to_jiffies(GOODIX_BUFFER_STATUS_TIMEOUT);	do {		error = goodix_i2c_read(ts->client, addr, data,					header_contact_keycode_size);		if (error)			return error; 		if (data[0] & GOODIX_BUFFER_STATUS_READY) {			touch_num = data[0] & 0x0f;			if (touch_num > ts->max_touch_num)				return -EPROTO; 			if (touch_num > 1) {				addr += header_contact_keycode_size;				data += header_contact_keycode_size;				error = goodix_i2c_read(ts->client,						addr, data,						ts->contact_size *							(touch_num - 1));				if (error)					return error;			} 			return touch_num;		} 		if (data[0] == 0 && ts->firmware_name) {			if (goodix_handle_fw_request(ts))				return 0;		} 		usleep_range(1000, 2000); /* Poll every 1 - 2 ms */	} while (time_before(jiffies, max_timeout)); 	/*	 * The Goodix panel will send spurious interrupts after a	 * 'finger up' event, which will always cause a timeout.	 */	return -ENOMSG;} static int goodix_create_pen_input(struct goodix_ts_data *ts){	struct device *dev = &ts->client->dev;	struct input_dev *input; 	input = devm_input_allocate_device(dev);	if (!input)		return -ENOMEM; 	input_copy_abs(input, ABS_X, ts->input_dev, ABS_MT_POSITION_X);	input_copy_abs(input, ABS_Y, ts->input_dev, ABS_MT_POSITION_Y);	/*	 * The resolution of these touchscreens is about 10 units/mm, the actual	 * resolution does not matter much since we set INPUT_PROP_DIRECT.	 * Userspace wants something here though, so just set it to 10 units/mm.	 */	input_abs_set_res(input, ABS_X, 10);	input_abs_set_res(input, ABS_Y, 10);	input_set_abs_params(input, ABS_PRESSURE, 0, 255, 0, 0); 	input_set_capability(input, EV_KEY, BTN_TOUCH);	input_set_capability(input, EV_KEY, BTN_TOOL_PEN);	input_set_capability(input, EV_KEY, BTN_STYLUS);	input_set_capability(input, EV_KEY, BTN_STYLUS2);	__set_bit(INPUT_PROP_DIRECT, input->propbit); 	input->name = "Goodix Active Pen";	input->phys = "input/pen";	input->id.bustype = BUS_I2C;	input->id.vendor = 0x0416;	if (kstrtou16(ts->id, 10, &input->id.product))		input->id.product = 0x1001;	input->id.version = ts->version; 	ts->input_pen = input;	return 0;} static void goodix_ts_report_pen_down(struct goodix_ts_data *ts, u8 *data){	int input_x, input_y, input_w, error;	u8 key_value; 	if (!ts->pen_input_registered) {		error = input_register_device(ts->input_pen);		ts->pen_input_registered = (error == 0) ? 1 : error;	} 	if (ts->pen_input_registered < 0)		return; 	if (ts->contact_size == 9) {		input_x = get_unaligned_le16(&data[4]);		input_y = get_unaligned_le16(&data[6]);		input_w = get_unaligned_le16(&data[8]);	} else {		input_x = get_unaligned_le16(&data[2]);		input_y = get_unaligned_le16(&data[4]);		input_w = get_unaligned_le16(&data[6]);	} 	touchscreen_report_pos(ts->input_pen, &ts->prop, input_x, input_y, false);	input_report_abs(ts->input_pen, ABS_PRESSURE, input_w); 	input_report_key(ts->input_pen, BTN_TOUCH, 1);	input_report_key(ts->input_pen, BTN_TOOL_PEN, 1); 	if (data[0] & GOODIX_HAVE_KEY) {		key_value = data[1 + ts->contact_size];		input_report_key(ts->input_pen, BTN_STYLUS, key_value & 0x10);		input_report_key(ts->input_pen, BTN_STYLUS2, key_value & 0x20);	} else {		input_report_key(ts->input_pen, BTN_STYLUS, 0);		input_report_key(ts->input_pen, BTN_STYLUS2, 0);	} 	input_sync(ts->input_pen);} static void goodix_ts_report_pen_up(struct goodix_ts_data *ts){	if (!ts->input_pen)		return; 	input_report_key(ts->input_pen, BTN_TOUCH, 0);	input_report_key(ts->input_pen, BTN_TOOL_PEN, 0);	input_report_key(ts->input_pen, BTN_STYLUS, 0);	input_report_key(ts->input_pen, BTN_STYLUS2, 0); 	input_sync(ts->input_pen);} static void goodix_ts_report_touch_8b(struct goodix_ts_data *ts, u8 *coor_data){	int id = coor_data[0] & 0x0F;	int input_x = get_unaligned_le16(&coor_data[1]);	int input_y = get_unaligned_le16(&coor_data[3]);	int input_w = get_unaligned_le16(&coor_data[5]); 	input_mt_slot(ts->input_dev, id);	input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true);	touchscreen_report_pos(ts->input_dev, &ts->prop,			       input_x, input_y, true);	input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, input_w);	input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, input_w);} static void goodix_ts_report_touch_9b(struct goodix_ts_data *ts, u8 *coor_data){	int id = coor_data[1] & 0x0F;	int input_x = get_unaligned_le16(&coor_data[3]);	int input_y = get_unaligned_le16(&coor_data[5]);	int input_w = get_unaligned_le16(&coor_data[7]); 	input_mt_slot(ts->input_dev, id);	input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true);	touchscreen_report_pos(ts->input_dev, &ts->prop,			       input_x, input_y, true);	input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, input_w);	input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, input_w);} static void goodix_ts_release_keys(struct goodix_ts_data *ts){	int i; 	for (i = 0; i < GOODIX_MAX_KEYS; i++)		input_report_key(ts->input_dev, ts->keymap[i], 0);} static void goodix_ts_report_key(struct goodix_ts_data *ts, u8 *data){	int touch_num;	u8 key_value;	int i; 	if (data[0] & GOODIX_HAVE_KEY) {		touch_num = data[0] & 0x0f;		key_value = data[1 + ts->contact_size * touch_num];		for (i = 0; i < GOODIX_MAX_KEYS; i++)			if (key_value & BIT(i))				input_report_key(ts->input_dev,						 ts->keymap[i], 1);	} else {		goodix_ts_release_keys(ts);	}} /** * goodix_process_events - Process incoming events * * @ts: our goodix_ts_data pointer * * Called when the IRQ is triggered. Read the current device state, and push * the input events to the user space. */static void goodix_process_events(struct goodix_ts_data *ts){	u8  point_data[2 + GOODIX_MAX_CONTACT_SIZE * GOODIX_MAX_CONTACTS];	int touch_num;	int i; 	touch_num = goodix_ts_read_input_report(ts, point_data);	if (touch_num < 0)		return; 	/* The pen being down is always reported as a single touch */	if (touch_num == 1 && (point_data[1] & 0x80)) {		goodix_ts_report_pen_down(ts, point_data);		goodix_ts_release_keys(ts);		goto sync; /* Release any previously registered touches */	} else {		goodix_ts_report_pen_up(ts);	} 	goodix_ts_report_key(ts, point_data); 	for (i = 0; i < touch_num; i++)		if (ts->contact_size == 9)			goodix_ts_report_touch_9b(ts,				&point_data[1 + ts->contact_size * i]);		else			goodix_ts_report_touch_8b(ts,				&point_data[1 + ts->contact_size * i]); sync:	input_mt_sync_frame(ts->input_dev);	input_sync(ts->input_dev);}

Lines 1–500 of 1580.