devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
@ 2016-04-29  9:45 jeffrey.lin
  2016-05-04 22:58 ` Dmitry Torokhov
                   ` (3 more replies)
  0 siblings, 4 replies; 42+ messages in thread
From: jeffrey.lin @ 2016-04-29  9:45 UTC (permalink / raw)
  To: dmitry.torokhov, rydberg, grant.likely, robh+dt, jeesw, bleung
  Cc: jeffrey.lin, roger.yang, KP.li, albert.shieh, linux-kernel,
	linux-input, devicetree

Raydium I2C touch driver.

Signed-off-by: jeffrey.lin <jeffrey.lin@rad-ic.com>
---
 drivers/input/touchscreen/Kconfig          |   12 +
 drivers/input/touchscreen/Makefile         |    1 +
 drivers/input/touchscreen/raydium_i2c_ts.c | 1207 ++++++++++++++++++++++++++++
 3 files changed, 1220 insertions(+)
 create mode 100644 drivers/input/touchscreen/raydium_i2c_ts.c

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 3f3f6ee..df0e2ed 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -915,6 +915,18 @@ config TOUCHSCREEN_PCAP
 	  To compile this driver as a module, choose M here: the
 	  module will be called pcap_ts.
 
+config TOUCHSCREEN_RM_TS
+	tristate "Raydium I2C Touchscreen"
+	depends on I2C
+	help
+	  Say Y here if you have Raydium series I2C touchscreen,
+	  such as RM32380,connected to your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called raydium_i2c_ts.
+
 config TOUCHSCREEN_ST1232
 	tristate "Sitronix ST1232 touchscreen controllers"
 	depends on I2C
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 4941f2d..99e08cf 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE)	+= usbtouchscreen.o
 obj-$(CONFIG_TOUCHSCREEN_PCAP)		+= pcap_ts.o
 obj-$(CONFIG_TOUCHSCREEN_PENMOUNT)	+= penmount.o
 obj-$(CONFIG_TOUCHSCREEN_PIXCIR)	+= pixcir_i2c_ts.o
+obj-$(CONFIG_TOUCHSCREEN_RM_TS)		+= raydium_i2c_ts.o
 obj-$(CONFIG_TOUCHSCREEN_S3C2410)	+= s3c2410_ts.o
 obj-$(CONFIG_TOUCHSCREEN_ST1232)	+= st1232.o
 obj-$(CONFIG_TOUCHSCREEN_STMPE)		+= stmpe-ts.o
diff --git a/drivers/input/touchscreen/raydium_i2c_ts.c b/drivers/input/touchscreen/raydium_i2c_ts.c
new file mode 100644
index 0000000..cee46e8
--- /dev/null
+++ b/drivers/input/touchscreen/raydium_i2c_ts.c
@@ -0,0 +1,1207 @@
+/*
+ * Raydium touchscreen I2C driver.
+ *
+ * Copyright (C) 2012-2014, Raydium Semiconductor Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Raydium reserves the right to make changes without further notice
+ * to the materials described herein. Raydium does not assume any
+ * liability arising out of the application described herein.
+ *
+ * Contact Raydium Semiconductor Corporation at www.rad-ic.com
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/firmware.h>
+#include <linux/input/mt.h>
+#include <linux/acpi.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+#include <asm/unaligned.h>
+#include <linux/gpio/consumer.h>
+
+/* Device, Driver information */
+#define DEVICE_NAME	"raydium_i2c"
+
+/* Slave I2C mode*/
+#define RM_BOOT_BLDR	0x02
+#define RM_BOOT_MAIN	0x03
+
+/*I2C bl command */
+#define CMD_BOOT_PAGE_WRT	0x0B		/*send bl page write*/
+#define CMD_BOOT_WRT		0x11		/*send bl write*/
+#define CMD_BOOT_ACK		0x22		/*send ack*/
+#define CMD_BOOT_CHK		0x33		/*send data check*/
+#define CMD_BOOT_READ		0x44		/*send wait bl data ready*/
+#define BOOT_RDY		0xFF		/*bl data ready*/
+/*I2C main command*/
+#define CMD_QUERY_BANK		0x2B
+#define CMD_DATA_BANK		0x4D
+#define CMD_ENTER_SLEEP		0x4E
+#define CMD_BANK_SWITCH		0xAA
+
+/* Touch relative info */
+#define MAX_RETRIES		3
+#define MAX_TOUCH_NUM		10
+#define MAX_PkG_SIZE		50
+#define BOOT_DELAY_MS	100
+
+/*Bootloader relative info */
+#define CMD_BOOT_HEADER_LEN		3	/*bl flash wrt cmd size*/
+#define RAYDIUM_TRANS_BUFSIZE	32	/*bl wrt pkg size*/
+#define MAX_BOOT_WRT_LEN	(RAYDIUM_TRANS_BUFSIZE + CMD_BOOT_HEADER_LEN)
+#define MAX_FW_UPDATE_RETRIES	30
+
+enum raydium_bl_cmd {
+	HEADER = 0,
+	PAGE_STR,
+	PKG_IDX,
+	DATA_STR,
+};
+
+enum raydium_bl_ack {
+	ACK_NULL = 0,
+	WAIT_READY,
+	PATH_READY,
+};
+
+#define RAYDIUM_PAGE_SIZE		128
+#define RAYDIUM_POWERON_DELAY_USEC	500
+#define RAYDIUM_RESET_DELAY_MSEC	50
+
+#define ADDR_INDEX		0x03
+#define DATA_INDEX		0x04
+
+#define HEADER_SIZE		4
+
+enum raydium_boot_mode {
+	RAYDIUM_TS_MAIN = 0,
+	RAYDIUM_TS_BLDR,
+};
+
+enum raydium_abs_idx {
+	POS_STATE = 0,/*1:touch, 0:no touch*/
+	POS_X,
+	POS_Y = 3,
+	POS_PRESSURE,
+	WIDTH_X,
+	WIDTH_Y,
+};
+
+struct raydium_info {
+	u32 hw_ver;	/*device ver, __le32*/
+	u8 main_ver;
+	u8 sub_ver;
+	u16 ft_ver;	/*test ver, __le16*/
+	u8 x_num;
+	u8 y_num;
+	u16 x_max;	/*disp reso, __le16*/
+	u16 y_max;	/*disp reso, __le16*/
+	u8 x_res;		/* units/mm */
+	u8 y_res;		/* units/mm */
+};
+
+struct raydium_object {
+	u32 data_bank_addr;
+	u8 pkg_size;
+	u8 tp_info_size;
+};
+
+/* struct raydium_data - represents state of Raydium touchscreen device */
+struct raydium_data {
+	struct i2c_client *client;
+	struct input_dev *input;
+
+	struct regulator *avdd;
+	struct regulator *vccio;
+	struct gpio_desc *reset_gpio;
+
+	u32 query_bank_info;
+
+	struct raydium_info info;
+	struct raydium_object obj;
+	enum raydium_boot_mode boot_mode;
+
+	struct mutex sysfs_mutex;
+	struct completion cmd_done;
+
+	bool wake_irq_enabled;
+};
+
+static int raydium_i2c_send(struct i2c_client *client,
+	u8 addr, u8 *data, size_t len)
+{
+	u8 buf[MAX_PkG_SIZE + 1];
+	int tries = 0;
+
+	buf[0] = addr;
+	memcpy(&buf[1], data, len);
+
+	do {
+		if (i2c_master_send(client, buf, len + 1) == (len + 1))
+			return 0;
+		msleep(20);
+	} while (++tries < MAX_RETRIES);
+
+	dev_err(&client->dev, "%s: i2c send failed\n", __func__);
+
+	return -EIO;
+}
+
+static int raydium_i2c_read(struct i2c_client *client,
+	u8 addr, size_t len, void *data)
+{
+	struct i2c_msg xfer[2];
+	int ret;
+
+	/* Write register */
+	xfer[0].addr = client->addr;
+	xfer[0].flags = 0;
+	xfer[0].len = 1;
+	xfer[0].buf = &addr;
+
+	/* Read data */
+	xfer[1].addr = client->addr;
+	xfer[1].flags = I2C_M_RD;
+	xfer[1].len = len;
+	xfer[1].buf = data;
+
+	ret = i2c_transfer(client->adapter, xfer, ARRAY_SIZE(xfer));
+	if (ret < 0)
+		return ret;
+
+	if (ret != ARRAY_SIZE(xfer))
+		return -EIO;
+
+	return 0;
+}
+
+static int raydium_i2c_read_message(struct i2c_client *client,
+	u32 addr, size_t len, void *data)
+{
+	u8 buf[HEADER_SIZE];
+	u8 read_cnt, idx_i;
+	int error;
+	size_t pkg_size;
+
+	if (len % MAX_PkG_SIZE)
+		read_cnt = len / MAX_PkG_SIZE + 1;
+	else
+		read_cnt = len / MAX_PkG_SIZE;
+
+	idx_i = 0;
+	for (idx_i = 0; idx_i < read_cnt; idx_i++) {
+		pkg_size = (len > MAX_PkG_SIZE) ? MAX_PkG_SIZE : len;
+
+		len -= MAX_PkG_SIZE;
+
+		put_unaligned_be32(addr, buf);
+
+		/*set data bank*/
+		error = raydium_i2c_send(client, CMD_BANK_SWITCH,
+			(u8 *)buf, HEADER_SIZE);
+		/*read potints data*/
+		if (!error)
+			error = raydium_i2c_read(client, buf[ADDR_INDEX],
+				pkg_size,
+				data + idx_i*MAX_PkG_SIZE);
+
+		addr += MAX_PkG_SIZE;
+		idx_i++;
+	}
+
+	return error;
+}
+
+static int raydium_i2c_send_message(struct i2c_client *client,
+	size_t len, void *data)
+{
+	int error;
+	__le32 cmd;
+
+	cmd = get_unaligned_le32(data);
+
+	/*set data bank*/
+	error = raydium_i2c_send(client, CMD_BANK_SWITCH, (u8 *)&cmd,
+		HEADER_SIZE);
+
+	/*send message*/
+	if (!error)
+		error = raydium_i2c_send(client, ((u8 *)data)[ADDR_INDEX],
+			data + DATA_INDEX, len);
+
+	return error;
+}
+
+static int raydium_i2c_sw_reset(struct i2c_client *client)
+{
+	static const u8 soft_rst_cmd[] = {0x40, 0x00, 0x00, 0x04, 0x01};
+	int error;
+
+	error = raydium_i2c_send_message(client, ARRAY_SIZE(soft_rst_cmd),
+		(void *)soft_rst_cmd);
+	if (error) {
+		dev_err(&client->dev, "software reset failed: %d\n", error);
+		return error;
+	}
+
+	msleep(RAYDIUM_RESET_DELAY_MSEC);
+
+	return 0;
+}
+
+static int raydium_i2c_query_ts_info(struct raydium_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	int error, retry_cnt;
+
+	for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
+		error = raydium_i2c_read(client, CMD_DATA_BANK,
+			sizeof(ts->obj), (void *)&ts->obj);
+			ts->obj.data_bank_addr =
+				get_unaligned_le32(&ts->obj.data_bank_addr);
+
+		if (!error) {
+			error = raydium_i2c_read(client, CMD_QUERY_BANK,
+				sizeof(ts->query_bank_info),
+				(void *)&ts->query_bank_info);
+			if (!error) {
+				error = raydium_i2c_read_message(client,
+					ts->query_bank_info, sizeof(ts->info),
+					(void *)&ts->info);
+
+				ts->info.hw_ver =
+					get_unaligned_le32(&ts->info.hw_ver);
+				ts->info.ft_ver =
+					get_unaligned_le16(&ts->info.ft_ver);
+				ts->info.x_max =
+					get_unaligned_le16(&ts->info.x_max);
+				ts->info.y_max =
+					get_unaligned_le16(&ts->info.y_max);
+				return 0;
+			}
+		}
+	}
+	dev_err(&client->dev, "Get touch data failed: %d\n", error);
+
+	return -EINVAL;
+}
+
+static int raydium_i2c_fastboot(struct i2c_client *client)
+{
+	static const u8 boot_cmd[] = { 0x50, 0x00, 0x06, 0x20 };
+	u8 buf[HEADER_SIZE];
+	int error;
+
+	error = raydium_i2c_read_message(client,
+		get_unaligned_be32(boot_cmd),
+		sizeof(boot_cmd), buf);
+
+	if (!error) {
+		if (buf[0] == RM_BOOT_BLDR) {
+			dev_dbg(&client->dev, "boot in fastboot mode\n");
+			return -EINVAL;
+		}
+		dev_dbg(&client->dev, "boot success -- 0x%x\n", client->addr);
+		return 0;
+	}
+
+	dev_err(&client->dev, "boot failed: %d\n", error);
+
+	return error;
+}
+
+static int raydium_i2c_check_fw_status(struct raydium_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	static const u8 bl_area[] = {0x62, 0x6f, 0x6f, 0x74};
+	static const u8 main_area[] = {0x66, 0x69, 0x72, 0x6d};
+	u8 buf[HEADER_SIZE];
+	int error;
+
+	error = raydium_i2c_read(client, CMD_BOOT_READ, HEADER_SIZE,
+		(void *)buf);
+	if (!error) {
+		if (buf[0] == bl_area[0])
+			ts->boot_mode = RAYDIUM_TS_BLDR;
+		else if (buf[0] == main_area[0])
+			ts->boot_mode = RAYDIUM_TS_MAIN;
+		return 0;
+	}
+
+	return error;
+}
+
+static int raydium_i2c_initialize(struct raydium_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	int error, retry_cnt;
+
+	for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
+		error = raydium_i2c_fastboot(client);
+		if (error) {
+			/* Continue initializing if it's the last try */
+			if (retry_cnt < MAX_RETRIES - 1)
+				continue;
+		}
+		/* Wait for Hello packet */
+		msleep(BOOT_DELAY_MS);
+
+		error = raydium_i2c_check_fw_status(ts);
+		if (ts->boot_mode == RAYDIUM_TS_BLDR ||
+			ts->boot_mode == RAYDIUM_TS_MAIN)
+			break;
+		else if (error) {
+			dev_err(&client->dev,
+				"failed to read 'hello' packet: %d\n", error);
+		}
+	}
+
+	if (error)
+		ts->boot_mode = RAYDIUM_TS_BLDR;
+
+	if (ts->boot_mode == RAYDIUM_TS_BLDR) {
+		ts->info.hw_ver = 0xffffffff;
+		ts->info.main_ver = 0xff;
+		ts->info.sub_ver = 0xff;
+	} else {
+		raydium_i2c_query_ts_info(ts);
+	}
+
+	return error;
+}
+
+static int raydium_i2c_bl_chk_state(struct i2c_client *client,
+		enum raydium_bl_ack state)
+{
+	static const u8 ack_ok[] = { 0xFF, 0x39, 0x30, 0x30, 0x54 };
+	u8 rbuf[5];
+	u8 retry;
+	int error;
+
+	if (state == ACK_NULL)
+		return 0;
+
+	for (retry = 0; retry < MAX_FW_UPDATE_RETRIES; retry++) {
+		if (state == WAIT_READY) {
+			error = raydium_i2c_read(client, CMD_BOOT_CHK,
+				1, &rbuf[0]);
+			if (!error) {
+				if (rbuf[0] == BOOT_RDY)
+					return 0;
+			}
+		} else if (state == PATH_READY) {
+			error = raydium_i2c_read(client, CMD_BOOT_CHK,
+				sizeof(ack_ok), &rbuf[0]);
+			if (!error) {
+				if (!memcmp(rbuf, ack_ok, sizeof(ack_ok)))
+					return 0;
+			}
+		} else
+			return -EINVAL;
+		msleep(20);
+	}
+
+	return -EINVAL;
+}
+
+static int raydium_i2c_wrt_object(struct i2c_client *client,
+	u8 *data, size_t len, enum raydium_bl_ack state)
+{
+	int error = 0;
+
+	error = raydium_i2c_send(client, CMD_BOOT_WRT, data, len);
+	if (error) {
+		dev_err(&client->dev, "WRT obj command failed: %d\n",
+			error);
+		return error;
+	}
+
+	error = raydium_i2c_send(client, CMD_BOOT_ACK, (u8 *)NULL, 0);
+	if (error) {
+		dev_err(&client->dev, "Ack obj command failed: %d\n", error);
+		return error;
+	}
+
+	error = raydium_i2c_bl_chk_state(client, state);
+	if (error) {
+		dev_err(&client->dev, "boot trigger state failed: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+static bool raydium_i2c_boot_trigger(struct i2c_client *client)
+{
+	int error;
+	u8 u8_idx;
+	static const u8 cmd[7][6] = {
+			{0x08, 0x0C, 0x09, 0x00, 0x50, 0xD7},
+			{0x08, 0x04, 0x09, 0x00, 0x50, 0xA5},
+			{0x08, 0x04, 0x09, 0x00, 0x50, 0x00},
+			{0x08, 0x04, 0x09, 0x00, 0x50, 0xA5},
+			{0x08, 0x0C, 0x09, 0x00, 0x50, 0x00},
+			{0x06, 0x01, 0x00, 0x00, 0x00, 0x00},
+			{0x02, 0xA2, 0x00, 0x00, 0x00, 0x00},
+		};
+
+	/*sequtial cmd*/
+	for (u8_idx = 0 ; u8_idx < 7 ; u8_idx++) {
+		error = raydium_i2c_wrt_object(client, (u8 *)cmd[u8_idx],
+				sizeof(cmd[u8_idx]), WAIT_READY);
+		if (error) {
+			dev_err(&client->dev, "send boot trigger 1st_cmd failed: %d\n",
+				error);
+			return error;
+		}
+	}
+	return 0;
+}
+
+static bool raydium_i2c_fw_trigger(struct i2c_client *client)
+{
+	int error;
+	u8 u8_idx;
+	static const u8 cmd[5][11] = {
+			{0, 0x09, 0x71, 0x0C, 0x09, 0x00, 0x50, 0xD7, 0, 0, 0},
+			{0, 0x09, 0x71, 0x04, 0x09, 0x00, 0x50, 0xA5, 0, 0, 0},
+			{0, 0x09, 0x71, 0x04, 0x09, 0x00, 0x50, 0x00, 0, 0, 0},
+			{0, 0x09, 0x71, 0x04, 0x09, 0x00, 0x50, 0xA5, 0, 0, 0},
+			{0, 0x09, 0x71, 0x0C, 0x09, 0x00, 0x50, 0x00, 0, 0, 0},
+		};
+
+	/*sequtial cmd*/
+	for (u8_idx = 0 ; u8_idx < 5 ; u8_idx++) {
+		error = raydium_i2c_wrt_object(client, (u8 *)cmd[u8_idx],
+				sizeof(cmd[u8_idx]), ACK_NULL);
+		if (error) {
+			dev_err(&client->dev, "send fw trigger 1st_cmd failed: %d\n",
+				error);
+			return error;
+		}
+	}
+	return 0;
+}
+
+static int raydium_i2c_check_path(struct i2c_client *client)
+{
+	static const u8 cmd[] = {0x09, 0x00, 0x09, 0x00, 0x50, 0x10, 0x00};
+	int error;
+
+	error = raydium_i2c_wrt_object(client, (u8 *)cmd, sizeof(cmd),
+			PATH_READY);
+	if (error) {
+		dev_err(&client->dev, "send chk path cmd fail: %d\n", error);
+		return error;
+	}
+
+	return error;
+}
+
+static int raydium_i2c_enter_bl(struct i2c_client *client)
+{
+	static const u8 cal_cmd[] = {0x00, 0x01, 0x52};
+	int error;
+
+	error = raydium_i2c_wrt_object(client, (u8 *)cal_cmd,
+			sizeof(cal_cmd), ACK_NULL);
+	if (error) {
+		dev_err(&client->dev, "send jump loader cmd fail: %d\n", error);
+		return error;
+	}
+	msleep(BOOT_DELAY_MS);
+	return 0;
+}
+
+static int raydium_i2c_leave_bl(struct i2c_client *client)
+{
+	static const u8 leave_cmd[] = {0x05, 0x00};
+	int error;
+
+	error = raydium_i2c_wrt_object(client, (u8 *)leave_cmd,
+			sizeof(leave_cmd), ACK_NULL);
+	if (error) {
+		dev_err(&client->dev, "send leave bl cmd fail: %d\n", error);
+		return error;
+	}
+	msleep(BOOT_DELAY_MS);
+	return 0;
+}
+
+static int raydium_i2c_write_checksum(struct i2c_client *client,
+	size_t length, u16 checksum)
+{
+	u8 checksum_cmd[] = {0x00, 0x05, 0x6D, 0x00, 0x00, 0x00, 0x00};
+	int error = 0;
+
+	checksum_cmd[3] = (u8)(length & 0xFF);
+	checksum_cmd[4] = (u8)((length & 0xFF00) >> 8);
+	checksum_cmd[5] = (u8)(checksum & 0xFF);
+	checksum_cmd[6] = (u8)((checksum & 0xFF00) >> 8);
+
+	error = raydium_i2c_wrt_object(client, (u8 *)checksum_cmd,
+			sizeof(checksum_cmd), ACK_NULL);
+	if (error) {
+		dev_err(&client->dev, "send wrt checksum cmd fail: %d\n",
+			error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int raydium_i2c_disable_watch_dog(struct i2c_client *client)
+{
+	static const u8 cmd[] = {0x0A, 0xAA};
+	int error = 0;
+
+	error = raydium_i2c_wrt_object(client, (u8 *)cmd, sizeof(cmd),
+			WAIT_READY);
+	if (error) {
+		dev_err(&client->dev, "send disable watchdog cmd fail: %d\n",
+			error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int raydium_i2c_fw_write_page(struct i2c_client *client,
+				u8 *page, size_t len)
+{
+	int error;
+	u8 buf[MAX_BOOT_WRT_LEN];
+	u8 u8_idx, div_cnt;
+
+	len -= CMD_BOOT_HEADER_LEN;
+
+	div_cnt = len % RAYDIUM_TRANS_BUFSIZE ?
+		len / RAYDIUM_TRANS_BUFSIZE + 1:len / RAYDIUM_TRANS_BUFSIZE;
+
+	for (u8_idx = 0 ; u8_idx < div_cnt ; u8_idx++) {
+		buf[HEADER] = page[0];
+		buf[PAGE_STR] = page[1];
+		buf[PKG_IDX] = u8_idx + 1;
+
+		memcpy(&buf[DATA_STR], page + DATA_STR +
+			u8_idx*RAYDIUM_TRANS_BUFSIZE,
+			RAYDIUM_TRANS_BUFSIZE);
+
+		error = raydium_i2c_wrt_object(client, (u8 *)buf, sizeof(buf),
+				WAIT_READY);
+		if (error) {
+			dev_err(&client->dev, "send page wrt cmd failed: %d\n",
+				error);
+			return error;
+		}
+		msleep(20);
+	}
+
+	return error;
+}
+
+static int raydium_i2c_do_update_firmware(struct raydium_data *ts,
+					 const struct firmware *fw)
+{
+	struct i2c_client *client = ts->client;
+	u8 u8_idx;
+	u16 fw_checksum;
+	u8 buf[RAYDIUM_PAGE_SIZE + CMD_BOOT_HEADER_LEN];
+	int page, n_fw_pages;
+	int error, fw_idx;
+
+	if (fw->size == 0) {
+		dev_err(&client->dev, "Invalid firmware length\n");
+		return -EINVAL;
+	}
+
+	if (fw->size % RAYDIUM_PAGE_SIZE)
+		n_fw_pages = fw->size/RAYDIUM_PAGE_SIZE + 1;
+	else
+		n_fw_pages = fw->size/RAYDIUM_PAGE_SIZE;
+
+	error = raydium_i2c_check_fw_status(ts);
+	if (error) {
+		dev_err(&client->dev, "Unable to access IC %d\n", error);
+			return error;
+	}
+
+	if (ts->boot_mode == RAYDIUM_TS_MAIN) {
+		for (u8_idx = 0; u8_idx < MAX_RETRIES; u8_idx++) {
+			error = raydium_i2c_enter_bl(client);
+			if (!error) {
+				error = raydium_i2c_check_fw_status(ts);
+				if (error) {
+					dev_err(&client->dev, "Unable to access IC %d\n",
+						error);
+					return error;
+				}
+				if (ts->boot_mode == RAYDIUM_TS_BLDR)
+					break;
+			}
+		}
+		if (ts->boot_mode == RAYDIUM_TS_MAIN) {
+			dev_err(&client->dev, "Fail jump to boot loader %d\n",
+					error);
+			return -EIO;
+		}
+	}
+
+	error = raydium_i2c_disable_watch_dog(client);
+	if (error) {
+		dev_err(&client->dev, "send disable watchdog cmd fail, %d\n",
+			error);
+		return error;
+	}
+
+	error = raydium_i2c_check_path(client);
+	if (error) {
+		dev_err(&client->dev, "send chk path fail, %d\n", error);
+		return error;
+	}
+
+	error = raydium_i2c_boot_trigger(client);
+	if (error) {
+		dev_err(&client->dev, "send boot trigger fail, %d\n", error);
+		return error;
+	}
+
+	fw_checksum = 0;
+	fw_idx = 0;
+	for (page = 0 ; page < n_fw_pages ; page++) {
+		memset(buf, 0xFF, sizeof(buf));
+		buf[HEADER] = 0x0B;
+		if (page == 0)
+			buf[PAGE_STR] = 0x00;
+
+		for (u8_idx = 0; u8_idx < RAYDIUM_PAGE_SIZE; u8_idx++) {
+			if (fw_idx < fw->size) {
+				buf[DATA_STR + u8_idx] =
+					fw->data[page*RAYDIUM_PAGE_SIZE +
+					u8_idx];
+				fw_checksum += buf[3 + u8_idx];
+				fw_idx++;
+			}
+		}
+
+		raydium_i2c_fw_write_page(client, (u8 *)buf, sizeof(buf));
+		msleep(20);
+	}
+
+	error = raydium_i2c_leave_bl(client);
+	if (error) {
+		dev_err(&client->dev, "leave boot loader fail: %d\n", error);
+		return error;
+	}
+	dev_err(&client->dev, "leave boot loader success\n");
+
+	error = raydium_i2c_check_fw_status(ts);
+	if (error) {
+		dev_err(&client->dev, "Unable to access IC %d\n", error);
+		return error;
+	}
+
+	if (ts->boot_mode == RAYDIUM_TS_MAIN) {
+
+		error = raydium_i2c_fw_trigger(client);
+		if (error) {
+			dev_err(&client->dev, "send fw trigger fail, %d\n",
+				error);
+			return error;
+		}
+
+		error = raydium_i2c_write_checksum(client, fw->size,
+			fw_checksum);
+		if (error) {
+			dev_err(&client->dev, "write checksum fail %d\n",
+				error);
+			return error;
+		}
+	} else {
+		dev_err(&client->dev, "switch to main_fw fail %d\n", error);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int raydium_i2c_fw_update(struct raydium_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	const struct firmware *fw = NULL;
+	const char *fw_file = "raydium.fw";
+	int error;
+
+	error = request_firmware(&fw, fw_file, &client->dev);
+	if (error) {
+		dev_err(&client->dev, "Unable to open firmware %s\n", fw_file);
+		return error;
+	}
+	/*disable irq*/
+	disable_irq(client->irq);
+
+	error = raydium_i2c_do_update_firmware(ts, fw);
+	if (error) {
+		dev_err(&client->dev, "firmware update failed: %d\n", error);
+		ts->boot_mode = RAYDIUM_TS_BLDR;
+		goto out_enable_irq;
+	}
+
+	error = raydium_i2c_initialize(ts);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to initialize device after firmware update: %d\n",
+			error);
+		ts->boot_mode = RAYDIUM_TS_BLDR;
+		goto out_enable_irq;
+	}
+
+	ts->boot_mode = RAYDIUM_TS_MAIN;
+
+out_enable_irq:
+	enable_irq(client->irq);
+	msleep(100);
+
+	return error;
+}
+
+static void raydium_mt_event(struct raydium_data *ts)
+{
+	u8 data[MAX_PkG_SIZE];
+	int error, i;
+	int x, y, f_state, pressure, wx, wy;
+
+	error = raydium_i2c_read_message(ts->client, ts->obj.data_bank_addr,
+		ts->obj.pkg_size, (void *)data);
+
+	if (error < 0) {
+		dev_err(&ts->client->dev, "%s: failed to read data: %d\n",
+			__func__, error);
+		return;
+	}
+
+	for (i = 0; i < MAX_TOUCH_NUM; i++) {
+		f_state = (data + i * ts->obj.tp_info_size)[POS_STATE];
+		pressure = (data + i * ts->obj.tp_info_size)[POS_PRESSURE];
+		wx = (data + i * ts->obj.tp_info_size)[WIDTH_X];
+		wy = (data + i * ts->obj.tp_info_size)[WIDTH_Y];
+
+		input_mt_slot(ts->input, i);
+		input_mt_report_slot_state(ts->input,
+				MT_TOOL_FINGER, f_state != 0);
+
+		if (!f_state)
+			continue;
+		x = get_unaligned_le16(&data[i * ts->obj.tp_info_size + POS_X]);
+		y = get_unaligned_le16(&data[i * ts->obj.tp_info_size + POS_Y]);
+
+		input_report_abs(ts->input, ABS_MT_POSITION_X, x);
+		input_report_abs(ts->input, ABS_MT_POSITION_Y, y);
+		input_report_abs(ts->input, ABS_MT_PRESSURE, pressure);
+		input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR,
+			max(wx, wy));
+		input_report_abs(ts->input, ABS_MT_TOUCH_MINOR,
+			min(wx, wy));
+	}
+
+	input_mt_sync_frame(ts->input);
+	input_sync(ts->input);
+}
+
+static irqreturn_t raydium_i2c_irq(int irq, void *_dev)
+{
+	struct raydium_data *ts = _dev;
+
+	if (ts->boot_mode != RAYDIUM_TS_BLDR)
+		raydium_mt_event(ts);
+
+	return IRQ_HANDLED;
+}
+
+static ssize_t raydium_calibrate(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t count)
+{
+	struct raydium_data *ts = dev_get_drvdata(dev);
+	struct i2c_client *client = ts->client;
+
+	static const u8 cal_cmd[] = {0x00, 0x01, 0x9E};
+	int error = 0;
+
+	error = raydium_i2c_wrt_object(client, (u8 *)cal_cmd,
+			sizeof(cal_cmd), WAIT_READY);
+	if (error) {
+		dev_err(&client->dev, "send chk path cmd fail: %d\n", error);
+		return error;
+	}
+
+	return error;
+}
+
+static ssize_t write_update_fw(struct device *dev,
+			struct device_attribute *attr,
+			const char *buf, size_t count)
+{
+	struct raydium_data *ts = dev_get_drvdata(dev);
+	int error;
+
+	error = mutex_lock_interruptible(&ts->sysfs_mutex);
+	if (error)
+		return error;
+
+	error = raydium_i2c_fw_update(ts);
+	dev_dbg(dev, "firmware update result: %d\n", error);
+
+	mutex_unlock(&ts->sysfs_mutex);
+	return error ?: count;
+}
+
+static ssize_t raydium_bootmode_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct raydium_data *ts = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%s\n", ts->boot_mode == RAYDIUM_TS_MAIN ?
+		"Normal" : "Recovery");
+}
+
+static ssize_t raydium_fw_ver_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct raydium_data *ts = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%d.%d\n", ts->info.main_ver, ts->info.sub_ver);
+}
+
+static ssize_t raydium_hw_ver_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct raydium_data *ts = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%04x\n", ts->info.hw_ver);
+}
+
+static DEVICE_ATTR(fw_version, S_IRUGO, raydium_fw_ver_show, NULL);
+static DEVICE_ATTR(hw_version, S_IRUGO, raydium_hw_ver_show, NULL);
+static DEVICE_ATTR(boot_mode, S_IRUGO, raydium_bootmode_show, NULL);
+static DEVICE_ATTR(update_fw, S_IWUSR, NULL, write_update_fw);
+static DEVICE_ATTR(calibrate, S_IWUSR, NULL, raydium_calibrate);
+
+static struct attribute *raydium_attributes[] = {
+	&dev_attr_update_fw.attr,
+	&dev_attr_boot_mode.attr,
+	&dev_attr_fw_version.attr,
+	&dev_attr_hw_version.attr,
+	&dev_attr_calibrate.attr,
+	NULL
+};
+
+static struct attribute_group raydium_attribute_group = {
+	.attrs = raydium_attributes,
+};
+
+static void raydium_i2c_remove_sysfs_group(void *_data)
+{
+	struct raydium_data *ts = _data;
+
+	sysfs_remove_group(&ts->client->dev.kobj, &raydium_attribute_group);
+}
+
+static int raydium_i2c_power_on(struct raydium_data *ts)
+{
+	int error;
+
+	if (IS_ERR_OR_NULL(ts->reset_gpio))
+		return 0;
+
+	gpiod_set_value_cansleep(ts->reset_gpio, 1);
+
+	error = regulator_enable(ts->avdd);
+	if (error) {
+		dev_err(&ts->client->dev,
+			"failed to enable avdd regulator: %d\n", error);
+		goto release_reset_gpio;
+	}
+
+	error = regulator_enable(ts->vccio);
+	if (error) {
+		regulator_disable(ts->avdd);
+		dev_err(&ts->client->dev,
+			"failed to enable vccio regulator: %d\n", error);
+		goto release_reset_gpio;
+	}
+
+	udelay(RAYDIUM_POWERON_DELAY_USEC);
+
+release_reset_gpio:
+	gpiod_set_value_cansleep(ts->reset_gpio, 0);
+
+	if (error)
+		return error;
+
+	msleep(RAYDIUM_RESET_DELAY_MSEC);
+
+	return 0;
+}
+
+static void raydium_i2c_power_off(void *_data)
+{
+	struct raydium_data *ts = _data;
+
+	if (!IS_ERR_OR_NULL(ts->reset_gpio)) {
+		gpiod_set_value_cansleep(ts->reset_gpio, 1);
+		regulator_disable(ts->vccio);
+		regulator_disable(ts->avdd);
+	}
+}
+
+static int raydium_i2c_probe(struct i2c_client *client,
+			    const struct i2c_device_id *id)
+{
+	union i2c_smbus_data dummy;
+	struct raydium_data *ts;
+	int error;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		dev_err(&client->dev,
+			"%s: i2c check functionality error\n", DEVICE_NAME);
+		return -ENXIO;
+	}
+
+	ts = devm_kzalloc(&client->dev, sizeof(struct raydium_data),
+		GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+
+	mutex_init(&ts->sysfs_mutex);
+	init_completion(&ts->cmd_done);
+
+	ts->client = client;
+	i2c_set_clientdata(client, ts);
+
+	ts->avdd = devm_regulator_get(&client->dev, "avdd");
+	if (IS_ERR(ts->avdd)) {
+		error = PTR_ERR(ts->avdd);
+		if (error != -EPROBE_DEFER)
+			dev_err(&client->dev,
+				"Failed to get 'avdd' regulator: %d\n", error);
+		return error;
+	}
+
+	ts->vccio = devm_regulator_get(&client->dev, "vccio");
+	if (IS_ERR(ts->vccio)) {
+		error = PTR_ERR(ts->vccio);
+		if (error != -EPROBE_DEFER)
+			dev_err(&client->dev,
+				"Failed to get 'vccio' regulator: %d\n", error);
+		return error;
+	}
+
+	ts->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
+		GPIOD_OUT_LOW);
+	if (IS_ERR(ts->reset_gpio)) {
+		error = PTR_ERR(ts->reset_gpio);
+		if (error != -EPROBE_DEFER) {
+			dev_err(&client->dev,
+				"failed to get reset gpio: %d\n", error);
+			return error;
+		}
+	}
+
+	error = raydium_i2c_power_on(ts);
+	if (error)
+		return error;
+
+	error = devm_add_action(&client->dev, raydium_i2c_power_off, ts);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to install power off action: %d\n", error);
+		raydium_i2c_power_off(ts);
+		return error;
+	}
+
+	/* Make sure there is something at this address */
+	if (i2c_smbus_xfer(client->adapter, client->addr, 0,
+			I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0) {
+		dev_err(&client->dev, "nothing at this address\n");
+		return -ENXIO;
+	}
+
+	error = raydium_i2c_initialize(ts);
+	if (error) {
+		dev_err(&client->dev, "failed to initialize: %d\n", error);
+		return error;
+	}
+
+	ts->input = devm_input_allocate_device(&client->dev);
+	if (!ts->input) {
+		dev_err(&client->dev, "Failed to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	ts->input->name = "Raydium Touchscreen";
+	ts->input->id.bustype = BUS_I2C;
+
+	/* Multitouch input params setup */
+	input_set_abs_params(ts->input, ABS_MT_POSITION_X, 0,
+		ts->info.x_max, 0, 0);
+	input_set_abs_params(ts->input, ABS_MT_POSITION_Y,
+		0, ts->info.y_max, 0, 0);
+	input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+	input_set_abs_params(ts->input, ABS_MT_PRESSURE, 0, 255, 0, 0);
+	input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->info.x_res);
+	input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->info.y_res);
+
+	input_set_drvdata(ts->input, ts);
+
+	error = input_mt_init_slots(ts->input, MAX_TOUCH_NUM,
+		INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to initialize MT slots: %d\n", error);
+		return error;
+	}
+
+	error = input_register_device(ts->input);
+	if (error) {
+		dev_err(&client->dev,
+			"unable to register input device: %d\n", error);
+		return error;
+	}
+
+	error = devm_request_threaded_irq(&client->dev, client->irq, NULL,
+		raydium_i2c_irq, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+			client->name, ts);
+	if (error) {
+		dev_err(&client->dev, "Failed to register interrupt\n");
+		return error;
+	}
+
+	error = sysfs_create_group(&client->dev.kobj, &raydium_attribute_group);
+	if (error) {
+		dev_err(&client->dev, "failed to create sysfs attributes: %d\n",
+			error);
+		return error;
+	}
+
+	error = devm_add_action(&client->dev,
+				raydium_i2c_remove_sysfs_group, ts);
+	if (error) {
+		raydium_i2c_remove_sysfs_group(ts);
+		dev_err(&client->dev,
+			"Failed to add sysfs cleanup action: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+static void __maybe_unused raydium_enter_sleep(struct i2c_client *client)
+{
+	static const u8 sleep_cmd[] = { 0x5A, 0xff, 0x00, 0x0f };
+	int error;
+
+	error = raydium_i2c_send(client, CMD_ENTER_SLEEP, (u8 *)sleep_cmd,
+		sizeof(sleep_cmd));
+	if (error)
+		dev_err(&client->dev,
+			"Send sleep failed: %d\n", error);
+}
+
+static int __maybe_unused raydium_i2c_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	/* Command not support in BLDR recovery mode */
+	if (ts->boot_mode != RAYDIUM_TS_MAIN)
+		return -ENOMEM;
+
+	disable_irq(client->irq);
+
+	if (device_may_wakeup(dev)) {
+		raydium_enter_sleep(client);
+
+		ts->wake_irq_enabled = (enable_irq_wake(client->irq) == 0);
+	} else {
+		raydium_i2c_power_off(ts);
+	}
+
+	return 0;
+}
+
+static int __maybe_unused raydium_i2c_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	if (device_may_wakeup(dev)) {
+		if (ts->wake_irq_enabled)
+			disable_irq_wake(client->irq);
+		raydium_i2c_sw_reset(client);
+	} else {
+		raydium_i2c_power_on(ts);
+		raydium_i2c_initialize(ts);
+	}
+
+	enable_irq(client->irq);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(raydium_i2c_pm_ops,
+			 raydium_i2c_suspend, raydium_i2c_resume);
+
+static const struct i2c_device_id raydium_i2c_id[] = {
+	{ DEVICE_NAME, 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, raydium_i2c_id);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id raydium_acpi_id[] = {
+	{ "RAYD0001", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(acpi, raydium_acpi_id);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id raydium_of_match[] = {
+	{ .compatible = "raydium,rm32380",},
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, raydium_of_match);
+#endif
+
+static struct i2c_driver raydium_i2c_driver = {
+	.probe = raydium_i2c_probe,
+	.id_table = raydium_i2c_id,
+	.driver = {
+		.name = "raydium_ts",
+		.pm = &raydium_i2c_pm_ops,
+		.acpi_match_table = ACPI_PTR(raydium_acpi_id),
+		.of_match_table = of_match_ptr(raydium_of_match),
+	},
+};
+
+module_i2c_driver(raydium_i2c_driver);
+
+MODULE_AUTHOR("Raydium");
+MODULE_DESCRIPTION("Raydium I2c Touchscreen driver");
+MODULE_LICENSE("GPL v2");
-- 
2.1.2


^ permalink raw reply related	[flat|nested] 42+ messages in thread

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2016-04-29  9:45 [PATCH] driver: input :touchscreen : add Raydium I2C touch driver jeffrey.lin
@ 2016-05-04 22:58 ` Dmitry Torokhov
  2016-05-05 10:23   ` Jeffrey Lin (林義章)
  2016-05-05 10:25   ` Jeffrey Lin (林義章)
  2016-05-06  8:24 ` jeffrey.lin
                   ` (2 subsequent siblings)
  3 siblings, 2 replies; 42+ messages in thread
From: Dmitry Torokhov @ 2016-05-04 22:58 UTC (permalink / raw)
  To: jeffrey.lin
  Cc: rydberg, grant.likely, robh+dt, jeesw, bleung, jeffrey.lin,
	roger.yang, KP.li, albert.shieh, linux-kernel, linux-input,
	devicetree

Hi Jeffrey,

On Fri, Apr 29, 2016 at 05:45:13PM +0800, jeffrey.lin wrote:
> Raydium I2C touch driver.
> 

In my previous e-mail I requested you to enumerate changes that are made
to the driver, compared to the previous version(s). There were also a
few questions that I did not get answer for.

Also:

> +static int raydium_i2c_query_ts_info(struct raydium_data *ts)
> +{
> +	struct i2c_client *client = ts->client;
> +	int error, retry_cnt;
> +
> +	for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
> +		error = raydium_i2c_read(client, CMD_DATA_BANK,
> +			sizeof(ts->obj), (void *)&ts->obj);
> +			ts->obj.data_bank_addr =
> +				get_unaligned_le32(&ts->obj.data_bank_addr);
> +
> +		if (!error) {
> +			error = raydium_i2c_read(client, CMD_QUERY_BANK,
> +				sizeof(ts->query_bank_info),
> +				(void *)&ts->query_bank_info);
> +			if (!error) {
> +				error = raydium_i2c_read_message(client,
> +					ts->query_bank_info, sizeof(ts->info),
> +					(void *)&ts->info);
> +
> +				ts->info.hw_ver =
> +					get_unaligned_le32(&ts->info.hw_ver);
> +				ts->info.ft_ver =
> +					get_unaligned_le16(&ts->info.ft_ver);
> +				ts->info.x_max =
> +					get_unaligned_le16(&ts->info.x_max);
> +				ts->info.y_max =
> +					get_unaligned_le16(&ts->info.y_max);
> +				return 0;
> +			}
> +		}
> +	}
> +	dev_err(&client->dev, "Get touch data failed: %d\n", error);
> +
> +	return -EINVAL;
> +}
> +
> +static int raydium_i2c_fastboot(struct i2c_client *client)
> +{
> +	static const u8 boot_cmd[] = { 0x50, 0x00, 0x06, 0x20 };
> +	u8 buf[HEADER_SIZE];
> +	int error;
> +
> +	error = raydium_i2c_read_message(client,
> +		get_unaligned_be32(boot_cmd),
> +		sizeof(boot_cmd), buf);

I still can't figure out the logic in read/write mesage handling. Here
we see that device is supposedly reporting data_bank_addr as LE integer,
but query_bank_info is returned in native CPU endianness. With fastboot
command we assume that we are dealing with BE-encoded data and convert
it to CPU-endianness before using it.

However in raydium_i2c_read_message() we convert from CPU endianness to
BE and this comfuses me (does the device really return data in one
endianness but accepts in another endianness?

By the way, why do you have raydium_i2c_read_message() handle reads
above MAX_PKG_SIZE? There are no callers that want it.

Thanks.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 42+ messages in thread

* RE: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2016-05-04 22:58 ` Dmitry Torokhov
@ 2016-05-05 10:23   ` Jeffrey Lin (林義章)
  2016-05-05 10:25   ` Jeffrey Lin (林義章)
  1 sibling, 0 replies; 42+ messages in thread
From: Jeffrey Lin (林義章) @ 2016-05-05 10:23 UTC (permalink / raw)
  To: Dmitry Torokhov, jeffrey.lin
  Cc: rydberg, grant.likely, robh+dt, jeesw, bleung,
	Roger Yang (楊鎮瑋),
	KP Li (李昆倍),
	Albert Shieh (謝欣瑋),
	linux-kernel, linux-input, devicetree

[-- Attachment #1: Type: text/plain, Size: 6292 bytes --]

Hi Dmitry:
Answer this mail as below.

From: Dmitry Torokhov [mailto:dmitry.torokhov@gmail.com]
Sent: Thursday, May 05, 2016 6:59 AM
To: jeffrey.lin
Cc: rydberg@euromail.se; grant.likely@linaro.org; robh+dt@kernel.org; jeesw@melfas.com; bleung@chromium.org; Jeffrey Lin (林義章); Roger Yang (楊鎮瑋); KP Li (李昆倍); Albert Shieh (謝欣瑋); linux-kernel@vger.kernel.org; linux-input@vger.kernel.org; devicetree@vger.kernel.org
Subject: Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver


Hi Jeffrey,

On Fri, Apr 29, 2016 at 05:45:13PM +0800, jeffrey.lin wrote:
> Raydium I2C touch driver.
>

In my previous e-mail I requested you to enumerate changes that are made to the driver, compared to the previous version(s). There were also a few questions that I did not get answer for.
In order to improve flash read/write security of boot loader. We increase handshake flow and command sequence complexity. Our previous flash write functions are single side action. New version we increase more handshake flow to check data accuracy. Firmware update flow increases as below:
1.      Check firmware file size, if file size is null, return fail.
2.      Check touch MCU run on main firmware or boot loader. Both cases have different return values. In case of flash write function, if touch MCU work on main code, touch driver need issue enter boot loader command to touch MCU and wait for MCU boot mode ready.
3.      Turn off touch MCU watchdog function to prevent interference.
4.      Check boot loader write state path ready.
5.      Send boot mode trigger command to unlock flash-write-protection.
6.      Execute page write command. Each write command will wait touch MCU acknowledge and then execute next step.
7.      Leave boot loader to check firmware work.
8.      Raydium touch driver check touch MCU work on main firmware or not. If touch MCU work on main firmware and return correct acknowledge to kernel, this means flash write command success.
9.      Write checksum information to flash.
Every touch driver flash write command accompany write command, acknowledge byte and wait for acknowledge command. Different sequences have different write commands and different state to return ack.

Previous version as below :
1.      Get firmware data
2.      Force jump to boot loader by soft reset command
3.      Wait for boot loader acknowledge
4.      Flash page write data directly
5.      Force jump to main firmware if page write data finish.

New flow increase more acknowledges for each I2C command to make sure boot loader got the message and finish the actions. After finish flash write data, new flow will le touch MCU jump to main firmware and check main code work well. Then write the total checksum to flash and check the consistence of flash code in every boot time.

New flash write flow is very different form previous version, but more safe. The difference is big. Sorry make you confuse.

Also:

> +static int raydium_i2c_query_ts_info(struct raydium_data *ts) {
> +     struct i2c_client *client = ts->client;
> +     int error, retry_cnt;
> +
> +     for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
> +             error = raydium_i2c_read(client, CMD_DATA_BANK,
> +                     sizeof(ts->obj), (void *)&ts->obj);
> +                     ts->obj.data_bank_addr =
> +                             get_unaligned_le32(&ts->obj.data_bank_addr);
> +
> +             if (!error) {
> +                     error = raydium_i2c_read(client, CMD_QUERY_BANK,
> +                             sizeof(ts->query_bank_info),
> +                             (void *)&ts->query_bank_info);
> +                     if (!error) {
> +                             error = raydium_i2c_read_message(client,
> +                                     ts->query_bank_info, sizeof(ts->info),
> +                                     (void *)&ts->info);
> +
> +                             ts->info.hw_ver =
> +                                     get_unaligned_le32(&ts->info.hw_ver);
> +                             ts->info.ft_ver =
> +                                     get_unaligned_le16(&ts->info.ft_ver);
> +                             ts->info.x_max =
> +                                     get_unaligned_le16(&ts->info.x_max);
> +                             ts->info.y_max =
> +                                     get_unaligned_le16(&ts->info.y_max);
> +                             return 0;
> +                     }
> +             }
> +     }
> +     dev_err(&client->dev, "Get touch data failed: %d\n", error);
> +
> +     return -EINVAL;
> +}
> +
> +static int raydium_i2c_fastboot(struct i2c_client *client) {
> +     static const u8 boot_cmd[] = { 0x50, 0x00, 0x06, 0x20 };
> +     u8 buf[HEADER_SIZE];
> +     int error;
> +
> +     error = raydium_i2c_read_message(client,
> +             get_unaligned_be32(boot_cmd),
> +             sizeof(boot_cmd), buf);

I still can't figure out the logic in read/write mesage handling. Here we see that device is supposedly reporting data_bank_addr as LE integer, but query_bank_info is returned in native CPU endianness. With fastboot command we assume that we are dealing with BE-encoded data and convert it to CPU-endianness before using it.

However in raydium_i2c_read_message() we convert from CPU endianness to BE and this comfuses me (does the device really return data in one endianness but accepts in another endianness?
Raydium MCU support direct access mode by I2C bus to access register or physical buffer directly. In this direct access mode, our touch MCU use BE integer by using commands of raydium_i2c_read_message/ raydium_i2c_send_message. Otherwise, raydum I2C normal mode use LE integer. By the way, the command buffer of soft_rst_cmd[] is upside down.

By the way, why do you have raydium_i2c_read_message() handle reads above MAX_PKG_SIZE? There are no callers that want it.
Generally, I2C bus has maximum packet size but total touch report points maybe exceed this size. Unfortunately my experimental chromebook device exceed this size. So that I’ll split this read command two or more to make sure all data are read.

Thanks.

--
Dmitry


Best Regards
Jeffrey Lin


[-- Attachment #2: Type: text/html, Size: 14693 bytes --]

^ permalink raw reply	[flat|nested] 42+ messages in thread

* RE: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2016-05-04 22:58 ` Dmitry Torokhov
  2016-05-05 10:23   ` Jeffrey Lin (林義章)
@ 2016-05-05 10:25   ` Jeffrey Lin (林義章)
  1 sibling, 0 replies; 42+ messages in thread
From: Jeffrey Lin (林義章) @ 2016-05-05 10:25 UTC (permalink / raw)
  To: Jeffrey Lin (林義章),
	Dmitry Torokhov  <dmitry.torokhov@gmail.com>,
	jeffrey.lin
  Cc: rydberg, grant.likely, robh+dt, jeesw, bleung,
	Roger Yang (楊鎮瑋),
	KP Li (李昆倍),
	Albert Shieh (謝欣瑋),
	linux-kernel, linux-input, devicetree

[-- Attachment #1: Type: text/plain, Size: 6542 bytes --]


Hi Dmitry:
Answer this mail as below.

From: Dmitry Torokhov [mailto:dmitry.torokhov@gmail.com]
Sent: Thursday, May 05, 2016 6:59 AM
To: jeffrey.lin
Cc: rydberg@euromail.se<mailto:rydberg@euromail.se>; grant.likely@linaro.org<mailto:grant.likely@linaro.org>; robh+dt@kernel.org<mailto:robh+dt@kernel.org>; jeesw@melfas.com<mailto:jeesw@melfas.com>; bleung@chromium.org<mailto:bleung@chromium.org>; Jeffrey Lin (林義章); Roger Yang (楊鎮瑋); KP Li (李昆倍); Albert Shieh (謝欣瑋); linux-kernel@vger.kernel.org<mailto:linux-kernel@vger.kernel.org>; linux-input@vger.kernel.org<mailto:linux-input@vger.kernel.org>; devicetree@vger.kernel.org<mailto:devicetree@vger.kernel.org>
Subject: Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver


Hi Jeffrey,

On Fri, Apr 29, 2016 at 05:45:13PM +0800, jeffrey.lin wrote:
> Raydium I2C touch driver.
>

In my previous e-mail I requested you to enumerate changes that are made to the driver, compared to the previous version(s). There were also a few questions that I did not get answer for.
In order to improve flash read/write security of boot loader. We increase handshake flow and command sequence complexity. Our previous flash write functions are single side action. New version we increase more handshake flow to check data accuracy. Firmware update flow increases as below:
1.      Check firmware file size, if file size is null, return fail.
2.      Check touch MCU run on main firmware or boot loader. Both cases have different return values. In case of flash write function, if touch MCU work on main code, touch driver need issue enter boot loader command to touch MCU and wait for MCU boot mode ready.
3.      Turn off touch MCU watchdog function to prevent interference.
4.      Check boot loader write state path ready.
5.      Send boot mode trigger command to unlock flash-write-protection.
6.      Execute page write command. Each write command will wait touch MCU acknowledge and then execute next step.
7.      Leave boot loader to check firmware work.
8.      Raydium touch driver check touch MCU work on main firmware or not. If touch MCU work on main firmware and return correct acknowledge to kernel, this means flash write command success.
9.      Write checksum information to flash.
Every touch driver flash write command accompany write command, acknowledge byte and wait for acknowledge command. Different sequences have different write commands and different state to return ack.

Previous version as below :
1.      Get firmware data
2.      Force jump to boot loader by soft reset command
3.      Wait for boot loader acknowledge
4.      Flash page write data directly
5.      Force jump to main firmware if page write data finish.

New flow increase more acknowledges for each I2C command to make sure boot loader got the message and finish the actions. After finish flash write data, new flow will le touch MCU jump to main firmware and check main code work well. Then write the total checksum to flash and check the consistence of flash code in every boot time.

New flash write flow is very different form previous version, but more safe. The difference is big. Sorry make you confuse.

Also:

> +static int raydium_i2c_query_ts_info(struct raydium_data *ts) {
> +     struct i2c_client *client = ts->client;
> +     int error, retry_cnt;
> +
> +     for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
> +             error = raydium_i2c_read(client, CMD_DATA_BANK,
> +                     sizeof(ts->obj), (void *)&ts->obj);
> +                     ts->obj.data_bank_addr =
> +                             get_unaligned_le32(&ts->obj.data_bank_addr);
> +
> +             if (!error) {
> +                     error = raydium_i2c_read(client, CMD_QUERY_BANK,
> +                             sizeof(ts->query_bank_info),
> +                             (void *)&ts->query_bank_info);
> +                     if (!error) {
> +                             error = raydium_i2c_read_message(client,
> +                                     ts->query_bank_info, sizeof(ts->info),
> +                                     (void *)&ts->info);
> +
> +                             ts->info.hw_ver =
> +                                     get_unaligned_le32(&ts->info.hw_ver);
> +                             ts->info.ft_ver =
> +                                     get_unaligned_le16(&ts->info.ft_ver);
> +                             ts->info.x_max =
> +                                     get_unaligned_le16(&ts->info.x_max);
> +                             ts->info.y_max =
> +                                     get_unaligned_le16(&ts->info.y_max);
> +                             return 0;
> +                     }
> +             }
> +     }
> +     dev_err(&client->dev, "Get touch data failed: %d\n", error);
> +
> +     return -EINVAL;
> +}
> +
> +static int raydium_i2c_fastboot(struct i2c_client *client) {
> +     static const u8 boot_cmd[] = { 0x50, 0x00, 0x06, 0x20 };
> +     u8 buf[HEADER_SIZE];
> +     int error;
> +
> +     error = raydium_i2c_read_message(client,
> +             get_unaligned_be32(boot_cmd),
> +             sizeof(boot_cmd), buf);

I still can't figure out the logic in read/write mesage handling. Here we see that device is supposedly reporting data_bank_addr as LE integer, but query_bank_info is returned in native CPU endianness. With fastboot command we assume that we are dealing with BE-encoded data and convert it to CPU-endianness before using it.

However in raydium_i2c_read_message() we convert from CPU endianness to BE and this comfuses me (does the device really return data in one endianness but accepts in another endianness?
Raydium MCU support direct access mode by I2C bus to access register or physical buffer directly. In this direct access mode, our touch MCU use BE integer by using commands of raydium_i2c_read_message/ raydium_i2c_send_message. Otherwise, raydum I2C normal mode use LE integer. By the way, the command buffer of soft_rst_cmd[] is upside down.

By the way, why do you have raydium_i2c_read_message() handle reads above MAX_PKG_SIZE? There are no callers that want it.
Generally, I2C bus has maximum packet size but total touch report points maybe exceed this size. Unfortunately my experimental chromebook device exceed this size. So that I’ll split this read command two or more to make sure all data are read.

Thanks.

--
Dmitry


Best Regards
Jeffrey Lin


[-- Attachment #2: Type: text/html, Size: 17580 bytes --]

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2016-04-29  9:45 [PATCH] driver: input :touchscreen : add Raydium I2C touch driver jeffrey.lin
  2016-05-04 22:58 ` Dmitry Torokhov
@ 2016-05-06  8:24 ` jeffrey.lin
  2016-05-11 16:04 ` jeffrey.lin
       [not found] ` <1461923113-426-1-git-send-email-jeffrey.lin-s3Ivl27awEzQT0dZR+AlfA@public.gmane.org>
  3 siblings, 0 replies; 42+ messages in thread
From: jeffrey.lin @ 2016-05-06  8:24 UTC (permalink / raw)
  To: dmitry.torokhov, rydberg, grant.likely, robh+dt, jeesw, bleung
  Cc: jeffrey.lin, roger.yang, KP.li, albert.shieh, linux-kernel,
	linux-input, devicetree

Hi Dmitry:

About question "In my previous e-mail I requested you to enumerate changes that are made to the driver, compared to the previous version(s). There were also a few questions that I did not get answer for."

In order to improve flash read/write security of boot loader. We increase handshake flow and command sequence complexity. Our previous flash write functions are single side action. New version we increase more handshake flow to check data accuracy. Firmware update flow increases as below:

    1.Check firmware file size, if file size is null, return fail.
    2.Check touch MCU run on main firmware or boot loader. Both cases have different return values. In case of flash write function, if touch MCU work on main code, touch driver need issue enter boot loader command to touch MCU and wait for MCU boot mode ready.
    3.Turn off touch MCU watchdog function to prevent interference.
    4.Check boot loader write state path ready.
    5.Send boot mode trigger command to unlock flash-write-protection.
    6.Execute page write command. Each write command will wait touch MCU acknowledge and then execute next step.
    7.Leave boot loader to check firmware work.
    8.Raydium touch driver check touch MCU work on main firmware or not. If touch MCU work on main firmware and return correct acknowledge to kernel, this means flash write command success.
    9.Write checksum information to flash.

Every touch driver flash write command accompany write command, acknowledge byte and wait for acknowledge command. Different sequences have different write commands and different state to return ack.

Previous version as below :

    1.Get firmware data
    2.Force jump to boot loader by soft reset command
    3.Wait for boot loader acknowledge
    4.Flash page write data directly
    5.Force jump to main firmware if page write data finish.

 
New flow increase more acknowledges for each I2C command to make sure boot loader got the message and finish the actions. After finish flash write data, new flow will le touch MCU jump to main firmware and check main code work well. Then write the total checksum to flash and check the consistence of flash code in every boot time.
 
New flash write flow is very different form previous version, but more safe. The difference is big. Sorry make you confuse.

The question "I still can't figure out the logic in read/write mesage handling. Here we see that device is supposedly reporting data_bank_addr as LE integer, but query_bank_info is returned in native CPU endianness. With fastboot command we assume that we are dealing with BE-encoded data and convert it to CPU-endianness before using it.
 
However in raydium_i2c_read_message() we convert from CPU endianness to BE and this comfuses me (does the device really return data in one endianness but accepts in another endianness?"

Raydium MCU support direct access mode by I2C bus to access register or physical buffer directly. In this direct access mode, our touch MCU use BE integer by using commands of raydium_i2c_read_message/ raydium_i2c_send_message. Otherwise, raydum I2C normal mode use LE integer. By the way, the command buffer of soft_rst_cmd[] is upside down.

The question "By the way, why do you have raydium_i2c_read_message() handle reads above MAX_PKG_SIZE? There are no callers that want it."

Generally, I2C bus has maximum packet size but total touch report points maybe exceed this size. Unfortunately my experimental chromebook device exceed this size. So that I’ll split this read command two or more to make sure all data are read.

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2016-04-29  9:45 [PATCH] driver: input :touchscreen : add Raydium I2C touch driver jeffrey.lin
  2016-05-04 22:58 ` Dmitry Torokhov
  2016-05-06  8:24 ` jeffrey.lin
@ 2016-05-11 16:04 ` jeffrey.lin
       [not found] ` <1461923113-426-1-git-send-email-jeffrey.lin-s3Ivl27awEzQT0dZR+AlfA@public.gmane.org>
  3 siblings, 0 replies; 42+ messages in thread
From: jeffrey.lin @ 2016-05-11 16:04 UTC (permalink / raw)
  To: dmitry.torokhov, rydberg, grant.likely, robh+dt, jeesw, bleung
  Cc: jeffrey.lin, roger.yang, KP.li, albert.shieh, linux-kernel,
	linux-input, devicetree

Hi Dmitry:

I've made a mistake as touch points data over "MAX_PKG_SIZE".This mistake will result in memory overwrite, so that I update a new patch to upstream. 
New patch I assign a new definition "MAX_RD_PKG_LEN" for maxinum I2C read command to prevent touch points data package over I2C bus buffer size. If I2C read command package over than MAX_RD_PKG_LEN, I’ll split this read command two or more to make sure all data are read.

Please review my new patch and give me some comments. Many thanks.

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
       [not found] ` <1461923113-426-1-git-send-email-jeffrey.lin-s3Ivl27awEzQT0dZR+AlfA@public.gmane.org>
@ 2016-05-13  4:18   ` Dmitry Torokhov
  2016-05-13 17:08     ` jeffrey.lin
                       ` (7 more replies)
  0 siblings, 8 replies; 42+ messages in thread
From: Dmitry Torokhov @ 2016-05-13  4:18 UTC (permalink / raw)
  To: jeffrey.lin
  Cc: grant.likely-QSEj5FYQhm4dnm+yROfE0A,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, jeesw-iQTvn4YitUrQT0dZR+AlfA,
	bleung-F7+t8E8rja9g9hUCZPvPmw,
	jeffrey.lin-s3Ivl27awEzQT0dZR+AlfA,
	roger.yang-s3Ivl27awEzQT0dZR+AlfA, KP.li-s3Ivl27awEzQT0dZR+AlfA,
	albert.shieh-s3Ivl27awEzQT0dZR+AlfA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA

Hi Jeffrey,
On Fri, Apr 29, 2016 at 05:45:13PM +0800, jeffrey.lin wrote:
> Raydium I2C touch driver.
> 
> Signed-off-by: jeffrey.lin <jeffrey.lin-s3Ivl27awEzQT0dZR+AlfA@public.gmane.org>

I was looking at the driver and there were a few issues (buffer
overflows, forgetting releasing firmware, general flow, etc), that I
tried correcting in the attached patch. Please try incorporating it into
yours (it should apply to this version of Raydium driver) and let me
know if it still works. There are a few questionable points that I
marked with "XXX FIXME" in the code, please respond to them by either
confirming that they are correct or fixing them.

Thanks.

-- 
Dmitry


Input: raydium misc changes

From: Dmitry Torokhov <dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

Signed-off-by: Dmitry Torokhov <dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
---
 drivers/input/touchscreen/raydium_i2c_ts.c |  941 ++++++++++++++--------------
 1 file changed, 482 insertions(+), 459 deletions(-)

diff --git a/drivers/input/touchscreen/raydium_i2c_ts.c b/drivers/input/touchscreen/raydium_i2c_ts.c
index cee46e8..1e4e3de 100644
--- a/drivers/input/touchscreen/raydium_i2c_ts.c
+++ b/drivers/input/touchscreen/raydium_i2c_ts.c
@@ -20,107 +20,105 @@
  * Contact Raydium Semiconductor Corporation at www.rad-ic.com
  */
 
-#include <linux/module.h>
-#include <linux/input.h>
-#include <linux/interrupt.h>
-#include <linux/i2c.h>
+#include <linux/acpi.h>
 #include <linux/delay.h>
-#include <linux/slab.h>
 #include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
 #include <linux/input/mt.h>
-#include <linux/acpi.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
 #include <linux/of.h>
 #include <linux/regulator/consumer.h>
+#include <linux/slab.h>
 #include <asm/unaligned.h>
-#include <linux/gpio/consumer.h>
 
-/* Device, Driver information */
-#define DEVICE_NAME	"raydium_i2c"
-
-/* Slave I2C mode*/
-#define RM_BOOT_BLDR	0x02
-#define RM_BOOT_MAIN	0x03
-
-/*I2C bl command */
-#define CMD_BOOT_PAGE_WRT	0x0B		/*send bl page write*/
-#define CMD_BOOT_WRT		0x11		/*send bl write*/
-#define CMD_BOOT_ACK		0x22		/*send ack*/
-#define CMD_BOOT_CHK		0x33		/*send data check*/
-#define CMD_BOOT_READ		0x44		/*send wait bl data ready*/
-#define BOOT_RDY		0xFF		/*bl data ready*/
-/*I2C main command*/
-#define CMD_QUERY_BANK		0x2B
-#define CMD_DATA_BANK		0x4D
-#define CMD_ENTER_SLEEP		0x4E
-#define CMD_BANK_SWITCH		0xAA
+/* Slave I2C mode */
+#define RM_BOOT_BLDR		0x02
+#define RM_BOOT_MAIN		0x03
 
-/* Touch relative info */
-#define MAX_RETRIES		3
-#define MAX_TOUCH_NUM		10
-#define MAX_PkG_SIZE		50
-#define BOOT_DELAY_MS	100
+/* I2C bootoloader commands */
+#define RM_CMD_BOOT_PAGE_WRT	0x0B		/* send bl page write */
+#define RM_CMD_BOOT_WRT		0x11		/* send bl write */
+#define RM_CMD_BOOT_ACK		0x22		/* send ack*/
+#define RM_CMD_BOOT_CHK		0x33		/* send data check */
+#define RM_CMD_BOOT_READ	0x44		/* send wait bl data ready*/
 
-/*Bootloader relative info */
-#define CMD_BOOT_HEADER_LEN		3	/*bl flash wrt cmd size*/
-#define RAYDIUM_TRANS_BUFSIZE	32	/*bl wrt pkg size*/
-#define MAX_BOOT_WRT_LEN	(RAYDIUM_TRANS_BUFSIZE + CMD_BOOT_HEADER_LEN)
-#define MAX_FW_UPDATE_RETRIES	30
+#define RM_BOOT_RDY		0xFF		/* bl data ready */
+
+/* I2C main commands */
+#define RM_CMD_QUERY_BANK	0x2B
+#define RM_CMD_DATA_BANK	0x4D
+#define RM_CMD_ENTER_SLEEP	0x4E
+#define RM_CMD_BANK_SWITCH	0xAA
+
+#define RM_RESET_MSG_ADDR	0x40000004
+#define RM_FASTBOOT_MSG_ADDR	0x50000620
+
+#define RM_MAX_READ_SIZE	63
+
+/* Touch relative info */
+#define RM_MAX_RETRIES		3
+#define RM_MAX_TOUCH_NUM	10
+#define RM_BOOT_DELAY_MS	100
+
+/* Offsets in contact data */
+#define RM_CONTACT_STATE_POS	0
+#define RM_CONTACT_X_POS	1
+#define RM_CONTACT_Y_POS	3
+#define RM_CONTACT_PRESSURE_POS	5 // XXX - check - original was 4
+#define RM_CONTACT_WIDTH_X_POS	6
+#define RM_CONTACT_WIDTH_Y_POS	7
+
+/* Bootloader relative info */
+#define RM_BL_WRT_CMD_SIZE	3	/* bl flash wrt cmd size */
+#define RM_BL_WRT_PKG_SIZE	32	/* bl wrt pkg size */
+#define RM_BL_WRT_LEN		(RM_BL_WRT_PKG_SIZE + RM_BL_WRT_CMD_SIZE)
+#define RM_FW_PAGE_SIZE		128
+#define RM_MAX_FW_RETRIES	30
+
+#define RM_POWERON_DELAY_USEC	500
+#define RM_RESET_DELAY_MSEC	50
 
 enum raydium_bl_cmd {
-	HEADER = 0,
-	PAGE_STR,
-	PKG_IDX,
-	DATA_STR,
+	BL_HEADER = 0,
+	BL_PAGE_STR,
+	BL_PKG_IDX,
+	BL_DATA_STR,
 };
 
 enum raydium_bl_ack {
-	ACK_NULL = 0,
-	WAIT_READY,
-	PATH_READY,
+	RAYDIUM_ACK_NULL = 0,
+	RAYDIUM_WAIT_READY,
+	RAYDIUM_PATH_READY,
 };
 
-#define RAYDIUM_PAGE_SIZE		128
-#define RAYDIUM_POWERON_DELAY_USEC	500
-#define RAYDIUM_RESET_DELAY_MSEC	50
-
-#define ADDR_INDEX		0x03
-#define DATA_INDEX		0x04
-
-#define HEADER_SIZE		4
-
 enum raydium_boot_mode {
 	RAYDIUM_TS_MAIN = 0,
 	RAYDIUM_TS_BLDR,
 };
 
-enum raydium_abs_idx {
-	POS_STATE = 0,/*1:touch, 0:no touch*/
-	POS_X,
-	POS_Y = 3,
-	POS_PRESSURE,
-	WIDTH_X,
-	WIDTH_Y,
+/* Response to RM_CMD_DATA_BANK request */
+struct raydium_data_info {
+	__le32 data_bank_addr;
+	u8 pkg_size;
+	u8 tp_info_size;
 };
 
 struct raydium_info {
-	u32 hw_ver;	/*device ver, __le32*/
+	__le32 hw_ver;		/*device version */
 	u8 main_ver;
 	u8 sub_ver;
-	u16 ft_ver;	/*test ver, __le16*/
+	__le16 ft_ver;		/* test version */
 	u8 x_num;
 	u8 y_num;
-	u16 x_max;	/*disp reso, __le16*/
-	u16 y_max;	/*disp reso, __le16*/
+	__le16 x_max;
+	__le16 y_max;
 	u8 x_res;		/* units/mm */
 	u8 y_res;		/* units/mm */
 };
 
-struct raydium_object {
-	u32 data_bank_addr;
-	u8 pkg_size;
-	u8 tp_info_size;
-};
-
 /* struct raydium_data - represents state of Raydium touchscreen device */
 struct raydium_data {
 	struct i2c_client *client;
@@ -130,136 +128,132 @@ struct raydium_data {
 	struct regulator *vccio;
 	struct gpio_desc *reset_gpio;
 
-	u32 query_bank_info;
-
 	struct raydium_info info;
-	struct raydium_object obj;
-	enum raydium_boot_mode boot_mode;
 
 	struct mutex sysfs_mutex;
-	struct completion cmd_done;
+
+	u8 *report_data;
+
+	u32 data_bank_addr;
+	u8 report_size;
+	u8 contact_size;
+
+	enum raydium_boot_mode boot_mode;
 
 	bool wake_irq_enabled;
 };
 
 static int raydium_i2c_send(struct i2c_client *client,
-	u8 addr, u8 *data, size_t len)
+			    u8 addr, const void *data, size_t len)
 {
-	u8 buf[MAX_PkG_SIZE + 1];
+	u8 *buf;
 	int tries = 0;
+	int ret;
+
+	buf = kmalloc(len + 1, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
 
 	buf[0] = addr;
-	memcpy(&buf[1], data, len);
+	memcpy(buf + 1, data, len);
 
 	do {
-		if (i2c_master_send(client, buf, len + 1) == (len + 1))
-			return 0;
+		ret = i2c_master_send(client, buf, len + 1);
+		if (likely(ret == len + 1))
+			break;
+
 		msleep(20);
-	} while (++tries < MAX_RETRIES);
+	} while (++tries < RM_MAX_RETRIES);
 
-	dev_err(&client->dev, "%s: i2c send failed\n", __func__);
+	kfree(buf);
 
-	return -EIO;
+	if (unlikely(ret != len + 1)) {
+		if (ret >= 0)
+			ret = -EIO;
+		dev_err(&client->dev, "%s failed: %d\n", __func__, ret);
+		return ret;
+	}
+
+	return 0;
 }
 
 static int raydium_i2c_read(struct i2c_client *client,
-	u8 addr, size_t len, void *data)
+			    u8 addr, void *data, size_t len)
 {
-	struct i2c_msg xfer[2];
+	struct i2c_msg xfer[] = {
+		{
+			.addr = client->addr,
+			.len = 1,
+			.buf = &addr,
+		},
+		{
+			.addr = client->addr,
+			.flags = I2C_M_RD,
+			.len = len,
+			.buf = data,
+		}
+	};
 	int ret;
 
-	/* Write register */
-	xfer[0].addr = client->addr;
-	xfer[0].flags = 0;
-	xfer[0].len = 1;
-	xfer[0].buf = &addr;
-
-	/* Read data */
-	xfer[1].addr = client->addr;
-	xfer[1].flags = I2C_M_RD;
-	xfer[1].len = len;
-	xfer[1].buf = data;
-
 	ret = i2c_transfer(client->adapter, xfer, ARRAY_SIZE(xfer));
-	if (ret < 0)
-		return ret;
-
-	if (ret != ARRAY_SIZE(xfer))
-		return -EIO;
+	if (unlikely(ret != ARRAY_SIZE(xfer)))
+		return ret < 0 ? ret : -EIO;
 
 	return 0;
 }
 
 static int raydium_i2c_read_message(struct i2c_client *client,
-	u32 addr, size_t len, void *data)
+				    u32 addr, void *data, size_t len)
 {
-	u8 buf[HEADER_SIZE];
-	u8 read_cnt, idx_i;
+	__be32 be_addr = cpu_to_be32(addr);
+	size_t xfer_len;
 	int error;
-	size_t pkg_size;
-
-	if (len % MAX_PkG_SIZE)
-		read_cnt = len / MAX_PkG_SIZE + 1;
-	else
-		read_cnt = len / MAX_PkG_SIZE;
-
-	idx_i = 0;
-	for (idx_i = 0; idx_i < read_cnt; idx_i++) {
-		pkg_size = (len > MAX_PkG_SIZE) ? MAX_PkG_SIZE : len;
 
-		len -= MAX_PkG_SIZE;
+	while (len) {
+		xfer_len = min_t(size_t, len, RM_MAX_READ_SIZE);
 
-		put_unaligned_be32(addr, buf);
-
-		/*set data bank*/
-		error = raydium_i2c_send(client, CMD_BANK_SWITCH,
-			(u8 *)buf, HEADER_SIZE);
-		/*read potints data*/
+		error = raydium_i2c_send(client, RM_CMD_BANK_SWITCH,
+					 &be_addr, sizeof(be_addr));
 		if (!error)
-			error = raydium_i2c_read(client, buf[ADDR_INDEX],
-				pkg_size,
-				data + idx_i*MAX_PkG_SIZE);
+			error = raydium_i2c_read(client, addr & 0xff,
+						 data, xfer_len);
+		if (error)
+			return error;
 
-		addr += MAX_PkG_SIZE;
-		idx_i++;
+		len -= xfer_len;
+		data += xfer_len;
 	}
 
-	return error;
+	return 0;
 }
 
 static int raydium_i2c_send_message(struct i2c_client *client,
-	size_t len, void *data)
+				    u32 addr, const void *data, size_t len)
 {
+	__be32 be_addr = cpu_to_be32(addr);
 	int error;
-	__le32 cmd;
-
-	cmd = get_unaligned_le32(data);
 
-	/*set data bank*/
-	error = raydium_i2c_send(client, CMD_BANK_SWITCH, (u8 *)&cmd,
-		HEADER_SIZE);
-
-	/*send message*/
+	error = raydium_i2c_send(client, RM_CMD_BANK_SWITCH,
+				 &be_addr, sizeof(be_addr));
 	if (!error)
-		error = raydium_i2c_send(client, ((u8 *)data)[ADDR_INDEX],
-			data + DATA_INDEX, len);
+		error = raydium_i2c_send(client, addr & 0xff, data, len);
 
 	return error;
 }
 
 static int raydium_i2c_sw_reset(struct i2c_client *client)
 {
-	static const u8 soft_rst_cmd[] = {0x40, 0x00, 0x00, 0x04, 0x01};
+	const u8 soft_rst_cmd = 0x01;
 	int error;
 
-	error = raydium_i2c_send_message(client, ARRAY_SIZE(soft_rst_cmd),
-		(void *)soft_rst_cmd);
+	error = raydium_i2c_send_message(client, RM_RESET_MSG_ADDR,
+					 &soft_rst_cmd, sizeof(soft_rst_cmd));
 	if (error) {
 		dev_err(&client->dev, "software reset failed: %d\n", error);
 		return error;
 	}
 
-	msleep(RAYDIUM_RESET_DELAY_MSEC);
+	msleep(RM_RESET_DELAY_MSEC);
 
 	return 0;
 }
@@ -267,75 +261,78 @@ static int raydium_i2c_sw_reset(struct i2c_client *client)
 static int raydium_i2c_query_ts_info(struct raydium_data *ts)
 {
 	struct i2c_client *client = ts->client;
+	struct raydium_data_info data_info;
+	__le32 query_bank_addr;
+
 	int error, retry_cnt;
 
-	for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
-		error = raydium_i2c_read(client, CMD_DATA_BANK,
-			sizeof(ts->obj), (void *)&ts->obj);
-			ts->obj.data_bank_addr =
-				get_unaligned_le32(&ts->obj.data_bank_addr);
+	for (retry_cnt = 0; retry_cnt < RM_MAX_RETRIES; retry_cnt++) {
+		error = raydium_i2c_read(client, RM_CMD_DATA_BANK,
+					 &data_info, sizeof(data_info));
+		if (error)
+			continue;
 
-		if (!error) {
-			error = raydium_i2c_read(client, CMD_QUERY_BANK,
-				sizeof(ts->query_bank_info),
-				(void *)&ts->query_bank_info);
-			if (!error) {
-				error = raydium_i2c_read_message(client,
-					ts->query_bank_info, sizeof(ts->info),
-					(void *)&ts->info);
-
-				ts->info.hw_ver =
-					get_unaligned_le32(&ts->info.hw_ver);
-				ts->info.ft_ver =
-					get_unaligned_le16(&ts->info.ft_ver);
-				ts->info.x_max =
-					get_unaligned_le16(&ts->info.x_max);
-				ts->info.y_max =
-					get_unaligned_le16(&ts->info.y_max);
-				return 0;
-			}
-		}
+		ts->data_bank_addr = le32_to_cpu(data_info.data_bank_addr);
+		ts->report_size = data_info.pkg_size;
+		ts->contact_size = data_info.tp_info_size;
+
+		dev_dbg(&client->dev,
+			"data_bank_addr: %#08x, report_size: %d, contact_size: %d\n",
+			ts->data_bank_addr, ts->report_size, ts->contact_size);
+
+		error = raydium_i2c_read(client, RM_CMD_QUERY_BANK,
+					 &query_bank_addr,
+					 sizeof(query_bank_addr));
+		if (error)
+			continue;
+
+		error = raydium_i2c_read_message(client,
+						 le32_to_cpu(query_bank_addr),
+						 &ts->info, sizeof(ts->info));
+		if (error)
+			continue;
+
+		return 0;
 	}
-	dev_err(&client->dev, "Get touch data failed: %d\n", error);
 
-	return -EINVAL;
+	dev_err(&client->dev, "failed to query device parameters: %d\n", error);
+	return error;
 }
 
 static int raydium_i2c_fastboot(struct i2c_client *client)
 {
-	static const u8 boot_cmd[] = { 0x50, 0x00, 0x06, 0x20 };
-	u8 buf[HEADER_SIZE];
+	u8 buf[4]; // XXX FIXME why size 4 and not 1?
 	int error;
 
-	error = raydium_i2c_read_message(client,
-		get_unaligned_be32(boot_cmd),
-		sizeof(boot_cmd), buf);
+	error = raydium_i2c_read_message(client, RM_FASTBOOT_MSG_ADDR,
+					 buf, sizeof(buf));
 
 	if (!error) {
 		if (buf[0] == RM_BOOT_BLDR) {
 			dev_dbg(&client->dev, "boot in fastboot mode\n");
 			return -EINVAL;
 		}
+
 		dev_dbg(&client->dev, "boot success -- 0x%x\n", client->addr);
 		return 0;
 	}
 
 	dev_err(&client->dev, "boot failed: %d\n", error);
-
 	return error;
 }
 
 static int raydium_i2c_check_fw_status(struct raydium_data *ts)
 {
 	struct i2c_client *client = ts->client;
-	static const u8 bl_area[] = {0x62, 0x6f, 0x6f, 0x74};
-	static const u8 main_area[] = {0x66, 0x69, 0x72, 0x6d};
-	u8 buf[HEADER_SIZE];
+	static const u8 bl_area[] = { 0x62, 0x6f, 0x6f, 0x74 };
+	static const u8 main_area[] = { 0x66, 0x69, 0x72, 0x6d };
+	u8 buf[4];
 	int error;
 
-	error = raydium_i2c_read(client, CMD_BOOT_READ, HEADER_SIZE,
-		(void *)buf);
+	error = raydium_i2c_read(client, RM_CMD_BOOT_READ, buf, sizeof(buf));
 	if (!error) {
+		// XXX FIXME: why do we compare only 1st bytes? Do we even
+		// need 4-byte constants?
 		if (buf[0] == bl_area[0])
 			ts->boot_mode = RAYDIUM_TS_BLDR;
 		else if (buf[0] == main_area[0])
@@ -351,23 +348,28 @@ static int raydium_i2c_initialize(struct raydium_data *ts)
 	struct i2c_client *client = ts->client;
 	int error, retry_cnt;
 
-	for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
+	for (retry_cnt = 0; retry_cnt < RM_MAX_RETRIES; retry_cnt++) {
 		error = raydium_i2c_fastboot(client);
 		if (error) {
 			/* Continue initializing if it's the last try */
-			if (retry_cnt < MAX_RETRIES - 1)
+			if (retry_cnt < RM_MAX_RETRIES - 1)
 				continue;
 		}
+
 		/* Wait for Hello packet */
-		msleep(BOOT_DELAY_MS);
+		msleep(RM_BOOT_DELAY_MS);
 
 		error = raydium_i2c_check_fw_status(ts);
+		if (error) {
+			dev_err(&client->dev,
+				"failed to read device status: %d\n", error);
+			if (retry_cnt < RM_MAX_RETRIES - 1)
+				continue;
+		}
+
 		if (ts->boot_mode == RAYDIUM_TS_BLDR ||
-			ts->boot_mode == RAYDIUM_TS_MAIN)
+		    ts->boot_mode == RAYDIUM_TS_MAIN) {
 			break;
-		else if (error) {
-			dev_err(&client->dev,
-				"failed to read 'hello' packet: %d\n", error);
 		}
 	}
 
@@ -375,7 +377,7 @@ static int raydium_i2c_initialize(struct raydium_data *ts)
 		ts->boot_mode = RAYDIUM_TS_BLDR;
 
 	if (ts->boot_mode == RAYDIUM_TS_BLDR) {
-		ts->info.hw_ver = 0xffffffff;
+		ts->info.hw_ver = cpu_to_le32(0xffffffffUL);
 		ts->info.main_ver = 0xff;
 		ts->info.sub_ver = 0xff;
 	} else {
@@ -386,52 +388,60 @@ static int raydium_i2c_initialize(struct raydium_data *ts)
 }
 
 static int raydium_i2c_bl_chk_state(struct i2c_client *client,
-		enum raydium_bl_ack state)
+				    enum raydium_bl_ack state)
 {
 	static const u8 ack_ok[] = { 0xFF, 0x39, 0x30, 0x30, 0x54 };
-	u8 rbuf[5];
+	u8 rbuf[sizeof(ack_ok)];
 	u8 retry;
 	int error;
 
-	if (state == ACK_NULL)
-		return 0;
+	for (retry = 0; retry < RM_MAX_FW_RETRIES; retry++) {
+		switch (state) {
+		case RAYDIUM_ACK_NULL:
+			return 0;
 
-	for (retry = 0; retry < MAX_FW_UPDATE_RETRIES; retry++) {
-		if (state == WAIT_READY) {
-			error = raydium_i2c_read(client, CMD_BOOT_CHK,
-				1, &rbuf[0]);
-			if (!error) {
-				if (rbuf[0] == BOOT_RDY)
-					return 0;
-			}
-		} else if (state == PATH_READY) {
-			error = raydium_i2c_read(client, CMD_BOOT_CHK,
-				sizeof(ack_ok), &rbuf[0]);
-			if (!error) {
-				if (!memcmp(rbuf, ack_ok, sizeof(ack_ok)))
-					return 0;
-			}
-		} else
+		case RAYDIUM_WAIT_READY:
+			error = raydium_i2c_read(client, RM_CMD_BOOT_CHK,
+						 &rbuf[0], 1);
+			if (!error && rbuf[0] == RM_BOOT_RDY)
+				return 0;
+
+			break;
+
+		case RAYDIUM_PATH_READY:
+			error = raydium_i2c_read(client, RM_CMD_BOOT_CHK,
+						 rbuf, sizeof(rbuf));
+			if (!error && !memcmp(rbuf, ack_ok, sizeof(ack_ok)))
+				return 0;
+
+			break;
+
+		default:
+			dev_err(&client->dev, "%s: invalid target state %d\n",
+				__func__, state);
 			return -EINVAL;
+		}
+
 		msleep(20);
 	}
 
-	return -EINVAL;
+	return -ETIMEDOUT;
 }
 
-static int raydium_i2c_wrt_object(struct i2c_client *client,
-	u8 *data, size_t len, enum raydium_bl_ack state)
+static int raydium_i2c_write_object(struct i2c_client *client,
+				    const void *data, size_t len,
+				    enum raydium_bl_ack state)
 {
-	int error = 0;
+	int error;
 
-	error = raydium_i2c_send(client, CMD_BOOT_WRT, data, len);
+	error = raydium_i2c_send(client, RM_CMD_BOOT_WRT, data, len);
 	if (error) {
 		dev_err(&client->dev, "WRT obj command failed: %d\n",
 			error);
 		return error;
 	}
 
-	error = raydium_i2c_send(client, CMD_BOOT_ACK, (u8 *)NULL, 0);
+	error = raydium_i2c_send(client, RM_CMD_BOOT_ACK, NULL, 0);
 	if (error) {
 		dev_err(&client->dev, "Ack obj command failed: %d\n", error);
 		return error;
@@ -439,7 +449,7 @@ static int raydium_i2c_wrt_object(struct i2c_client *client,
 
 	error = raydium_i2c_bl_chk_state(client, state);
 	if (error) {
-		dev_err(&client->dev, "boot trigger state failed: %d\n", error);
+		dev_err(&client->dev, "BL check state failed: %d\n", error);
 		return error;
 	}
 
@@ -448,65 +458,67 @@ static int raydium_i2c_wrt_object(struct i2c_client *client,
 
 static bool raydium_i2c_boot_trigger(struct i2c_client *client)
 {
-	int error;
-	u8 u8_idx;
 	static const u8 cmd[7][6] = {
-			{0x08, 0x0C, 0x09, 0x00, 0x50, 0xD7},
-			{0x08, 0x04, 0x09, 0x00, 0x50, 0xA5},
-			{0x08, 0x04, 0x09, 0x00, 0x50, 0x00},
-			{0x08, 0x04, 0x09, 0x00, 0x50, 0xA5},
-			{0x08, 0x0C, 0x09, 0x00, 0x50, 0x00},
-			{0x06, 0x01, 0x00, 0x00, 0x00, 0x00},
-			{0x02, 0xA2, 0x00, 0x00, 0x00, 0x00},
-		};
-
-	/*sequtial cmd*/
-	for (u8_idx = 0 ; u8_idx < 7 ; u8_idx++) {
-		error = raydium_i2c_wrt_object(client, (u8 *)cmd[u8_idx],
-				sizeof(cmd[u8_idx]), WAIT_READY);
+		{ 0x08, 0x0C, 0x09, 0x00, 0x50, 0xD7 },
+		{ 0x08, 0x04, 0x09, 0x00, 0x50, 0xA5 },
+		{ 0x08, 0x04, 0x09, 0x00, 0x50, 0x00 },
+		{ 0x08, 0x04, 0x09, 0x00, 0x50, 0xA5 },
+		{ 0x08, 0x0C, 0x09, 0x00, 0x50, 0x00 },
+		{ 0x06, 0x01, 0x00, 0x00, 0x00, 0x00 },
+		{ 0x02, 0xA2, 0x00, 0x00, 0x00, 0x00 },
+	};
+	int i;
+	int error;
+
+	for (i = 0; i < 7; i++) {
+		error = raydium_i2c_write_object(client, cmd[i], sizeof(cmd[i]),
+						 RAYDIUM_WAIT_READY);
 		if (error) {
-			dev_err(&client->dev, "send boot trigger 1st_cmd failed: %d\n",
-				error);
+			dev_err(&client->dev,
+				"boot trigger failed at step %d: %d\n",
+				i, error);
 			return error;
 		}
 	}
+
 	return 0;
 }
 
 static bool raydium_i2c_fw_trigger(struct i2c_client *client)
 {
-	int error;
-	u8 u8_idx;
 	static const u8 cmd[5][11] = {
-			{0, 0x09, 0x71, 0x0C, 0x09, 0x00, 0x50, 0xD7, 0, 0, 0},
-			{0, 0x09, 0x71, 0x04, 0x09, 0x00, 0x50, 0xA5, 0, 0, 0},
-			{0, 0x09, 0x71, 0x04, 0x09, 0x00, 0x50, 0x00, 0, 0, 0},
-			{0, 0x09, 0x71, 0x04, 0x09, 0x00, 0x50, 0xA5, 0, 0, 0},
-			{0, 0x09, 0x71, 0x0C, 0x09, 0x00, 0x50, 0x00, 0, 0, 0},
-		};
-
-	/*sequtial cmd*/
-	for (u8_idx = 0 ; u8_idx < 5 ; u8_idx++) {
-		error = raydium_i2c_wrt_object(client, (u8 *)cmd[u8_idx],
-				sizeof(cmd[u8_idx]), ACK_NULL);
+		{ 0, 0x09, 0x71, 0x0C, 0x09, 0x00, 0x50, 0xD7, 0, 0, 0 },
+		{ 0, 0x09, 0x71, 0x04, 0x09, 0x00, 0x50, 0xA5, 0, 0, 0 },
+		{ 0, 0x09, 0x71, 0x04, 0x09, 0x00, 0x50, 0x00, 0, 0, 0 },
+		{ 0, 0x09, 0x71, 0x04, 0x09, 0x00, 0x50, 0xA5, 0, 0, 0 },
+		{ 0, 0x09, 0x71, 0x0C, 0x09, 0x00, 0x50, 0x00, 0, 0, 0 },
+	};
+	int i;
+	int error;
+
+	for (i = 0; i < 5; i++) {
+		error = raydium_i2c_write_object(client, cmd[i], sizeof(cmd[i]),
+						 RAYDIUM_ACK_NULL);
 		if (error) {
-			dev_err(&client->dev, "send fw trigger 1st_cmd failed: %d\n",
-				error);
+			dev_err(&client->dev,
+				"fw trigger failed at step %d: %d\n",
+				i, error);
 			return error;
 		}
 	}
+
 	return 0;
 }
 
 static int raydium_i2c_check_path(struct i2c_client *client)
 {
-	static const u8 cmd[] = {0x09, 0x00, 0x09, 0x00, 0x50, 0x10, 0x00};
+	static const u8 cmd[] = { 0x09, 0x00, 0x09, 0x00, 0x50, 0x10, 0x00 };
 	int error;
 
-	error = raydium_i2c_wrt_object(client, (u8 *)cmd, sizeof(cmd),
-			PATH_READY);
+	error = raydium_i2c_write_object(client, cmd, sizeof(cmd),
+					 RAYDIUM_PATH_READY);
 	if (error) {
-		dev_err(&client->dev, "send chk path cmd fail: %d\n", error);
+		dev_err(&client->dev, "check path command failed: %d\n", error);
 		return error;
 	}
 
@@ -515,49 +527,50 @@ static int raydium_i2c_check_path(struct i2c_client *client)
 
 static int raydium_i2c_enter_bl(struct i2c_client *client)
 {
-	static const u8 cal_cmd[] = {0x00, 0x01, 0x52};
+	static const u8 cal_cmd[] = { 0x00, 0x01, 0x52 };
 	int error;
 
-	error = raydium_i2c_wrt_object(client, (u8 *)cal_cmd,
-			sizeof(cal_cmd), ACK_NULL);
+	error = raydium_i2c_write_object(client, cal_cmd, sizeof(cal_cmd),
+					 RAYDIUM_ACK_NULL);
 	if (error) {
-		dev_err(&client->dev, "send jump loader cmd fail: %d\n", error);
+		dev_err(&client->dev, "enter bl command failed: %d\n", error);
 		return error;
 	}
-	msleep(BOOT_DELAY_MS);
+
+	msleep(RM_BOOT_DELAY_MS);
 	return 0;
 }
 
 static int raydium_i2c_leave_bl(struct i2c_client *client)
 {
-	static const u8 leave_cmd[] = {0x05, 0x00};
+	static const u8 leave_cmd[] = { 0x05, 0x00 };
 	int error;
 
-	error = raydium_i2c_wrt_object(client, (u8 *)leave_cmd,
-			sizeof(leave_cmd), ACK_NULL);
+	error = raydium_i2c_write_object(client, leave_cmd, sizeof(leave_cmd),
+					 RAYDIUM_ACK_NULL);
 	if (error) {
-		dev_err(&client->dev, "send leave bl cmd fail: %d\n", error);
+		dev_err(&client->dev, "leave bl command failed: %d\n", error);
 		return error;
 	}
-	msleep(BOOT_DELAY_MS);
+
+	msleep(RM_BOOT_DELAY_MS);
 	return 0;
 }
 
 static int raydium_i2c_write_checksum(struct i2c_client *client,
-	size_t length, u16 checksum)
+				      size_t length, u16 checksum)
 {
-	u8 checksum_cmd[] = {0x00, 0x05, 0x6D, 0x00, 0x00, 0x00, 0x00};
-	int error = 0;
+	u8 checksum_cmd[] = { 0x00, 0x05, 0x6D, 0x00, 0x00, 0x00, 0x00 };
+	int error;
 
-	checksum_cmd[3] = (u8)(length & 0xFF);
-	checksum_cmd[4] = (u8)((length & 0xFF00) >> 8);
-	checksum_cmd[5] = (u8)(checksum & 0xFF);
-	checksum_cmd[6] = (u8)((checksum & 0xFF00) >> 8);
+	put_unaligned_le16(length, &checksum_cmd[3]);
+	put_unaligned_le16(checksum, &checksum_cmd[5]);
 
-	error = raydium_i2c_wrt_object(client, (u8 *)checksum_cmd,
-			sizeof(checksum_cmd), ACK_NULL);
+	error = raydium_i2c_write_object(client,
+					 checksum_cmd, sizeof(checksum_cmd),
+					 RAYDIUM_ACK_NULL);
 	if (error) {
-		dev_err(&client->dev, "send wrt checksum cmd fail: %d\n",
+		dev_err(&client->dev, "failed to write checksum: %d\n",
 			error);
 		return error;
 	}
@@ -567,13 +580,13 @@ static int raydium_i2c_write_checksum(struct i2c_client *client,
 
 static int raydium_i2c_disable_watch_dog(struct i2c_client *client)
 {
-	static const u8 cmd[] = {0x0A, 0xAA};
-	int error = 0;
+	static const u8 cmd[] = { 0x0A, 0xAA };
+	int error;
 
-	error = raydium_i2c_wrt_object(client, (u8 *)cmd, sizeof(cmd),
-			WAIT_READY);
+	error = raydium_i2c_write_object(client, cmd, sizeof(cmd),
+					 RAYDIUM_WAIT_READY);
 	if (error) {
-		dev_err(&client->dev, "send disable watchdog cmd fail: %d\n",
+		dev_err(&client->dev, "disable watchdog command failed: %d\n",
 			error);
 		return error;
 	}
@@ -582,34 +595,38 @@ static int raydium_i2c_disable_watch_dog(struct i2c_client *client)
 }
 
 static int raydium_i2c_fw_write_page(struct i2c_client *client,
-				u8 *page, size_t len)
+				     u8 page_idx, const void *data, size_t len)
 {
+	u8 buf[RM_BL_WRT_LEN];
+	u8 pkg_idx = 1;
+	size_t xfer_len;
 	int error;
-	u8 buf[MAX_BOOT_WRT_LEN];
-	u8 u8_idx, div_cnt;
-
-	len -= CMD_BOOT_HEADER_LEN;
-
-	div_cnt = len % RAYDIUM_TRANS_BUFSIZE ?
-		len / RAYDIUM_TRANS_BUFSIZE + 1:len / RAYDIUM_TRANS_BUFSIZE;
 
-	for (u8_idx = 0 ; u8_idx < div_cnt ; u8_idx++) {
-		buf[HEADER] = page[0];
-		buf[PAGE_STR] = page[1];
-		buf[PKG_IDX] = u8_idx + 1;
+	while (len) {
+		xfer_len = min_t(size_t, len, RM_BL_WRT_PKG_SIZE);
 
-		memcpy(&buf[DATA_STR], page + DATA_STR +
-			u8_idx*RAYDIUM_TRANS_BUFSIZE,
-			RAYDIUM_TRANS_BUFSIZE);
+		buf[BL_HEADER] = 0x0b;
+		// XXX FIXME: is this correct? Do we really want all pages
+		// after 1st to have 0xff? Should it be a counter?
+		// Why do we need both pages and packages within pages?
+		buf[BL_PAGE_STR] = page_idx ? 0 : 0xff;
+		buf[BL_PKG_IDX] = pkg_idx;
+		memcpy(&buf[BL_DATA_STR], data, xfer_len);
 
-		error = raydium_i2c_wrt_object(client, (u8 *)buf, sizeof(buf),
-				WAIT_READY);
+		error = raydium_i2c_write_object(client, buf, xfer_len,
+						 RAYDIUM_WAIT_READY);
 		if (error) {
-			dev_err(&client->dev, "send page wrt cmd failed: %d\n",
-				error);
+			dev_err(&client->dev,
+				"page write command failed for page %d, chunk %d: %d\n",
+				page_idx, pkg_idx, error);
 			return error;
 		}
+
 		msleep(20);
+
+		data += xfer_len;
+		len -= xfer_len;
+		pkg_idx++;
 	}
 
 	return error;
@@ -619,124 +636,121 @@ static int raydium_i2c_do_update_firmware(struct raydium_data *ts,
 					 const struct firmware *fw)
 {
 	struct i2c_client *client = ts->client;
-	u8 u8_idx;
+	const void *data;
+	size_t data_len;
+	size_t len;
+	int page_nr;
+	int i;
+	int error;
 	u16 fw_checksum;
-	u8 buf[RAYDIUM_PAGE_SIZE + CMD_BOOT_HEADER_LEN];
-	int page, n_fw_pages;
-	int error, fw_idx;
 
 	if (fw->size == 0) {
 		dev_err(&client->dev, "Invalid firmware length\n");
 		return -EINVAL;
 	}
 
-	if (fw->size % RAYDIUM_PAGE_SIZE)
-		n_fw_pages = fw->size/RAYDIUM_PAGE_SIZE + 1;
-	else
-		n_fw_pages = fw->size/RAYDIUM_PAGE_SIZE;
-
 	error = raydium_i2c_check_fw_status(ts);
 	if (error) {
 		dev_err(&client->dev, "Unable to access IC %d\n", error);
-			return error;
+		return error;
 	}
 
 	if (ts->boot_mode == RAYDIUM_TS_MAIN) {
-		for (u8_idx = 0; u8_idx < MAX_RETRIES; u8_idx++) {
+		for (i = 0; i < RM_MAX_RETRIES; i++) {
 			error = raydium_i2c_enter_bl(client);
 			if (!error) {
 				error = raydium_i2c_check_fw_status(ts);
 				if (error) {
-					dev_err(&client->dev, "Unable to access IC %d\n",
+					dev_err(&client->dev,
+						"unable to access IC: %d\n",
 						error);
 					return error;
 				}
+
 				if (ts->boot_mode == RAYDIUM_TS_BLDR)
 					break;
 			}
 		}
+
 		if (ts->boot_mode == RAYDIUM_TS_MAIN) {
-			dev_err(&client->dev, "Fail jump to boot loader %d\n",
-					error);
+			dev_err(&client->dev,
+				"failied to jump to boot loader: %d\n",
+				error);
 			return -EIO;
 		}
 	}
 
 	error = raydium_i2c_disable_watch_dog(client);
-	if (error) {
-		dev_err(&client->dev, "send disable watchdog cmd fail, %d\n",
-			error);
+	if (error)
 		return error;
-	}
 
 	error = raydium_i2c_check_path(client);
-	if (error) {
-		dev_err(&client->dev, "send chk path fail, %d\n", error);
+	if (error)
 		return error;
-	}
 
 	error = raydium_i2c_boot_trigger(client);
 	if (error) {
-		dev_err(&client->dev, "send boot trigger fail, %d\n", error);
+		dev_err(&client->dev, "send boot trigger fail: %d\n", error);
 		return error;
 	}
 
-	fw_checksum = 0;
-	fw_idx = 0;
-	for (page = 0 ; page < n_fw_pages ; page++) {
-		memset(buf, 0xFF, sizeof(buf));
-		buf[HEADER] = 0x0B;
-		if (page == 0)
-			buf[PAGE_STR] = 0x00;
-
-		for (u8_idx = 0; u8_idx < RAYDIUM_PAGE_SIZE; u8_idx++) {
-			if (fw_idx < fw->size) {
-				buf[DATA_STR + u8_idx] =
-					fw->data[page*RAYDIUM_PAGE_SIZE +
-					u8_idx];
-				fw_checksum += buf[3 + u8_idx];
-				fw_idx++;
-			}
-		}
+	data = fw->data;
+	data_len = fw->size;
+	page_nr = 0;
+
+	while (data_len) {
+		len = min_t(size_t, data_len, RM_FW_PAGE_SIZE);
+
+		error = raydium_i2c_fw_write_page(client, page_nr++, data, len);
+		if (error)
+			return error;
 
-		raydium_i2c_fw_write_page(client, (u8 *)buf, sizeof(buf));
+		// XXX FIXME: we already sleep in raydium_i2c_fw_write_page(),
+		// do we really need to sleep here as well?
 		msleep(20);
+
+		data += len;
+		data_len -= len;
 	}
 
 	error = raydium_i2c_leave_bl(client);
 	if (error) {
-		dev_err(&client->dev, "leave boot loader fail: %d\n", error);
+		dev_err(&client->dev,
+			"failed to leave boot loader: %d\n", error);
 		return error;
 	}
-	dev_err(&client->dev, "leave boot loader success\n");
+
+	dev_dbg(&client->dev, "left boot loader mode\n");
 
 	error = raydium_i2c_check_fw_status(ts);
 	if (error) {
-		dev_err(&client->dev, "Unable to access IC %d\n", error);
+		dev_err(&client->dev,
+			"failed to check fw status after write: %d\n",
+			error);
 		return error;
 	}
 
-	if (ts->boot_mode == RAYDIUM_TS_MAIN) {
-
-		error = raydium_i2c_fw_trigger(client);
-		if (error) {
-			dev_err(&client->dev, "send fw trigger fail, %d\n",
-				error);
-			return error;
-		}
-
-		error = raydium_i2c_write_checksum(client, fw->size,
-			fw_checksum);
-		if (error) {
-			dev_err(&client->dev, "write checksum fail %d\n",
-				error);
-			return error;
-		}
-	} else {
-		dev_err(&client->dev, "switch to main_fw fail %d\n", error);
+	if (ts->boot_mode != RAYDIUM_TS_MAIN) {
+		dev_err(&client->dev,
+			"failed to switch to main fw after writing firmware: %d\n",
+			error);
 		return -EINVAL;
 	}
 
+	error = raydium_i2c_fw_trigger(client);
+	if (error) {
+		dev_err(&client->dev, "failed to trigger fw: %d\n", error);
+		return error;
+	}
+
+	fw_checksum = 0;
+	for (i = 0; i < fw->size; i++)
+		fw_checksum += fw->data[i];
+
+	error = raydium_i2c_write_checksum(client, fw->size, fw_checksum);
+	if (error)
+		return error;
+
 	return 0;
 }
 
@@ -752,7 +766,7 @@ static int raydium_i2c_fw_update(struct raydium_data *ts)
 		dev_err(&client->dev, "Unable to open firmware %s\n", fw_file);
 		return error;
 	}
-	/*disable irq*/
+
 	disable_irq(client->irq);
 
 	error = raydium_i2c_do_update_firmware(ts, fw);
@@ -777,46 +791,48 @@ out_enable_irq:
 	enable_irq(client->irq);
 	msleep(100);
 
+	release_firmware(fw);
+
 	return error;
 }
 
 static void raydium_mt_event(struct raydium_data *ts)
 {
-	u8 data[MAX_PkG_SIZE];
-	int error, i;
-	int x, y, f_state, pressure, wx, wy;
-
-	error = raydium_i2c_read_message(ts->client, ts->obj.data_bank_addr,
-		ts->obj.pkg_size, (void *)data);
+	int i;
+	int error;
 
-	if (error < 0) {
+	error = raydium_i2c_read_message(ts->client, ts->data_bank_addr,
+					 ts->report_data, ts->report_size);
+	if (error) {
 		dev_err(&ts->client->dev, "%s: failed to read data: %d\n",
 			__func__, error);
 		return;
 	}
 
-	for (i = 0; i < MAX_TOUCH_NUM; i++) {
-		f_state = (data + i * ts->obj.tp_info_size)[POS_STATE];
-		pressure = (data + i * ts->obj.tp_info_size)[POS_PRESSURE];
-		wx = (data + i * ts->obj.tp_info_size)[WIDTH_X];
-		wy = (data + i * ts->obj.tp_info_size)[WIDTH_Y];
+
+	for (i = 0; i < ts->report_size / ts->contact_size; i++) {
+		u8 *contact = &ts->report_data[ts->contact_size * i];
+		bool state = contact[RM_CONTACT_STATE_POS];
+		u8 wx, wy;
 
 		input_mt_slot(ts->input, i);
-		input_mt_report_slot_state(ts->input,
-				MT_TOOL_FINGER, f_state != 0);
+		input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, state);
 
-		if (!f_state)
+		if (!state)
 			continue;
-		x = get_unaligned_le16(&data[i * ts->obj.tp_info_size + POS_X]);
-		y = get_unaligned_le16(&data[i * ts->obj.tp_info_size + POS_Y]);
-
-		input_report_abs(ts->input, ABS_MT_POSITION_X, x);
-		input_report_abs(ts->input, ABS_MT_POSITION_Y, y);
-		input_report_abs(ts->input, ABS_MT_PRESSURE, pressure);
-		input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR,
-			max(wx, wy));
-		input_report_abs(ts->input, ABS_MT_TOUCH_MINOR,
-			min(wx, wy));
+
+		input_report_abs(ts->input, ABS_MT_POSITION_X,
+				get_unaligned_le16(&contact[RM_CONTACT_X_POS]));
+		input_report_abs(ts->input, ABS_MT_POSITION_Y,
+				get_unaligned_le16(&contact[RM_CONTACT_Y_POS]));
+		input_report_abs(ts->input, ABS_MT_PRESSURE,
+				contact[RM_CONTACT_PRESSURE_POS]);
+
+		wx = contact[RM_CONTACT_WIDTH_X_POS];
+		wy = contact[RM_CONTACT_WIDTH_Y_POS];
+
+		input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, max(wx, wy));
+		input_report_abs(ts->input, ABS_MT_TOUCH_MINOR, min(wx, wy));
 	}
 
 	input_mt_sync_frame(ts->input);
@@ -833,31 +849,42 @@ static irqreturn_t raydium_i2c_irq(int irq, void *_dev)
 	return IRQ_HANDLED;
 }
 
-static ssize_t raydium_calibrate(struct device *dev,
-				   struct device_attribute *attr,
-				   const char *buf, size_t count)
+static ssize_t raydium_i2c_fw_ver_show(struct device *dev,
+				       struct device_attribute *attr, char *buf)
 {
-	struct raydium_data *ts = dev_get_drvdata(dev);
-	struct i2c_client *client = ts->client;
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	return sprintf(buf, "%d.%d\n", ts->info.main_ver, ts->info.sub_ver);
+}
 
-	static const u8 cal_cmd[] = {0x00, 0x01, 0x9E};
-	int error = 0;
+static ssize_t raydium_i2c_hw_ver_show(struct device *dev,
+				       struct device_attribute *attr, char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
 
-	error = raydium_i2c_wrt_object(client, (u8 *)cal_cmd,
-			sizeof(cal_cmd), WAIT_READY);
-	if (error) {
-		dev_err(&client->dev, "send chk path cmd fail: %d\n", error);
-		return error;
-	}
+	return sprintf(buf, "%#04x\n", le32_to_cpu(ts->info.hw_ver));
+}
 
-	return error;
+static ssize_t raydium_i2c_boot_mode_show(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	return sprintf(buf, "%s\n",
+		       ts->boot_mode == RAYDIUM_TS_MAIN ?
+				"Normal" : "Recovery");
 }
 
-static ssize_t write_update_fw(struct device *dev,
-			struct device_attribute *attr,
-			const char *buf, size_t count)
+static ssize_t raydium_i2c_update_fw_store(struct device *dev,
+					   struct device_attribute *attr,
+					   const char *buf, size_t count)
 {
-	struct raydium_data *ts = dev_get_drvdata(dev);
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
 	int error;
 
 	error = mutex_lock_interruptible(&ts->sysfs_mutex);
@@ -871,38 +898,35 @@ static ssize_t write_update_fw(struct device *dev,
 	return error ?: count;
 }
 
-static ssize_t raydium_bootmode_show(struct device *dev,
-		struct device_attribute *attr, char *buf)
+static ssize_t raydium_i2c_calibrate_store(struct device *dev,
+					   struct device_attribute *attr,
+					   const char *buf, size_t count)
 {
-	struct raydium_data *ts = dev_get_drvdata(dev);
-
-	return sprintf(buf, "%s\n", ts->boot_mode == RAYDIUM_TS_MAIN ?
-		"Normal" : "Recovery");
-}
-
-static ssize_t raydium_fw_ver_show(struct device *dev,
-			struct device_attribute *attr, char *buf)
-{
-	struct raydium_data *ts = dev_get_drvdata(dev);
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
+	static const u8 cal_cmd[] = { 0x00, 0x01, 0x9E };
+	int error;
 
-	return sprintf(buf, "%d.%d\n", ts->info.main_ver, ts->info.sub_ver);
-}
+	error = mutex_lock_interruptible(&ts->sysfs_mutex);
+	if (error)
+		return error;
 
-static ssize_t raydium_hw_ver_show(struct device *dev,
-			struct device_attribute *attr, char *buf)
-{
-	struct raydium_data *ts = dev_get_drvdata(dev);
+	error = raydium_i2c_write_object(client, cal_cmd, sizeof(cal_cmd),
+					 RAYDIUM_WAIT_READY);
+	if (error)
+		dev_err(&client->dev, "calibrate command failed: %d\n", error);
 
-	return sprintf(buf, "%04x\n", ts->info.hw_ver);
+	mutex_unlock(&ts->sysfs_mutex);
+	return error ?: count;
 }
 
-static DEVICE_ATTR(fw_version, S_IRUGO, raydium_fw_ver_show, NULL);
-static DEVICE_ATTR(hw_version, S_IRUGO, raydium_hw_ver_show, NULL);
-static DEVICE_ATTR(boot_mode, S_IRUGO, raydium_bootmode_show, NULL);
-static DEVICE_ATTR(update_fw, S_IWUSR, NULL, write_update_fw);
-static DEVICE_ATTR(calibrate, S_IWUSR, NULL, raydium_calibrate);
+static DEVICE_ATTR(fw_version, S_IRUGO, raydium_i2c_fw_ver_show, NULL);
+static DEVICE_ATTR(hw_version, S_IRUGO, raydium_i2c_hw_ver_show, NULL);
+static DEVICE_ATTR(boot_mode, S_IRUGO, raydium_i2c_boot_mode_show, NULL);
+static DEVICE_ATTR(update_fw, S_IWUSR, NULL, raydium_i2c_update_fw_store);
+static DEVICE_ATTR(calibrate, S_IWUSR, NULL, raydium_i2c_calibrate_store);
 
-static struct attribute *raydium_attributes[] = {
+static struct attribute *raydium_i2c_attributes[] = {
 	&dev_attr_update_fw.attr,
 	&dev_attr_boot_mode.attr,
 	&dev_attr_fw_version.attr,
@@ -911,15 +935,15 @@ static struct attribute *raydium_attributes[] = {
 	NULL
 };
 
-static struct attribute_group raydium_attribute_group = {
-	.attrs = raydium_attributes,
+static struct attribute_group raydium_i2c_attribute_group = {
+	.attrs = raydium_i2c_attributes,
 };
 
 static void raydium_i2c_remove_sysfs_group(void *_data)
 {
 	struct raydium_data *ts = _data;
 
-	sysfs_remove_group(&ts->client->dev.kobj, &raydium_attribute_group);
+	sysfs_remove_group(&ts->client->dev.kobj, &raydium_i2c_attribute_group);
 }
 
 static int raydium_i2c_power_on(struct raydium_data *ts)
@@ -946,7 +970,7 @@ static int raydium_i2c_power_on(struct raydium_data *ts)
 		goto release_reset_gpio;
 	}
 
-	udelay(RAYDIUM_POWERON_DELAY_USEC);
+	udelay(RM_POWERON_DELAY_USEC);
 
 release_reset_gpio:
 	gpiod_set_value_cansleep(ts->reset_gpio, 0);
@@ -954,7 +978,7 @@ release_reset_gpio:
 	if (error)
 		return error;
 
-	msleep(RAYDIUM_RESET_DELAY_MSEC);
+	msleep(RM_RESET_DELAY_MSEC);
 
 	return 0;
 }
@@ -971,7 +995,7 @@ static void raydium_i2c_power_off(void *_data)
 }
 
 static int raydium_i2c_probe(struct i2c_client *client,
-			    const struct i2c_device_id *id)
+			     const struct i2c_device_id *id)
 {
 	union i2c_smbus_data dummy;
 	struct raydium_data *ts;
@@ -979,17 +1003,15 @@ static int raydium_i2c_probe(struct i2c_client *client,
 
 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
 		dev_err(&client->dev,
-			"%s: i2c check functionality error\n", DEVICE_NAME);
+			"i2c check functionality error (need I2C_FUNC_I2C)\n");
 		return -ENXIO;
 	}
 
-	ts = devm_kzalloc(&client->dev, sizeof(struct raydium_data),
-		GFP_KERNEL);
+	ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL);
 	if (!ts)
 		return -ENOMEM;
 
 	mutex_init(&ts->sysfs_mutex);
-	init_completion(&ts->cmd_done);
 
 	ts->client = client;
 	i2c_set_clientdata(client, ts);
@@ -1013,7 +1035,7 @@ static int raydium_i2c_probe(struct i2c_client *client,
 	}
 
 	ts->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
-		GPIOD_OUT_LOW);
+						 GPIOD_OUT_LOW);
 	if (IS_ERR(ts->reset_gpio)) {
 		error = PTR_ERR(ts->reset_gpio);
 		if (error != -EPROBE_DEFER) {
@@ -1037,7 +1059,7 @@ static int raydium_i2c_probe(struct i2c_client *client,
 
 	/* Make sure there is something at this address */
 	if (i2c_smbus_xfer(client->adapter, client->addr, 0,
-			I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0) {
+			   I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0) {
 		dev_err(&client->dev, "nothing at this address\n");
 		return -ENXIO;
 	}
@@ -1057,20 +1079,20 @@ static int raydium_i2c_probe(struct i2c_client *client,
 	ts->input->name = "Raydium Touchscreen";
 	ts->input->id.bustype = BUS_I2C;
 
-	/* Multitouch input params setup */
-	input_set_abs_params(ts->input, ABS_MT_POSITION_X, 0,
-		ts->info.x_max, 0, 0);
+	input_set_drvdata(ts->input, ts);
+
+	input_set_abs_params(ts->input, ABS_MT_POSITION_X,
+			     0, le32_to_cpu(ts->info.x_max), 0, 0);
 	input_set_abs_params(ts->input, ABS_MT_POSITION_Y,
-		0, ts->info.y_max, 0, 0);
-	input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
-	input_set_abs_params(ts->input, ABS_MT_PRESSURE, 0, 255, 0, 0);
+			     0, le32_to_cpu(ts->info.y_max), 0, 0);
 	input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->info.x_res);
 	input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->info.y_res);
 
-	input_set_drvdata(ts->input, ts);
+	input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+	input_set_abs_params(ts->input, ABS_MT_PRESSURE, 0, 255, 0, 0);
 
-	error = input_mt_init_slots(ts->input, MAX_TOUCH_NUM,
-		INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+	error = input_mt_init_slots(ts->input, RM_MAX_TOUCH_NUM,
+				    INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
 	if (error) {
 		dev_err(&client->dev,
 			"failed to initialize MT slots: %d\n", error);
@@ -1084,15 +1106,16 @@ static int raydium_i2c_probe(struct i2c_client *client,
 		return error;
 	}
 
-	error = devm_request_threaded_irq(&client->dev, client->irq, NULL,
-		raydium_i2c_irq, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
-			client->name, ts);
+	error = devm_request_threaded_irq(&client->dev, client->irq,
+					  NULL, raydium_i2c_irq,
+					  IRQF_ONESHOT, client->name, ts);
 	if (error) {
 		dev_err(&client->dev, "Failed to register interrupt\n");
 		return error;
 	}
 
-	error = sysfs_create_group(&client->dev.kobj, &raydium_attribute_group);
+	error = sysfs_create_group(&client->dev.kobj,
+				   &raydium_i2c_attribute_group);
 	if (error) {
 		dev_err(&client->dev, "failed to create sysfs attributes: %d\n",
 			error);
@@ -1116,11 +1139,11 @@ static void __maybe_unused raydium_enter_sleep(struct i2c_client *client)
 	static const u8 sleep_cmd[] = { 0x5A, 0xff, 0x00, 0x0f };
 	int error;
 
-	error = raydium_i2c_send(client, CMD_ENTER_SLEEP, (u8 *)sleep_cmd,
-		sizeof(sleep_cmd));
+	error = raydium_i2c_send(client, RM_CMD_ENTER_SLEEP,
+				 sleep_cmd, sizeof(sleep_cmd));
 	if (error)
 		dev_err(&client->dev,
-			"Send sleep failed: %d\n", error);
+			"sleep command failed: %d\n", error);
 }
 
 static int __maybe_unused raydium_i2c_suspend(struct device *dev)
@@ -1128,7 +1151,7 @@ static int __maybe_unused raydium_i2c_suspend(struct device *dev)
 	struct i2c_client *client = to_i2c_client(dev);
 	struct raydium_data *ts = i2c_get_clientdata(client);
 
-	/* Command not support in BLDR recovery mode */
+	/* Sleep is not available in BLDR recovery mode */
 	if (ts->boot_mode != RAYDIUM_TS_MAIN)
 		return -ENOMEM;
 
@@ -1168,22 +1191,23 @@ static SIMPLE_DEV_PM_OPS(raydium_i2c_pm_ops,
 			 raydium_i2c_suspend, raydium_i2c_resume);
 
 static const struct i2c_device_id raydium_i2c_id[] = {
-	{ DEVICE_NAME, 0 },
-	{ }
+	{ "raydium_i2c" , 0 },
+	{ "rm32380", 0 },
+	{ /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(i2c, raydium_i2c_id);
 
 #ifdef CONFIG_ACPI
 static const struct acpi_device_id raydium_acpi_id[] = {
 	{ "RAYD0001", 0 },
-	{ }
+	{ /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(acpi, raydium_acpi_id);
 #endif
 
 #ifdef CONFIG_OF
 static const struct of_device_id raydium_of_match[] = {
-	{ .compatible = "raydium,rm32380",},
+	{ .compatible = "raydium,rm32380", },
 	{ /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, raydium_of_match);
@@ -1199,7 +1223,6 @@ static struct i2c_driver raydium_i2c_driver = {
 		.of_match_table = of_match_ptr(raydium_of_match),
 	},
 };
-
 module_i2c_driver(raydium_i2c_driver);
 
 MODULE_AUTHOR("Raydium");
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2016-05-13  4:18   ` Dmitry Torokhov
@ 2016-05-13 17:08     ` jeffrey.lin
  2016-05-16 15:46     ` jeffrey.lin
                       ` (6 subsequent siblings)
  7 siblings, 0 replies; 42+ messages in thread
From: jeffrey.lin @ 2016-05-13 17:08 UTC (permalink / raw)
  To: dmitry.torokhov, rydberg, grant.likely, robh+dt, jeesw, bleung
  Cc: jeffrey.lin, roger.yang, KP.li, albert.shieh, linux-kernel,
	linux-input, devicetree

Hi Dmitry:
This patch you submitted had some problems. I still debug in progress. Should I comment the issues in this patch or create a new patch if I finish the issues?

 >static int raydium_i2c_fastboot(struct i2c_client *client)
 > {
 >-	static const u8 boot_cmd[] = { 0x50, 0x00, 0x06, 0x20 };
 >-	u8 buf[HEADER_SIZE];
 >+	u8 buf[4]; // XXX FIXME why size 4 and not 1?
Correct.Raydium direct access mode is word alignment, so that 4 bytes reading is correct but only lower bytes show the message I need.

 >static int raydium_i2c_check_fw_status(struct raydium_data *ts)
 >{
 >	struct i2c_client *client = ts->client;
 >-	static const u8 bl_area[] = {0x62, 0x6f, 0x6f, 0x74};
 >-	static const u8 main_area[] = {0x66, 0x69, 0x72, 0x6d};
 >-	u8 buf[HEADER_SIZE];
 >+	static const u8 bl_area[] = { 0x62, 0x6f, 0x6f, 0x74 };
 >+	static const u8 main_area[] = { 0x66, 0x69, 0x72, 0x6d };
 >+	u8 buf[4];
 > 	int error;
 > 
 >-	error = raydium_i2c_read(client, CMD_BOOT_READ, HEADER_SIZE,
 >-		(void *)buf);
 >+	error = raydium_i2c_read(client, RM_CMD_BOOT_READ, buf, sizeof(buf));
 > 	if (!error) {
 >+		// XXX FIXME: why do we compare only 1st bytes? Do we even
 >+		// need 4-byte constants?
One bytes comparison is fine. I'll change as below:

static int raydium_i2c_check_fw_status(struct raydium_data *ts)
{
	struct i2c_client *client = ts->client;
	static const u8 bl_ack = 0x62;
	static const u8 main_ack = 0x66;
	u8 buf[4];
	int error;

	error = raydium_i2c_read(client, RM_CMD_BOOT_READ, buf, sizeof(buf));
	if (!error) {
		// XXX FIXME: why do we compare only 1st bytes? Do we even
		// need 4-byte constants?
		if (buf[0] == bl_ack)
			ts->boot_mode = RAYDIUM_TS_BLDR;
		else if (buf[0] == main_ack)
			ts->boot_mode = RAYDIUM_TS_MAIN;
		return 0;

>+	while (len) {
>+		xfer_len = min_t(size_t, len, RM_BL_WRT_PKG_SIZE);
> 
>-		memcpy(&buf[DATA_STR], page + DATA_STR +
>-			u8_idx*RAYDIUM_TRANS_BUFSIZE,
>-			RAYDIUM_TRANS_BUFSIZE);
>+		buf[BL_HEADER] = 0x0b;
>+		// XXX FIXME: is this correct? Do we really want all pages
>+		// after 1st to have 0xff? Should it be a counter?
>+		// Why do we need both pages and packages within pages?
>+		buf[BL_PAGE_STR] = page_idx ? 0 : 0xff;
This is correct. Touch MCU need this index as start page.
>+		buf[BL_PKG_IDX] = pkg_idx;
This should be:
		buf[BL_PKG_IDX] = pkg_idx++;
>+		memcpy(&buf[BL_DATA_STR], data, xfer_len);

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2016-05-13  4:18   ` Dmitry Torokhov
  2016-05-13 17:08     ` jeffrey.lin
@ 2016-05-16 15:46     ` jeffrey.lin
       [not found]       ` <1463413611-367-1-git-send-email-jeffrey.lin-s3Ivl27awEzQT0dZR+AlfA@public.gmane.org>
  2016-05-16 15:57     ` jeffrey.lin
                       ` (5 subsequent siblings)
  7 siblings, 1 reply; 42+ messages in thread
From: jeffrey.lin @ 2016-05-16 15:46 UTC (permalink / raw)
  To: dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w,
	rydberg-Hk7bIW8heu4wFerOooGFRg,
	grant.likely-QSEj5FYQhm4dnm+yROfE0A,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, jeesw-iQTvn4YitUrQT0dZR+AlfA,
	bleung-F7+t8E8rja9g9hUCZPvPmw
  Cc: jeffrey.lin-s3Ivl27awEzQT0dZR+AlfA,
	roger.yang-s3Ivl27awEzQT0dZR+AlfA, KP.li-s3Ivl27awEzQT0dZR+AlfA,
	albert.shieh-s3Ivl27awEzQT0dZR+AlfA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA

Hi Dmitry:
I've finished issues under the format you suggested as below.

>#define RM_RESET_MSG_ADDR	0x40000004
>#define RM_FASTBOOT_MSG_ADDR	0x50000620

>#define RM_MAX_READ_SIZE	63
change maximum read size to 56 bytes
#define RM_MAX_READ_SIZE	56

>#define RM_CONTACT_X_POS	1
>#define RM_CONTACT_Y_POS	3
>#define RM_CONTACT_PRESSURE_POS	5 // XXX - check - original was 4
#define RM_CONTACT_PRESSURE_POS	5	/*FIXME, correct 5*/

>#define RM_BL_WRT_CMD_SIZE	3	/* bl flash wrt cmd size */
>#define RM_BL_WRT_PKG_SIZE	32	/* bl wrt pkg size */
>#define RM_BL_WRT_LEN		(RM_BL_WRT_PKG_SIZE + RM_BL_WRT_CMD_SIZE)
>#define RM_FW_PAGE_SIZE		128
>#define RM_MAX_FW_RETRIES	30
additional maximum firmware size for protection
#define RM_MAX_FW_SIZE		(0xD000)	/*define max firmware size*/

>static int raydium_i2c_read_message(struct i2c_client *client,
>				    u32 addr, void *data, size_t len)
>{
>	__be32 be_addr;
>	size_t xfer_len;
>	int error;
>	while (len) {
>		xfer_len = min_t(size_t, len, RM_MAX_READ_SIZE);
>
>		be_addr = cpu_to_be32(addr);
>
>		error = raydium_i2c_send(client, RM_CMD_BANK_SWITCH,
>					 &be_addr, sizeof(be_addr));
>		if (!error)
>			error = raydium_i2c_read(client, addr & 0xff,
>						 data, xfer_len);
Change as:
		if (!error)
			error = raydium_i2c_read(client, (be_addr >> 24) & 0xff,
						 data, xfer_len);

>static int raydium_i2c_send_message(struct i2c_client *client,
>				    u32 addr, const void *data, size_t len)
>{
>	__be32 be_addr = cpu_to_be32(addr);
>	int error;
>	error = raydium_i2c_send(client, RM_CMD_BANK_SWITCH,
>				 &be_addr, sizeof(be_addr));
>	if (!error)
>		error = raydium_i2c_send(client, addr & 0xff, data, len);
Change as:
		error = raydium_i2c_send(client, (be_addr >> 24) & 0xff,
				data, len);


>static int raydium_i2c_fastboot(struct i2c_client *client)
>{
	/*FIXME;Correct, direct access mode is word alignment*/
>	u8 buf[4];
>	int error;
>
>	error = raydium_i2c_read_message(client, RM_FASTBOOT_MSG_ADDR,
>					 buf, sizeof(buf));

>static int raydium_i2c_check_fw_status(struct raydium_data *ts)
>{
>	struct i2c_client *client = ts->client;
>	static const u8 bl_area[] = { 0x62, 0x6f, 0x6f, 0x74 };
>	static const u8 main_area[] = { 0x66, 0x69, 0x72, 0x6d };
>	u8 buf[4];
>	int error;
>
>	error = raydium_i2c_read(client, RM_CMD_BOOT_READ, buf, sizeof(buf));
>	if (!error) {
>		// XXX FIXME: why do we compare only 1st bytes? Do we even
>		// need 4-byte constants?
>		if (buf[0] == bl_area[0])
>			ts->boot_mode = RAYDIUM_TS_BLDR;
>		else if (buf[0] == main_area[0])
>			ts->boot_mode = RAYDIUM_TS_MAIN;
>		return 0;
>	}
>
>	return error;
>}
Change as below,
static int raydium_i2c_check_fw_status(struct raydium_data *ts)
{
	struct i2c_client *client = ts->client;
	static const u8 bl_ack = 0x62;
	static const u8 main_ack = 0x66;
	u8 buf[4];
	int error;

	error = raydium_i2c_read(client, RM_CMD_BOOT_READ, buf, sizeof(buf));
	if (!error) {
		/*FIXME, changed as one byte comparison*/
		if (buf[0] == bl_ack)
			ts->boot_mode = RAYDIUM_TS_BLDR;
		else if (buf[0] == main_ack)
			ts->boot_mode = RAYDIUM_TS_MAIN;
		return 0;
	}

	return error;
}

>static int raydium_i2c_fw_write_page(struct i2c_client *client,
>				     u8 page_idx, void *data, size_t len)
>{
>	u8 buf[RM_BL_WRT_LEN];
>	u8 pkg_idx = 1;
>	size_t xfer_len;
>	int error;
>
>	while (len) {
>		xfer_len = min_t(size_t, len, RM_BL_WRT_PKG_SIZE);
>
>		buf[BL_HEADER] = 0x0b;
>		// XXX FIXME: is this correct? Do we really want all pages
>		// after 1st to have 0xff? Should it be a counter?
>		// Why do we need both pages and packages within pages?
>		buf[BL_PAGE_STR] = page_idx ? 0 : 0xff;
>		buf[BL_PKG_IDX] = pkg_idx;
>		memcpy(&buf[BL_DATA_STR], data, xfer_len);
>
>		error = raydium_i2c_write_object(client, buf, xfer_len,
>						 RAYDIUM_WAIT_READY);
>		if (error) {
>			dev_err(&client->dev,
>				"page write command failed for page %d, chunk %d: %d\n",
>				page_idx, pkg_idx, error);
>			return error;
>		}
>
>		msleep(20);
>
>		data += xfer_len;
>		len -= xfer_len;
>		pkg_idx++;
>	}
>
>	return error;
>}
Because of need fully fill 128bytes in pages, so that function change as below:
static int raydium_i2c_fw_write_page(struct i2c_client *client,
				     u16 page_idx, const void *data, size_t len)
{
	u8 buf[RM_BL_WRT_LEN];
	u8 pkg_idx = 1;
	u8 div_cnt;
	size_t xfer_len;
	int error;
	int i;

	div_cnt = len % RM_BL_WRT_PKG_SIZE ?
		len / RM_BL_WRT_PKG_SIZE + 1:len / RM_BL_WRT_PKG_SIZE;

	for (i = 0; i < div_cnt; i++) {
		xfer_len = min_t(size_t, len, RM_BL_WRT_PKG_SIZE);
		buf[BL_HEADER] = RM_CMD_BOOT_PAGE_WRT;
		/*FIXME,Touch MCU need zero index as start page*/
		buf[BL_PAGE_STR] = page_idx ? 0xff : 0;
		buf[BL_PKG_IDX] = pkg_idx++;

		memcpy(&buf[BL_DATA_STR], data, xfer_len);

		if (len == 0)
			memset(buf + BL_DATA_STR, 0xff, RM_BL_WRT_PKG_SIZE);
		else if (len < RM_BL_WRT_PKG_SIZE)
			memset(buf + BL_DATA_STR + xfer_len, 0xff,
			RM_BL_WRT_PKG_SIZE - xfer_len);

		error = raydium_i2c_write_object(client, buf, RM_BL_WRT_LEN,
						 RAYDIUM_WAIT_READY);
		if (error) {
			dev_err(&client->dev,
				"page write command failed for page %d, chunk %d: %d\n",
				page_idx, pkg_idx, error);
			return error;
		}
		data += RM_BL_WRT_PKG_SIZE;
		len -= RM_BL_WRT_PKG_SIZE;
	}

	return error;
}

>static void raydium_mt_event(struct raydium_data *ts)
>{
>	int i;
>	int error;
memory allocate
	ts->report_data = kmalloc(ts->report_size, GFP_KERNEL);
	if (!ts->report_data)
		return;
>
>	error = raydium_i2c_read_message(ts->client, ts->data_bank_addr,
>					 ts->report_data, ts->report_size);
>	if (error) {
>		dev_err(&ts->client->dev, "%s: failed to read data: %d\n",
>			__func__, error);
>		return;
		goto out_of_event;
>	}
>
>
>	for (i = 0; i < ts->report_size / ts->contact_size; i++) {
>		u8 *contact = &ts->report_data[ts->contact_size * i];
>		bool state = contact[RM_CONTACT_STATE_POS];
>		u8 wx, wy;
>
>		input_mt_slot(ts->input, i);
>		input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, state);
>
>		if (!state)
>			continue;
>
>		input_report_abs(ts->input, ABS_MT_POSITION_X,
>				get_unaligned_le16(&contact[RM_CONTACT_X_POS]));
>		input_report_abs(ts->input, ABS_MT_POSITION_Y,
>				get_unaligned_le16(&contact[RM_CONTACT_Y_POS]));
>		input_report_abs(ts->input, ABS_MT_PRESSURE,
>				contact[RM_CONTACT_PRESSURE_POS]);
>
>		wx = contact[RM_CONTACT_WIDTH_X_POS];
>		wy = contact[RM_CONTACT_WIDTH_Y_POS];
>
>		input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, max(wx, wy));
>		input_report_abs(ts->input, ABS_MT_TOUCH_MINOR, min(wx, wy));
>	}
>
>	input_mt_sync_frame(ts->input);
>	input_sync(ts->input);
out_of_event:
	kfree(ts->report_data);
>}


--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2016-05-13  4:18   ` Dmitry Torokhov
  2016-05-13 17:08     ` jeffrey.lin
  2016-05-16 15:46     ` jeffrey.lin
@ 2016-05-16 15:57     ` jeffrey.lin
  2016-05-17 16:07     ` jeffrey.lin
                       ` (4 subsequent siblings)
  7 siblings, 0 replies; 42+ messages in thread
From: jeffrey.lin @ 2016-05-16 15:57 UTC (permalink / raw)
  To: dmitry.torokhov, rydberg, grant.likely, robh+dt, jeesw, bleung
  Cc: jeffrey.lin, roger.yang, KP.li, albert.shieh, linux-kernel,
	linux-input, devicetree

>static int raydium_i2c_do_update_firmware(struct raydium_data *ts,
>					 const struct firmware *fw)
>{
>	struct i2c_client *client = ts->client;
>	const void *data;
>	size_t data_len;
>	size_t len;
>	int page_nr;
>	int i;
>	int error;
>	u16 fw_checksum;
>
>	if (fw->size == 0) {
additional protect.
	if (fw->size == 0 || fw->size > RM_MAX_FW_SIZE) {
>		dev_err(&client->dev, "Invalid firmware length\n");
>		return -EINVAL;
>	}
>
>	error = raydium_i2c_check_fw_status(ts);
>	if (error) {
>		dev_err(&client->dev, "Unable to access IC %d\n", error);
>		return error;
>	}
>
>	if (ts->boot_mode == RAYDIUM_TS_MAIN) {
>		for (i = 0; i < RM_MAX_RETRIES; i++) {
>			error = raydium_i2c_enter_bl(client);
>			if (!error) {
>				error = raydium_i2c_check_fw_status(ts);
>				if (error) {
>					dev_err(&client->dev,
>						"unable to access IC: %d\n",
>						error);
>					return error;
>				}
>
>				if (ts->boot_mode == RAYDIUM_TS_BLDR)
>					break;
>			}
>		}
>
>		if (ts->boot_mode == RAYDIUM_TS_MAIN) {
>			dev_err(&client->dev,
>				"failied to jump to boot loader: %d\n",
>				error);
>			return -EIO;
>		}
>	}
>
>	error = raydium_i2c_disable_watch_dog(client);
>	if (error)
>		return error;
>
>	error = raydium_i2c_check_path(client);
>	if (error)
>		return error;
>
>	error = raydium_i2c_boot_trigger(client);
>	if (error) {
>		dev_err(&client->dev, "send boot trigger fail: %d\n", error);
>		return error;
>	}
additonal delay for safe
	msleep(RM_BOOT_DELAY_MS);
>
>	data = fw->data;
>	data_len = fw->size;
>	page_nr = 0;
>
>	while (data_len) {
>		len = min_t(size_t, data_len, RM_FW_PAGE_SIZE);
>
>		error = raydium_i2c_fw_write_page(client, page_nr++, data, len);
>		if (error)
>			return error;
>
>		// XXX FIXME: we already sleep in raydium_i2c_fw_write_page(),
>		// do we really need to sleep here as well?
		/*FIXME, remove delay in raydium_i2c_fw_write_page()*/
>		msleep(20);
>
>		data += len;
>		data_len -= len;
>	}

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
       [not found]       ` <1463413611-367-1-git-send-email-jeffrey.lin-s3Ivl27awEzQT0dZR+AlfA@public.gmane.org>
@ 2016-05-16 16:41         ` Dmitry Torokhov
  2016-05-16 16:43           ` Dmitry Torokhov
  0 siblings, 1 reply; 42+ messages in thread
From: Dmitry Torokhov @ 2016-05-16 16:41 UTC (permalink / raw)
  To: jeffrey.lin
  Cc: rydberg-Hk7bIW8heu4wFerOooGFRg,
	grant.likely-QSEj5FYQhm4dnm+yROfE0A,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, jeesw-iQTvn4YitUrQT0dZR+AlfA,
	bleung-F7+t8E8rja9g9hUCZPvPmw,
	jeffrey.lin-s3Ivl27awEzQT0dZR+AlfA,
	roger.yang-s3Ivl27awEzQT0dZR+AlfA, KP.li-s3Ivl27awEzQT0dZR+AlfA,
	albert.shieh-s3Ivl27awEzQT0dZR+AlfA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA

On Mon, May 16, 2016 at 11:46:51PM +0800, jeffrey.lin wrote:
> Hi Dmitry:
> I've finished issues under the format you suggested as below.
> 
> >#define RM_RESET_MSG_ADDR	0x40000004
> >#define RM_FASTBOOT_MSG_ADDR	0x50000620
> 
> >#define RM_MAX_READ_SIZE	63
> change maximum read size to 56 bytes
> #define RM_MAX_READ_SIZE	56

OK.

> 
> >#define RM_CONTACT_X_POS	1
> >#define RM_CONTACT_Y_POS	3
> >#define RM_CONTACT_PRESSURE_POS	5 // XXX - check - original was 4
> #define RM_CONTACT_PRESSURE_POS	5	/*FIXME, correct 5*/

OK.

> 
> >#define RM_BL_WRT_CMD_SIZE	3	/* bl flash wrt cmd size */
> >#define RM_BL_WRT_PKG_SIZE	32	/* bl wrt pkg size */
> >#define RM_BL_WRT_LEN		(RM_BL_WRT_PKG_SIZE + RM_BL_WRT_CMD_SIZE)
> >#define RM_FW_PAGE_SIZE		128
> >#define RM_MAX_FW_RETRIES	30
> additional maximum firmware size for protection
> #define RM_MAX_FW_SIZE		(0xD000)	/*define max firmware size*/

OK.

> 
> >static int raydium_i2c_read_message(struct i2c_client *client,
> >				    u32 addr, void *data, size_t len)
> >{
> >	__be32 be_addr;
> >	size_t xfer_len;
> >	int error;
> >	while (len) {
> >		xfer_len = min_t(size_t, len, RM_MAX_READ_SIZE);
> >
> >		be_addr = cpu_to_be32(addr);
> >
> >		error = raydium_i2c_send(client, RM_CMD_BANK_SWITCH,
> >					 &be_addr, sizeof(be_addr));
> >		if (!error)
> >			error = raydium_i2c_read(client, addr & 0xff,
> >						 data, xfer_len);
> Change as:
> 		if (!error)
> 			error = raydium_i2c_read(client, (be_addr >> 24) & 0xff,
> 						 data, xfer_len);

I think it is the same on LE, and I suspect it will not work correctly
on BE... You want to have the 8 least significant bits of the bank to be
used as the address, right?

> 
> >static int raydium_i2c_send_message(struct i2c_client *client,
> >				    u32 addr, const void *data, size_t len)
> >{
> >	__be32 be_addr = cpu_to_be32(addr);
> >	int error;
> >	error = raydium_i2c_send(client, RM_CMD_BANK_SWITCH,
> >				 &be_addr, sizeof(be_addr));
> >	if (!error)
> >		error = raydium_i2c_send(client, addr & 0xff, data, len);
> Change as:
> 		error = raydium_i2c_send(client, (be_addr >> 24) & 0xff,
> 				data, len);

Same here.

> 
> 
> >static int raydium_i2c_fastboot(struct i2c_client *client)
> >{
> 	/*FIXME;Correct, direct access mode is word alignment*/
> >	u8 buf[4];
> >	int error;
> >
> >	error = raydium_i2c_read_message(client, RM_FASTBOOT_MSG_ADDR,
> >					 buf, sizeof(buf));
> 
> >static int raydium_i2c_check_fw_status(struct raydium_data *ts)
> >{
> >	struct i2c_client *client = ts->client;
> >	static const u8 bl_area[] = { 0x62, 0x6f, 0x6f, 0x74 };
> >	static const u8 main_area[] = { 0x66, 0x69, 0x72, 0x6d };
> >	u8 buf[4];
> >	int error;
> >
> >	error = raydium_i2c_read(client, RM_CMD_BOOT_READ, buf, sizeof(buf));
> >	if (!error) {
> >		// XXX FIXME: why do we compare only 1st bytes? Do we even
> >		// need 4-byte constants?
> >		if (buf[0] == bl_area[0])
> >			ts->boot_mode = RAYDIUM_TS_BLDR;
> >		else if (buf[0] == main_area[0])
> >			ts->boot_mode = RAYDIUM_TS_MAIN;
> >		return 0;
> >	}
> >
> >	return error;
> >}
> Change as below,
> static int raydium_i2c_check_fw_status(struct raydium_data *ts)
> {
> 	struct i2c_client *client = ts->client;
> 	static const u8 bl_ack = 0x62;
> 	static const u8 main_ack = 0x66;
> 	u8 buf[4];
> 	int error;
> 
> 	error = raydium_i2c_read(client, RM_CMD_BOOT_READ, buf, sizeof(buf));
> 	if (!error) {
> 		/*FIXME, changed as one byte comparison*/
> 		if (buf[0] == bl_ack)
> 			ts->boot_mode = RAYDIUM_TS_BLDR;
> 		else if (buf[0] == main_ack)
> 			ts->boot_mode = RAYDIUM_TS_MAIN;
> 		return 0;
> 	}
> 
> 	return error;
> }
> 
> >static int raydium_i2c_fw_write_page(struct i2c_client *client,
> >				     u8 page_idx, void *data, size_t len)
> >{
> >	u8 buf[RM_BL_WRT_LEN];
> >	u8 pkg_idx = 1;
> >	size_t xfer_len;
> >	int error;
> >
> >	while (len) {
> >		xfer_len = min_t(size_t, len, RM_BL_WRT_PKG_SIZE);
> >
> >		buf[BL_HEADER] = 0x0b;
> >		// XXX FIXME: is this correct? Do we really want all pages
> >		// after 1st to have 0xff? Should it be a counter?
> >		// Why do we need both pages and packages within pages?
> >		buf[BL_PAGE_STR] = page_idx ? 0 : 0xff;
> >		buf[BL_PKG_IDX] = pkg_idx;
> >		memcpy(&buf[BL_DATA_STR], data, xfer_len);
> >
> >		error = raydium_i2c_write_object(client, buf, xfer_len,
> >						 RAYDIUM_WAIT_READY);
> >		if (error) {
> >			dev_err(&client->dev,
> >				"page write command failed for page %d, chunk %d: %d\n",
> >				page_idx, pkg_idx, error);
> >			return error;
> >		}
> >
> >		msleep(20);
> >
> >		data += xfer_len;
> >		len -= xfer_len;
> >		pkg_idx++;
> >	}
> >
> >	return error;
> >}
> Because of need fully fill 128bytes in pages, so that function change as below:

I see. In this case, how about we do:

> static int raydium_i2c_fw_write_page(struct i2c_client *client,
> 				     u16 page_idx, const void *data, size_t len)
> {
> 	u8 buf[RM_BL_WRT_LEN];
> 	u8 pkg_idx = 1;
> 	u8 div_cnt;
> 	size_t xfer_len;
> 	int error;
> 	int i;
> 
> 	div_cnt = len % RM_BL_WRT_PKG_SIZE ?
> 		len / RM_BL_WRT_PKG_SIZE + 1:len / RM_BL_WRT_PKG_SIZE;

Drop this. BTW, if you ever need it we have DIV_ROUND_UP macro.

> 
> 	for (i = 0; i < div_cnt; i++) {

	while (len) {

> 		xfer_len = min_t(size_t, len, RM_BL_WRT_PKG_SIZE);
> 		buf[BL_HEADER] = RM_CMD_BOOT_PAGE_WRT;
> 		/*FIXME,Touch MCU need zero index as start page*/
> 		buf[BL_PAGE_STR] = page_idx ? 0xff : 0;
> 		buf[BL_PKG_IDX] = pkg_idx++;
> 
> 		memcpy(&buf[BL_DATA_STR], data, xfer_len);

		/* we need to pad to full page size */
		if (len < RM_BL_WRT_PKG_SIZE)
			memset(&buf[BL_DATA_STR] + len, 0xff,
				RM_BL_WRT_PKG_SIZE - len);
> 
> 		if (len == 0)
> 			memset(buf + BL_DATA_STR, 0xff, RM_BL_WRT_PKG_SIZE);
> 		else if (len < RM_BL_WRT_PKG_SIZE)
> 			memset(buf + BL_DATA_STR + xfer_len, 0xff,
> 			RM_BL_WRT_PKG_SIZE - xfer_len);
> 
> 		error = raydium_i2c_write_object(client, buf, RM_BL_WRT_LEN,
> 						 RAYDIUM_WAIT_READY);
> 		if (error) {
> 			dev_err(&client->dev,
> 				"page write command failed for page %d, chunk %d: %d\n",
> 				page_idx, pkg_idx, error);
> 			return error;
> 		}
> 		data += RM_BL_WRT_PKG_SIZE;
> 		len -= RM_BL_WRT_PKG_SIZE;
> 	}
> 
> 	return error;
> }
> 
> >static void raydium_mt_event(struct raydium_data *ts)
> >{
> >	int i;
> >	int error;
> memory allocate
> 	ts->report_data = kmalloc(ts->report_size, GFP_KERNEL);
> 	if (!ts->report_data)
> 		return;

I thought I was allocating it after I queried the touchscreen
parameters... If not it was oversight. I do not think we should be
doing this on every interrupt, so please do the allocation in ->probe()
code.

> >
> >	error = raydium_i2c_read_message(ts->client, ts->data_bank_addr,
> >					 ts->report_data, ts->report_size);
> >	if (error) {
> >		dev_err(&ts->client->dev, "%s: failed to read data: %d\n",
> >			__func__, error);
> >		return;
> 		goto out_of_event;
> >	}
> >
> >
> >	for (i = 0; i < ts->report_size / ts->contact_size; i++) {
> >		u8 *contact = &ts->report_data[ts->contact_size * i];
> >		bool state = contact[RM_CONTACT_STATE_POS];
> >		u8 wx, wy;
> >
> >		input_mt_slot(ts->input, i);
> >		input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, state);
> >
> >		if (!state)
> >			continue;
> >
> >		input_report_abs(ts->input, ABS_MT_POSITION_X,
> >				get_unaligned_le16(&contact[RM_CONTACT_X_POS]));
> >		input_report_abs(ts->input, ABS_MT_POSITION_Y,
> >				get_unaligned_le16(&contact[RM_CONTACT_Y_POS]));
> >		input_report_abs(ts->input, ABS_MT_PRESSURE,
> >				contact[RM_CONTACT_PRESSURE_POS]);
> >
> >		wx = contact[RM_CONTACT_WIDTH_X_POS];
> >		wy = contact[RM_CONTACT_WIDTH_Y_POS];
> >
> >		input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, max(wx, wy));
> >		input_report_abs(ts->input, ABS_MT_TOUCH_MINOR, min(wx, wy));
> >	}
> >
> >	input_mt_sync_frame(ts->input);
> >	input_sync(ts->input);
> out_of_event:
> 	kfree(ts->report_data);
> >}
> 
> 

Please prepare full new patch and post it so I can give it one more look
over before I can merge it.

Thanks!

-- 
Dmitry
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2016-05-16 16:41         ` Dmitry Torokhov
@ 2016-05-16 16:43           ` Dmitry Torokhov
  0 siblings, 0 replies; 42+ messages in thread
From: Dmitry Torokhov @ 2016-05-16 16:43 UTC (permalink / raw)
  To: jeffrey.lin
  Cc: rydberg, grant.likely, robh+dt, jeesw, bleung, jeffrey.lin,
	roger.yang, KP.li, albert.shieh, linux-kernel, linux-input,
	devicetree

On Mon, May 16, 2016 at 09:41:45AM -0700, Dmitry Torokhov wrote:
> On Mon, May 16, 2016 at 11:46:51PM +0800, jeffrey.lin wrote:
> > Hi Dmitry:
> > I've finished issues under the format you suggested as below.
> > 
> > >#define RM_RESET_MSG_ADDR	0x40000004
> > >#define RM_FASTBOOT_MSG_ADDR	0x50000620
> > 
> > >#define RM_MAX_READ_SIZE	63
> > change maximum read size to 56 bytes
> > #define RM_MAX_READ_SIZE	56
> 
> OK.
> 
> > 
> > >#define RM_CONTACT_X_POS	1
> > >#define RM_CONTACT_Y_POS	3
> > >#define RM_CONTACT_PRESSURE_POS	5 // XXX - check - original was 4
> > #define RM_CONTACT_PRESSURE_POS	5	/*FIXME, correct 5*/
> 
> OK.
> 
> > 
> > >#define RM_BL_WRT_CMD_SIZE	3	/* bl flash wrt cmd size */
> > >#define RM_BL_WRT_PKG_SIZE	32	/* bl wrt pkg size */
> > >#define RM_BL_WRT_LEN		(RM_BL_WRT_PKG_SIZE + RM_BL_WRT_CMD_SIZE)
> > >#define RM_FW_PAGE_SIZE		128
> > >#define RM_MAX_FW_RETRIES	30
> > additional maximum firmware size for protection
> > #define RM_MAX_FW_SIZE		(0xD000)	/*define max firmware size*/
> 
> OK.
> 
> > 
> > >static int raydium_i2c_read_message(struct i2c_client *client,
> > >				    u32 addr, void *data, size_t len)
> > >{
> > >	__be32 be_addr;
> > >	size_t xfer_len;
> > >	int error;
> > >	while (len) {
> > >		xfer_len = min_t(size_t, len, RM_MAX_READ_SIZE);
> > >
> > >		be_addr = cpu_to_be32(addr);
> > >
> > >		error = raydium_i2c_send(client, RM_CMD_BANK_SWITCH,
> > >					 &be_addr, sizeof(be_addr));
> > >		if (!error)
> > >			error = raydium_i2c_read(client, addr & 0xff,
> > >						 data, xfer_len);
> > Change as:
> > 		if (!error)
> > 			error = raydium_i2c_read(client, (be_addr >> 24) & 0xff,
> > 						 data, xfer_len);
> 
> I think it is the same on LE, and I suspect it will not work correctly
> on BE... You want to have the 8 least significant bits of the bank to be
> used as the address, right?
> 
> > 
> > >static int raydium_i2c_send_message(struct i2c_client *client,
> > >				    u32 addr, const void *data, size_t len)
> > >{
> > >	__be32 be_addr = cpu_to_be32(addr);
> > >	int error;
> > >	error = raydium_i2c_send(client, RM_CMD_BANK_SWITCH,
> > >				 &be_addr, sizeof(be_addr));
> > >	if (!error)
> > >		error = raydium_i2c_send(client, addr & 0xff, data, len);
> > Change as:
> > 		error = raydium_i2c_send(client, (be_addr >> 24) & 0xff,
> > 				data, len);
> 
> Same here.
> 
> > 
> > 
> > >static int raydium_i2c_fastboot(struct i2c_client *client)
> > >{
> > 	/*FIXME;Correct, direct access mode is word alignment*/
> > >	u8 buf[4];
> > >	int error;
> > >
> > >	error = raydium_i2c_read_message(client, RM_FASTBOOT_MSG_ADDR,
> > >					 buf, sizeof(buf));
> > 
> > >static int raydium_i2c_check_fw_status(struct raydium_data *ts)
> > >{
> > >	struct i2c_client *client = ts->client;
> > >	static const u8 bl_area[] = { 0x62, 0x6f, 0x6f, 0x74 };
> > >	static const u8 main_area[] = { 0x66, 0x69, 0x72, 0x6d };
> > >	u8 buf[4];
> > >	int error;
> > >
> > >	error = raydium_i2c_read(client, RM_CMD_BOOT_READ, buf, sizeof(buf));
> > >	if (!error) {
> > >		// XXX FIXME: why do we compare only 1st bytes? Do we even
> > >		// need 4-byte constants?
> > >		if (buf[0] == bl_area[0])
> > >			ts->boot_mode = RAYDIUM_TS_BLDR;
> > >		else if (buf[0] == main_area[0])
> > >			ts->boot_mode = RAYDIUM_TS_MAIN;
> > >		return 0;
> > >	}
> > >
> > >	return error;
> > >}
> > Change as below,
> > static int raydium_i2c_check_fw_status(struct raydium_data *ts)
> > {
> > 	struct i2c_client *client = ts->client;
> > 	static const u8 bl_ack = 0x62;
> > 	static const u8 main_ack = 0x66;
> > 	u8 buf[4];
> > 	int error;
> > 
> > 	error = raydium_i2c_read(client, RM_CMD_BOOT_READ, buf, sizeof(buf));
> > 	if (!error) {
> > 		/*FIXME, changed as one byte comparison*/
> > 		if (buf[0] == bl_ack)
> > 			ts->boot_mode = RAYDIUM_TS_BLDR;
> > 		else if (buf[0] == main_ack)
> > 			ts->boot_mode = RAYDIUM_TS_MAIN;
> > 		return 0;
> > 	}
> > 
> > 	return error;
> > }
> > 
> > >static int raydium_i2c_fw_write_page(struct i2c_client *client,
> > >				     u8 page_idx, void *data, size_t len)
> > >{
> > >	u8 buf[RM_BL_WRT_LEN];
> > >	u8 pkg_idx = 1;
> > >	size_t xfer_len;
> > >	int error;
> > >
> > >	while (len) {
> > >		xfer_len = min_t(size_t, len, RM_BL_WRT_PKG_SIZE);
> > >
> > >		buf[BL_HEADER] = 0x0b;
> > >		// XXX FIXME: is this correct? Do we really want all pages
> > >		// after 1st to have 0xff? Should it be a counter?
> > >		// Why do we need both pages and packages within pages?
> > >		buf[BL_PAGE_STR] = page_idx ? 0 : 0xff;
> > >		buf[BL_PKG_IDX] = pkg_idx;
> > >		memcpy(&buf[BL_DATA_STR], data, xfer_len);
> > >
> > >		error = raydium_i2c_write_object(client, buf, xfer_len,
> > >						 RAYDIUM_WAIT_READY);
> > >		if (error) {
> > >			dev_err(&client->dev,
> > >				"page write command failed for page %d, chunk %d: %d\n",
> > >				page_idx, pkg_idx, error);
> > >			return error;
> > >		}
> > >
> > >		msleep(20);
> > >
> > >		data += xfer_len;
> > >		len -= xfer_len;
> > >		pkg_idx++;
> > >	}
> > >
> > >	return error;
> > >}
> > Because of need fully fill 128bytes in pages, so that function change as below:
> 
> I see. In this case, how about we do:
> 
> > static int raydium_i2c_fw_write_page(struct i2c_client *client,
> > 				     u16 page_idx, const void *data, size_t len)
> > {
> > 	u8 buf[RM_BL_WRT_LEN];
> > 	u8 pkg_idx = 1;
> > 	u8 div_cnt;
> > 	size_t xfer_len;
> > 	int error;
> > 	int i;
> > 
> > 	div_cnt = len % RM_BL_WRT_PKG_SIZE ?
> > 		len / RM_BL_WRT_PKG_SIZE + 1:len / RM_BL_WRT_PKG_SIZE;
> 
> Drop this. BTW, if you ever need it we have DIV_ROUND_UP macro.
> 
> > 
> > 	for (i = 0; i < div_cnt; i++) {
> 
> 	while (len) {
> 
> > 		xfer_len = min_t(size_t, len, RM_BL_WRT_PKG_SIZE);
> > 		buf[BL_HEADER] = RM_CMD_BOOT_PAGE_WRT;
> > 		/*FIXME,Touch MCU need zero index as start page*/
> > 		buf[BL_PAGE_STR] = page_idx ? 0xff : 0;
> > 		buf[BL_PKG_IDX] = pkg_idx++;
> > 
> > 		memcpy(&buf[BL_DATA_STR], data, xfer_len);
> 
> 		/* we need to pad to full page size */
> 		if (len < RM_BL_WRT_PKG_SIZE)
> 			memset(&buf[BL_DATA_STR] + len, 0xff,
> 				RM_BL_WRT_PKG_SIZE - len);
> > 
> > 		if (len == 0)
> > 			memset(buf + BL_DATA_STR, 0xff, RM_BL_WRT_PKG_SIZE);
> > 		else if (len < RM_BL_WRT_PKG_SIZE)
> > 			memset(buf + BL_DATA_STR + xfer_len, 0xff,
> > 			RM_BL_WRT_PKG_SIZE - xfer_len);
> > 
> > 		error = raydium_i2c_write_object(client, buf, RM_BL_WRT_LEN,
> > 						 RAYDIUM_WAIT_READY);
> > 		if (error) {
> > 			dev_err(&client->dev,
> > 				"page write command failed for page %d, chunk %d: %d\n",
> > 				page_idx, pkg_idx, error);
> > 			return error;
> > 		}
> > 		data += RM_BL_WRT_PKG_SIZE;
> > 		len -= RM_BL_WRT_PKG_SIZE;
> > 	}
> > 
> > 	return error;
> > }
> > 
> > >static void raydium_mt_event(struct raydium_data *ts)
> > >{
> > >	int i;
> > >	int error;
> > memory allocate
> > 	ts->report_data = kmalloc(ts->report_size, GFP_KERNEL);
> > 	if (!ts->report_data)
> > 		return;
> 
> I thought I was allocating it after I queried the touchscreen
> parameters... If not it was oversight. I do not think we should be
> doing this on every interrupt, so please do the allocation in ->probe()
> code.
> 
> > >
> > >	error = raydium_i2c_read_message(ts->client, ts->data_bank_addr,
> > >					 ts->report_data, ts->report_size);
> > >	if (error) {
> > >		dev_err(&ts->client->dev, "%s: failed to read data: %d\n",
> > >			__func__, error);
> > >		return;
> > 		goto out_of_event;
> > >	}
> > >
> > >
> > >	for (i = 0; i < ts->report_size / ts->contact_size; i++) {
> > >		u8 *contact = &ts->report_data[ts->contact_size * i];
> > >		bool state = contact[RM_CONTACT_STATE_POS];
> > >		u8 wx, wy;
> > >
> > >		input_mt_slot(ts->input, i);
> > >		input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, state);
> > >
> > >		if (!state)
> > >			continue;
> > >
> > >		input_report_abs(ts->input, ABS_MT_POSITION_X,
> > >				get_unaligned_le16(&contact[RM_CONTACT_X_POS]));
> > >		input_report_abs(ts->input, ABS_MT_POSITION_Y,
> > >				get_unaligned_le16(&contact[RM_CONTACT_Y_POS]));
> > >		input_report_abs(ts->input, ABS_MT_PRESSURE,
> > >				contact[RM_CONTACT_PRESSURE_POS]);
> > >
> > >		wx = contact[RM_CONTACT_WIDTH_X_POS];
> > >		wy = contact[RM_CONTACT_WIDTH_Y_POS];
> > >
> > >		input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, max(wx, wy));
> > >		input_report_abs(ts->input, ABS_MT_TOUCH_MINOR, min(wx, wy));
> > >	}
> > >
> > >	input_mt_sync_frame(ts->input);
> > >	input_sync(ts->input);
> > out_of_event:
> > 	kfree(ts->report_data);
> > >}
> > 
> > 
> 
> Please prepare full new patch and post it so I can give it one more look
> over before I can merge it.
> 
> Thanks!

Also please prepare binding documentation for the device, similar to
Documentation/devicetree/bindings/input/elants_i2c.txt (but have it in
Documentation/devicetree/bindings/input/touchscreen/).

-- 
Dmitry

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2016-05-13  4:18   ` Dmitry Torokhov
                       ` (2 preceding siblings ...)
  2016-05-16 15:57     ` jeffrey.lin
@ 2016-05-17 16:07     ` jeffrey.lin
       [not found]       ` <1463501223-20723-1-git-send-email-jeffrey.lin-s3Ivl27awEzQT0dZR+AlfA@public.gmane.org>
  2016-05-22  9:32     ` jeffrey.lin
                       ` (3 subsequent siblings)
  7 siblings, 1 reply; 42+ messages in thread
From: jeffrey.lin @ 2016-05-17 16:07 UTC (permalink / raw)
  To: dmitry.torokhov, rydberg, grant.likely, robh+dt, jeesw, bleung
  Cc: jeffrey.lin, roger.yang, KP.li, albert.shieh, linux-kernel,
	linux-input, devicetree

Hi Dmitry:
> >static int raydium_i2c_read_message(struct i2c_client *client,
> >				    u32 addr, void *data, size_t len)
> >{
> >	__be32 be_addr;
> >	size_t xfer_len;
> >	int error;
> >	while (len) {
> >		xfer_len = min_t(size_t, len, RM_MAX_READ_SIZE);
> >
> >		be_addr = cpu_to_be32(addr);
> >
> >		error = raydium_i2c_send(client, RM_CMD_BANK_SWITCH,
> >					 &be_addr, sizeof(be_addr));
> >		if (!error)
> >			error = raydium_i2c_read(client, addr & 0xff,
> >						 data, xfer_len);
> Change as:
> 		if (!error)
> 			error = raydium_i2c_read(client, (be_addr >> 24) & 0xff,
> 						 data, xfer_len);

>I think it is the same on LE, and I suspect it will not work correctly
>on BE... You want to have the 8 least significant bits of the bank to be
>used as the address, right?
This function work correctly with the kernel 3.18 of chromebook in my hand. Raydium touch direct access mode can recieve the BE address after bank switch command 0xaa. For example, if we'll read 10 bytes data begin on 0x12345678. We need send the command sequences as 0xaa-> 0x12->0x34->0x56-> 0x78 and then recive 10 bytes from 0x78.

> static int raydium_i2c_fw_write_page(struct i2c_client *client,
> 				     u16 page_idx, const void *data, size_t len)
> {
> 	u8 buf[RM_BL_WRT_LEN];
> 	u8 pkg_idx = 1;
> 	u8 div_cnt;
> 	size_t xfer_len;
> 	int error;
> 	int i;
> 
> 	div_cnt = len % RM_BL_WRT_PKG_SIZE ?
> 		len / RM_BL_WRT_PKG_SIZE + 1:len / RM_BL_WRT_PKG_SIZE;
>
>Drop this. BTW, if you ever need it we have DIV_ROUND_UP macro.

> 
> 	for (i = 0; i < div_cnt; i++) {
>
>	while (len) {
>
> 		xfer_len = min_t(size_t, len, RM_BL_WRT_PKG_SIZE);
> 		buf[BL_HEADER] = RM_CMD_BOOT_PAGE_WRT;
> 		/*FIXME,Touch MCU need zero index as start page*/
> 		buf[BL_PAGE_STR] = page_idx ? 0xff : 0;
> 		buf[BL_PKG_IDX] = pkg_idx++;
> 
> 		memcpy(&buf[BL_DATA_STR], data, xfer_len);
>
>		/* we need to pad to full page size */
>		if (len < RM_BL_WRT_PKG_SIZE)
>			memset(&buf[BL_DATA_STR] + len, 0xff,
>				RM_BL_WRT_PKG_SIZE - len);
> 
> 		if (len == 0)
> 			memset(buf + BL_DATA_STR, 0xff, RM_BL_WRT_PKG_SIZE);
> 		else if (len < RM_BL_WRT_PKG_SIZE)
> 			memset(buf + BL_DATA_STR + xfer_len, 0xff,
> 			RM_BL_WRT_PKG_SIZE - xfer_len);
> 
> 		error = raydium_i2c_write_object(client, buf, RM_BL_WRT_LEN,
> 						 RAYDIUM_WAIT_READY);
> 		if (error) {
> 			dev_err(&client->dev,
> 				"page write command failed for page %d, chunk %d: %d\n",
> 				page_idx, pkg_idx, error);
> 			return error;
> 		}
> 		data += RM_BL_WRT_PKG_SIZE;
> 		len -= RM_BL_WRT_PKG_SIZE;
> 	}
> 
> 	return error;
> }
Modify as below.

static int raydium_i2c_fw_write_page(struct i2c_client *client,
				     u16 page_idx, const void *data, size_t len)
{
	u8 buf[RM_BL_WRT_LEN];
	u8 pkg_idx = 1;
	size_t xfer_len;
	int error;

	while (len) {
		xfer_len = min_t(size_t, len, RM_BL_WRT_PKG_SIZE);
		buf[BL_HEADER] = RM_CMD_BOOT_PAGE_WRT;
		/*FIXME,Touch MCU need zero index as start page*/
		buf[BL_PAGE_STR] = page_idx ? 0xff : 0;
		buf[BL_PKG_IDX] = pkg_idx++;

		memcpy(&buf[BL_DATA_STR], data, xfer_len);

		if (len < RM_BL_WRT_PKG_SIZE) {
			buf[BL_PKG_IDX] = 4;
			memset(buf + BL_DATA_STR + xfer_len, 0xff,
				RM_BL_WRT_PKG_SIZE - xfer_len);
		}

		error = raydium_i2c_write_object(client, buf, RM_BL_WRT_LEN,
						 RAYDIUM_WAIT_READY);
		if (error) {
			dev_err(&client->dev,
				"page write command failed for page %d, chunk %d: %d\n",
				page_idx, pkg_idx, error);
			return error;
		}
		data += xfer_len;
		len -= xfer_len;
	}

	return error;
> 
> >static void raydium_mt_event(struct raydium_data *ts)
> >{
> >	int i;
> >	int error;
> memory allocate
> 	ts->report_data = kmalloc(ts->report_size, GFP_KERNEL);
> 	if (!ts->report_data)
> 		return;
>
>I thought I was allocating it after I queried the touchscreen
>parameters... If not it was oversight. I do not think we should be
>doing this on every interrupt, so please do the allocation in ->probe()
>code.
Move memory allocate to raydium_i2c_probe(). 

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
       [not found]       ` <1463501223-20723-1-git-send-email-jeffrey.lin-s3Ivl27awEzQT0dZR+AlfA@public.gmane.org>
@ 2016-05-21 18:18         ` Dmitry Torokhov
  0 siblings, 0 replies; 42+ messages in thread
From: Dmitry Torokhov @ 2016-05-21 18:18 UTC (permalink / raw)
  To: jeffrey.lin
  Cc: rydberg-Hk7bIW8heu4wFerOooGFRg,
	grant.likely-QSEj5FYQhm4dnm+yROfE0A,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, jeesw-iQTvn4YitUrQT0dZR+AlfA,
	bleung-F7+t8E8rja9g9hUCZPvPmw,
	jeffrey.lin-s3Ivl27awEzQT0dZR+AlfA,
	roger.yang-s3Ivl27awEzQT0dZR+AlfA, KP.li-s3Ivl27awEzQT0dZR+AlfA,
	albert.shieh-s3Ivl27awEzQT0dZR+AlfA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA

On Wed, May 18, 2016 at 12:07:02AM +0800, jeffrey.lin wrote:
> Hi Dmitry:
> > >static int raydium_i2c_read_message(struct i2c_client *client,
> > >				    u32 addr, void *data, size_t len)
> > >{
> > >	__be32 be_addr;
> > >	size_t xfer_len;
> > >	int error;
> > >	while (len) {
> > >		xfer_len = min_t(size_t, len, RM_MAX_READ_SIZE);
> > >
> > >		be_addr = cpu_to_be32(addr);
> > >
> > >		error = raydium_i2c_send(client, RM_CMD_BANK_SWITCH,
> > >					 &be_addr, sizeof(be_addr));
> > >		if (!error)
> > >			error = raydium_i2c_read(client, addr & 0xff,
> > >						 data, xfer_len);
> > Change as:
> > 		if (!error)
> > 			error = raydium_i2c_read(client, (be_addr >> 24) & 0xff,
> > 						 data, xfer_len);
> 
> >I think it is the same on LE, and I suspect it will not work correctly
> >on BE... You want to have the 8 least significant bits of the bank to be
> >used as the address, right?

> This function work correctly with the kernel 3.18 of chromebook in my
> hand. Raydium touch direct access mode can recieve the BE address

That is because it is a little-endian device.

>
> after bank switch command 0xaa. For example, if we'll read 10 bytes
> data begin on 0x12345678. We need send the command sequences as 0xaa->
> 0x12->0x34->0x56-> 0x78 and then recive 10 bytes from 0x78.
> 

Right. So the thing is - on any architecture, be it little- or
big-endian, expression "value & 0xff" will extract the 8 least
significant bits from the value, while "(value >> 24) & 0xff" will
extract the most significant bits (assuming that the value is 32 bits). So with your example if you do
cpu_to_be32() on LE architecture it will actually reshuffle the bytes so
that former LSB will become MSB and then you will extract that MSB and
use it. On BE arches cpu_to_be32() is a noop, so addr and be_addr will
have the same value, and your expression will produce 0x12 and not 0x78
as you expect. On the other hand, doing "addr & 0xff" will produce 0x78
regardless of endianness.

> > static int raydium_i2c_fw_write_page(struct i2c_client *client,
> > 				     u16 page_idx, const void *data, size_t len)
> > {
> > 	u8 buf[RM_BL_WRT_LEN];
> > 	u8 pkg_idx = 1;
> > 	u8 div_cnt;
> > 	size_t xfer_len;
> > 	int error;
> > 	int i;
> > 
> > 	div_cnt = len % RM_BL_WRT_PKG_SIZE ?
> > 		len / RM_BL_WRT_PKG_SIZE + 1:len / RM_BL_WRT_PKG_SIZE;
> >
> >Drop this. BTW, if you ever need it we have DIV_ROUND_UP macro.
> 
> > 
> > 	for (i = 0; i < div_cnt; i++) {
> >
> >	while (len) {
> >
> > 		xfer_len = min_t(size_t, len, RM_BL_WRT_PKG_SIZE);
> > 		buf[BL_HEADER] = RM_CMD_BOOT_PAGE_WRT;
> > 		/*FIXME,Touch MCU need zero index as start page*/
> > 		buf[BL_PAGE_STR] = page_idx ? 0xff : 0;
> > 		buf[BL_PKG_IDX] = pkg_idx++;
> > 
> > 		memcpy(&buf[BL_DATA_STR], data, xfer_len);
> >
> >		/* we need to pad to full page size */
> >		if (len < RM_BL_WRT_PKG_SIZE)
> >			memset(&buf[BL_DATA_STR] + len, 0xff,
> >				RM_BL_WRT_PKG_SIZE - len);
> > 
> > 		if (len == 0)
> > 			memset(buf + BL_DATA_STR, 0xff, RM_BL_WRT_PKG_SIZE);
> > 		else if (len < RM_BL_WRT_PKG_SIZE)
> > 			memset(buf + BL_DATA_STR + xfer_len, 0xff,
> > 			RM_BL_WRT_PKG_SIZE - xfer_len);
> > 
> > 		error = raydium_i2c_write_object(client, buf, RM_BL_WRT_LEN,
> > 						 RAYDIUM_WAIT_READY);
> > 		if (error) {
> > 			dev_err(&client->dev,
> > 				"page write command failed for page %d, chunk %d: %d\n",
> > 				page_idx, pkg_idx, error);
> > 			return error;
> > 		}
> > 		data += RM_BL_WRT_PKG_SIZE;
> > 		len -= RM_BL_WRT_PKG_SIZE;
> > 	}
> > 
> > 	return error;
> > }
> Modify as below.
> 
> static int raydium_i2c_fw_write_page(struct i2c_client *client,
> 				     u16 page_idx, const void *data, size_t len)
> {
> 	u8 buf[RM_BL_WRT_LEN];
> 	u8 pkg_idx = 1;
> 	size_t xfer_len;
> 	int error;
> 
> 	while (len) {
> 		xfer_len = min_t(size_t, len, RM_BL_WRT_PKG_SIZE);
> 		buf[BL_HEADER] = RM_CMD_BOOT_PAGE_WRT;
> 		/*FIXME,Touch MCU need zero index as start page*/
> 		buf[BL_PAGE_STR] = page_idx ? 0xff : 0;
> 		buf[BL_PKG_IDX] = pkg_idx++;
> 
> 		memcpy(&buf[BL_DATA_STR], data, xfer_len);
> 
> 		if (len < RM_BL_WRT_PKG_SIZE) {
> 			buf[BL_PKG_IDX] = 4;

Why 4???

> 			memset(buf + BL_DATA_STR + xfer_len, 0xff,
> 				RM_BL_WRT_PKG_SIZE - xfer_len);
> 		}
> 
> 		error = raydium_i2c_write_object(client, buf, RM_BL_WRT_LEN,
> 						 RAYDIUM_WAIT_READY);
> 		if (error) {
> 			dev_err(&client->dev,
> 				"page write command failed for page %d, chunk %d: %d\n",
> 				page_idx, pkg_idx, error);
> 			return error;
> 		}
> 		data += xfer_len;
> 		len -= xfer_len;
> 	}
> 
> 	return error;
> > 
> > >static void raydium_mt_event(struct raydium_data *ts)
> > >{
> > >	int i;
> > >	int error;
> > memory allocate
> > 	ts->report_data = kmalloc(ts->report_size, GFP_KERNEL);
> > 	if (!ts->report_data)
> > 		return;
> >
> >I thought I was allocating it after I queried the touchscreen
> >parameters... If not it was oversight. I do not think we should be
> >doing this on every interrupt, so please do the allocation in ->probe()
> >code.
> Move memory allocate to raydium_i2c_probe(). 

Thanks.

-- 
Dmitry
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2016-05-13  4:18   ` Dmitry Torokhov
                       ` (3 preceding siblings ...)
  2016-05-17 16:07     ` jeffrey.lin
@ 2016-05-22  9:32     ` jeffrey.lin
  2016-05-22 22:37       ` Dmitry Torokhov
  2016-05-22  9:35     ` jeffrey.lin
                       ` (2 subsequent siblings)
  7 siblings, 1 reply; 42+ messages in thread
From: jeffrey.lin @ 2016-05-22  9:32 UTC (permalink / raw)
  To: dmitry.torokhov, rydberg, grant.likely, robh+dt, jeesw, bleung
  Cc: jeffrey.lin, roger.yang, KP.li, albert.shieh, linux-kernel,
	linux-input, devicetree

Hi Dmitry:

+static int raydium_i2c_read_message(struct i2c_client *client,
+				    u32 addr, void *data, size_t len)
+{
+	__be32 be_addr;
+	size_t xfer_len;
+	int error;
+
+	while (len) {
+		xfer_len = min_t(size_t, len, RM_MAX_READ_SIZE);
+
+		be_addr = cpu_to_be32(addr);
+
+		error = raydium_i2c_send(client, RM_CMD_BANK_SWITCH,
+					 &be_addr, sizeof(be_addr));
+		if (!error)
+			error = raydium_i2c_read(client, (be_addr >> 24) & 0xff,
+						 data, xfer_len);
+		if (error)
+			return error;
+
+		len -= xfer_len;
+		data += xfer_len;
+		addr += xfer_len;
+	}
+
+	return 0;
+}
I change access address method of touch MCU direct mode so that I can change raydium i2c driver as below.

static int raydium_i2c_read_message(struct i2c_client *client,
				    u32 addr, void *data, size_t len)
{
	__le32 le_addr;
	size_t xfer_len;
	u32 shift_addr;
	int error;

	while (len) {
		xfer_len = min_t(size_t, len, RM_MAX_READ_SIZE);

		le_addr = cpu_to_le32(addr);

		shift_addr = le_addr >> 8;/*send the first 3rd byte addr.*/
		error = raydium_i2c_send(client, RM_CMD_BANK_SWITCH,
					 &shift_addr, sizeof(le_addr));
		if (!error)/*read from last byte addr.*/
			error = raydium_i2c_read(client, le_addr & 0xff,
						 data, xfer_len);
		if (error)
			return error;

		len -= xfer_len;
		data += xfer_len;
		addr += xfer_len;
	}

	return 0;
}
Is this okay? If okay, I will use this form update one new patch.

>> static int raydium_i2c_fw_write_page(struct i2c_client *client,
>> 				     u16 page_idx, const void *data, size_t len)
>> {
>> 	u8 buf[RM_BL_WRT_LEN];
>> 	u8 pkg_idx = 1;
>> 	size_t xfer_len;
>> 	int error;
>> 
>> 	while (len) {
>> 		xfer_len = min_t(size_t, len, RM_BL_WRT_PKG_SIZE);
>> 		buf[BL_HEADER] = RM_CMD_BOOT_PAGE_WRT;
>> 		/*FIXME,Touch MCU need zero index as start page*/
>> 		buf[BL_PAGE_STR] = page_idx ? 0xff : 0;
>> 		buf[BL_PKG_IDX] = pkg_idx++;
>> 
>> 		memcpy(&buf[BL_DATA_STR], data, xfer_len);
>> 
>> 		if (len < RM_BL_WRT_PKG_SIZE) {
>> 			buf[BL_PKG_IDX] = 4;

>Why 4???
4 is trigger index for write flash. Our page write size is 128 bytes, but in order to meet maximum I2C bus read/write byte limite and need fill full all pages of 128 bytes. So that I split 128 bytes to "4" section, and start burning flash if touch MCU get index "4". 

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2016-05-13  4:18   ` Dmitry Torokhov
                       ` (4 preceding siblings ...)
  2016-05-22  9:32     ` jeffrey.lin
@ 2016-05-22  9:35     ` jeffrey.lin
  2016-05-23 14:43     ` jeffrey.lin
  2016-05-24  9:31     ` jeffrey.lin
  7 siblings, 0 replies; 42+ messages in thread
From: jeffrey.lin @ 2016-05-22  9:35 UTC (permalink / raw)
  To: dmitry.torokhov, rydberg, grant.likely, robh+dt, jeesw, bleung
  Cc: jeffrey.lin, roger.yang, KP.li, albert.shieh, linux-kernel,
	linux-input, devicetree

Hi Dmitry:

>> 		if (len < RM_BL_WRT_PKG_SIZE) {
>> 			buf[BL_PKG_IDX] = 4;

>Why 4???
4 is trigger index for write flash. Our page write size is 128 bytes, 
but in order to meet maximum I2C bus read/write byte limite and need 
fill full all pages of 128 bytes. So that I split 128 bytes to "4" 
section, and start burning flash if touch MCU get index "4". 

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2016-05-22  9:32     ` jeffrey.lin
@ 2016-05-22 22:37       ` Dmitry Torokhov
  0 siblings, 0 replies; 42+ messages in thread
From: Dmitry Torokhov @ 2016-05-22 22:37 UTC (permalink / raw)
  To: jeffrey.lin, bleung
  Cc: jeffrey.lin, roger.yang, KP.li, albert.shieh, linux-kernel,
	linux-input, devicetree

On May 22, 2016 2:32:59 AM PDT, "jeffrey.lin" <yajohn@gmail.com> wrote:
>Hi Dmitry:
>
>+static int raydium_i2c_read_message(struct i2c_client *client,
>+				    u32 addr, void *data, size_t len)
>+{
>+	__be32 be_addr;
>+	size_t xfer_len;
>+	int error;
>+
>+	while (len) {
>+		xfer_len = min_t(size_t, len, RM_MAX_READ_SIZE);
>+
>+		be_addr = cpu_to_be32(addr);
>+
>+		error = raydium_i2c_send(client, RM_CMD_BANK_SWITCH,
>+					 &be_addr, sizeof(be_addr));
>+		if (!error)
>+			error = raydium_i2c_read(client, (be_addr >> 24) & 0xff,
>+						 data, xfer_len);
>+		if (error)
>+			return error;
>+
>+		len -= xfer_len;
>+		data += xfer_len;
>+		addr += xfer_len;
>+	}
>+
>+	return 0;
>+}
>I change access address method of touch MCU direct mode so that I can
>change raydium i2c driver as below.
>
>static int raydium_i2c_read_message(struct i2c_client *client,
>				    u32 addr, void *data, size_t len)
>{
>	__le32 le_addr;
>	size_t xfer_len;
>	u32 shift_addr;
>	int error;
>
>	while (len) {
>		xfer_len = min_t(size_t, len, RM_MAX_READ_SIZE);
>
>		le_addr = cpu_to_le32(addr);
>
>		shift_addr = le_addr >> 8;/*send the first 3rd byte addr.*/
>		error = raydium_i2c_send(client, RM_CMD_BANK_SWITCH,
>					 &shift_addr, sizeof(le_addr));
>		if (!error)/*read from last byte addr.*/
>			error = raydium_i2c_read(client, le_addr & 0xff,
>						 data, xfer_len);
>		if (error)
>			return error;
>
>		len -= xfer_len;
>		data += xfer_len;
>		addr += xfer_len;
>	}
>
>	return 0;
>}
>Is this okay? If okay, I will use this form update one new patch.

Why? It has the same problem - you can not do calculations on a variable in fixed endianness, because you do not know if your code will run on big or little endian CPU.

Doesn't my version of the code work?

>
>>> static int raydium_i2c_fw_write_page(struct i2c_client *client,
>>> 				     u16 page_idx, const void *data, size_t len)
>>> {
>>> 	u8 buf[RM_BL_WRT_LEN];
>>> 	u8 pkg_idx = 1;
>>> 	size_t xfer_len;
>>> 	int error;
>>> 
>>> 	while (len) {
>>> 		xfer_len = min_t(size_t, len, RM_BL_WRT_PKG_SIZE);
>>> 		buf[BL_HEADER] = RM_CMD_BOOT_PAGE_WRT;
>>> 		/*FIXME,Touch MCU need zero index as start page*/
>>> 		buf[BL_PAGE_STR] = page_idx ? 0xff : 0;
>>> 		buf[BL_PKG_IDX] = pkg_idx++;
>>> 
>>> 		memcpy(&buf[BL_DATA_STR], data, xfer_len);
>>> 
>>> 		if (len < RM_BL_WRT_PKG_SIZE) {
>>> 			buf[BL_PKG_IDX] = 4;
>
>>Why 4???
>4 is trigger index for write flash. Our page write size is 128 bytes,
>but in order to meet maximum I2C bus read/write byte limite and need
>fill full all pages of 128 bytes. So that I split 128 bytes to "4"
>section, and start burning flash if touch MCU get index "4". 

That is not the best way of handling this. What if you get 2 blocks of RM_BL_WRT_PKG_SIZE data worth? You will never get to index 4. You should be tracking number of bytes received and do flash when you get full page.

Hi Jeffrey,
Thanks.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2016-05-13  4:18   ` Dmitry Torokhov
                       ` (5 preceding siblings ...)
  2016-05-22  9:35     ` jeffrey.lin
@ 2016-05-23 14:43     ` jeffrey.lin
       [not found]       ` <1464014633-20829-1-git-send-email-jeffrey.lin-s3Ivl27awEzQT0dZR+AlfA@public.gmane.org>
  2016-05-24  9:31     ` jeffrey.lin
  7 siblings, 1 reply; 42+ messages in thread
From: jeffrey.lin @ 2016-05-23 14:43 UTC (permalink / raw)
  To: dmitry.torokhov, rydberg, grant.likely, robh+dt, jeesw, bleung
  Cc: jeffrey.lin, roger.yang, KP.li, albert.shieh, linux-kernel,
	linux-input, devicetree

Hi Dmitry:

>static int raydium_i2c_read_message(struct i2c_client *client,
>				    u32 addr, void *data, size_t len)
>{
>	__le32 le_addr;
>	size_t xfer_len;
>	u32 shift_addr;
>	int error;
>
>	while (len) {
>		xfer_len = min_t(size_t, len, RM_MAX_READ_SIZE);
>
>		le_addr = cpu_to_le32(addr);
>
>		shift_addr = le_addr >> 8;/*send the first 3rd byte addr.*/
Drop this. Change touch MCU setting to solve this issue
>		error = raydium_i2c_send(client, RM_CMD_BANK_SWITCH,
>					 &shift_addr, sizeof(le_addr));
>		if (!error)/*read from last byte addr.*/
>			error = raydium_i2c_read(client, le_addr & 0xff,
>						 data, xfer_len);
>		if (error)
>			return error;
>
>		len -= xfer_len;
>		data += xfer_len;
>		addr += xfer_len;
>	}
>
>	return 0;
>}
modify as below.

static int raydium_i2c_read_message(struct i2c_client *client,
				    u32 addr, void *data, size_t len)
{
	__le32 le_addr;
	size_t xfer_len;
	int error;

	while (len) {
		xfer_len = min_t(size_t, len, RM_MAX_READ_SIZE);

		le_addr = cpu_to_le32(addr);

		error = raydium_i2c_send(client, RM_CMD_BANK_SWITCH,
					 &le_addr, sizeof(le_addr));
		if (!error)/*read from last byte addr.*/
			error = raydium_i2c_read(client, le_addr & 0xff,
						 data, xfer_len);
		if (error)
			return error;

		len -= xfer_len;
		data += xfer_len;
		addr += xfer_len;
	}

	return 0;
}

>
>>> static int raydium_i2c_fw_write_page(struct i2c_client *client,
>>> 				     u16 page_idx, const void *data, size_t len)
>>> {
>>> 	u8 buf[RM_BL_WRT_LEN];
>>> 	u8 pkg_idx = 1;
>>> 	size_t xfer_len;
>>> 	int error;
>>> 
>>> 	while (len) {
>>> 		xfer_len = min_t(size_t, len, RM_BL_WRT_PKG_SIZE);
>>> 		buf[BL_HEADER] = RM_CMD_BOOT_PAGE_WRT;
>>> 		/*FIXME,Touch MCU need zero index as start page*/
>>> 		buf[BL_PAGE_STR] = page_idx ? 0xff : 0;
>>> 		buf[BL_PKG_IDX] = pkg_idx++;
>>> 
>>> 		memcpy(&buf[BL_DATA_STR], data, xfer_len);
>>> 
>>> 		if (len < RM_BL_WRT_PKG_SIZE) {
>>> 			buf[BL_PKG_IDX] = 4;
Drop this one. Modfy boot loader firmware to meet this issue. So final function as below.
static int raydium_i2c_fw_write_page(struct i2c_client *client,
				     u16 page_idx, const void *data, size_t len)
{
	u8 buf[RM_BL_WRT_LEN];
	u8 pkg_idx = 1;
	size_t xfer_len;
	int error;

	while (len) {
		xfer_len = min_t(size_t, len, RM_BL_WRT_PKG_SIZE);
		buf[BL_HEADER] = RM_CMD_BOOT_PAGE_WRT;
		/*FIXME,Touch MCU need zero index as start page*/
		buf[BL_PAGE_STR] = page_idx ? 0xff : 0;
		buf[BL_PKG_IDX] = pkg_idx++;

		memcpy(&buf[BL_DATA_STR], data, xfer_len);

		if (len < RM_BL_WRT_PKG_SIZE) {
			memset(buf + BL_DATA_STR + xfer_len, 0xff,
				RM_BL_WRT_PKG_SIZE - xfer_len);
		}

		error = raydium_i2c_write_object(client, buf, RM_BL_WRT_LEN,
						 RAYDIUM_WAIT_READY);
		if (error) {
			dev_err(&client->dev,
				"page write command failed for page %d, chunk %d: %d\n",
				page_idx, pkg_idx, error);
			return error;
		}
		data += xfer_len;
		len -= xfer_len;
	}

	return error;
}
Otherwise, Do you have any concern about my patch? If not, I'll send new patch soon.

Thanks.

Jeffrey

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
       [not found]       ` <1464014633-20829-1-git-send-email-jeffrey.lin-s3Ivl27awEzQT0dZR+AlfA@public.gmane.org>
@ 2016-05-23 16:27         ` Dmitry Torokhov
  0 siblings, 0 replies; 42+ messages in thread
From: Dmitry Torokhov @ 2016-05-23 16:27 UTC (permalink / raw)
  To: jeffrey.lin
  Cc: rydberg-Hk7bIW8heu4wFerOooGFRg,
	grant.likely-QSEj5FYQhm4dnm+yROfE0A,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, jeesw-iQTvn4YitUrQT0dZR+AlfA,
	bleung-F7+t8E8rja9g9hUCZPvPmw,
	jeffrey.lin-s3Ivl27awEzQT0dZR+AlfA,
	roger.yang-s3Ivl27awEzQT0dZR+AlfA, KP.li-s3Ivl27awEzQT0dZR+AlfA,
	albert.shieh-s3Ivl27awEzQT0dZR+AlfA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA

Hi Jeffrey,

On Mon, May 23, 2016 at 10:43:53PM +0800, jeffrey.lin wrote:
> Hi Dmitry:
> 
> >static int raydium_i2c_read_message(struct i2c_client *client,
> >				    u32 addr, void *data, size_t len)
> >{
> >	__le32 le_addr;
> >	size_t xfer_len;
> >	u32 shift_addr;
> >	int error;
> >
> >	while (len) {
> >		xfer_len = min_t(size_t, len, RM_MAX_READ_SIZE);
> >
> >		le_addr = cpu_to_le32(addr);
> >
> >		shift_addr = le_addr >> 8;/*send the first 3rd byte addr.*/
> Drop this. Change touch MCU setting to solve this issue
> >		error = raydium_i2c_send(client, RM_CMD_BANK_SWITCH,
> >					 &shift_addr, sizeof(le_addr));
> >		if (!error)/*read from last byte addr.*/
> >			error = raydium_i2c_read(client, le_addr & 0xff,
> >						 data, xfer_len);
> >		if (error)
> >			return error;
> >
> >		len -= xfer_len;
> >		data += xfer_len;
> >		addr += xfer_len;
> >	}
> >
> >	return 0;
> >}
> modify as below.
> 
> static int raydium_i2c_read_message(struct i2c_client *client,
> 				    u32 addr, void *data, size_t len)
> {
> 	__le32 le_addr;
> 	size_t xfer_len;
> 	int error;
> 
> 	while (len) {
> 		xfer_len = min_t(size_t, len, RM_MAX_READ_SIZE);
> 
> 		le_addr = cpu_to_le32(addr);
> 
> 		error = raydium_i2c_send(client, RM_CMD_BANK_SWITCH,
> 					 &le_addr, sizeof(le_addr));
> 		if (!error)/*read from last byte addr.*/
> 			error = raydium_i2c_read(client, le_addr & 0xff,
> 						 data, xfer_len);

No, not "le_addr & 0xff", "addr & 0xff"! When you do calculations, you
have to do it using values in CPU endianness, not fixed endianness (BE
or LE) so that the code will work on all architectures.

> 		if (error)
> 			return error;
> 
> 		len -= xfer_len;
> 		data += xfer_len;
> 		addr += xfer_len;
> 	}
> 
> 	return 0;
> }
> 
> >
> >>> static int raydium_i2c_fw_write_page(struct i2c_client *client,
> >>> 				     u16 page_idx, const void *data, size_t len)
> >>> {
> >>> 	u8 buf[RM_BL_WRT_LEN];
> >>> 	u8 pkg_idx = 1;
> >>> 	size_t xfer_len;
> >>> 	int error;
> >>> 
> >>> 	while (len) {
> >>> 		xfer_len = min_t(size_t, len, RM_BL_WRT_PKG_SIZE);
> >>> 		buf[BL_HEADER] = RM_CMD_BOOT_PAGE_WRT;
> >>> 		/*FIXME,Touch MCU need zero index as start page*/
> >>> 		buf[BL_PAGE_STR] = page_idx ? 0xff : 0;
> >>> 		buf[BL_PKG_IDX] = pkg_idx++;
> >>> 
> >>> 		memcpy(&buf[BL_DATA_STR], data, xfer_len);
> >>> 
> >>> 		if (len < RM_BL_WRT_PKG_SIZE) {
> >>> 			buf[BL_PKG_IDX] = 4;
> Drop this one. Modfy boot loader firmware to meet this issue. So final function as below.
> static int raydium_i2c_fw_write_page(struct i2c_client *client,
> 				     u16 page_idx, const void *data, size_t len)
> {
> 	u8 buf[RM_BL_WRT_LEN];
> 	u8 pkg_idx = 1;
> 	size_t xfer_len;
> 	int error;
> 
> 	while (len) {
> 		xfer_len = min_t(size_t, len, RM_BL_WRT_PKG_SIZE);
> 		buf[BL_HEADER] = RM_CMD_BOOT_PAGE_WRT;
> 		/*FIXME,Touch MCU need zero index as start page*/
> 		buf[BL_PAGE_STR] = page_idx ? 0xff : 0;
> 		buf[BL_PKG_IDX] = pkg_idx++;
> 
> 		memcpy(&buf[BL_DATA_STR], data, xfer_len);
> 
> 		if (len < RM_BL_WRT_PKG_SIZE) {
> 			memset(buf + BL_DATA_STR + xfer_len, 0xff,
> 				RM_BL_WRT_PKG_SIZE - xfer_len);
> 		}
> 
> 		error = raydium_i2c_write_object(client, buf, RM_BL_WRT_LEN,
> 						 RAYDIUM_WAIT_READY);
> 		if (error) {
> 			dev_err(&client->dev,
> 				"page write command failed for page %d, chunk %d: %d\n",
> 				page_idx, pkg_idx, error);
> 			return error;
> 		}
> 		data += xfer_len;
> 		len -= xfer_len;
> 	}
> 
> 	return error;
> }

What will be the trigger for the flash? If you really need full page,
then you want to :

	BUILD_BUG_ON((RM_FW_PAGE_SIZE % RM_BL_WRT_PKG_SIZE) != 0);

	for (i = 0; i < RM_FW_PAGE_SIZE / RM_BL_WRT_PKG_SIZE; i++) {
		buf[BL_HEADER] = RM_CMD_BOOT_PAGE_WRT;
		buf[BL_PAGE_STR] = i ? 0xff : 0;
		buf[BL_PKG_IDX] = i;

		xfer_len = min_t(size_t, len, RM_BL_WRT_PKG_SIZE);
		memcpy(&buf[BL_DATA_STR], data, xfer_len);
		if (len < RM_BL_WRT_PKG_SIZE)
			memset(&buf[BL_DATA_STR + xfer_len], 0xff,
				RM_BL_WRT_PKG_SIZE - xfer_len);

		error = raydium_i2c_write_object(client, buf, RM_BL_WRT_LEN,
						 RAYDIUM_WAIT_READY);
		if (error) {
			dev_err(&client->dev,
				"page write command failed for page %d, chunk %d: %d\n",
				page_idx, pkg_idx, error);
			return error;
		}

		data += xfer_len;
		len -= xfer_len;
	}

The BUILD_BUG_ON is because I do not want to handle the case where last
chunk would cross over the page boundary so we'd have to cut it short.
It simply is not worth it.

Thanks.

-- 
Dmitry
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2016-05-13  4:18   ` Dmitry Torokhov
                       ` (6 preceding siblings ...)
  2016-05-23 14:43     ` jeffrey.lin
@ 2016-05-24  9:31     ` jeffrey.lin
       [not found]       ` <1464082301-11539-1-git-send-email-jeffrey.lin-s3Ivl27awEzQT0dZR+AlfA@public.gmane.org>
  7 siblings, 1 reply; 42+ messages in thread
From: jeffrey.lin @ 2016-05-24  9:31 UTC (permalink / raw)
  To: dmitry.torokhov, rydberg, grant.likely, robh+dt, jeesw, bleung
  Cc: jeffrey.lin, roger.yang, KP.li, albert.shieh, linux-kernel,
	linux-input, devicetree

Hi Dmitry:
>
>	BUILD_BUG_ON((RM_FW_PAGE_SIZE % RM_BL_WRT_PKG_SIZE) != 0);
>
>	for (i = 0; i < RM_FW_PAGE_SIZE / RM_BL_WRT_PKG_SIZE; i++) {
>		buf[BL_HEADER] = RM_CMD_BOOT_PAGE_WRT;
>		buf[BL_PAGE_STR] = i ? 0xff : 0;
Change to buf[BL_PAGE_STR] = page_idx ? 0xff : 0;
>		buf[BL_PKG_IDX] = i;
>
>		xfer_len = min_t(size_t, len, RM_BL_WRT_PKG_SIZE);
>		memcpy(&buf[BL_DATA_STR], data, xfer_len);
>		if (len < RM_BL_WRT_PKG_SIZE)
>			memset(&buf[BL_DATA_STR + xfer_len], 0xff,
>				RM_BL_WRT_PKG_SIZE - xfer_len);
>
>		error = raydium_i2c_write_object(client, buf, RM_BL_WRT_LEN,
>						 RAYDIUM_WAIT_READY);
>		if (error) {
>			dev_err(&client->dev,
>				"page write command failed for page %d, chunk %d: %d\n",
>				page_idx, pkg_idx, error);
>			return error;
>		}
>
>		data += xfer_len;
>		len -= xfer_len;
>	}
This work on my hand chromebook.

> static int raydium_i2c_read_message(struct i2c_client *client,
> 				    u32 addr, void *data, size_t len)
> {
> 	__le32 le_addr;
> 	size_t xfer_len;
> 	int error;
> 
> 	while (len) {
> 		xfer_len = min_t(size_t, len, RM_MAX_READ_SIZE);
> 
> 		le_addr = cpu_to_le32(addr);
> 
> 		error = raydium_i2c_send(client, RM_CMD_BANK_SWITCH,
> 					 &le_addr, sizeof(le_addr));
> 		if (!error)/*read from last byte addr.*/
> 			error = raydium_i2c_read(client, le_addr & 0xff,
> 						 data, xfer_len)
Change as
			error = raydium_i2c_read(client, addr & 0xff,
						 data, xfer_len);

I've fixed this issues and submit new patch to upstream. Please help check that.

Thanks.

Jeffrey

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
       [not found]       ` <1464082301-11539-1-git-send-email-jeffrey.lin-s3Ivl27awEzQT0dZR+AlfA@public.gmane.org>
@ 2016-05-27 16:34         ` Dmitry Torokhov
  0 siblings, 0 replies; 42+ messages in thread
From: Dmitry Torokhov @ 2016-05-27 16:34 UTC (permalink / raw)
  To: jeffrey.lin
  Cc: rydberg-Hk7bIW8heu4wFerOooGFRg,
	grant.likely-QSEj5FYQhm4dnm+yROfE0A,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, jeesw-iQTvn4YitUrQT0dZR+AlfA,
	bleung-F7+t8E8rja9g9hUCZPvPmw,
	jeffrey.lin-s3Ivl27awEzQT0dZR+AlfA,
	roger.yang-s3Ivl27awEzQT0dZR+AlfA, KP.li-s3Ivl27awEzQT0dZR+AlfA,
	albert.shieh-s3Ivl27awEzQT0dZR+AlfA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA

Hi Jeffrey,

On Tue, May 24, 2016 at 05:31:41PM +0800, jeffrey.lin wrote:
> Hi Dmitry:
> >
> >	BUILD_BUG_ON((RM_FW_PAGE_SIZE % RM_BL_WRT_PKG_SIZE) != 0);
> >
> >	for (i = 0; i < RM_FW_PAGE_SIZE / RM_BL_WRT_PKG_SIZE; i++) {
> >		buf[BL_HEADER] = RM_CMD_BOOT_PAGE_WRT;
> >		buf[BL_PAGE_STR] = i ? 0xff : 0;
> Change to buf[BL_PAGE_STR] = page_idx ? 0xff : 0;
> >		buf[BL_PKG_IDX] = i;
> >
> >		xfer_len = min_t(size_t, len, RM_BL_WRT_PKG_SIZE);
> >		memcpy(&buf[BL_DATA_STR], data, xfer_len);
> >		if (len < RM_BL_WRT_PKG_SIZE)
> >			memset(&buf[BL_DATA_STR + xfer_len], 0xff,
> >				RM_BL_WRT_PKG_SIZE - xfer_len);
> >
> >		error = raydium_i2c_write_object(client, buf, RM_BL_WRT_LEN,
> >						 RAYDIUM_WAIT_READY);
> >		if (error) {
> >			dev_err(&client->dev,
> >				"page write command failed for page %d, chunk %d: %d\n",
> >				page_idx, pkg_idx, error);
> >			return error;
> >		}
> >
> >		data += xfer_len;
> >		len -= xfer_len;
> >	}
> This work on my hand chromebook.
> 
> > static int raydium_i2c_read_message(struct i2c_client *client,
> > 				    u32 addr, void *data, size_t len)
> > {
> > 	__le32 le_addr;
> > 	size_t xfer_len;
> > 	int error;
> > 
> > 	while (len) {
> > 		xfer_len = min_t(size_t, len, RM_MAX_READ_SIZE);
> > 
> > 		le_addr = cpu_to_le32(addr);
> > 
> > 		error = raydium_i2c_send(client, RM_CMD_BANK_SWITCH,
> > 					 &le_addr, sizeof(le_addr));
> > 		if (!error)/*read from last byte addr.*/
> > 			error = raydium_i2c_read(client, le_addr & 0xff,
> > 						 data, xfer_len)
> Change as
> 			error = raydium_i2c_read(client, addr & 0xff,
> 						 data, xfer_len);
> 
> I've fixed this issues and submit new patch to upstream. Please help check that.

I do not think that we want the gratuitous change from BE to LE here, as
this will give users trouble with devices that still use older
bootloader firmwares.

I did change this back to BE, along with a couple of other tweaks, and
uploaded what I hope is the final version of the patch to "raydium"
branch of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git

Please give it a try on your side and if everything works I will queue
the driver to be merged in the next merge window.

Thanks!

-- 
Dmitry
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
@ 2016-05-24  9:05 jeffrey.lin
  0 siblings, 0 replies; 42+ messages in thread
From: jeffrey.lin @ 2016-05-24  9:05 UTC (permalink / raw)
  To: dmitry.torokhov, rydberg, grant.likely, robh+dt, jeesw, bleung
  Cc: jeffrey.lin, roger.yang, KP.li, albert.shieh, linux-kernel,
	linux-input, devicetree

Raydium I2C touch driver.

Signed-off-by: jeffrey.lin <jeffrey.lin@rad-ic.com>
---
 drivers/input/touchscreen/Kconfig          |   12 +
 drivers/input/touchscreen/Makefile         |    1 +
 drivers/input/touchscreen/raydium_i2c_ts.c | 1208 ++++++++++++++++++++++++++++
 3 files changed, 1221 insertions(+)
 create mode 100644 drivers/input/touchscreen/raydium_i2c_ts.c

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 3f3f6ee..df0e2ed 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -915,6 +915,18 @@ config TOUCHSCREEN_PCAP
 	  To compile this driver as a module, choose M here: the
 	  module will be called pcap_ts.
 
+config TOUCHSCREEN_RM_TS
+	tristate "Raydium I2C Touchscreen"
+	depends on I2C
+	help
+	  Say Y here if you have Raydium series I2C touchscreen,
+	  such as RM32380,connected to your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called raydium_i2c_ts.
+
 config TOUCHSCREEN_ST1232
 	tristate "Sitronix ST1232 touchscreen controllers"
 	depends on I2C
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 4941f2d..99e08cf 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE)	+= usbtouchscreen.o
 obj-$(CONFIG_TOUCHSCREEN_PCAP)		+= pcap_ts.o
 obj-$(CONFIG_TOUCHSCREEN_PENMOUNT)	+= penmount.o
 obj-$(CONFIG_TOUCHSCREEN_PIXCIR)	+= pixcir_i2c_ts.o
+obj-$(CONFIG_TOUCHSCREEN_RM_TS)		+= raydium_i2c_ts.o
 obj-$(CONFIG_TOUCHSCREEN_S3C2410)	+= s3c2410_ts.o
 obj-$(CONFIG_TOUCHSCREEN_ST1232)	+= st1232.o
 obj-$(CONFIG_TOUCHSCREEN_STMPE)		+= stmpe-ts.o
diff --git a/drivers/input/touchscreen/raydium_i2c_ts.c b/drivers/input/touchscreen/raydium_i2c_ts.c
new file mode 100644
index 0000000..27c84c5
--- /dev/null
+++ b/drivers/input/touchscreen/raydium_i2c_ts.c
@@ -0,0 +1,1208 @@
+/*
+ * Raydium touchscreen I2C driver.
+ *
+ * Copyright (C) 2012-2014, Raydium Semiconductor Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Raydium reserves the right to make changes without further notice
+ * to the materials described herein. Raydium does not assume any
+ * liability arising out of the application described herein.
+ *
+ * Contact Raydium Semiconductor Corporation at www.rad-ic.com
+ */
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <asm/unaligned.h>
+/* Slave I2C mode */
+#define RM_BOOT_BLDR		0x02
+#define RM_BOOT_MAIN		0x03
+
+/* I2C bootoloader commands */
+#define RM_CMD_BOOT_PAGE_WRT	0x0B		/* send bl page write */
+#define RM_CMD_BOOT_WRT		0x11		/* send bl write */
+#define RM_CMD_BOOT_ACK		0x22		/* send ack*/
+#define RM_CMD_BOOT_CHK		0x33		/* send data check */
+#define RM_CMD_BOOT_READ	0x44		/* send wait bl data ready*/
+
+#define RM_BOOT_RDY		0xFF		/* bl data ready */
+
+/* I2C main commands */
+#define RM_CMD_QUERY_BANK	0x2B
+#define RM_CMD_DATA_BANK	0x4D
+#define RM_CMD_ENTER_SLEEP	0x4E
+#define RM_CMD_BANK_SWITCH	0xAA
+
+#define RM_RESET_MSG_ADDR	0x40000004
+
+#define RM_MAX_READ_SIZE	56
+
+/* Touch relative info */
+#define RM_MAX_RETRIES		3
+#define RM_MAX_TOUCH_NUM	10
+#define RM_BOOT_DELAY_MS	100
+
+/* Offsets in contact data */
+#define RM_CONTACT_STATE_POS	0
+#define RM_CONTACT_X_POS	1
+#define RM_CONTACT_Y_POS	3
+#define RM_CONTACT_PRESSURE_POS	5	/*FIXME, correct 5*/
+#define RM_CONTACT_WIDTH_X_POS	6
+#define RM_CONTACT_WIDTH_Y_POS	7
+
+/* Bootloader relative info */
+#define RM_BL_WRT_CMD_SIZE	3	/* bl flash wrt cmd size */
+#define RM_BL_WRT_PKG_SIZE	32	/* bl wrt pkg size */
+#define RM_BL_WRT_LEN		(RM_BL_WRT_PKG_SIZE + RM_BL_WRT_CMD_SIZE)
+#define RM_FW_PAGE_SIZE		128
+#define RM_MAX_FW_RETRIES	30
+#define RM_MAX_FW_SIZE		(0xD000)	/*define max firmware size*/
+
+#define RM_POWERON_DELAY_USEC	500
+#define RM_RESET_DELAY_MSEC	50
+
+enum raydium_bl_cmd {
+	BL_HEADER = 0,
+	BL_PAGE_STR,
+	BL_PKG_IDX,
+	BL_DATA_STR,
+};
+
+enum raydium_bl_ack {
+	RAYDIUM_ACK_NULL = 0,
+	RAYDIUM_WAIT_READY,
+	RAYDIUM_PATH_READY,
+};
+
+enum raydium_boot_mode {
+	RAYDIUM_TS_MAIN = 0,
+	RAYDIUM_TS_BLDR,
+};
+
+/* Response to RM_CMD_DATA_BANK request */
+struct raydium_data_info {
+	__le32 data_bank_addr;
+	u8 pkg_size;
+	u8 tp_info_size;
+};
+
+struct raydium_info {
+	__le32 hw_ver;		/*device version */
+	u8 main_ver;
+	u8 sub_ver;
+	__le16 ft_ver;		/* test version */
+	u8 x_num;
+	u8 y_num;
+	__le16 x_max;
+	__le16 y_max;
+	u8 x_res;		/* units/mm */
+	u8 y_res;		/* units/mm */
+};
+
+/* struct raydium_data - represents state of Raydium touchscreen device */
+struct raydium_data {
+	struct i2c_client *client;
+	struct input_dev *input;
+
+	struct regulator *avdd;
+	struct regulator *vccio;
+	struct gpio_desc *reset_gpio;
+
+	struct raydium_info info;
+
+	struct mutex sysfs_mutex;
+
+	u8 *report_data;
+
+	u32 data_bank_addr;
+	u8 report_size;
+	u8 contact_size;
+
+	enum raydium_boot_mode boot_mode;
+
+	bool wake_irq_enabled;
+};
+
+static int raydium_i2c_send(struct i2c_client *client,
+			    u8 addr, const void *data, size_t len)
+{
+	u8 *buf;
+	int tries = 0;
+	int ret;
+
+	buf = kmalloc(len + 1, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	buf[0] = addr;
+	memcpy(buf + 1, data, len);
+
+	do {
+		ret = i2c_master_send(client, buf, len + 1);
+		if (likely(ret == len + 1))
+			break;
+
+		msleep(20);
+	} while (++tries < RM_MAX_RETRIES);
+
+	kfree(buf);
+
+	if (unlikely(ret != len + 1)) {
+		if (ret >= 0)
+			ret = -EIO;
+		dev_err(&client->dev, "%s failed: %d\n", __func__, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int raydium_i2c_read(struct i2c_client *client,
+			    u8 addr, void *data, size_t len)
+{
+	struct i2c_msg xfer[] = {
+		{
+			.addr = client->addr,
+			.len = 1,
+			.buf = &addr,
+		},
+		{
+			.addr = client->addr,
+			.flags = I2C_M_RD,
+			.len = len,
+			.buf = data,
+		}
+	};
+	int ret;
+
+	ret = i2c_transfer(client->adapter, xfer, ARRAY_SIZE(xfer));
+	if (unlikely(ret != ARRAY_SIZE(xfer)))
+		return ret < 0 ? ret : -EIO;
+
+	return 0;
+}
+
+static int raydium_i2c_read_message(struct i2c_client *client,
+				    u32 addr, void *data, size_t len)
+{
+	__le32 le_addr;
+	size_t xfer_len;
+	int error;
+
+	while (len) {
+		xfer_len = min_t(size_t, len, RM_MAX_READ_SIZE);
+
+		le_addr = cpu_to_le32(addr);
+
+		error = raydium_i2c_send(client, RM_CMD_BANK_SWITCH,
+					 &le_addr, sizeof(le_addr));
+		if (!error)
+			error = raydium_i2c_read(client, addr & 0xff,
+						 data, xfer_len);
+		if (error)
+			return error;
+
+		len -= xfer_len;
+		data += xfer_len;
+		addr += xfer_len;
+	}
+
+	return 0;
+}
+
+static int raydium_i2c_send_message(struct i2c_client *client,
+				    u32 addr, const void *data, size_t len)
+{
+	__le32 le_addr = cpu_to_le32(addr);
+	int error;
+
+	error = raydium_i2c_send(client, RM_CMD_BANK_SWITCH,
+				 &le_addr, sizeof(le_addr));
+	if (!error)
+		error = raydium_i2c_send(client, le_addr & 0xff,
+				data, len);
+
+	return error;
+}
+
+static int raydium_i2c_sw_reset(struct i2c_client *client)
+{
+	const u8 soft_rst_cmd = 0x01;
+	int error;
+
+	error = raydium_i2c_send_message(client,
+		RM_RESET_MSG_ADDR, &soft_rst_cmd, sizeof(soft_rst_cmd));
+	if (error) {
+		dev_err(&client->dev, "software reset failed: %d\n", error);
+		return error;
+	}
+
+	msleep(RM_RESET_DELAY_MSEC);
+
+	return 0;
+}
+
+static int raydium_i2c_query_ts_info(struct raydium_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	struct raydium_data_info data_info;
+	__le32 query_bank_addr;
+
+	int error, retry_cnt;
+
+	for (retry_cnt = 0; retry_cnt < RM_MAX_RETRIES; retry_cnt++) {
+		error = raydium_i2c_read(client, RM_CMD_DATA_BANK,
+					 &data_info, sizeof(data_info));
+		if (error)
+			continue;
+
+		ts->data_bank_addr = le32_to_cpu(data_info.data_bank_addr);
+		ts->report_size = data_info.pkg_size;
+		ts->contact_size = data_info.tp_info_size;
+
+		dev_dbg(&client->dev,
+			"data_bank_addr: %#08x, report_size: %d, contact_size: %d\n",
+			ts->data_bank_addr, ts->report_size, ts->contact_size);
+
+		error = raydium_i2c_read(client, RM_CMD_QUERY_BANK,
+					 &query_bank_addr,
+					 sizeof(query_bank_addr));
+		if (error)
+			continue;
+
+		error = raydium_i2c_read_message(client,
+						 le32_to_cpu(query_bank_addr),
+						 &ts->info, sizeof(ts->info));
+		if (error)
+			continue;
+
+		return 0;
+	}
+	dev_err(&client->dev, "failed to query device parameters: %d\n", error);
+	return error;
+}
+
+static int raydium_i2c_check_fw_status(struct raydium_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	static const u8 bl_ack = 0x62;
+	static const u8 main_ack = 0x66;
+	u8 buf[4];
+	int error;
+
+	error = raydium_i2c_read(client, RM_CMD_BOOT_READ, buf, sizeof(buf));
+	if (!error) {
+		/*FIXME, changed as one byte comparison*/
+		if (buf[0] == bl_ack)
+			ts->boot_mode = RAYDIUM_TS_BLDR;
+		else if (buf[0] == main_ack)
+			ts->boot_mode = RAYDIUM_TS_MAIN;
+		return 0;
+	}
+
+	return error;
+}
+
+static int raydium_i2c_initialize(struct raydium_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	int error, retry_cnt;
+
+	for (retry_cnt = 0; retry_cnt < RM_MAX_RETRIES; retry_cnt++) {
+		/* Wait for Hello packet */
+		msleep(RM_BOOT_DELAY_MS);
+
+		error = raydium_i2c_check_fw_status(ts);
+		if (ts->boot_mode == RAYDIUM_TS_BLDR ||
+			ts->boot_mode == RAYDIUM_TS_MAIN)
+			break;
+		else if (error) {
+			dev_err(&client->dev,
+				"failed to read 'hello' packet: %d\n", error);
+		}
+	}
+
+	if (error)
+		ts->boot_mode = RAYDIUM_TS_BLDR;
+
+	if (ts->boot_mode == RAYDIUM_TS_BLDR) {
+		ts->info.hw_ver = cpu_to_le32(0xffffffffUL);
+		ts->info.main_ver = 0xff;
+		ts->info.sub_ver = 0xff;
+	} else {
+		raydium_i2c_query_ts_info(ts);
+	}
+
+	return error;
+}
+
+
+static int raydium_i2c_bl_chk_state(struct i2c_client *client,
+				    enum raydium_bl_ack state)
+{
+	static const u8 ack_ok[] = { 0xFF, 0x39, 0x30, 0x30, 0x54 };
+	u8 rbuf[sizeof(ack_ok)];
+	u8 retry;
+	int error;
+
+	for (retry = 0; retry < RM_MAX_FW_RETRIES; retry++) {
+		switch (state) {
+		case RAYDIUM_ACK_NULL:
+			return 0;
+
+		case RAYDIUM_WAIT_READY:
+			error = raydium_i2c_read(client, RM_CMD_BOOT_CHK,
+						 &rbuf[0], 1);
+			if (!error && rbuf[0] == RM_BOOT_RDY)
+				return 0;
+
+			break;
+
+		case RAYDIUM_PATH_READY:
+			error = raydium_i2c_read(client, RM_CMD_BOOT_CHK,
+						 rbuf, sizeof(rbuf));
+			if (!error && !memcmp(rbuf, ack_ok, sizeof(ack_ok)))
+				return 0;
+
+			break;
+
+		default:
+			dev_err(&client->dev, "%s: invalid target state %d\n",
+				__func__, state);
+			return -EINVAL;
+		}
+
+		msleep(20);
+	}
+
+	return -ETIMEDOUT;
+}
+
+static int raydium_i2c_write_object(struct i2c_client *client,
+				    const void *data, size_t len,
+				    enum raydium_bl_ack state)
+{
+	int error;
+
+	error = raydium_i2c_send(client, RM_CMD_BOOT_WRT, data, len);
+	if (error) {
+		dev_err(&client->dev, "WRT obj command failed: %d\n",
+			error);
+		return error;
+	}
+
+	error = raydium_i2c_send(client, RM_CMD_BOOT_ACK, NULL, 0);
+	if (error) {
+		dev_err(&client->dev, "Ack obj command failed: %d\n", error);
+		return error;
+	}
+
+	error = raydium_i2c_bl_chk_state(client, state);
+	if (error) {
+		dev_err(&client->dev, "BL check state failed: %d\n", error);
+		return error;
+	}
+	return 0;
+}
+
+static bool raydium_i2c_boot_trigger(struct i2c_client *client)
+{
+	static const u8 cmd[7][6] = {
+		{ 0x08, 0x0C, 0x09, 0x00, 0x50, 0xD7 },
+		{ 0x08, 0x04, 0x09, 0x00, 0x50, 0xA5 },
+		{ 0x08, 0x04, 0x09, 0x00, 0x50, 0x00 },
+		{ 0x08, 0x04, 0x09, 0x00, 0x50, 0xA5 },
+		{ 0x08, 0x0C, 0x09, 0x00, 0x50, 0x00 },
+		{ 0x06, 0x01, 0x00, 0x00, 0x00, 0x00 },
+		{ 0x02, 0xA2, 0x00, 0x00, 0x00, 0x00 },
+	};
+	int i;
+	int error;
+
+	for (i = 0; i < 7; i++) {
+		error = raydium_i2c_write_object(client, cmd[i], sizeof(cmd[i]),
+						 RAYDIUM_WAIT_READY);
+		if (error) {
+			dev_err(&client->dev,
+				"boot trigger failed at step %d: %d\n",
+				i, error);
+			return error;
+		}
+	}
+
+	return 0;
+}
+
+static bool raydium_i2c_fw_trigger(struct i2c_client *client)
+{
+	static const u8 cmd[5][11] = {
+		{ 0, 0x09, 0x71, 0x0C, 0x09, 0x00, 0x50, 0xD7, 0, 0, 0 },
+		{ 0, 0x09, 0x71, 0x04, 0x09, 0x00, 0x50, 0xA5, 0, 0, 0 },
+		{ 0, 0x09, 0x71, 0x04, 0x09, 0x00, 0x50, 0x00, 0, 0, 0 },
+		{ 0, 0x09, 0x71, 0x04, 0x09, 0x00, 0x50, 0xA5, 0, 0, 0 },
+		{ 0, 0x09, 0x71, 0x0C, 0x09, 0x00, 0x50, 0x00, 0, 0, 0 },
+	};
+	int i;
+	int error;
+
+	for (i = 0; i < 5; i++) {
+		error = raydium_i2c_write_object(client, cmd[i], sizeof(cmd[i]),
+						 RAYDIUM_ACK_NULL);
+		if (error) {
+			dev_err(&client->dev,
+				"fw trigger failed at step %d: %d\n",
+				i, error);
+			return error;
+		}
+	}
+
+	return 0;
+}
+
+static int raydium_i2c_check_path(struct i2c_client *client)
+{
+	static const u8 cmd[] = { 0x09, 0x00, 0x09, 0x00, 0x50, 0x10, 0x00 };
+	int error;
+
+	error = raydium_i2c_write_object(client, cmd, sizeof(cmd),
+					 RAYDIUM_PATH_READY);
+	if (error) {
+		dev_err(&client->dev, "check path command failed: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int raydium_i2c_enter_bl(struct i2c_client *client)
+{
+	static const u8 cal_cmd[] = { 0x00, 0x01, 0x52 };
+	int error;
+
+	error = raydium_i2c_write_object(client, cal_cmd, sizeof(cal_cmd),
+					 RAYDIUM_ACK_NULL);
+	if (error) {
+		dev_err(&client->dev, "enter bl command failed: %d\n", error);
+		return error;
+	}
+
+	msleep(RM_BOOT_DELAY_MS);
+	return 0;
+}
+
+static int raydium_i2c_leave_bl(struct i2c_client *client)
+{
+	static const u8 leave_cmd[] = { 0x05, 0x00 };
+	int error;
+
+	error = raydium_i2c_write_object(client, leave_cmd, sizeof(leave_cmd),
+					 RAYDIUM_ACK_NULL);
+	if (error) {
+		dev_err(&client->dev, "leave bl command failed: %d\n", error);
+		return error;
+	}
+
+	msleep(RM_BOOT_DELAY_MS);
+	return 0;
+}
+
+static int raydium_i2c_write_checksum(struct i2c_client *client,
+				      size_t length, u16 checksum)
+{
+	u8 checksum_cmd[] = { 0x00, 0x05, 0x6D, 0x00, 0x00, 0x00, 0x00 };
+	int error;
+
+	put_unaligned_le16(length, &checksum_cmd[3]);
+	put_unaligned_le16(checksum, &checksum_cmd[5]);
+
+	error = raydium_i2c_write_object(client,
+					 checksum_cmd, sizeof(checksum_cmd),
+					 RAYDIUM_ACK_NULL);
+	if (error) {
+		dev_err(&client->dev, "failed to write checksum: %d\n",
+			error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int raydium_i2c_disable_watch_dog(struct i2c_client *client)
+{
+	static const u8 cmd[] = { 0x0A, 0xAA };
+	int error;
+
+	error = raydium_i2c_write_object(client, cmd, sizeof(cmd),
+					 RAYDIUM_WAIT_READY);
+	if (error) {
+		dev_err(&client->dev, "disable watchdog command failed: %d\n",
+			error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int raydium_i2c_fw_write_page(struct i2c_client *client,
+				     u16 page_idx, const void *data, size_t len)
+{
+	u8 buf[RM_BL_WRT_LEN];
+	size_t xfer_len;
+	int error;
+	int i;
+
+	BUILD_BUG_ON((RM_FW_PAGE_SIZE % RM_BL_WRT_PKG_SIZE) != 0);
+
+	for (i = 0; i < RM_FW_PAGE_SIZE / RM_BL_WRT_PKG_SIZE; i++) {
+		buf[BL_HEADER] = RM_CMD_BOOT_PAGE_WRT;
+		buf[BL_PAGE_STR] = page_idx ? 0xff : 0;
+		buf[BL_PKG_IDX] = i + 1;
+
+		xfer_len = min_t(size_t, len, RM_BL_WRT_PKG_SIZE);
+		memcpy(&buf[BL_DATA_STR], data, xfer_len);
+		if (len < RM_BL_WRT_PKG_SIZE)
+			memset(&buf[BL_DATA_STR + xfer_len], 0xff,
+				RM_BL_WRT_PKG_SIZE - xfer_len);
+
+		error = raydium_i2c_write_object(client, buf, RM_BL_WRT_LEN,
+						RAYDIUM_WAIT_READY);
+		if (error) {
+			dev_err(&client->dev,
+				"page write command failed for page %d, chunk %d: %d\n",
+				page_idx, i, error);
+			return error;
+		}
+		data += xfer_len;
+		len -= xfer_len;
+	}
+
+	return error;
+}
+
+static int raydium_i2c_do_update_firmware(struct raydium_data *ts,
+					 const struct firmware *fw)
+{
+	struct i2c_client *client = ts->client;
+	const void *data;
+	size_t data_len;
+	size_t len;
+	int page_nr;
+	int i;
+	int error;
+	u16 fw_checksum;
+
+	if (fw->size == 0 || fw->size > RM_MAX_FW_SIZE) {
+		dev_err(&client->dev, "Invalid firmware length\n");
+		return -EINVAL;
+	}
+
+	error = raydium_i2c_check_fw_status(ts);
+	if (error) {
+		dev_err(&client->dev, "Unable to access IC %d\n", error);
+		return error;
+	}
+
+	if (ts->boot_mode == RAYDIUM_TS_MAIN) {
+		for (i = 0; i < RM_MAX_RETRIES; i++) {
+			error = raydium_i2c_enter_bl(client);
+			if (!error) {
+				error = raydium_i2c_check_fw_status(ts);
+				if (error) {
+					dev_err(&client->dev,
+						"unable to access IC: %d\n",
+						error);
+					return error;
+				}
+
+				if (ts->boot_mode == RAYDIUM_TS_BLDR)
+					break;
+			}
+		}
+
+		if (ts->boot_mode == RAYDIUM_TS_MAIN) {
+			dev_err(&client->dev,
+				"failied to jump to boot loader: %d\n",
+				error);
+			return -EIO;
+		}
+	}
+
+	error = raydium_i2c_disable_watch_dog(client);
+	if (error)
+		return error;
+
+	error = raydium_i2c_check_path(client);
+	if (error)
+		return error;
+
+	error = raydium_i2c_boot_trigger(client);
+	if (error) {
+		dev_err(&client->dev, "send boot trigger fail: %d\n", error);
+		return error;
+	}
+
+	msleep(RM_BOOT_DELAY_MS);
+
+	data = fw->data;
+	data_len = fw->size;
+	page_nr = 0;
+
+	while (data_len) {
+		len = min_t(size_t, data_len, RM_FW_PAGE_SIZE);
+
+		error = raydium_i2c_fw_write_page(client, page_nr++,
+				data, len);
+		if (error)
+			return error;
+
+		/*FIXME, remove delay in raydium_i2c_fw_write_page()*/
+		msleep(20);
+
+		data += len;
+		data_len -= len;
+	}
+
+	error = raydium_i2c_leave_bl(client);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to leave boot loader: %d\n", error);
+		return error;
+	}
+
+	dev_dbg(&client->dev, "left boot loader mode\n");
+	msleep(RM_BOOT_DELAY_MS);
+
+	error = raydium_i2c_check_fw_status(ts);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to check fw status after write: %d\n",
+			error);
+		return error;
+	}
+
+	if (ts->boot_mode != RAYDIUM_TS_MAIN) {
+		dev_err(&client->dev,
+			"failed to switch to main fw after writing firmware: %d\n",
+			error);
+		return -EINVAL;
+	}
+
+	error = raydium_i2c_fw_trigger(client);
+	if (error) {
+		dev_err(&client->dev, "failed to trigger fw: %d\n", error);
+		return error;
+	}
+
+	fw_checksum = 0;
+	for (i = 0; i < fw->size; i++)
+		fw_checksum += fw->data[i];
+
+	error = raydium_i2c_write_checksum(client, fw->size, fw_checksum);
+	if (error)
+		return error;
+
+	return 0;
+}
+
+static int raydium_i2c_fw_update(struct raydium_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	const struct firmware *fw = NULL;
+	const char *fw_file = "raydium.fw";
+	int error;
+
+	error = request_firmware(&fw, fw_file, &client->dev);
+	if (error) {
+		dev_err(&client->dev, "Unable to open firmware %s\n", fw_file);
+		return error;
+	}
+
+	disable_irq(client->irq);
+
+	error = raydium_i2c_do_update_firmware(ts, fw);
+	if (error) {
+		dev_err(&client->dev, "firmware update failed: %d\n", error);
+		ts->boot_mode = RAYDIUM_TS_BLDR;
+		goto out_enable_irq;
+	}
+
+	error = raydium_i2c_initialize(ts);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to initialize device after firmware update: %d\n",
+			error);
+		ts->boot_mode = RAYDIUM_TS_BLDR;
+		goto out_enable_irq;
+	}
+
+	ts->boot_mode = RAYDIUM_TS_MAIN;
+
+out_enable_irq:
+
+	enable_irq(client->irq);
+	msleep(100);
+
+	release_firmware(fw);
+
+	return error;
+}
+
+static void raydium_mt_event(struct raydium_data *ts)
+{
+	int i;
+	int error;
+
+	error = raydium_i2c_read_message(ts->client, ts->data_bank_addr,
+					 ts->report_data, ts->report_size);
+	if (error) {
+		dev_err(&ts->client->dev, "%s: failed to read data: %d\n",
+			__func__, error);
+		return;
+	}
+
+	for (i = 0; i < ts->report_size / ts->contact_size; i++) {
+		u8 *contact = &ts->report_data[ts->contact_size * i];
+		bool state = contact[RM_CONTACT_STATE_POS];
+		u8 wx, wy;
+
+		input_mt_slot(ts->input, i);
+		input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, state);
+
+		if (!state)
+			continue;
+
+		input_report_abs(ts->input, ABS_MT_POSITION_X,
+				get_unaligned_le16(&contact[RM_CONTACT_X_POS]));
+		input_report_abs(ts->input, ABS_MT_POSITION_Y,
+				get_unaligned_le16(&contact[RM_CONTACT_Y_POS]));
+		input_report_abs(ts->input, ABS_MT_PRESSURE,
+				contact[RM_CONTACT_PRESSURE_POS]);
+
+		wx = contact[RM_CONTACT_WIDTH_X_POS];
+		wy = contact[RM_CONTACT_WIDTH_Y_POS];
+
+		input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, max(wx, wy));
+		input_report_abs(ts->input, ABS_MT_TOUCH_MINOR, min(wx, wy));
+	}
+
+	input_mt_sync_frame(ts->input);
+	input_sync(ts->input);
+}
+
+static irqreturn_t raydium_i2c_irq(int irq, void *_dev)
+{
+	struct raydium_data *ts = _dev;
+
+	if (ts->boot_mode != RAYDIUM_TS_BLDR)
+		raydium_mt_event(ts);
+
+	return IRQ_HANDLED;
+}
+
+static ssize_t raydium_i2c_fw_ver_show(struct device *dev,
+				       struct device_attribute *attr, char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	return sprintf(buf, "%d.%d\n", ts->info.main_ver, ts->info.sub_ver);
+}
+
+static ssize_t raydium_i2c_hw_ver_show(struct device *dev,
+				       struct device_attribute *attr, char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	return sprintf(buf, "%#04x\n", le32_to_cpu(ts->info.hw_ver));
+}
+
+static ssize_t raydium_i2c_boot_mode_show(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	return sprintf(buf, "%s\n",
+		       ts->boot_mode == RAYDIUM_TS_MAIN ?
+				"Normal" : "Recovery");
+}
+
+static ssize_t raydium_i2c_update_fw_store(struct device *dev,
+					   struct device_attribute *attr,
+					   const char *buf, size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
+	int error;
+
+	error = mutex_lock_interruptible(&ts->sysfs_mutex);
+	if (error)
+		return error;
+
+	error = raydium_i2c_fw_update(ts);
+
+	mutex_unlock(&ts->sysfs_mutex);
+
+	return error ?: count;
+}
+
+static ssize_t raydium_i2c_calibrate_store(struct device *dev,
+					   struct device_attribute *attr,
+					   const char *buf, size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
+	static const u8 cal_cmd[] = { 0x00, 0x01, 0x9E };
+	int error;
+
+	error = mutex_lock_interruptible(&ts->sysfs_mutex);
+	if (error)
+		return error;
+
+	error = raydium_i2c_write_object(client, cal_cmd, sizeof(cal_cmd),
+					 RAYDIUM_WAIT_READY);
+	if (error)
+		dev_err(&client->dev, "calibrate command failed: %d\n", error);
+
+	mutex_unlock(&ts->sysfs_mutex);
+	return error ?: count;
+}
+
+static DEVICE_ATTR(fw_version, S_IRUGO, raydium_i2c_fw_ver_show, NULL);
+static DEVICE_ATTR(hw_version, S_IRUGO, raydium_i2c_hw_ver_show, NULL);
+static DEVICE_ATTR(boot_mode, S_IRUGO, raydium_i2c_boot_mode_show, NULL);
+static DEVICE_ATTR(update_fw, S_IWUSR, NULL, raydium_i2c_update_fw_store);
+static DEVICE_ATTR(calibrate, S_IWUSR, NULL, raydium_i2c_calibrate_store);
+
+static struct attribute *raydium_i2c_attributes[] = {
+	&dev_attr_update_fw.attr,
+	&dev_attr_boot_mode.attr,
+	&dev_attr_fw_version.attr,
+	&dev_attr_hw_version.attr,
+	&dev_attr_calibrate.attr,
+	NULL
+};
+
+static struct attribute_group raydium_i2c_attribute_group = {
+	.attrs = raydium_i2c_attributes,
+};
+
+static void raydium_i2c_remove_sysfs_group(void *_data)
+{
+	struct raydium_data *ts = _data;
+
+	sysfs_remove_group(&ts->client->dev.kobj, &raydium_i2c_attribute_group);
+}
+
+static int raydium_i2c_power_on(struct raydium_data *ts)
+{
+	int error;
+
+	if (IS_ERR_OR_NULL(ts->reset_gpio))
+		return 0;
+
+	gpiod_set_value_cansleep(ts->reset_gpio, 1);
+
+	error = regulator_enable(ts->avdd);
+	if (error) {
+		dev_err(&ts->client->dev,
+			"failed to enable avdd regulator: %d\n", error);
+		goto release_reset_gpio;
+	}
+
+	error = regulator_enable(ts->vccio);
+	if (error) {
+		regulator_disable(ts->avdd);
+		dev_err(&ts->client->dev,
+			"failed to enable vccio regulator: %d\n", error);
+		goto release_reset_gpio;
+	}
+
+	udelay(RM_POWERON_DELAY_USEC);
+
+release_reset_gpio:
+	gpiod_set_value_cansleep(ts->reset_gpio, 0);
+
+	if (error)
+		return error;
+
+	msleep(RM_RESET_DELAY_MSEC);
+
+	return 0;
+}
+
+static void raydium_i2c_power_off(void *_data)
+{
+	struct raydium_data *ts = _data;
+
+	if (!IS_ERR_OR_NULL(ts->reset_gpio)) {
+		gpiod_set_value_cansleep(ts->reset_gpio, 1);
+		regulator_disable(ts->vccio);
+		regulator_disable(ts->avdd);
+	}
+}
+
+static int raydium_i2c_probe(struct i2c_client *client,
+			     const struct i2c_device_id *id)
+{
+	union i2c_smbus_data dummy;
+	struct raydium_data *ts;
+	int error;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		dev_err(&client->dev,
+			"i2c check functionality error (need I2C_FUNC_I2C)\n");
+		return -ENXIO;
+	}
+
+	ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+
+	mutex_init(&ts->sysfs_mutex);
+
+	ts->client = client;
+	i2c_set_clientdata(client, ts);
+
+	ts->avdd = devm_regulator_get(&client->dev, "avdd");
+	if (IS_ERR(ts->avdd)) {
+		error = PTR_ERR(ts->avdd);
+		if (error != -EPROBE_DEFER)
+			dev_err(&client->dev,
+				"Failed to get 'avdd' regulator: %d\n", error);
+		return error;
+	}
+
+	ts->vccio = devm_regulator_get(&client->dev, "vccio");
+	if (IS_ERR(ts->vccio)) {
+		error = PTR_ERR(ts->vccio);
+		if (error != -EPROBE_DEFER)
+			dev_err(&client->dev,
+				"Failed to get 'vccio' regulator: %d\n", error);
+		return error;
+	}
+
+	ts->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
+						 GPIOD_OUT_LOW);
+	if (IS_ERR(ts->reset_gpio)) {
+		error = PTR_ERR(ts->reset_gpio);
+		if (error != -EPROBE_DEFER) {
+			dev_err(&client->dev,
+				"failed to get reset gpio: %d\n", error);
+			return error;
+		}
+	}
+
+	error = raydium_i2c_power_on(ts);
+	if (error)
+		return error;
+
+	error = devm_add_action(&client->dev, raydium_i2c_power_off, ts);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to install power off action: %d\n", error);
+		raydium_i2c_power_off(ts);
+		return error;
+	}
+
+	/* Make sure there is something at this address */
+	if (i2c_smbus_xfer(client->adapter, client->addr, 0,
+			   I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0) {
+		dev_err(&client->dev, "nothing at this address\n");
+		return -ENXIO;
+	}
+
+	error = raydium_i2c_initialize(ts);
+	if (error) {
+		dev_err(&client->dev, "failed to initialize: %d\n", error);
+		return error;
+	}
+
+	ts->input = devm_input_allocate_device(&client->dev);
+	if (!ts->input) {
+		dev_err(&client->dev, "Failed to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	ts->report_data = kmalloc(ts->report_size, GFP_KERNEL);
+	if (!ts->report_data)
+		return -ENOMEM;
+
+	ts->input->name = "Raydium Touchscreen";
+	ts->input->id.bustype = BUS_I2C;
+
+	input_set_drvdata(ts->input, ts);
+
+	input_set_abs_params(ts->input, ABS_MT_POSITION_X,
+			     0, le32_to_cpu(ts->info.x_max), 0, 0);
+	input_set_abs_params(ts->input, ABS_MT_POSITION_Y,
+			     0, le32_to_cpu(ts->info.y_max), 0, 0);
+	input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->info.x_res);
+	input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->info.y_res);
+
+	input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+	input_set_abs_params(ts->input, ABS_MT_PRESSURE, 0, 255, 0, 0);
+
+	error = input_mt_init_slots(ts->input, RM_MAX_TOUCH_NUM,
+				    INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to initialize MT slots: %d\n", error);
+		goto out_of_probe;
+	}
+
+	error = input_register_device(ts->input);
+	if (error) {
+		dev_err(&client->dev,
+			"unable to register input device: %d\n", error);
+		goto out_of_probe;
+	}
+
+	error = devm_request_threaded_irq(&client->dev, client->irq,
+					  NULL, raydium_i2c_irq,
+					  IRQF_ONESHOT, client->name, ts);
+	if (error) {
+		dev_err(&client->dev, "Failed to register interrupt\n");
+		goto out_of_probe;
+	}
+
+	error = sysfs_create_group(&client->dev.kobj,
+				   &raydium_i2c_attribute_group);
+	if (error) {
+		dev_err(&client->dev, "failed to create sysfs attributes: %d\n",
+			error);
+		goto out_of_probe;
+	}
+
+	error = devm_add_action(&client->dev,
+				raydium_i2c_remove_sysfs_group, ts);
+	if (error) {
+		raydium_i2c_remove_sysfs_group(ts);
+		dev_err(&client->dev,
+			"Failed to add sysfs cleanup action: %d\n", error);
+		goto out_of_probe;
+	}
+
+	return 0;
+
+out_of_probe:
+	kfree(ts->report_data);
+	return error;
+}
+
+static void __maybe_unused raydium_enter_sleep(struct i2c_client *client)
+{
+	static const u8 sleep_cmd[] = { 0x5A, 0xff, 0x00, 0x0f };
+	int error;
+
+	error = raydium_i2c_send(client, RM_CMD_ENTER_SLEEP,
+				 sleep_cmd, sizeof(sleep_cmd));
+	if (error)
+		dev_err(&client->dev,
+			"sleep command failed: %d\n", error);
+}
+
+static int __maybe_unused raydium_i2c_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	/* Sleep is not available in BLDR recovery mode */
+	if (ts->boot_mode != RAYDIUM_TS_MAIN)
+		return -ENOMEM;
+
+	disable_irq(client->irq);
+
+	if (device_may_wakeup(dev)) {
+		raydium_enter_sleep(client);
+
+		ts->wake_irq_enabled = (enable_irq_wake(client->irq) == 0);
+	} else {
+		raydium_i2c_power_off(ts);
+	}
+
+	return 0;
+}
+
+static int __maybe_unused raydium_i2c_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	if (device_may_wakeup(dev)) {
+		if (ts->wake_irq_enabled)
+			disable_irq_wake(client->irq);
+		raydium_i2c_sw_reset(client);
+	} else {
+		raydium_i2c_power_on(ts);
+		raydium_i2c_initialize(ts);
+	}
+
+	enable_irq(client->irq);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(raydium_i2c_pm_ops,
+			 raydium_i2c_suspend, raydium_i2c_resume);
+
+static const struct i2c_device_id raydium_i2c_id[] = {
+	{ "raydium_i2c" , 0 },
+	{ "rm32380", 0 },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(i2c, raydium_i2c_id);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id raydium_acpi_id[] = {
+	{ "RAYD0001", 0 },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(acpi, raydium_acpi_id);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id raydium_of_match[] = {
+	{ .compatible = "raydium,rm32380", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, raydium_of_match);
+#endif
+
+static struct i2c_driver raydium_i2c_driver = {
+	.probe = raydium_i2c_probe,
+	.id_table = raydium_i2c_id,
+	.driver = {
+		.name = "raydium_ts",
+		.pm = &raydium_i2c_pm_ops,
+		.acpi_match_table = ACPI_PTR(raydium_acpi_id),
+		.of_match_table = of_match_ptr(raydium_of_match),
+	},
+};
+module_i2c_driver(raydium_i2c_driver);
+
+MODULE_AUTHOR("Raydium");
+MODULE_DESCRIPTION("Raydium I2c Touchscreen driver");
+MODULE_LICENSE("GPL v2");
+
-- 
2.1.2

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
@ 2016-05-17 15:34 jeffrey.lin
  0 siblings, 0 replies; 42+ messages in thread
From: jeffrey.lin @ 2016-05-17 15:34 UTC (permalink / raw)
  To: dmitry.torokhov, rydberg, grant.likely, robh+dt, jeesw, bleung
  Cc: jeffrey.lin, roger.yang, KP.li, albert.shieh, linux-kernel,
	linux-input, devicetree

Raydium I2C touch driver.

Signed-off-by: jeffrey.lin <jeffrey.lin@rad-ic.com>
---
 drivers/input/touchscreen/Kconfig          |   12 +
 drivers/input/touchscreen/Makefile         |    1 +
 drivers/input/touchscreen/raydium_i2c_ts.c | 1208 ++++++++++++++++++++++++++++
 3 files changed, 1221 insertions(+)
 create mode 100644 drivers/input/touchscreen/raydium_i2c_ts.c

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 3f3f6ee..df0e2ed 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -915,6 +915,18 @@ config TOUCHSCREEN_PCAP
 	  To compile this driver as a module, choose M here: the
 	  module will be called pcap_ts.
 
+config TOUCHSCREEN_RM_TS
+	tristate "Raydium I2C Touchscreen"
+	depends on I2C
+	help
+	  Say Y here if you have Raydium series I2C touchscreen,
+	  such as RM32380,connected to your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called raydium_i2c_ts.
+
 config TOUCHSCREEN_ST1232
 	tristate "Sitronix ST1232 touchscreen controllers"
 	depends on I2C
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 4941f2d..99e08cf 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE)	+= usbtouchscreen.o
 obj-$(CONFIG_TOUCHSCREEN_PCAP)		+= pcap_ts.o
 obj-$(CONFIG_TOUCHSCREEN_PENMOUNT)	+= penmount.o
 obj-$(CONFIG_TOUCHSCREEN_PIXCIR)	+= pixcir_i2c_ts.o
+obj-$(CONFIG_TOUCHSCREEN_RM_TS)		+= raydium_i2c_ts.o
 obj-$(CONFIG_TOUCHSCREEN_S3C2410)	+= s3c2410_ts.o
 obj-$(CONFIG_TOUCHSCREEN_ST1232)	+= st1232.o
 obj-$(CONFIG_TOUCHSCREEN_STMPE)		+= stmpe-ts.o
diff --git a/drivers/input/touchscreen/raydium_i2c_ts.c b/drivers/input/touchscreen/raydium_i2c_ts.c
new file mode 100644
index 0000000..3d7ff5a
--- /dev/null
+++ b/drivers/input/touchscreen/raydium_i2c_ts.c
@@ -0,0 +1,1208 @@
+/*
+ * Raydium touchscreen I2C driver.
+ *
+ * Copyright (C) 2012-2014, Raydium Semiconductor Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Raydium reserves the right to make changes without further notice
+ * to the materials described herein. Raydium does not assume any
+ * liability arising out of the application described herein.
+ *
+ * Contact Raydium Semiconductor Corporation at www.rad-ic.com
+ */
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <asm/unaligned.h>
+/* Slave I2C mode */
+#define RM_BOOT_BLDR		0x02
+#define RM_BOOT_MAIN		0x03
+
+/* I2C bootoloader commands */
+#define RM_CMD_BOOT_PAGE_WRT	0x0B		/* send bl page write */
+#define RM_CMD_BOOT_WRT		0x11		/* send bl write */
+#define RM_CMD_BOOT_ACK		0x22		/* send ack*/
+#define RM_CMD_BOOT_CHK		0x33		/* send data check */
+#define RM_CMD_BOOT_READ	0x44		/* send wait bl data ready*/
+
+#define RM_BOOT_RDY		0xFF		/* bl data ready */
+
+/* I2C main commands */
+#define RM_CMD_QUERY_BANK	0x2B
+#define RM_CMD_DATA_BANK	0x4D
+#define RM_CMD_ENTER_SLEEP	0x4E
+#define RM_CMD_BANK_SWITCH	0xAA
+
+#define RM_RESET_MSG_ADDR	0x40000004
+
+#define RM_MAX_READ_SIZE	56
+
+/* Touch relative info */
+#define RM_MAX_RETRIES		3
+#define RM_MAX_TOUCH_NUM	10
+#define RM_BOOT_DELAY_MS	100
+
+/* Offsets in contact data */
+#define RM_CONTACT_STATE_POS	0
+#define RM_CONTACT_X_POS	1
+#define RM_CONTACT_Y_POS	3
+#define RM_CONTACT_PRESSURE_POS	5	/*FIXME, correct 5*/
+#define RM_CONTACT_WIDTH_X_POS	6
+#define RM_CONTACT_WIDTH_Y_POS	7
+
+/* Bootloader relative info */
+#define RM_BL_WRT_CMD_SIZE	3	/* bl flash wrt cmd size */
+#define RM_BL_WRT_PKG_SIZE	32	/* bl wrt pkg size */
+#define RM_BL_WRT_LEN		(RM_BL_WRT_PKG_SIZE + RM_BL_WRT_CMD_SIZE)
+#define RM_FW_PAGE_SIZE		128
+#define RM_MAX_FW_RETRIES	30
+#define RM_MAX_FW_SIZE		(0xD000)	/*define max firmware size*/
+
+#define RM_POWERON_DELAY_USEC	500
+#define RM_RESET_DELAY_MSEC	50
+
+enum raydium_bl_cmd {
+	BL_HEADER = 0,
+	BL_PAGE_STR,
+	BL_PKG_IDX,
+	BL_DATA_STR,
+};
+
+enum raydium_bl_ack {
+	RAYDIUM_ACK_NULL = 0,
+	RAYDIUM_WAIT_READY,
+	RAYDIUM_PATH_READY,
+};
+
+enum raydium_boot_mode {
+	RAYDIUM_TS_MAIN = 0,
+	RAYDIUM_TS_BLDR,
+};
+
+/* Response to RM_CMD_DATA_BANK request */
+struct raydium_data_info {
+	__le32 data_bank_addr;
+	u8 pkg_size;
+	u8 tp_info_size;
+};
+
+struct raydium_info {
+	__le32 hw_ver;		/*device version */
+	u8 main_ver;
+	u8 sub_ver;
+	__le16 ft_ver;		/* test version */
+	u8 x_num;
+	u8 y_num;
+	__le16 x_max;
+	__le16 y_max;
+	u8 x_res;		/* units/mm */
+	u8 y_res;		/* units/mm */
+};
+
+/* struct raydium_data - represents state of Raydium touchscreen device */
+struct raydium_data {
+	struct i2c_client *client;
+	struct input_dev *input;
+
+	struct regulator *avdd;
+	struct regulator *vccio;
+	struct gpio_desc *reset_gpio;
+
+	struct raydium_info info;
+
+	struct mutex sysfs_mutex;
+
+	u8 *report_data;
+
+	u32 data_bank_addr;
+	u8 report_size;
+	u8 contact_size;
+
+	enum raydium_boot_mode boot_mode;
+
+	bool wake_irq_enabled;
+};
+
+static int raydium_i2c_send(struct i2c_client *client,
+			    u8 addr, const void *data, size_t len)
+{
+	u8 *buf;
+	int tries = 0;
+	int ret;
+
+	buf = kmalloc(len + 1, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	buf[0] = addr;
+	memcpy(buf + 1, data, len);
+
+	do {
+		ret = i2c_master_send(client, buf, len + 1);
+		if (likely(ret == len + 1))
+			break;
+
+		msleep(20);
+	} while (++tries < RM_MAX_RETRIES);
+
+	kfree(buf);
+
+	if (unlikely(ret != len + 1)) {
+		if (ret >= 0)
+			ret = -EIO;
+		dev_err(&client->dev, "%s failed: %d\n", __func__, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int raydium_i2c_read(struct i2c_client *client,
+			    u8 addr, void *data, size_t len)
+{
+	struct i2c_msg xfer[] = {
+		{
+			.addr = client->addr,
+			.len = 1,
+			.buf = &addr,
+		},
+		{
+			.addr = client->addr,
+			.flags = I2C_M_RD,
+			.len = len,
+			.buf = data,
+		}
+	};
+	int ret;
+
+	ret = i2c_transfer(client->adapter, xfer, ARRAY_SIZE(xfer));
+	if (unlikely(ret != ARRAY_SIZE(xfer)))
+		return ret < 0 ? ret : -EIO;
+
+	return 0;
+}
+
+static int raydium_i2c_read_message(struct i2c_client *client,
+				    u32 addr, void *data, size_t len)
+{
+	__be32 be_addr;
+	size_t xfer_len;
+	int error;
+
+	while (len) {
+		xfer_len = min_t(size_t, len, RM_MAX_READ_SIZE);
+
+		be_addr = cpu_to_be32(addr);
+
+		error = raydium_i2c_send(client, RM_CMD_BANK_SWITCH,
+					 &be_addr, sizeof(be_addr));
+		if (!error)
+			error = raydium_i2c_read(client, (be_addr >> 24) & 0xff,
+						 data, xfer_len);
+		if (error)
+			return error;
+
+		len -= xfer_len;
+		data += xfer_len;
+		addr += xfer_len;
+	}
+
+	return 0;
+}
+
+static int raydium_i2c_send_message(struct i2c_client *client,
+				    u32 addr, const void *data, size_t len)
+{
+	__be32 be_addr = cpu_to_be32(addr);
+	int error;
+
+	error = raydium_i2c_send(client, RM_CMD_BANK_SWITCH,
+				 &be_addr, sizeof(be_addr));
+	if (!error)
+		error = raydium_i2c_send(client, (be_addr >> 24) & 0xff,
+				data, len);
+
+	return error;
+}
+
+static int raydium_i2c_sw_reset(struct i2c_client *client)
+{
+	const u8 soft_rst_cmd = 0x01;
+	int error;
+
+	error = raydium_i2c_send_message(client,
+		RM_RESET_MSG_ADDR, &soft_rst_cmd, sizeof(soft_rst_cmd));
+	if (error) {
+		dev_err(&client->dev, "software reset failed: %d\n", error);
+		return error;
+	}
+
+	msleep(RM_RESET_DELAY_MSEC);
+
+	return 0;
+}
+
+static int raydium_i2c_query_ts_info(struct raydium_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	struct raydium_data_info data_info;
+	__le32 query_bank_addr;
+
+	int error, retry_cnt;
+
+	for (retry_cnt = 0; retry_cnt < RM_MAX_RETRIES; retry_cnt++) {
+		error = raydium_i2c_read(client, RM_CMD_DATA_BANK,
+					 &data_info, sizeof(data_info));
+		if (error)
+			continue;
+
+		ts->data_bank_addr = le32_to_cpu(data_info.data_bank_addr);
+		ts->report_size = data_info.pkg_size;
+		ts->contact_size = data_info.tp_info_size;
+
+		dev_dbg(&client->dev,
+			"data_bank_addr: %#08x, report_size: %d, contact_size: %d\n",
+			ts->data_bank_addr, ts->report_size, ts->contact_size);
+
+		error = raydium_i2c_read(client, RM_CMD_QUERY_BANK,
+					 &query_bank_addr,
+					 sizeof(query_bank_addr));
+		if (error)
+			continue;
+
+		error = raydium_i2c_read_message(client,
+						 le32_to_cpu(query_bank_addr),
+						 &ts->info, sizeof(ts->info));
+		if (error)
+			continue;
+
+		return 0;
+	}
+	dev_err(&client->dev, "failed to query device parameters: %d\n", error);
+	return error;
+}
+
+static int raydium_i2c_check_fw_status(struct raydium_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	static const u8 bl_ack = 0x62;
+	static const u8 main_ack = 0x66;
+	u8 buf[4];
+	int error;
+
+	error = raydium_i2c_read(client, RM_CMD_BOOT_READ, buf, sizeof(buf));
+	if (!error) {
+		/*FIXME, changed as one byte comparison*/
+		if (buf[0] == bl_ack)
+			ts->boot_mode = RAYDIUM_TS_BLDR;
+		else if (buf[0] == main_ack)
+			ts->boot_mode = RAYDIUM_TS_MAIN;
+		return 0;
+	}
+
+	return error;
+}
+
+static int raydium_i2c_initialize(struct raydium_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	int error, retry_cnt;
+
+	for (retry_cnt = 0; retry_cnt < RM_MAX_RETRIES; retry_cnt++) {
+		/* Wait for Hello packet */
+		msleep(RM_BOOT_DELAY_MS);
+
+		error = raydium_i2c_check_fw_status(ts);
+		if (ts->boot_mode == RAYDIUM_TS_BLDR ||
+			ts->boot_mode == RAYDIUM_TS_MAIN)
+			break;
+		else if (error) {
+			dev_err(&client->dev,
+				"failed to read 'hello' packet: %d\n", error);
+		}
+	}
+
+	if (error)
+		ts->boot_mode = RAYDIUM_TS_BLDR;
+
+	if (ts->boot_mode == RAYDIUM_TS_BLDR) {
+		ts->info.hw_ver = cpu_to_le32(0xffffffffUL);
+		ts->info.main_ver = 0xff;
+		ts->info.sub_ver = 0xff;
+	} else {
+		raydium_i2c_query_ts_info(ts);
+	}
+
+	return error;
+}
+
+
+static int raydium_i2c_bl_chk_state(struct i2c_client *client,
+				    enum raydium_bl_ack state)
+{
+	static const u8 ack_ok[] = { 0xFF, 0x39, 0x30, 0x30, 0x54 };
+	u8 rbuf[sizeof(ack_ok)];
+	u8 retry;
+	int error;
+
+	for (retry = 0; retry < RM_MAX_FW_RETRIES; retry++) {
+		switch (state) {
+		case RAYDIUM_ACK_NULL:
+			return 0;
+
+		case RAYDIUM_WAIT_READY:
+			error = raydium_i2c_read(client, RM_CMD_BOOT_CHK,
+						 &rbuf[0], 1);
+			if (!error && rbuf[0] == RM_BOOT_RDY)
+				return 0;
+
+			break;
+
+		case RAYDIUM_PATH_READY:
+			error = raydium_i2c_read(client, RM_CMD_BOOT_CHK,
+						 rbuf, sizeof(rbuf));
+			if (!error && !memcmp(rbuf, ack_ok, sizeof(ack_ok)))
+				return 0;
+
+			break;
+
+		default:
+			dev_err(&client->dev, "%s: invalid target state %d\n",
+				__func__, state);
+			return -EINVAL;
+		}
+
+		msleep(20);
+	}
+
+	return -ETIMEDOUT;
+}
+
+static int raydium_i2c_write_object(struct i2c_client *client,
+				    const void *data, size_t len,
+				    enum raydium_bl_ack state)
+{
+	int error;
+
+	error = raydium_i2c_send(client, RM_CMD_BOOT_WRT, data, len);
+	if (error) {
+		dev_err(&client->dev, "WRT obj command failed: %d\n",
+			error);
+		return error;
+	}
+
+	error = raydium_i2c_send(client, RM_CMD_BOOT_ACK, NULL, 0);
+	if (error) {
+		dev_err(&client->dev, "Ack obj command failed: %d\n", error);
+		return error;
+	}
+
+	error = raydium_i2c_bl_chk_state(client, state);
+	if (error) {
+		dev_err(&client->dev, "BL check state failed: %d\n", error);
+		return error;
+	}
+	return 0;
+}
+
+static bool raydium_i2c_boot_trigger(struct i2c_client *client)
+{
+	static const u8 cmd[7][6] = {
+		{ 0x08, 0x0C, 0x09, 0x00, 0x50, 0xD7 },
+		{ 0x08, 0x04, 0x09, 0x00, 0x50, 0xA5 },
+		{ 0x08, 0x04, 0x09, 0x00, 0x50, 0x00 },
+		{ 0x08, 0x04, 0x09, 0x00, 0x50, 0xA5 },
+		{ 0x08, 0x0C, 0x09, 0x00, 0x50, 0x00 },
+		{ 0x06, 0x01, 0x00, 0x00, 0x00, 0x00 },
+		{ 0x02, 0xA2, 0x00, 0x00, 0x00, 0x00 },
+	};
+	int i;
+	int error;
+
+	for (i = 0; i < 7; i++) {
+		error = raydium_i2c_write_object(client, cmd[i], sizeof(cmd[i]),
+						 RAYDIUM_WAIT_READY);
+		if (error) {
+			dev_err(&client->dev,
+				"boot trigger failed at step %d: %d\n",
+				i, error);
+			return error;
+		}
+	}
+
+	return 0;
+}
+
+static bool raydium_i2c_fw_trigger(struct i2c_client *client)
+{
+	static const u8 cmd[5][11] = {
+		{ 0, 0x09, 0x71, 0x0C, 0x09, 0x00, 0x50, 0xD7, 0, 0, 0 },
+		{ 0, 0x09, 0x71, 0x04, 0x09, 0x00, 0x50, 0xA5, 0, 0, 0 },
+		{ 0, 0x09, 0x71, 0x04, 0x09, 0x00, 0x50, 0x00, 0, 0, 0 },
+		{ 0, 0x09, 0x71, 0x04, 0x09, 0x00, 0x50, 0xA5, 0, 0, 0 },
+		{ 0, 0x09, 0x71, 0x0C, 0x09, 0x00, 0x50, 0x00, 0, 0, 0 },
+	};
+	int i;
+	int error;
+
+	for (i = 0; i < 5; i++) {
+		error = raydium_i2c_write_object(client, cmd[i], sizeof(cmd[i]),
+						 RAYDIUM_ACK_NULL);
+		if (error) {
+			dev_err(&client->dev,
+				"fw trigger failed at step %d: %d\n",
+				i, error);
+			return error;
+		}
+	}
+
+	return 0;
+}
+
+static int raydium_i2c_check_path(struct i2c_client *client)
+{
+	static const u8 cmd[] = { 0x09, 0x00, 0x09, 0x00, 0x50, 0x10, 0x00 };
+	int error;
+
+	error = raydium_i2c_write_object(client, cmd, sizeof(cmd),
+					 RAYDIUM_PATH_READY);
+	if (error) {
+		dev_err(&client->dev, "check path command failed: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int raydium_i2c_enter_bl(struct i2c_client *client)
+{
+	static const u8 cal_cmd[] = { 0x00, 0x01, 0x52 };
+	int error;
+
+	error = raydium_i2c_write_object(client, cal_cmd, sizeof(cal_cmd),
+					 RAYDIUM_ACK_NULL);
+	if (error) {
+		dev_err(&client->dev, "enter bl command failed: %d\n", error);
+		return error;
+	}
+
+	msleep(RM_BOOT_DELAY_MS);
+	return 0;
+}
+
+static int raydium_i2c_leave_bl(struct i2c_client *client)
+{
+	static const u8 leave_cmd[] = { 0x05, 0x00 };
+	int error;
+
+	error = raydium_i2c_write_object(client, leave_cmd, sizeof(leave_cmd),
+					 RAYDIUM_ACK_NULL);
+	if (error) {
+		dev_err(&client->dev, "leave bl command failed: %d\n", error);
+		return error;
+	}
+
+	msleep(RM_BOOT_DELAY_MS);
+	return 0;
+}
+
+static int raydium_i2c_write_checksum(struct i2c_client *client,
+				      size_t length, u16 checksum)
+{
+	u8 checksum_cmd[] = { 0x00, 0x05, 0x6D, 0x00, 0x00, 0x00, 0x00 };
+	int error;
+
+	put_unaligned_le16(length, &checksum_cmd[3]);
+	put_unaligned_le16(checksum, &checksum_cmd[5]);
+
+	error = raydium_i2c_write_object(client,
+					 checksum_cmd, sizeof(checksum_cmd),
+					 RAYDIUM_ACK_NULL);
+	if (error) {
+		dev_err(&client->dev, "failed to write checksum: %d\n",
+			error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int raydium_i2c_disable_watch_dog(struct i2c_client *client)
+{
+	static const u8 cmd[] = { 0x0A, 0xAA };
+	int error;
+
+	error = raydium_i2c_write_object(client, cmd, sizeof(cmd),
+					 RAYDIUM_WAIT_READY);
+	if (error) {
+		dev_err(&client->dev, "disable watchdog command failed: %d\n",
+			error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int raydium_i2c_fw_write_page(struct i2c_client *client,
+				     u16 page_idx, const void *data, size_t len)
+{
+	u8 buf[RM_BL_WRT_LEN];
+	u8 pkg_idx = 1;
+	size_t xfer_len;
+	int error;
+
+	while (len) {
+		xfer_len = min_t(size_t, len, RM_BL_WRT_PKG_SIZE);
+		buf[BL_HEADER] = RM_CMD_BOOT_PAGE_WRT;
+		/*FIXME,Touch MCU need zero index as start page*/
+		buf[BL_PAGE_STR] = page_idx ? 0xff : 0;
+		buf[BL_PKG_IDX] = pkg_idx++;
+
+		memcpy(&buf[BL_DATA_STR], data, xfer_len);
+
+		if (len < RM_BL_WRT_PKG_SIZE) {
+			buf[BL_PKG_IDX] = 4;
+			memset(buf + BL_DATA_STR + xfer_len, 0xff,
+				RM_BL_WRT_PKG_SIZE - xfer_len);
+		}
+
+		error = raydium_i2c_write_object(client, buf, RM_BL_WRT_LEN,
+						 RAYDIUM_WAIT_READY);
+		if (error) {
+			dev_err(&client->dev,
+				"page write command failed for page %d, chunk %d: %d\n",
+				page_idx, pkg_idx, error);
+			return error;
+		}
+		data += xfer_len;
+		len -= xfer_len;
+	}
+
+	return error;
+}
+
+static int raydium_i2c_do_update_firmware(struct raydium_data *ts,
+					 const struct firmware *fw)
+{
+	struct i2c_client *client = ts->client;
+	const void *data;
+	size_t data_len;
+	size_t len;
+	int page_nr;
+	int i;
+	int error;
+	u16 fw_checksum;
+
+	if (fw->size == 0 || fw->size > RM_MAX_FW_SIZE) {
+		dev_err(&client->dev, "Invalid firmware length\n");
+		return -EINVAL;
+	}
+
+	error = raydium_i2c_check_fw_status(ts);
+	if (error) {
+		dev_err(&client->dev, "Unable to access IC %d\n", error);
+		return error;
+	}
+
+	if (ts->boot_mode == RAYDIUM_TS_MAIN) {
+		for (i = 0; i < RM_MAX_RETRIES; i++) {
+			error = raydium_i2c_enter_bl(client);
+			if (!error) {
+				error = raydium_i2c_check_fw_status(ts);
+				if (error) {
+					dev_err(&client->dev,
+						"unable to access IC: %d\n",
+						error);
+					return error;
+				}
+
+				if (ts->boot_mode == RAYDIUM_TS_BLDR)
+					break;
+			}
+		}
+
+		if (ts->boot_mode == RAYDIUM_TS_MAIN) {
+			dev_err(&client->dev,
+				"failied to jump to boot loader: %d\n",
+				error);
+			return -EIO;
+		}
+	}
+
+	error = raydium_i2c_disable_watch_dog(client);
+	if (error)
+		return error;
+
+	error = raydium_i2c_check_path(client);
+	if (error)
+		return error;
+
+	error = raydium_i2c_boot_trigger(client);
+	if (error) {
+		dev_err(&client->dev, "send boot trigger fail: %d\n", error);
+		return error;
+	}
+
+	msleep(RM_BOOT_DELAY_MS);
+
+	data = fw->data;
+	data_len = fw->size;
+	page_nr = 0;
+
+	while (data_len) {
+		len = min_t(size_t, data_len, RM_FW_PAGE_SIZE);
+
+		error = raydium_i2c_fw_write_page(client, page_nr++,
+				data, len);
+		if (error)
+			return error;
+
+		/*FIXME, remove delay in raydium_i2c_fw_write_page()*/
+		msleep(20);
+
+		data += len;
+		data_len -= len;
+	}
+
+	error = raydium_i2c_leave_bl(client);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to leave boot loader: %d\n", error);
+		return error;
+	}
+
+	dev_dbg(&client->dev, "left boot loader mode\n");
+
+	error = raydium_i2c_check_fw_status(ts);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to check fw status after write: %d\n",
+			error);
+		return error;
+	}
+
+	if (ts->boot_mode != RAYDIUM_TS_MAIN) {
+		dev_err(&client->dev,
+			"failed to switch to main fw after writing firmware: %d\n",
+			error);
+		return -EINVAL;
+	}
+
+	error = raydium_i2c_fw_trigger(client);
+	if (error) {
+		dev_err(&client->dev, "failed to trigger fw: %d\n", error);
+		return error;
+	}
+
+	fw_checksum = 0;
+	for (i = 0; i < fw->size; i++)
+		fw_checksum += fw->data[i];
+
+	error = raydium_i2c_write_checksum(client, fw->size, fw_checksum);
+	if (error)
+		return error;
+
+	return 0;
+}
+
+static int raydium_i2c_fw_update(struct raydium_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	const struct firmware *fw = NULL;
+	const char *fw_file = "raydium.fw";
+	int error;
+
+	error = request_firmware(&fw, fw_file, &client->dev);
+	if (error) {
+		dev_err(&client->dev, "Unable to open firmware %s\n", fw_file);
+		return error;
+	}
+
+	disable_irq(client->irq);
+
+	error = raydium_i2c_do_update_firmware(ts, fw);
+	if (error) {
+		dev_err(&client->dev, "firmware update failed: %d\n", error);
+		ts->boot_mode = RAYDIUM_TS_BLDR;
+		goto out_enable_irq;
+	}
+
+	error = raydium_i2c_initialize(ts);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to initialize device after firmware update: %d\n",
+			error);
+		ts->boot_mode = RAYDIUM_TS_BLDR;
+		goto out_enable_irq;
+	}
+
+	ts->boot_mode = RAYDIUM_TS_MAIN;
+
+out_enable_irq:
+
+	enable_irq(client->irq);
+	msleep(100);
+
+	release_firmware(fw);
+
+	return error;
+}
+
+static void raydium_mt_event(struct raydium_data *ts)
+{
+	int i;
+	int error;
+
+	error = raydium_i2c_read_message(ts->client, ts->data_bank_addr,
+					 ts->report_data, ts->report_size);
+	if (error) {
+		dev_err(&ts->client->dev, "%s: failed to read data: %d\n",
+			__func__, error);
+		return;
+	}
+
+	for (i = 0; i < ts->report_size / ts->contact_size; i++) {
+		u8 *contact = &ts->report_data[ts->contact_size * i];
+		bool state = contact[RM_CONTACT_STATE_POS];
+		u8 wx, wy;
+
+		input_mt_slot(ts->input, i);
+		input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, state);
+
+		if (!state)
+			continue;
+		input_report_abs(ts->input, ABS_MT_POSITION_X,
+				get_unaligned_le16(&contact[RM_CONTACT_X_POS]));
+		input_report_abs(ts->input, ABS_MT_POSITION_Y,
+				get_unaligned_le16(&contact[RM_CONTACT_Y_POS]));
+		input_report_abs(ts->input, ABS_MT_PRESSURE,
+				contact[RM_CONTACT_PRESSURE_POS]);
+
+		wx = contact[RM_CONTACT_WIDTH_X_POS];
+		wy = contact[RM_CONTACT_WIDTH_Y_POS];
+
+		input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, max(wx, wy));
+		input_report_abs(ts->input, ABS_MT_TOUCH_MINOR, min(wx, wy));
+	}
+
+	input_mt_sync_frame(ts->input);
+	input_sync(ts->input);
+}
+
+static irqreturn_t raydium_i2c_irq(int irq, void *_dev)
+{
+	struct raydium_data *ts = _dev;
+
+	if (ts->boot_mode != RAYDIUM_TS_BLDR)
+		raydium_mt_event(ts);
+
+	return IRQ_HANDLED;
+}
+
+static ssize_t raydium_i2c_fw_ver_show(struct device *dev,
+				       struct device_attribute *attr, char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	return sprintf(buf, "%d.%d\n", ts->info.main_ver, ts->info.sub_ver);
+}
+
+static ssize_t raydium_i2c_hw_ver_show(struct device *dev,
+				       struct device_attribute *attr, char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	return sprintf(buf, "%#04x\n", le32_to_cpu(ts->info.hw_ver));
+}
+
+static ssize_t raydium_i2c_boot_mode_show(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	return sprintf(buf, "%s\n",
+		       ts->boot_mode == RAYDIUM_TS_MAIN ?
+				"Normal" : "Recovery");
+}
+
+static ssize_t raydium_i2c_update_fw_store(struct device *dev,
+					   struct device_attribute *attr,
+					   const char *buf, size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
+	int error;
+
+	error = mutex_lock_interruptible(&ts->sysfs_mutex);
+	if (error)
+		return error;
+
+	error = raydium_i2c_fw_update(ts);
+
+	mutex_unlock(&ts->sysfs_mutex);
+
+	return error ?: count;
+}
+
+static ssize_t raydium_i2c_calibrate_store(struct device *dev,
+					   struct device_attribute *attr,
+					   const char *buf, size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
+	static const u8 cal_cmd[] = { 0x00, 0x01, 0x9E };
+	int error;
+
+	error = mutex_lock_interruptible(&ts->sysfs_mutex);
+	if (error)
+		return error;
+
+	error = raydium_i2c_write_object(client, cal_cmd, sizeof(cal_cmd),
+					 RAYDIUM_WAIT_READY);
+	if (error)
+		dev_err(&client->dev, "calibrate command failed: %d\n", error);
+
+	mutex_unlock(&ts->sysfs_mutex);
+	return error ?: count;
+}
+
+static DEVICE_ATTR(fw_version, S_IRUGO, raydium_i2c_fw_ver_show, NULL);
+static DEVICE_ATTR(hw_version, S_IRUGO, raydium_i2c_hw_ver_show, NULL);
+static DEVICE_ATTR(boot_mode, S_IRUGO, raydium_i2c_boot_mode_show, NULL);
+static DEVICE_ATTR(update_fw, S_IWUSR, NULL, raydium_i2c_update_fw_store);
+static DEVICE_ATTR(calibrate, S_IWUSR, NULL, raydium_i2c_calibrate_store);
+
+static struct attribute *raydium_i2c_attributes[] = {
+	&dev_attr_update_fw.attr,
+	&dev_attr_boot_mode.attr,
+	&dev_attr_fw_version.attr,
+	&dev_attr_hw_version.attr,
+	&dev_attr_calibrate.attr,
+	NULL
+};
+
+static struct attribute_group raydium_i2c_attribute_group = {
+	.attrs = raydium_i2c_attributes,
+};
+
+static void raydium_i2c_remove_sysfs_group(void *_data)
+{
+	struct raydium_data *ts = _data;
+
+	sysfs_remove_group(&ts->client->dev.kobj, &raydium_i2c_attribute_group);
+}
+
+static int raydium_i2c_power_on(struct raydium_data *ts)
+{
+	int error;
+
+	if (IS_ERR_OR_NULL(ts->reset_gpio))
+		return 0;
+
+	gpiod_set_value_cansleep(ts->reset_gpio, 1);
+
+	error = regulator_enable(ts->avdd);
+	if (error) {
+		dev_err(&ts->client->dev,
+			"failed to enable avdd regulator: %d\n", error);
+		goto release_reset_gpio;
+	}
+
+	error = regulator_enable(ts->vccio);
+	if (error) {
+		regulator_disable(ts->avdd);
+		dev_err(&ts->client->dev,
+			"failed to enable vccio regulator: %d\n", error);
+		goto release_reset_gpio;
+	}
+
+	udelay(RM_POWERON_DELAY_USEC);
+
+release_reset_gpio:
+	gpiod_set_value_cansleep(ts->reset_gpio, 0);
+
+	if (error)
+		return error;
+
+	msleep(RM_RESET_DELAY_MSEC);
+
+	return 0;
+}
+
+static void raydium_i2c_power_off(void *_data)
+{
+	struct raydium_data *ts = _data;
+
+	if (!IS_ERR_OR_NULL(ts->reset_gpio)) {
+		gpiod_set_value_cansleep(ts->reset_gpio, 1);
+		regulator_disable(ts->vccio);
+		regulator_disable(ts->avdd);
+	}
+}
+
+static int raydium_i2c_probe(struct i2c_client *client,
+			     const struct i2c_device_id *id)
+{
+	union i2c_smbus_data dummy;
+	struct raydium_data *ts;
+	int error;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		dev_err(&client->dev,
+			"i2c check functionality error (need I2C_FUNC_I2C)\n");
+		return -ENXIO;
+	}
+
+	ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+
+	mutex_init(&ts->sysfs_mutex);
+
+	ts->client = client;
+	i2c_set_clientdata(client, ts);
+
+	ts->avdd = devm_regulator_get(&client->dev, "avdd");
+	if (IS_ERR(ts->avdd)) {
+		error = PTR_ERR(ts->avdd);
+		if (error != -EPROBE_DEFER)
+			dev_err(&client->dev,
+				"Failed to get 'avdd' regulator: %d\n", error);
+		return error;
+	}
+
+	ts->vccio = devm_regulator_get(&client->dev, "vccio");
+	if (IS_ERR(ts->vccio)) {
+		error = PTR_ERR(ts->vccio);
+		if (error != -EPROBE_DEFER)
+			dev_err(&client->dev,
+				"Failed to get 'vccio' regulator: %d\n", error);
+		return error;
+	}
+
+	ts->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
+						 GPIOD_OUT_LOW);
+	if (IS_ERR(ts->reset_gpio)) {
+		error = PTR_ERR(ts->reset_gpio);
+		if (error != -EPROBE_DEFER) {
+			dev_err(&client->dev,
+				"failed to get reset gpio: %d\n", error);
+			return error;
+		}
+	}
+
+	error = raydium_i2c_power_on(ts);
+	if (error)
+		return error;
+
+	error = devm_add_action(&client->dev, raydium_i2c_power_off, ts);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to install power off action: %d\n", error);
+		raydium_i2c_power_off(ts);
+		return error;
+	}
+
+	/* Make sure there is something at this address */
+	if (i2c_smbus_xfer(client->adapter, client->addr, 0,
+			   I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0) {
+		dev_err(&client->dev, "nothing at this address\n");
+		return -ENXIO;
+	}
+
+	error = raydium_i2c_initialize(ts);
+	if (error) {
+		dev_err(&client->dev, "failed to initialize: %d\n", error);
+		return error;
+	}
+
+	ts->input = devm_input_allocate_device(&client->dev);
+	if (!ts->input) {
+		dev_err(&client->dev, "Failed to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	ts->report_data = kmalloc(ts->report_size, GFP_KERNEL);
+	if (!ts->report_data)
+		return -ENOMEM;
+
+	ts->input->name = "Raydium Touchscreen";
+	ts->input->id.bustype = BUS_I2C;
+
+	input_set_drvdata(ts->input, ts);
+
+	input_set_abs_params(ts->input, ABS_MT_POSITION_X,
+			     0, le32_to_cpu(ts->info.x_max), 0, 0);
+	input_set_abs_params(ts->input, ABS_MT_POSITION_Y,
+			     0, le32_to_cpu(ts->info.y_max), 0, 0);
+	input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->info.x_res);
+	input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->info.y_res);
+
+	input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+	input_set_abs_params(ts->input, ABS_MT_PRESSURE, 0, 255, 0, 0);
+
+	error = input_mt_init_slots(ts->input, RM_MAX_TOUCH_NUM,
+				    INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to initialize MT slots: %d\n", error);
+		goto out_of_probe;
+	}
+
+	error = input_register_device(ts->input);
+	if (error) {
+		dev_err(&client->dev,
+			"unable to register input device: %d\n", error);
+		goto out_of_probe;
+	}
+
+	error = devm_request_threaded_irq(&client->dev, client->irq,
+					  NULL, raydium_i2c_irq,
+					  IRQF_ONESHOT, client->name, ts);
+	if (error) {
+		dev_err(&client->dev, "Failed to register interrupt\n");
+		goto out_of_probe;
+	}
+
+	error = sysfs_create_group(&client->dev.kobj,
+				   &raydium_i2c_attribute_group);
+	if (error) {
+		dev_err(&client->dev, "failed to create sysfs attributes: %d\n",
+			error);
+		goto out_of_probe;
+	}
+
+	error = devm_add_action(&client->dev,
+				raydium_i2c_remove_sysfs_group, ts);
+	if (error) {
+		raydium_i2c_remove_sysfs_group(ts);
+		dev_err(&client->dev,
+			"Failed to add sysfs cleanup action: %d\n", error);
+		goto out_of_probe;
+	}
+
+	return 0;
+
+out_of_probe:
+	kfree(ts->report_data);
+	return error;
+}
+
+static void __maybe_unused raydium_enter_sleep(struct i2c_client *client)
+{
+	static const u8 sleep_cmd[] = { 0x5A, 0xff, 0x00, 0x0f };
+	int error;
+
+	error = raydium_i2c_send(client, RM_CMD_ENTER_SLEEP,
+				 sleep_cmd, sizeof(sleep_cmd));
+	if (error)
+		dev_err(&client->dev,
+			"sleep command failed: %d\n", error);
+}
+
+static int __maybe_unused raydium_i2c_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	/* Sleep is not available in BLDR recovery mode */
+	if (ts->boot_mode != RAYDIUM_TS_MAIN)
+		return -ENOMEM;
+
+	disable_irq(client->irq);
+
+	if (device_may_wakeup(dev)) {
+		raydium_enter_sleep(client);
+
+		ts->wake_irq_enabled = (enable_irq_wake(client->irq) == 0);
+	} else {
+		raydium_i2c_power_off(ts);
+	}
+
+	return 0;
+}
+
+static int __maybe_unused raydium_i2c_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	if (device_may_wakeup(dev)) {
+		if (ts->wake_irq_enabled)
+			disable_irq_wake(client->irq);
+		raydium_i2c_sw_reset(client);
+	} else {
+		raydium_i2c_power_on(ts);
+		raydium_i2c_initialize(ts);
+	}
+
+	enable_irq(client->irq);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(raydium_i2c_pm_ops,
+			 raydium_i2c_suspend, raydium_i2c_resume);
+
+static const struct i2c_device_id raydium_i2c_id[] = {
+	{ "raydium_i2c" , 0 },
+	{ "rm32380", 0 },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(i2c, raydium_i2c_id);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id raydium_acpi_id[] = {
+	{ "RAYD0001", 0 },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(acpi, raydium_acpi_id);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id raydium_of_match[] = {
+	{ .compatible = "raydium,rm32380", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, raydium_of_match);
+#endif
+
+static struct i2c_driver raydium_i2c_driver = {
+	.probe = raydium_i2c_probe,
+	.id_table = raydium_i2c_id,
+	.driver = {
+		.name = "raydium_ts",
+		.pm = &raydium_i2c_pm_ops,
+		.acpi_match_table = ACPI_PTR(raydium_acpi_id),
+		.of_match_table = of_match_ptr(raydium_of_match),
+	},
+};
+module_i2c_driver(raydium_i2c_driver);
+
+MODULE_AUTHOR("Raydium");
+MODULE_DESCRIPTION("Raydium I2c Touchscreen driver");
+MODULE_LICENSE("GPL v2");
+
-- 
2.1.2


^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
@ 2016-05-11 13:51 jeffrey.lin
  0 siblings, 0 replies; 42+ messages in thread
From: jeffrey.lin @ 2016-05-11 13:51 UTC (permalink / raw)
  To: dmitry.torokhov, rydberg, grant.likely, robh+dt, jeesw, bleung
  Cc: jeffrey.lin, roger.yang, KP.li, albert.shieh, linux-kernel,
	linux-input, devicetree

Raydium I2C touch driver.

Signed-off-by: jeffrey.lin <jeffrey.lin@rad-ic.com>
---
 drivers/input/touchscreen/Kconfig          |   12 +
 drivers/input/touchscreen/Makefile         |    1 +
 drivers/input/touchscreen/raydium_i2c_ts.c | 1208 ++++++++++++++++++++++++++++
 3 files changed, 1221 insertions(+)
 create mode 100644 drivers/input/touchscreen/raydium_i2c_ts.c

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 3f3f6ee..df0e2ed 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -915,6 +915,18 @@ config TOUCHSCREEN_PCAP
 	  To compile this driver as a module, choose M here: the
 	  module will be called pcap_ts.
 
+config TOUCHSCREEN_RM_TS
+	tristate "Raydium I2C Touchscreen"
+	depends on I2C
+	help
+	  Say Y here if you have Raydium series I2C touchscreen,
+	  such as RM32380,connected to your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called raydium_i2c_ts.
+
 config TOUCHSCREEN_ST1232
 	tristate "Sitronix ST1232 touchscreen controllers"
 	depends on I2C
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 4941f2d..99e08cf 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE)	+= usbtouchscreen.o
 obj-$(CONFIG_TOUCHSCREEN_PCAP)		+= pcap_ts.o
 obj-$(CONFIG_TOUCHSCREEN_PENMOUNT)	+= penmount.o
 obj-$(CONFIG_TOUCHSCREEN_PIXCIR)	+= pixcir_i2c_ts.o
+obj-$(CONFIG_TOUCHSCREEN_RM_TS)		+= raydium_i2c_ts.o
 obj-$(CONFIG_TOUCHSCREEN_S3C2410)	+= s3c2410_ts.o
 obj-$(CONFIG_TOUCHSCREEN_ST1232)	+= st1232.o
 obj-$(CONFIG_TOUCHSCREEN_STMPE)		+= stmpe-ts.o
diff --git a/drivers/input/touchscreen/raydium_i2c_ts.c b/drivers/input/touchscreen/raydium_i2c_ts.c
new file mode 100644
index 0000000..6491920
--- /dev/null
+++ b/drivers/input/touchscreen/raydium_i2c_ts.c
@@ -0,0 +1,1208 @@
+/*
+ * Raydium touchscreen I2C driver.
+ *
+ * Copyright (C) 2012-2014, Raydium Semiconductor Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Raydium reserves the right to make changes without further notice
+ * to the materials described herein. Raydium does not assume any
+ * liability arising out of the application described herein.
+ *
+ * Contact Raydium Semiconductor Corporation at www.rad-ic.com
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/firmware.h>
+#include <linux/input/mt.h>
+#include <linux/acpi.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+#include <asm/unaligned.h>
+#include <linux/gpio/consumer.h>
+
+/* Device, Driver information */
+#define DEVICE_NAME	"raydium_i2c"
+
+/* Slave I2C mode*/
+#define RM_BOOT_BLDR	0x02
+#define RM_BOOT_MAIN	0x03
+
+/*I2C bl command */
+#define CMD_BOOT_PAGE_WRT	0x0B		/*send bl page write*/
+#define CMD_BOOT_WRT		0x11		/*send bl write*/
+#define CMD_BOOT_ACK		0x22		/*send ack*/
+#define CMD_BOOT_CHK		0x33		/*send data check*/
+#define CMD_BOOT_READ		0x44		/*send wait bl data ready*/
+#define BOOT_RDY		0xFF		/*bl data ready*/
+/*I2C main command*/
+#define CMD_QUERY_BANK		0x2B
+#define CMD_DATA_BANK		0x4D
+#define CMD_ENTER_SLEEP		0x4E
+#define CMD_BANK_SWITCH		0xAA
+
+/* Touch relative info */
+#define MAX_RETRIES		3
+#define MAX_TOUCH_NUM		10
+#define MAX_PKG_SIZE		128
+#define MAX_RD_PKG_LEN	48
+#define BOOT_DELAY_MS	100
+
+/*Bootloader relative info */
+#define CMD_BOOT_HEADER_LEN		3	/*bl flash wrt cmd size*/
+#define RAYDIUM_TRANS_BUFSIZE	32	/*bl wrt pkg size*/
+#define MAX_BOOT_WRT_LEN	(RAYDIUM_TRANS_BUFSIZE + CMD_BOOT_HEADER_LEN)
+#define MAX_FW_UPDATE_RETRIES	30
+
+enum raydium_bl_cmd {
+	HEADER = 0,
+	PAGE_STR,
+	PKG_IDX,
+	DATA_STR,
+};
+
+enum raydium_bl_ack {
+	ACK_NULL = 0,
+	WAIT_READY,
+	PATH_READY,
+};
+
+#define RAYDIUM_PAGE_SIZE		128
+#define RAYDIUM_POWERON_DELAY_USEC	500
+#define RAYDIUM_RESET_DELAY_MSEC	50
+
+#define ADDR_INDEX		0x03
+#define DATA_INDEX		0x04
+
+#define HEADER_SIZE		4
+
+enum raydium_boot_mode {
+	RAYDIUM_TS_MAIN = 0,
+	RAYDIUM_TS_BLDR,
+};
+
+enum raydium_abs_idx {
+	POS_STATE = 0,/*1:touch, 0:no touch*/
+	POS_X,
+	POS_Y = 3,
+	POS_PRESSURE,
+	WIDTH_X,
+	WIDTH_Y,
+};
+
+struct raydium_info {
+	u32 hw_ver;	/*device ver, __le32*/
+	u8 main_ver;
+	u8 sub_ver;
+	u16 ft_ver;	/*test ver, __le16*/
+	u8 x_num;
+	u8 y_num;
+	u16 x_max;	/*disp reso, __le16*/
+	u16 y_max;	/*disp reso, __le16*/
+	u8 x_res;		/* units/mm */
+	u8 y_res;		/* units/mm */
+};
+
+struct raydium_object {
+	u32 data_bank_addr;
+	u8 pkg_size;
+	u8 tp_info_size;
+};
+
+/* struct raydium_data - represents state of Raydium touchscreen device */
+struct raydium_data {
+	struct i2c_client *client;
+	struct input_dev *input;
+
+	struct regulator *avdd;
+	struct regulator *vccio;
+	struct gpio_desc *reset_gpio;
+
+	u32 query_bank_info;
+
+	struct raydium_info info;
+	struct raydium_object obj;
+	enum raydium_boot_mode boot_mode;
+
+	struct mutex sysfs_mutex;
+	struct completion cmd_done;
+
+	bool wake_irq_enabled;
+};
+
+static int raydium_i2c_send(struct i2c_client *client,
+	u8 addr, u8 *data, size_t len)
+{
+	u8 buf[MAX_PKG_SIZE];
+	int tries = 0;
+
+	buf[0] = addr;
+	memcpy(&buf[1], data, len);
+
+	do {
+		if (i2c_master_send(client, buf, len + 1) == (len + 1))
+			return 0;
+		msleep(20);
+	} while (++tries < MAX_RETRIES);
+
+	dev_err(&client->dev, "%s: i2c send failed\n", __func__);
+
+	return -EIO;
+}
+
+static int raydium_i2c_read(struct i2c_client *client,
+	u8 addr, size_t len, void *data)
+{
+	struct i2c_msg xfer[2];
+	int ret;
+
+	/* Write register */
+	xfer[0].addr = client->addr;
+	xfer[0].flags = 0;
+	xfer[0].len = 1;
+	xfer[0].buf = &addr;
+
+	/* Read data */
+	xfer[1].addr = client->addr;
+	xfer[1].flags = I2C_M_RD;
+	xfer[1].len = len;
+	xfer[1].buf = data;
+
+	ret = i2c_transfer(client->adapter, xfer, ARRAY_SIZE(xfer));
+	if (ret < 0)
+		return ret;
+
+	if (ret != ARRAY_SIZE(xfer))
+		return -EIO;
+
+	return 0;
+}
+
+static int raydium_i2c_read_message(struct i2c_client *client,
+	u32 addr, size_t len, void *data)
+{
+	u8 buf[HEADER_SIZE];
+	u8 read_cnt, idx_i;
+	int error;
+	size_t pkg_size;
+
+	if (len % MAX_RD_PKG_LEN)
+		read_cnt = len / MAX_RD_PKG_LEN + 1;
+	else
+		read_cnt = len / MAX_RD_PKG_LEN;
+
+	for (idx_i = 0; idx_i < read_cnt; idx_i++) {
+		pkg_size = (len > MAX_RD_PKG_LEN) ? MAX_RD_PKG_LEN : len;
+
+		len -= MAX_RD_PKG_LEN;
+
+		put_unaligned_be32(addr, buf);
+
+		/*set data bank*/
+		error = raydium_i2c_send(client, CMD_BANK_SWITCH,
+			(u8 *)buf, HEADER_SIZE);
+		/*read potints data*/
+		if (!error)
+			error = raydium_i2c_read(client, buf[ADDR_INDEX],
+				pkg_size,
+				data + idx_i*MAX_RD_PKG_LEN);
+
+		addr += MAX_RD_PKG_LEN;
+	}
+
+	return error;
+}
+
+static int raydium_i2c_send_message(struct i2c_client *client,
+	size_t len, void *data)
+{
+	int error;
+	__le32 cmd;
+
+	cmd = get_unaligned_le32(data);
+
+	/*set data bank*/
+	error = raydium_i2c_send(client, CMD_BANK_SWITCH, (u8 *)&cmd,
+		HEADER_SIZE);
+
+	/*send message*/
+	if (!error)
+		error = raydium_i2c_send(client, ((u8 *)data)[ADDR_INDEX],
+			data + DATA_INDEX, len);
+
+	return error;
+}
+
+static int raydium_i2c_sw_reset(struct i2c_client *client)
+{
+	static const u8 soft_rst_cmd[] = {0x40, 0x00, 0x00, 0x04, 0x01};
+	int error;
+
+	error = raydium_i2c_send_message(client, ARRAY_SIZE(soft_rst_cmd),
+		(void *)soft_rst_cmd);
+	if (error) {
+		dev_err(&client->dev, "software reset failed: %d\n", error);
+		return error;
+	}
+
+	msleep(RAYDIUM_RESET_DELAY_MSEC);
+
+	return 0;
+}
+
+static int raydium_i2c_query_ts_info(struct raydium_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	int error, retry_cnt;
+
+	for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
+		error = raydium_i2c_read(client, CMD_DATA_BANK,
+			sizeof(ts->obj), (void *)&ts->obj);
+			ts->obj.data_bank_addr =
+				get_unaligned_le32(&ts->obj.data_bank_addr);
+
+		if (!error) {
+			error = raydium_i2c_read(client, CMD_QUERY_BANK,
+				sizeof(ts->query_bank_info),
+				(void *)&ts->query_bank_info);
+			if (!error) {
+				error = raydium_i2c_read_message(client,
+					ts->query_bank_info, sizeof(ts->info),
+					(void *)&ts->info);
+
+				ts->info.hw_ver =
+					get_unaligned_le32(&ts->info.hw_ver);
+				ts->info.ft_ver =
+					get_unaligned_le16(&ts->info.ft_ver);
+				ts->info.x_max =
+					get_unaligned_le16(&ts->info.x_max);
+				ts->info.y_max =
+					get_unaligned_le16(&ts->info.y_max);
+				return 0;
+			}
+		}
+	}
+	dev_err(&client->dev, "Get touch data failed: %d\n", error);
+
+	return -EINVAL;
+}
+
+static int raydium_i2c_fastboot(struct i2c_client *client)
+{
+	static const u8 boot_cmd[] = { 0x50, 0x00, 0x06, 0x20 };
+	u8 buf[HEADER_SIZE];
+	int error;
+
+	error = raydium_i2c_read_message(client,
+		get_unaligned_be32(boot_cmd),
+		sizeof(boot_cmd), buf);
+
+	if (!error) {
+		if (buf[0] == RM_BOOT_BLDR) {
+			dev_dbg(&client->dev, "boot in fastboot mode\n");
+			return -EINVAL;
+		}
+		dev_dbg(&client->dev, "boot success -- 0x%x\n", client->addr);
+		return 0;
+	}
+
+	dev_err(&client->dev, "boot failed: %d\n", error);
+
+	return error;
+}
+
+static int raydium_i2c_check_fw_status(struct raydium_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	static const u8 bl_area[] = {0x62, 0x6f, 0x6f, 0x74};
+	static const u8 main_area[] = {0x66, 0x69, 0x72, 0x6d};
+	u8 buf[HEADER_SIZE];
+	int error;
+
+	error = raydium_i2c_read(client, CMD_BOOT_READ, HEADER_SIZE,
+		(void *)buf);
+	if (!error) {
+		if (buf[0] == bl_area[0])
+			ts->boot_mode = RAYDIUM_TS_BLDR;
+		else if (buf[0] == main_area[0])
+			ts->boot_mode = RAYDIUM_TS_MAIN;
+		return 0;
+	}
+
+	return error;
+}
+
+static int raydium_i2c_initialize(struct raydium_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	int error, retry_cnt;
+
+	for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
+		error = raydium_i2c_fastboot(client);
+		if (error) {
+			/* Continue initializing if it's the last try */
+			if (retry_cnt < MAX_RETRIES - 1)
+				continue;
+		}
+		/* Wait for Hello packet */
+		msleep(BOOT_DELAY_MS);
+
+		error = raydium_i2c_check_fw_status(ts);
+		if (ts->boot_mode == RAYDIUM_TS_BLDR ||
+			ts->boot_mode == RAYDIUM_TS_MAIN)
+			break;
+		else if (error) {
+			dev_err(&client->dev,
+				"failed to read 'hello' packet: %d\n", error);
+		}
+	}
+
+	if (error)
+		ts->boot_mode = RAYDIUM_TS_BLDR;
+
+	if (ts->boot_mode == RAYDIUM_TS_BLDR) {
+		ts->info.hw_ver = 0xffffffff;
+		ts->info.main_ver = 0xff;
+		ts->info.sub_ver = 0xff;
+	} else {
+		raydium_i2c_query_ts_info(ts);
+	}
+
+	return error;
+}
+
+static int raydium_i2c_bl_chk_state(struct i2c_client *client,
+		enum raydium_bl_ack state)
+{
+	static const u8 ack_ok[] = { 0xFF, 0x39, 0x30, 0x30, 0x54 };
+	u8 rbuf[5];
+	u8 retry;
+	int error;
+
+	if (state == ACK_NULL)
+		return 0;
+
+	for (retry = 0; retry < MAX_FW_UPDATE_RETRIES; retry++) {
+		if (state == WAIT_READY) {
+			error = raydium_i2c_read(client, CMD_BOOT_CHK,
+				1, &rbuf[0]);
+			if (!error) {
+				if (rbuf[0] == BOOT_RDY)
+					return 0;
+			}
+		} else if (state == PATH_READY) {
+			error = raydium_i2c_read(client, CMD_BOOT_CHK,
+				sizeof(ack_ok), &rbuf[0]);
+			if (!error) {
+				if (!memcmp(rbuf, ack_ok, sizeof(ack_ok)))
+					return 0;
+			}
+		} else
+			return -EINVAL;
+		msleep(20);
+	}
+
+	return -EINVAL;
+}
+
+static int raydium_i2c_wrt_object(struct i2c_client *client,
+	u8 *data, size_t len, enum raydium_bl_ack state)
+{
+	int error = 0;
+
+	error = raydium_i2c_send(client, CMD_BOOT_WRT, data, len);
+	if (error) {
+		dev_err(&client->dev, "WRT obj command failed: %d\n",
+			error);
+		return error;
+	}
+
+	error = raydium_i2c_send(client, CMD_BOOT_ACK, (u8 *)NULL, 0);
+	if (error) {
+		dev_err(&client->dev, "Ack obj command failed: %d\n", error);
+		return error;
+	}
+
+	error = raydium_i2c_bl_chk_state(client, state);
+	if (error) {
+		dev_err(&client->dev, "boot trigger state failed: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+static bool raydium_i2c_boot_trigger(struct i2c_client *client)
+{
+	int error;
+	u8 u8_idx;
+	static const u8 cmd[7][6] = {
+			{0x08, 0x0C, 0x09, 0x00, 0x50, 0xD7},
+			{0x08, 0x04, 0x09, 0x00, 0x50, 0xA5},
+			{0x08, 0x04, 0x09, 0x00, 0x50, 0x00},
+			{0x08, 0x04, 0x09, 0x00, 0x50, 0xA5},
+			{0x08, 0x0C, 0x09, 0x00, 0x50, 0x00},
+			{0x06, 0x01, 0x00, 0x00, 0x00, 0x00},
+			{0x02, 0xA2, 0x00, 0x00, 0x00, 0x00},
+		};
+
+	/*sequtial cmd*/
+	for (u8_idx = 0 ; u8_idx < 7 ; u8_idx++) {
+		error = raydium_i2c_wrt_object(client, (u8 *)cmd[u8_idx],
+				sizeof(cmd[u8_idx]), WAIT_READY);
+		if (error) {
+			dev_err(&client->dev, "send boot trigger 1st_cmd failed: %d\n",
+				error);
+			return error;
+		}
+	}
+	return 0;
+}
+
+static bool raydium_i2c_fw_trigger(struct i2c_client *client)
+{
+	int error;
+	u8 u8_idx;
+	static const u8 cmd[5][11] = {
+			{0, 0x09, 0x71, 0x0C, 0x09, 0x00, 0x50, 0xD7, 0, 0, 0},
+			{0, 0x09, 0x71, 0x04, 0x09, 0x00, 0x50, 0xA5, 0, 0, 0},
+			{0, 0x09, 0x71, 0x04, 0x09, 0x00, 0x50, 0x00, 0, 0, 0},
+			{0, 0x09, 0x71, 0x04, 0x09, 0x00, 0x50, 0xA5, 0, 0, 0},
+			{0, 0x09, 0x71, 0x0C, 0x09, 0x00, 0x50, 0x00, 0, 0, 0},
+		};
+
+	/*sequtial cmd*/
+	for (u8_idx = 0 ; u8_idx < 5 ; u8_idx++) {
+		error = raydium_i2c_wrt_object(client, (u8 *)cmd[u8_idx],
+				sizeof(cmd[u8_idx]), ACK_NULL);
+		if (error) {
+			dev_err(&client->dev, "send fw trigger 1st_cmd failed: %d\n",
+				error);
+			return error;
+		}
+	}
+	return 0;
+}
+
+static int raydium_i2c_check_path(struct i2c_client *client)
+{
+	static const u8 cmd[] = {0x09, 0x00, 0x09, 0x00, 0x50, 0x10, 0x00};
+	int error;
+
+	error = raydium_i2c_wrt_object(client, (u8 *)cmd, sizeof(cmd),
+			PATH_READY);
+	if (error) {
+		dev_err(&client->dev, "send chk path cmd fail: %d\n", error);
+		return error;
+	}
+
+	return error;
+}
+
+static int raydium_i2c_enter_bl(struct i2c_client *client)
+{
+	static const u8 cal_cmd[] = {0x00, 0x01, 0x52};
+	int error;
+
+	error = raydium_i2c_wrt_object(client, (u8 *)cal_cmd,
+			sizeof(cal_cmd), ACK_NULL);
+	if (error) {
+		dev_err(&client->dev, "send jump loader cmd fail: %d\n", error);
+		return error;
+	}
+	msleep(BOOT_DELAY_MS);
+	return 0;
+}
+
+static int raydium_i2c_leave_bl(struct i2c_client *client)
+{
+	static const u8 leave_cmd[] = {0x05, 0x00};
+	int error;
+
+	error = raydium_i2c_wrt_object(client, (u8 *)leave_cmd,
+			sizeof(leave_cmd), ACK_NULL);
+	if (error) {
+		dev_err(&client->dev, "send leave bl cmd fail: %d\n", error);
+		return error;
+	}
+	msleep(BOOT_DELAY_MS);
+	return 0;
+}
+
+static int raydium_i2c_write_checksum(struct i2c_client *client,
+	size_t length, u16 checksum)
+{
+	u8 checksum_cmd[] = {0x00, 0x05, 0x6D, 0x00, 0x00, 0x00, 0x00};
+	int error = 0;
+
+	checksum_cmd[3] = (u8)(length & 0xFF);
+	checksum_cmd[4] = (u8)((length & 0xFF00) >> 8);
+	checksum_cmd[5] = (u8)(checksum & 0xFF);
+	checksum_cmd[6] = (u8)((checksum & 0xFF00) >> 8);
+
+	error = raydium_i2c_wrt_object(client, (u8 *)checksum_cmd,
+			sizeof(checksum_cmd), ACK_NULL);
+	if (error) {
+		dev_err(&client->dev, "send wrt checksum cmd fail: %d\n",
+			error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int raydium_i2c_disable_watch_dog(struct i2c_client *client)
+{
+	static const u8 cmd[] = {0x0A, 0xAA};
+	int error = 0;
+
+	error = raydium_i2c_wrt_object(client, (u8 *)cmd, sizeof(cmd),
+			WAIT_READY);
+	if (error) {
+		dev_err(&client->dev, "send disable watchdog cmd fail: %d\n",
+			error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int raydium_i2c_fw_write_page(struct i2c_client *client,
+				u8 *page, size_t len)
+{
+	int error;
+	u8 buf[MAX_BOOT_WRT_LEN];
+	u8 u8_idx, div_cnt;
+
+	len -= CMD_BOOT_HEADER_LEN;
+
+	div_cnt = len % RAYDIUM_TRANS_BUFSIZE ?
+		len / RAYDIUM_TRANS_BUFSIZE + 1:len / RAYDIUM_TRANS_BUFSIZE;
+
+	for (u8_idx = 0 ; u8_idx < div_cnt ; u8_idx++) {
+		buf[HEADER] = page[0];
+		buf[PAGE_STR] = page[1];
+		buf[PKG_IDX] = u8_idx + 1;
+
+		memcpy(&buf[DATA_STR], page + DATA_STR +
+			u8_idx*RAYDIUM_TRANS_BUFSIZE,
+			RAYDIUM_TRANS_BUFSIZE);
+
+		error = raydium_i2c_wrt_object(client, (u8 *)buf, sizeof(buf),
+				WAIT_READY);
+		if (error) {
+			dev_err(&client->dev, "send page wrt cmd failed: %d\n",
+				error);
+			return error;
+		}
+		msleep(20);
+	}
+
+	return error;
+}
+
+static int raydium_i2c_do_update_firmware(struct raydium_data *ts,
+					 const struct firmware *fw)
+{
+	struct i2c_client *client = ts->client;
+	u8 u8_idx;
+	u16 fw_checksum;
+	u8 buf[RAYDIUM_PAGE_SIZE + CMD_BOOT_HEADER_LEN];
+	int page, n_fw_pages;
+	int error, fw_idx;
+
+	if (fw->size == 0) {
+		dev_err(&client->dev, "Invalid firmware length\n");
+		return -EINVAL;
+	}
+
+	if (fw->size % RAYDIUM_PAGE_SIZE)
+		n_fw_pages = fw->size/RAYDIUM_PAGE_SIZE + 1;
+	else
+		n_fw_pages = fw->size/RAYDIUM_PAGE_SIZE;
+
+	error = raydium_i2c_check_fw_status(ts);
+	if (error) {
+		dev_err(&client->dev, "Unable to access IC %d\n", error);
+			return error;
+	}
+
+	if (ts->boot_mode == RAYDIUM_TS_MAIN) {
+		for (u8_idx = 0; u8_idx < MAX_RETRIES; u8_idx++) {
+			error = raydium_i2c_enter_bl(client);
+			if (!error) {
+				error = raydium_i2c_check_fw_status(ts);
+				if (error) {
+					dev_err(&client->dev, "Unable to access IC %d\n",
+						error);
+					return error;
+				}
+				if (ts->boot_mode == RAYDIUM_TS_BLDR)
+					break;
+			}
+		}
+		if (ts->boot_mode == RAYDIUM_TS_MAIN) {
+			dev_err(&client->dev, "Fail jump to boot loader %d\n",
+					error);
+			return -EIO;
+		}
+	}
+
+	error = raydium_i2c_disable_watch_dog(client);
+	if (error) {
+		dev_err(&client->dev, "send disable watchdog cmd fail, %d\n",
+			error);
+		return error;
+	}
+
+	error = raydium_i2c_check_path(client);
+	if (error) {
+		dev_err(&client->dev, "send chk path fail, %d\n", error);
+		return error;
+	}
+
+	error = raydium_i2c_boot_trigger(client);
+	if (error) {
+		dev_err(&client->dev, "send boot trigger fail, %d\n", error);
+		return error;
+	}
+
+	msleep(BOOT_DELAY_MS);
+
+	fw_checksum = 0;
+	fw_idx = 0;
+	for (page = 0 ; page < n_fw_pages ; page++) {
+		memset(buf, 0xFF, sizeof(buf));
+		buf[HEADER] = 0x0B;
+		if (page == 0)
+			buf[PAGE_STR] = 0x00;
+
+		for (u8_idx = 0; u8_idx < RAYDIUM_PAGE_SIZE; u8_idx++) {
+			if (fw_idx < fw->size) {
+				buf[DATA_STR + u8_idx] =
+					fw->data[page*RAYDIUM_PAGE_SIZE +
+					u8_idx];
+				fw_checksum += buf[3 + u8_idx];
+				fw_idx++;
+			}
+		}
+
+		raydium_i2c_fw_write_page(client, (u8 *)buf, sizeof(buf));
+		msleep(20);
+	}
+
+	error = raydium_i2c_leave_bl(client);
+	if (error) {
+		dev_err(&client->dev, "leave boot loader fail: %d\n", error);
+		return error;
+	}
+	dev_err(&client->dev, "leave boot loader success\n");
+
+	error = raydium_i2c_check_fw_status(ts);
+	if (error) {
+		dev_err(&client->dev, "Unable to access IC %d\n", error);
+		return error;
+	}
+
+	if (ts->boot_mode == RAYDIUM_TS_MAIN) {
+
+		error = raydium_i2c_fw_trigger(client);
+		if (error) {
+			dev_err(&client->dev, "send fw trigger fail, %d\n",
+				error);
+			return error;
+		}
+
+		error = raydium_i2c_write_checksum(client, fw->size,
+			fw_checksum);
+		if (error) {
+			dev_err(&client->dev, "write checksum fail %d\n",
+				error);
+			return error;
+		}
+	} else {
+		dev_err(&client->dev, "switch to main_fw fail %d\n", error);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int raydium_i2c_fw_update(struct raydium_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	const struct firmware *fw = NULL;
+	const char *fw_file = "raydium.fw";
+	int error;
+
+	error = request_firmware(&fw, fw_file, &client->dev);
+	if (error) {
+		dev_err(&client->dev, "Unable to open firmware %s\n", fw_file);
+		return error;
+	}
+	/*disable irq*/
+	disable_irq(client->irq);
+
+	error = raydium_i2c_do_update_firmware(ts, fw);
+	if (error) {
+		dev_err(&client->dev, "firmware update failed: %d\n", error);
+		ts->boot_mode = RAYDIUM_TS_BLDR;
+		goto out_enable_irq;
+	}
+
+	error = raydium_i2c_initialize(ts);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to initialize device after firmware update: %d\n",
+			error);
+		ts->boot_mode = RAYDIUM_TS_BLDR;
+		goto out_enable_irq;
+	}
+
+	ts->boot_mode = RAYDIUM_TS_MAIN;
+
+out_enable_irq:
+	enable_irq(client->irq);
+	msleep(100);
+
+	return error;
+}
+
+static void raydium_mt_event(struct raydium_data *ts)
+{
+	u8 data[MAX_PKG_SIZE];
+	int error, i;
+	int x, y, f_state, pressure, wx, wy;
+
+	error = raydium_i2c_read_message(ts->client, ts->obj.data_bank_addr,
+		ts->obj.pkg_size, (void *)data);
+
+	if (error < 0) {
+		dev_err(&ts->client->dev, "%s: failed to read data: %d\n",
+			__func__, error);
+		return;
+	}
+
+	for (i = 0; i < MAX_TOUCH_NUM; i++) {
+		f_state = (data + i * ts->obj.tp_info_size)[POS_STATE];
+		pressure = (data + i * ts->obj.tp_info_size)[POS_PRESSURE];
+		wx = (data + i * ts->obj.tp_info_size)[WIDTH_X];
+		wy = (data + i * ts->obj.tp_info_size)[WIDTH_Y];
+
+		input_mt_slot(ts->input, i);
+		input_mt_report_slot_state(ts->input,
+				MT_TOOL_FINGER, f_state != 0);
+
+		if (!f_state)
+			continue;
+		x = get_unaligned_le16(&data[i * ts->obj.tp_info_size + POS_X]);
+		y = get_unaligned_le16(&data[i * ts->obj.tp_info_size + POS_Y]);
+
+		input_report_abs(ts->input, ABS_MT_POSITION_X, x);
+		input_report_abs(ts->input, ABS_MT_POSITION_Y, y);
+		input_report_abs(ts->input, ABS_MT_PRESSURE, pressure);
+		input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR,
+			max(wx, wy));
+		input_report_abs(ts->input, ABS_MT_TOUCH_MINOR,
+			min(wx, wy));
+	}
+
+	input_mt_sync_frame(ts->input);
+	input_sync(ts->input);
+}
+
+static irqreturn_t raydium_i2c_irq(int irq, void *_dev)
+{
+	struct raydium_data *ts = _dev;
+
+	if (ts->boot_mode != RAYDIUM_TS_BLDR)
+		raydium_mt_event(ts);
+
+	return IRQ_HANDLED;
+}
+
+static ssize_t raydium_calibrate(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t count)
+{
+	struct raydium_data *ts = dev_get_drvdata(dev);
+	struct i2c_client *client = ts->client;
+
+	static const u8 cal_cmd[] = {0x00, 0x01, 0x9E};
+	int error = 0;
+
+	error = raydium_i2c_wrt_object(client, (u8 *)cal_cmd,
+			sizeof(cal_cmd), WAIT_READY);
+	if (error) {
+		dev_err(&client->dev, "send chk path cmd fail: %d\n", error);
+		return error;
+	}
+
+	return error;
+}
+
+static ssize_t write_update_fw(struct device *dev,
+			struct device_attribute *attr,
+			const char *buf, size_t count)
+{
+	struct raydium_data *ts = dev_get_drvdata(dev);
+	int error;
+
+	error = mutex_lock_interruptible(&ts->sysfs_mutex);
+	if (error)
+		return error;
+
+	error = raydium_i2c_fw_update(ts);
+	dev_dbg(dev, "firmware update result: %d\n", error);
+
+	mutex_unlock(&ts->sysfs_mutex);
+	return error ?: count;
+}
+
+static ssize_t raydium_bootmode_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct raydium_data *ts = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%s\n", ts->boot_mode == RAYDIUM_TS_MAIN ?
+		"Normal" : "Recovery");
+}
+
+static ssize_t raydium_fw_ver_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct raydium_data *ts = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%d.%d\n", ts->info.main_ver, ts->info.sub_ver);
+}
+
+static ssize_t raydium_hw_ver_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct raydium_data *ts = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%04x\n", ts->info.hw_ver);
+}
+
+static DEVICE_ATTR(fw_version, S_IRUGO, raydium_fw_ver_show, NULL);
+static DEVICE_ATTR(hw_version, S_IRUGO, raydium_hw_ver_show, NULL);
+static DEVICE_ATTR(boot_mode, S_IRUGO, raydium_bootmode_show, NULL);
+static DEVICE_ATTR(update_fw, S_IWUSR, NULL, write_update_fw);
+static DEVICE_ATTR(calibrate, S_IWUSR, NULL, raydium_calibrate);
+
+static struct attribute *raydium_attributes[] = {
+	&dev_attr_update_fw.attr,
+	&dev_attr_boot_mode.attr,
+	&dev_attr_fw_version.attr,
+	&dev_attr_hw_version.attr,
+	&dev_attr_calibrate.attr,
+	NULL
+};
+
+static struct attribute_group raydium_attribute_group = {
+	.attrs = raydium_attributes,
+};
+
+static void raydium_i2c_remove_sysfs_group(void *_data)
+{
+	struct raydium_data *ts = _data;
+
+	sysfs_remove_group(&ts->client->dev.kobj, &raydium_attribute_group);
+}
+
+static int raydium_i2c_power_on(struct raydium_data *ts)
+{
+	int error;
+
+	if (IS_ERR_OR_NULL(ts->reset_gpio))
+		return 0;
+
+	gpiod_set_value_cansleep(ts->reset_gpio, 1);
+
+	error = regulator_enable(ts->avdd);
+	if (error) {
+		dev_err(&ts->client->dev,
+			"failed to enable avdd regulator: %d\n", error);
+		goto release_reset_gpio;
+	}
+
+	error = regulator_enable(ts->vccio);
+	if (error) {
+		regulator_disable(ts->avdd);
+		dev_err(&ts->client->dev,
+			"failed to enable vccio regulator: %d\n", error);
+		goto release_reset_gpio;
+	}
+
+	udelay(RAYDIUM_POWERON_DELAY_USEC);
+
+release_reset_gpio:
+	gpiod_set_value_cansleep(ts->reset_gpio, 0);
+
+	if (error)
+		return error;
+
+	msleep(RAYDIUM_RESET_DELAY_MSEC);
+
+	return 0;
+}
+
+static void raydium_i2c_power_off(void *_data)
+{
+	struct raydium_data *ts = _data;
+
+	if (!IS_ERR_OR_NULL(ts->reset_gpio)) {
+		gpiod_set_value_cansleep(ts->reset_gpio, 1);
+		regulator_disable(ts->vccio);
+		regulator_disable(ts->avdd);
+	}
+}
+
+static int raydium_i2c_probe(struct i2c_client *client,
+			    const struct i2c_device_id *id)
+{
+	union i2c_smbus_data dummy;
+	struct raydium_data *ts;
+	int error;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		dev_err(&client->dev,
+			"%s: i2c check functionality error\n", DEVICE_NAME);
+		return -ENXIO;
+	}
+
+	ts = devm_kzalloc(&client->dev, sizeof(struct raydium_data),
+		GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+
+	mutex_init(&ts->sysfs_mutex);
+	init_completion(&ts->cmd_done);
+
+	ts->client = client;
+	i2c_set_clientdata(client, ts);
+
+	ts->avdd = devm_regulator_get(&client->dev, "avdd");
+	if (IS_ERR(ts->avdd)) {
+		error = PTR_ERR(ts->avdd);
+		if (error != -EPROBE_DEFER)
+			dev_err(&client->dev,
+				"Failed to get 'avdd' regulator: %d\n", error);
+		return error;
+	}
+
+	ts->vccio = devm_regulator_get(&client->dev, "vccio");
+	if (IS_ERR(ts->vccio)) {
+		error = PTR_ERR(ts->vccio);
+		if (error != -EPROBE_DEFER)
+			dev_err(&client->dev,
+				"Failed to get 'vccio' regulator: %d\n", error);
+		return error;
+	}
+
+	ts->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
+		GPIOD_OUT_LOW);
+	if (IS_ERR(ts->reset_gpio)) {
+		error = PTR_ERR(ts->reset_gpio);
+		if (error != -EPROBE_DEFER) {
+			dev_err(&client->dev,
+				"failed to get reset gpio: %d\n", error);
+			return error;
+		}
+	}
+
+	error = raydium_i2c_power_on(ts);
+	if (error)
+		return error;
+
+	error = devm_add_action(&client->dev, raydium_i2c_power_off, ts);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to install power off action: %d\n", error);
+		raydium_i2c_power_off(ts);
+		return error;
+	}
+
+	/* Make sure there is something at this address */
+	if (i2c_smbus_xfer(client->adapter, client->addr, 0,
+			I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0) {
+		dev_err(&client->dev, "nothing at this address\n");
+		return -ENXIO;
+	}
+
+	error = raydium_i2c_initialize(ts);
+	if (error) {
+		dev_err(&client->dev, "failed to initialize: %d\n", error);
+		return error;
+	}
+
+	ts->input = devm_input_allocate_device(&client->dev);
+	if (!ts->input) {
+		dev_err(&client->dev, "Failed to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	ts->input->name = "Raydium Touchscreen";
+	ts->input->id.bustype = BUS_I2C;
+
+	/* Multitouch input params setup */
+	input_set_abs_params(ts->input, ABS_MT_POSITION_X, 0,
+		ts->info.x_max, 0, 0);
+	input_set_abs_params(ts->input, ABS_MT_POSITION_Y,
+		0, ts->info.y_max, 0, 0);
+	input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+	input_set_abs_params(ts->input, ABS_MT_PRESSURE, 0, 255, 0, 0);
+	input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->info.x_res);
+	input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->info.y_res);
+
+	input_set_drvdata(ts->input, ts);
+
+	error = input_mt_init_slots(ts->input, MAX_TOUCH_NUM,
+		INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to initialize MT slots: %d\n", error);
+		return error;
+	}
+
+	error = input_register_device(ts->input);
+	if (error) {
+		dev_err(&client->dev,
+			"unable to register input device: %d\n", error);
+		return error;
+	}
+
+	error = devm_request_threaded_irq(&client->dev, client->irq, NULL,
+		raydium_i2c_irq, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+			client->name, ts);
+	if (error) {
+		dev_err(&client->dev, "Failed to register interrupt\n");
+		return error;
+	}
+
+	error = sysfs_create_group(&client->dev.kobj, &raydium_attribute_group);
+	if (error) {
+		dev_err(&client->dev, "failed to create sysfs attributes: %d\n",
+			error);
+		return error;
+	}
+
+	error = devm_add_action(&client->dev,
+				raydium_i2c_remove_sysfs_group, ts);
+	if (error) {
+		raydium_i2c_remove_sysfs_group(ts);
+		dev_err(&client->dev,
+			"Failed to add sysfs cleanup action: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+static void __maybe_unused raydium_enter_sleep(struct i2c_client *client)
+{
+	static const u8 sleep_cmd[] = { 0x5A, 0xff, 0x00, 0x0f };
+	int error;
+
+	error = raydium_i2c_send(client, CMD_ENTER_SLEEP, (u8 *)sleep_cmd,
+		sizeof(sleep_cmd));
+	if (error)
+		dev_err(&client->dev,
+			"Send sleep failed: %d\n", error);
+}
+
+static int __maybe_unused raydium_i2c_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	/* Command not support in BLDR recovery mode */
+	if (ts->boot_mode != RAYDIUM_TS_MAIN)
+		return -ENOMEM;
+
+	disable_irq(client->irq);
+
+	if (device_may_wakeup(dev)) {
+		raydium_enter_sleep(client);
+
+		ts->wake_irq_enabled = (enable_irq_wake(client->irq) == 0);
+	} else {
+		raydium_i2c_power_off(ts);
+	}
+
+	return 0;
+}
+
+static int __maybe_unused raydium_i2c_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	if (device_may_wakeup(dev)) {
+		if (ts->wake_irq_enabled)
+			disable_irq_wake(client->irq);
+		raydium_i2c_sw_reset(client);
+	} else {
+		raydium_i2c_power_on(ts);
+		raydium_i2c_initialize(ts);
+	}
+
+	enable_irq(client->irq);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(raydium_i2c_pm_ops,
+			 raydium_i2c_suspend, raydium_i2c_resume);
+
+static const struct i2c_device_id raydium_i2c_id[] = {
+	{ DEVICE_NAME, 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, raydium_i2c_id);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id raydium_acpi_id[] = {
+	{ "RAYD0001", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(acpi, raydium_acpi_id);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id raydium_of_match[] = {
+	{ .compatible = "raydium,rm32380",},
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, raydium_of_match);
+#endif
+
+static struct i2c_driver raydium_i2c_driver = {
+	.probe = raydium_i2c_probe,
+	.id_table = raydium_i2c_id,
+	.driver = {
+		.name = "raydium_ts",
+		.pm = &raydium_i2c_pm_ops,
+		.acpi_match_table = ACPI_PTR(raydium_acpi_id),
+		.of_match_table = of_match_ptr(raydium_of_match),
+	},
+};
+
+module_i2c_driver(raydium_i2c_driver);
+
+MODULE_AUTHOR("Raydium");
+MODULE_DESCRIPTION("Raydium I2c Touchscreen driver");
+MODULE_LICENSE("GPL v2");
-- 
2.1.2

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2016-04-22 22:50   ` Dmitry Torokhov
@ 2016-04-25  2:48     ` yajohn lin
  0 siblings, 0 replies; 42+ messages in thread
From: yajohn lin @ 2016-04-25  2:48 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: rydberg, Grant Likely, robh+dt, jeesw, Benson Leung, jeffrey.lin,
	roger.yang, KP.li, albert.shieh, linux-kernel, linux-input,
	devicetree

[-- Attachment #1: Type: text/plain, Size: 42105 bytes --]

hi Dmitry:
Thanks for your quickly response. We'll modify the parts you mentioned in
the mail and send update new patch before 4/26.

Thank you!

2016-04-23 6:50 GMT+08:00 Dmitry Torokhov <dmitry.torokhov@gmail.com>:

> Hi Dan,
>
> On Fri, Apr 22, 2016 at 06:01:08PM +0800, dan.huang wrote:
> > From: "jeffrey.lin" <jeffrey.lin@rad-ic.com>
> >
> > Raydium I2C touch driver.
> >
> > Signed-off-by: jeffrey.lin <jeffrey.lin@rad-ic.com>
>
> When you are sending a patch over you need to add your sign-off to it.
>
> Also I am still getting compile errors/warnings:
>
>   CC [M]  drivers/input/touchscreen/raydium_i2c_ts.o
> drivers/input/touchscreen/raydium_i2c_ts.c: In function
> ‘raydium_i2c_probe’:
> drivers/input/touchscreen/raydium_i2c_ts.c:1036:35: warning: ‘irqflags’
> may be used uninitialized in this function [-Wmaybe-uninitialized]
>   error = devm_request_threaded_irq(&client->dev, client->irq,
>
> Also please mention what is changed between previous and new version so
> I do not need to hunt for surprises.
>
> > +/*I2C command */
> > +#define CMD_BOOT_WRT         0x11
> > +#define CMD_BOOT_ACK         0x22
> > +#define CMD_BOOT_CHK         0x33
> > +#define CMD_BOOT_READ                0x44
> > +#define CMD_BOOT_WAIT_READY  0x1A
> > +#define CMD_BOOT_PATH_READY  0x1B
>
> I see that in this version the command codes are completely redone. Why
> is this? I think there is general confusion as to the encoding of the
> "header" for the command message little endian vs big endian) and
> position of the register in the header.
>
> Would you mind spelling out the format of command message, please?
>
> > +#define BOOT_RDY             0xFF
> > +#define CMD_QUERY_BANK               0x2B
> > +#define CMD_DATA_BANK                0x4D
> > +#define CMD_ENTER_SLEEP              0x4E
> > +#define CMD_BANK_SWITCH              0xAA
> > +
> > +/* Touch relative info */
> > +#define MAX_RETRIES          3
> > +#define MAX_FW_UPDATE_RETRIES        30
> > +#define MAX_TOUCH_NUM                10
> > +#define MAX_PACKET_SIZE              32
> > +#define BOOT_DELAY_MS        100
> > +
> > +#define RAYDIUM_FW_PAGESIZE          128
> > +#define RAYDIUM_POWERON_DELAY_USEC   500
> > +#define RAYDIUM_RESET_DELAY_MSEC     50
> > +
> > +#define ADDR_INDEX           0x03
> > +#define DATA_INDEX           0x04
> > +
> > +#define HEADER_SIZE          4
> > +
> > +enum raydium_boot_mode {
> > +     RAYDIUM_TS_MAIN = 0,
> > +     RAYDIUM_TS_BLDR,
> > +};
> > +
> > +enum raydium_abs_idx {
> > +     POS_STATE = 0,/*1:touch, 0:no touch*/
> > +     POS_X,
> > +     POS_Y = 3,
> > +     POS_PRESSURE,
> > +     WIDTH_X,
> > +     WIDTH_Y,
> > +};
> > +
> > +struct raydium_info {
> > +     u32 hw_ver;     /*device ver, __le32*/
> > +     u8 main_ver;
> > +     u8 sub_ver;
> > +     u16 ft_ver;     /*test ver, __le16*/
> > +     u8 x_num;
> > +     u8 y_num;
> > +     u16 x_max;      /*disp reso, __le16*/
> > +     u16 y_max;      /*disp reso, __le16*/
> > +     u8 x_res;               /* units/mm */
> > +     u8 y_res;               /* units/mm */
> > +};
> > +
> > +struct raydium_object {
> > +     u32 data_bank_addr;
> > +     u8 pkg_size;
> > +     u8 tp_info_size;
> > +};
> > +
> > +/* struct raydium_data - represents state of Raydium touchscreen device
> */
> > +struct raydium_data {
> > +     struct i2c_client *client;
> > +     struct input_dev *input;
> > +
> > +     struct regulator *avdd;
> > +     struct regulator *vccio;
> > +     struct gpio_desc *reset_gpio;
> > +
> > +     u32 query_bank_info;
> > +
> > +     struct raydium_info info;
> > +     struct raydium_object obj;
> > +     enum raydium_boot_mode boot_mode;
> > +
> > +     struct mutex sysfs_mutex;
> > +     struct completion cmd_done;
> > +
> > +     bool wake_irq_enabled;
> > +};
> > +
> > +static int raydium_i2c_send(struct i2c_client *client,
> > +     u8 addr, u8 *data, size_t len)
> > +{
> > +     u8 buf[MAX_PACKET_SIZE + 1], idx_i;
> > +     u16 pkg_len, use_len;
> > +     int tries = 0;
> > +
> > +     buf[0] = addr;
> > +     use_len = len;
> > +     use_len = 0;
> > +
> > +     while (use_len) {
> > +             pkg_len = (use_len < MAX_PACKET_SIZE) ?
> > +                     use_len : MAX_PACKET_SIZE;
> > +             memcpy(&buf[1], data + idx_i*MAX_PACKET_SIZE, pkg_len);
> > +
> > +             tries = 0;
> > +             do {
> > +                     if (i2c_master_send(client, buf, pkg_len + 1)
> > +                             == (pkg_len + 1))
> > +                             break;
> > +                     msleep(20);
> > +             } while (++tries < MAX_RETRIES);
> > +             idx_i++;
> > +             use_len = (use_len < MAX_PACKET_SIZE) ?
> > +                     0 : (use_len - MAX_PACKET_SIZE);
> > +     }
> > +
> > +     dev_err(&client->dev, "%s: i2c send failed\n", __func__);
> > +
> > +     return -EIO;
> > +}
> > +
> > +static int raydium_i2c_read(struct i2c_client *client,
> > +     u8 addr, size_t len, void *data)
> > +{
> > +     struct i2c_msg xfer[2];
> > +     int ret;
> > +
> > +     /* Write register */
> > +     xfer[0].addr = client->addr;
> > +     xfer[0].flags = 0;
> > +     xfer[0].len = 1;
> > +     xfer[0].buf = &addr;
> > +
> > +     /* Read data */
> > +     xfer[1].addr = client->addr;
> > +     xfer[1].flags = I2C_M_RD;
> > +     xfer[1].len = len;
> > +     xfer[1].buf = data;
> > +
> > +     ret = i2c_transfer(client->adapter, xfer, ARRAY_SIZE(xfer));
> > +     if (ret < 0)
> > +             return ret;
> > +
> > +     if (ret != ARRAY_SIZE(xfer))
> > +             return -EIO;
> > +
> > +     return 0;
> > +}
> > +
> > +static int raydium_i2c_read_message(struct i2c_client *client,
> > +     u32 addr, size_t len, void *data)
> > +{
> > +     u16 pkg_size, use_len;
> > +     u8 buf[HEADER_SIZE], idx_i;
> > +     int error;
> > +
> > +     use_len = len;
> > +     idx_i = 0;
> > +     while (use_len > 0) {
> > +             pkg_size = (use_len < MAX_PACKET_SIZE) ?
> > +                     use_len : MAX_PACKET_SIZE;
> > +
> > +             put_unaligned_be32(addr, buf);
> > +
> > +             /*set data bank*/
> > +             error = raydium_i2c_send(client, CMD_BANK_SWITCH,
> > +                     (u8 *)buf, HEADER_SIZE);
> > +             /*read potints data*/
> > +             if (!error)
> > +                     error = raydium_i2c_read(client, buf[ADDR_INDEX],
> > +                             pkg_size,
> > +                             data + idx_i*MAX_PACKET_SIZE);
> > +
> > +             pkg_size += MAX_PACKET_SIZE;
> > +             addr += MAX_PACKET_SIZE;
> > +             use_len = (use_len < MAX_PACKET_SIZE) ?
> > +                     0 : (use_len - MAX_PACKET_SIZE);
> > +             idx_i++;
> > +     }
> > +
> > +     return error;
> > +}
> > +
> > +static int raydium_i2c_send_message(struct i2c_client *client,
> > +     size_t len, void *data)
> > +{
> > +     int error;
> > +     __le32 cmd;
> > +
> > +     cmd = get_unaligned_le32(data);
> > +
> > +     /*set data bank*/
> > +     error = raydium_i2c_send(client, CMD_BANK_SWITCH, (u8 *)&cmd,
> > +             HEADER_SIZE);
> > +
> > +     /*send message*/
> > +     if (!error)
> > +             error = raydium_i2c_send(client, ((u8 *)data)[ADDR_INDEX],
> > +                     data + DATA_INDEX, len);
> > +
> > +     return error;
> > +}
> > +
> > +static int raydium_i2c_sw_reset(struct i2c_client *client)
> > +{
> > +     static const u8 soft_rst_cmd[] = {0x40, 0x00, 0x00, 0x04, 0x01};
> > +     int error;
> > +
> > +     error = raydium_i2c_send_message(client, ARRAY_SIZE(soft_rst_cmd),
> > +             (void *)soft_rst_cmd);
> > +     if (error) {
> > +             dev_err(&client->dev, "software reset failed: %d\n",
> error);
> > +             return error;
> > +     }
> > +
> > +     msleep(RAYDIUM_RESET_DELAY_MSEC);
> > +
> > +     return 0;
> > +}
> > +
> > +static int raydium_i2c_query_ts_info(struct raydium_data *ts)
> > +{
> > +     struct i2c_client *client = ts->client;
> > +     int error, retry_cnt;
> > +
> > +     for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
> > +             error = raydium_i2c_read(client, CMD_DATA_BANK,
> > +                     sizeof(ts->obj), (void *)&ts->obj);
> > +                     ts->obj.data_bank_addr =
> > +
>  get_unaligned_le32(&ts->obj.data_bank_addr);
> > +
> > +             if (!error) {
> > +                     error = raydium_i2c_read(client, CMD_QUERY_BANK,
> > +                             sizeof(ts->query_bank_info),
> > +                             (void *)&ts->query_bank_info);
> > +                     if (!error) {
> > +                             error = raydium_i2c_read_message(client,
> > +                                     ts->query_bank_info,
> sizeof(ts->info),
> > +                                     (void *)&ts->info);
> > +
> > +                             ts->info.hw_ver =
> > +
>  get_unaligned_le32(&ts->info.hw_ver);
> > +                             ts->info.ft_ver =
> > +
>  get_unaligned_le16(&ts->info.ft_ver);
> > +                             ts->info.x_max =
> > +
>  get_unaligned_le16(&ts->info.x_max);
> > +                             ts->info.y_max =
> > +
>  get_unaligned_le16(&ts->info.y_max);
> > +                             return 0;
> > +                     }
> > +             }
> > +     }
> > +     dev_err(&client->dev, "Get touch data failed: %d\n", error);
> > +
> > +     return -EINVAL;
> > +}
> > +
> > +static int raydium_i2c_fastboot(struct i2c_client *client)
> > +{
> > +     static const u8 boot_cmd[] = { 0x50, 0x00, 0x06, 0x20 };
> > +     u8 buf[HEADER_SIZE];
> > +     int error;
> > +
> > +     error = raydium_i2c_read_message(client,
> > +             get_unaligned_be32(boot_cmd),
> > +             sizeof(boot_cmd), buf);
> > +
> > +     if (!error) {
> > +             if (buf[0] == RM_BOOT_BLDR) {
> > +                     dev_dbg(&client->dev, "boot in fastboot mode\n");
> > +                     return -EINVAL;
> > +             }
> > +             dev_dbg(&client->dev, "boot success -- 0x%x\n",
> client->addr);
> > +             return 0;
> > +     }
> > +
> > +     dev_err(&client->dev, "boot failed: %d\n", error);
> > +
> > +     return error;
> > +}
> > +
> > +static int raydium_i2c_check_fw_status(struct raydium_data *ts)
> > +{
> > +     struct i2c_client *client = ts->client;
> > +     static const u8 bl_area[] = {0x62, 0x6f, 0x6f, 0x74};
> > +     static const u8 main_area[] = {0x66, 0x69, 0x72, 0x6d};
> > +     u8 buf[HEADER_SIZE];
> > +     int error;
> > +
> > +     error = raydium_i2c_read(client, CMD_BOOT_READ, HEADER_SIZE,
> > +             (void *)buf);
> > +     if (!error) {
> > +             if (memcmp(buf, bl_area, sizeof(bl_area)))
> > +                     ts->boot_mode = RAYDIUM_TS_BLDR;
> > +             else if (memcmp(buf, main_area, sizeof(main_area)))
> > +                     ts->boot_mode = RAYDIUM_TS_MAIN;
> > +             else
> > +                     return -EINVAL;
> > +
> > +             return 0;
> > +     }
> > +
> > +     dev_err(&client->dev, "check bl status failed: %d\n", error);
> > +
> > +     return error;
> > +}
> > +
> > +static int raydium_i2c_initialize(struct raydium_data *ts)
> > +{
> > +     struct i2c_client *client = ts->client;
> > +     int error, retry_cnt;
> > +
> > +     for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
> > +             error = raydium_i2c_fastboot(client);
> > +             if (error) {
> > +                     /* Continue initializing if it's the last try */
> > +                     if (retry_cnt < MAX_RETRIES - 1)
> > +                             continue;
> > +             }
> > +             /* Wait for Hello packet */
> > +             msleep(BOOT_DELAY_MS);
> > +
> > +             error = raydium_i2c_check_fw_status(ts);
> > +             if (error) {
> > +                     dev_err(&client->dev,
> > +                             "failed to read 'hello' packet: %d\n",
> error);
> > +             }
> > +     }
> > +
> > +     if (error)
> > +             ts->boot_mode = RAYDIUM_TS_BLDR;
> > +     else
> > +             raydium_i2c_query_ts_info(ts);
> > +
> > +     return error;
> > +}
> > +
> > +static int raydium_i2c_recv(struct i2c_client *client, u8 *buf, size_t
> count)
> > +{
> > +     int error = 0;
> > +
> > +     error = i2c_master_recv(client, buf, count);
> > +
> > +     if (error == count) {
> > +             error = 0;
> > +     } else if (error != count) {
> > +             error = (error < 0) ? error : -EIO;
> > +             dev_err(&client->dev, "i2c recv failed (%d)\n", error);
> > +     }
> > +
> > +     return error;
> > +}
> > +
> > +static int raydium_i2c_bl_chk_state(struct i2c_client *client, u8 state)
> > +{
> > +     static const u8 ack_ok[] = { 0xFF, 0x39, 0x30, 0x30, 0x54 };
> > +     u8 rbuf[5];
> > +     u8 retry;
> > +     int error;
> > +
> > +     for (retry = 0; retry < MAX_FW_UPDATE_RETRIES; retry++) {
> > +             if (state == CMD_BOOT_WAIT_READY) {
> > +                     error = raydium_i2c_recv(client, &rbuf[0], 1);
> > +                     if (!error) {
> > +                             if (rbuf[0] == BOOT_RDY)
> > +                                     return 0;
> > +                     }
> > +             } else if (state == CMD_BOOT_WAIT_READY) {
>
> The condition on branches is the same (CMD_BOOT_WAIT_READY).
>
> > +                     error = raydium_i2c_recv(client, &rbuf[0],
> > +                             sizeof(ack_ok));
> > +                     if (!error) {
> > +                             if (memcmp(rbuf, ack_ok, sizeof(ack_ok)))
> > +                                     return 0;
> > +                     }
> > +             } else
> > +                     return -EINVAL;
> > +
> > +             msleep(20);
> > +     }
> > +
> > +     return -EINVAL;
> > +}
> > +
> > +static int raydium_i2c_wrt_object(struct i2c_client *client,
> > +     u8 *data, size_t len, u8 state)
> > +{
> > +     int error = 0;
> > +
> > +     error = raydium_i2c_send(client, CMD_BOOT_WRT, data, len);
> > +     if (error) {
> > +             dev_err(&client->dev, "WRT obj command failed: %d\n",
> error);
> > +             return error;
> > +     }
> > +
> > +     error = raydium_i2c_send(client, CMD_BOOT_ACK, (u8 *)NULL, 0);
> > +     if (error) {
> > +             dev_err(&client->dev, "Ack obj command failed: %d\n",
> error);
> > +             return error;
> > +     }
>
> I am looking at raydium_i2c_send() implementation and it seemd to be a
> no-op with len == 0, what gives?
>
> > +
> > +     error = raydium_i2c_send(client, CMD_BOOT_CHK, (u8 *)NULL, 0);
> > +     if (error) {
> > +             dev_err(&client->dev, "Boot chk failed: %d\n", error);
> > +             return error;
> > +     }
>
> Same here.
>
> > +
> > +     error = raydium_i2c_bl_chk_state(client, state);
> > +     if (error) {
> > +             dev_err(&client->dev, "boot trigger state failed: %d\n",
> error);
> > +             return error;
> > +     }
> > +     return 0;
> > +}
> > +
> > +static bool raydium_i2c_boot_trigger(struct i2c_client *client)
> > +{
> > +     int error;
> > +     u8 u8_idx;
> > +     static const u8 cmd[7][6] = {
> > +                     {0x08, 0x0C, 0x09, 0x00, 0x50, 0xD7},
> > +                     {0x08, 0x04, 0x09, 0x00, 0x50, 0xA5},
> > +                     {0x08, 0x04, 0x09, 0x00, 0x50, 0x00},
> > +                     {0x08, 0x04, 0x09, 0x00, 0x50, 0xA5},
> > +                     {0x08, 0x0C, 0x09, 0x00, 0x50, 0x00},
> > +                     {0x06, 0x01},
> > +                     {0x02, 0xA2},
> > +             };
> > +
> > +     for (u8_idx = 0 ; u8_idx < 7 ; u8_idx++) {
> > +             error = raydium_i2c_wrt_object(client, (u8 *)cmd[u8_idx],
> > +                     sizeof(cmd[u8_idx]), CMD_BOOT_WAIT_READY);
>
> If you expect that for last 2 elements the sizeof(cmd[u8_idx]) will
> return 2 you are mistaken, they all will be 6.
>
> > +             if (error) {
> > +                     dev_err(&client->dev, "send boot trigger cmd
> failed: %d\n",
> > +                             error);
> > +                     return error;
> > +             }
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int raydium_i2c_check_path(struct i2c_client *client)
> > +{
> > +     static const u8 cmd[] = {0x09, 0x00, 0x09, 0x00, 0x50, 0x10, 0x00};
> > +     int error;
> > +
> > +     error = raydium_i2c_wrt_object(client, (u8 *)cmd, sizeof(cmd),
> > +             CMD_BOOT_PATH_READY);
> > +     if (error) {
> > +             dev_err(&client->dev, "send chk path cmd fail: %d\n",
> error);
> > +             return error;
> > +     }
> > +
> > +     return error;
> > +}
> > +
> > +static int raydium_i2c_enter_bl(struct i2c_client *client)
> > +{
> > +     static const u8 cal_cmd[] = {0x00, 0x01, 0x52};
> > +     int error;
> > +
> > +     error = raydium_i2c_wrt_object(client, (u8 *)cal_cmd,
> > +             sizeof(cal_cmd), CMD_BOOT_WAIT_READY);
> > +     if (error) {
> > +             dev_err(&client->dev, "send jump loader cmd fail: %d\n",
> error);
> > +             return error;
> > +     }
> > +     return 0;
> > +}
> > +
> > +static int raydium_i2c_leave_bl(struct i2c_client *client)
> > +{
> > +     static const u8 leave_cmd[] = {0x05, 0x00};
> > +     int error;
> > +
> > +     error = raydium_i2c_wrt_object(client, (u8 *)leave_cmd,
> > +             sizeof(leave_cmd), CMD_BOOT_WAIT_READY);
> > +     if (error) {
> > +             dev_err(&client->dev, "send leave bl cmd fail: %d\n",
> error);
> > +             return error;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int raydium_i2c_write_checksum(struct i2c_client *client,
> > +     u16 length, u16 checksum)
> > +{
> > +     u8 checksum_cmd[] = {0x00, 0x05, 0x6D, 0x00, 0x00, 0x00, 0x00};
> > +     int error = 0;
> > +
> > +     checksum_cmd[3] = (u8)(length & 0xFF);
> > +     checksum_cmd[4] = (u8)((length & 0xFF00) >> 8);
> > +     checksum_cmd[5] = (u8)(checksum & 0xFF);
> > +     checksum_cmd[6] = (u8)((checksum & 0xFF00) >> 8);
> > +
> > +     error = raydium_i2c_wrt_object(client, (u8 *)checksum_cmd,
> > +             sizeof(checksum_cmd), CMD_BOOT_WAIT_READY);
> > +     if (error) {
> > +             dev_err(&client->dev, "send wrt checksum cmd fail: %d\n",
> > +                     error);
> > +             return error;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int raydium_i2c_disable_watch_dog(struct i2c_client *client)
> > +{
> > +     static const u8 cmd[] = {0x0A, 0xAA};
> > +     int error = 0;
> > +
> > +     error = raydium_i2c_wrt_object(client, (u8 *)cmd, sizeof(cmd),
> > +             CMD_BOOT_WAIT_READY);
> > +     if (error) {
> > +             dev_err(&client->dev, "send disable watchdog cmd fail:
> %d\n",
> > +                     error);
> > +             return error;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int raydium_i2c_fw_write_page(struct i2c_client *client,
> > +                             const void *page, size_t len)
> > +{
> > +     int error;
> > +
> > +     error = raydium_i2c_wrt_object(client,  (u8 *)page, len,
> > +             CMD_BOOT_WAIT_READY);
> > +     if (error) {
> > +             dev_err(&client->dev, "send page wrt cmd failed: %d\n",
> error);
> > +             return error;
> > +     }
> > +
> > +     return error;
> > +}
> > +
> > +static int raydium_i2c_do_update_firmware(struct raydium_data *ts,
> > +                                      const struct firmware *fw)
> > +{
> > +     struct i2c_client *client = ts->client;
> > +     u8 u8_idx;
> > +     u16 fw_length;
> > +     u16 fw_checksum;
> > +     u8 buf[RAYDIUM_FW_PAGESIZE + 2];
> > +     int page, n_fw_pages;
> > +     int error;
> > +
> > +     fw_length = (int)(fw->size);
> > +     if (fw_length == 0) {
> > +             dev_err(&client->dev, "Invalid firmware length\n");
> > +             return -EINVAL;
> > +     }
> > +
> > +     if (fw_length % RAYDIUM_FW_PAGESIZE)
> > +             n_fw_pages = fw_length/RAYDIUM_FW_PAGESIZE;
> > +     else
> > +             n_fw_pages = fw_length/RAYDIUM_FW_PAGESIZE + 1;
> > +
> > +     error = raydium_i2c_check_fw_status(ts);
> > +     if (error) {
> > +             dev_err(&client->dev, "Unable to access IC %d\n", error);
> > +             return error;
> > +     }
> > +
> > +     if (ts->boot_mode == RAYDIUM_TS_MAIN) {
> > +             error = raydium_i2c_enter_bl(client);
> > +             if (error) {
> > +                     dev_err(&client->dev, "Unable jump to boot loader
> %d\n",
> > +                             error);
> > +                     return error;
> > +             }
> > +     }
> > +
> > +     error = raydium_i2c_disable_watch_dog(client);
> > +     if (error) {
> > +             dev_err(&client->dev, "send disable watchdog cmd fail,
> %d\n",
> > +                     error);
> > +             return error;
> > +     }
> > +
> > +     error = raydium_i2c_check_path(client);
> > +     if (error) {
> > +             dev_err(&client->dev, "send chk path fail, %d\n", error);
> > +             return error;
> > +     }
> > +
> > +     error = raydium_i2c_boot_trigger(client);
> > +     if (error) {
> > +             dev_err(&client->dev, "send boot trigger fail, %d\n",
> error);
> > +             return error;
> > +     }
> > +
> > +     fw_checksum = 0;
> > +     for (page = 0 ; page < n_fw_pages ; page++) {
> > +             memset(buf, 0xFF, sizeof(buf));
> > +             buf[0] = 0x03;
> > +             if (page == 0)
> > +                     buf[1] = 0x00;
> > +
> > +             memcpy(&buf[2], fw->data + page * RAYDIUM_FW_PAGESIZE,
> > +                     RAYDIUM_FW_PAGESIZE);
> > +             /*calculate chksum*/
> > +             for (u8_idx = 0; u8_idx < RAYDIUM_FW_PAGESIZE; u8_idx++)
> > +                     fw_checksum += buf[2 + u8_idx];
> > +
> > +             error = raydium_i2c_fw_write_page(client, (const void
> *)buf,
> > +                     sizeof(buf));
> > +             if (error) {
> > +                     dev_err(&client->dev, "flash page write fail,
> %d\n",
> > +                             error);
> > +                     return error;
> > +             }
> > +
> > +             msleep(20);
> > +     }
> > +
> > +     error = raydium_i2c_leave_bl(client);
> > +     if (error) {
> > +             dev_err(&client->dev, "leave boot loader fail: %d\n",
> error);
> > +             return error;
> > +     }
> > +
> > +     msleep(BOOT_DELAY_MS);
> > +
> > +     error = raydium_i2c_check_fw_status(ts);
> > +     if (error) {
> > +             dev_err(&client->dev, "Unable to access IC %d\n", error);
> > +             return error;
> > +     }
> > +
> > +     if (ts->boot_mode == RAYDIUM_TS_MAIN) {
> > +             error = raydium_i2c_write_checksum(client, fw_length,
> > +                     fw_checksum);
> > +             if (error) {
> > +                     dev_err(&client->dev, "write checksum fail %d\n",
> > +                             error);
> > +                     return error;
> > +             }
> > +     } else {
> > +             dev_err(&client->dev, "switch to main_fw fail %d\n",
> error);
> > +             return -EINVAL;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int raydium_i2c_fw_update(struct raydium_data *ts)
> > +{
> > +     struct i2c_client *client = ts->client;
> > +     const struct firmware *fw = NULL;
> > +     const char *fn = "raydium.fw";
> > +     int error;
> > +
> > +     error = request_firmware(&fw, fn, &client->dev);
> > +     if (error) {
> > +             dev_err(&client->dev, "Unable to open firmware %s\n", fn);
> > +             return error;
> > +     }
> > +     /*disable irq*/
> > +     disable_irq(client->irq);
> > +
> > +     error = raydium_i2c_do_update_firmware(ts, fw);
> > +     if (error) {
> > +             dev_err(&client->dev, "firmware update failed: %d\n",
> error);
> > +             ts->boot_mode = RAYDIUM_TS_BLDR;
> > +             goto out_enable_irq;
> > +     }
> > +
> > +     error = raydium_i2c_initialize(ts);
> > +     if (error) {
> > +             dev_err(&client->dev,
> > +                     "failed to initialize device after firmware
> update: %d\n",
> > +                     error);
> > +             ts->boot_mode = RAYDIUM_TS_BLDR;
> > +             goto out_enable_irq;
> > +     }
> > +
> > +     ts->boot_mode = RAYDIUM_TS_MAIN;
> > +
> > +out_enable_irq:
> > +     enable_irq(client->irq);
> > +     msleep(100);
> > +
> > +     return error;
> > +}
> > +
> > +static void raydium_mt_event(struct raydium_data *ts)
> > +{
> > +     u8 data[MAX_PACKET_SIZE];
> > +     int error, i;
> > +     int x, y, f_state, pressure, wx, wy;
> > +
> > +     error = raydium_i2c_read_message(ts->client,
> ts->obj.data_bank_addr,
> > +             ts->obj.pkg_size, (void *)data);
> > +
> > +     if (error < 0) {
> > +             dev_err(&ts->client->dev, "%s: failed to read data: %d\n",
> > +                     __func__, error);
> > +             return;
> > +     }
> > +
> > +     for (i = 0; i < MAX_TOUCH_NUM; i++) {
> > +             f_state = (data + i * ts->obj.tp_info_size)[POS_STATE];
> > +             pressure = (data + i * ts->obj.tp_info_size)[POS_PRESSURE];
> > +             wx = (data + i * ts->obj.tp_info_size)[WIDTH_X];
> > +             wy = (data + i * ts->obj.tp_info_size)[WIDTH_Y];
> > +
> > +             input_mt_slot(ts->input, i);
> > +             input_mt_report_slot_state(ts->input,
> > +                             MT_TOOL_FINGER, f_state != 0);
> > +
> > +             if (!f_state)
> > +                     continue;
> > +             x = get_unaligned_le16(&data[i * ts->obj.tp_info_size +
> POS_X]);
> > +             y = get_unaligned_le16(&data[i * ts->obj.tp_info_size +
> POS_Y]);
> > +
> > +             input_report_abs(ts->input, ABS_MT_POSITION_X, x);
> > +             input_report_abs(ts->input, ABS_MT_POSITION_Y, y);
> > +             input_report_abs(ts->input, ABS_MT_PRESSURE, pressure);
> > +             input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR,
> > +                     max(wx, wy));
> > +             input_report_abs(ts->input, ABS_MT_TOUCH_MINOR,
> > +                     min(wx, wy));
> > +     }
> > +
> > +     input_mt_sync_frame(ts->input);
> > +     input_sync(ts->input);
> > +}
> > +
> > +static irqreturn_t raydium_i2c_irq(int irq, void *_dev)
> > +{
> > +     struct raydium_data *ts = _dev;
> > +
> > +     if (ts->boot_mode != RAYDIUM_TS_BLDR)
> > +             raydium_mt_event(ts);
> > +
> > +     return IRQ_HANDLED;
> > +}
> > +
> > +static ssize_t raydium_calibrate(struct device *dev,
> > +                                struct device_attribute *attr,
> > +                                const char *buf, size_t count)
> > +{
> > +     struct raydium_data *ts = dev_get_drvdata(dev);
> > +     struct i2c_client *client = ts->client;
> > +
> > +     static const u8 cal_cmd[] = {0x00, 0x01, 0x9E};
> > +     int error = 0;
> > +
> > +     error = raydium_i2c_wrt_object(client, (u8 *)cal_cmd,
> sizeof(cal_cmd),
> > +             CMD_BOOT_WAIT_READY);
> > +     if (error) {
> > +             dev_err(&client->dev, "send chk path cmd fail: %d\n",
> error);
> > +             return error;
> > +     }
> > +
> > +     return error;
> > +}
> > +
> > +static ssize_t write_update_fw(struct device *dev,
> > +                     struct device_attribute *attr,
> > +                     const char *buf, size_t count)
> > +{
> > +     struct raydium_data *ts = dev_get_drvdata(dev);
> > +     int error;
> > +
> > +     error = mutex_lock_interruptible(&ts->sysfs_mutex);
> > +     if (error)
> > +             return error;
> > +
> > +     error = raydium_i2c_fw_update(ts);
> > +     dev_dbg(dev, "firmware update result: %d\n", error);
> > +
> > +     mutex_unlock(&ts->sysfs_mutex);
> > +     return error ?: count;
> > +}
> > +
> > +static ssize_t raydium_bootmode_show(struct device *dev,
> > +             struct device_attribute *attr, char *buf)
> > +{
> > +     struct raydium_data *ts = dev_get_drvdata(dev);
> > +
> > +     return sprintf(buf, "%s\n", ts->boot_mode == RAYDIUM_TS_MAIN ?
> > +             "Normal" : "Recovery");
> > +}
> > +
> > +static ssize_t raydium_fw_ver_show(struct device *dev,
> > +                     struct device_attribute *attr, char *buf)
> > +{
> > +     struct raydium_data *ts = dev_get_drvdata(dev);
> > +
> > +     return sprintf(buf, "%d.%d\n", ts->info.main_ver,
> ts->info.sub_ver);
> > +}
> > +
> > +static ssize_t raydium_hw_ver_show(struct device *dev,
> > +                     struct device_attribute *attr, char *buf)
> > +{
> > +     struct raydium_data *ts = dev_get_drvdata(dev);
> > +
> > +     return sprintf(buf, "0x%04x\n", ts->info.hw_ver);
>
> "%#04x\n"
>
> > +}
> > +
> > +static DEVICE_ATTR(fw_version, S_IRUGO, raydium_fw_ver_show, NULL);
> > +static DEVICE_ATTR(hw_version, S_IRUGO, raydium_hw_ver_show, NULL);
> > +static DEVICE_ATTR(boot_mode, S_IRUGO, raydium_bootmode_show, NULL);
> > +static DEVICE_ATTR(update_fw, S_IWUSR, NULL, write_update_fw);
> > +static DEVICE_ATTR(calibrate, S_IWUSR, NULL, raydium_calibrate);
> > +
> > +static struct attribute *raydium_attributes[] = {
> > +     &dev_attr_update_fw.attr,
> > +     &dev_attr_boot_mode.attr,
> > +     &dev_attr_fw_version.attr,
> > +     &dev_attr_hw_version.attr,
> > +     &dev_attr_calibrate.attr,
> > +     NULL
> > +};
> > +
> > +static struct attribute_group raydium_attribute_group = {
> > +     .attrs = raydium_attributes,
> > +};
> > +
> > +static void raydium_i2c_remove_sysfs_group(void *_data)
> > +{
> > +     struct raydium_data *ts = _data;
> > +
> > +     sysfs_remove_group(&ts->client->dev.kobj,
> &raydium_attribute_group);
> > +}
> > +
> > +static int raydium_i2c_power_on(struct raydium_data *ts)
> > +{
> > +     int error;
> > +
> > +     if (IS_ERR_OR_NULL(ts->reset_gpio))
> > +             return 0;
> > +
> > +     gpiod_set_value_cansleep(ts->reset_gpio, 1);
> > +
> > +     error = regulator_enable(ts->avdd);
> > +     if (error) {
> > +             dev_err(&ts->client->dev,
> > +                     "failed to enable avdd regulator: %d\n", error);
> > +             goto release_reset_gpio;
> > +     }
> > +
> > +     error = regulator_enable(ts->vccio);
> > +     if (error) {
> > +             regulator_disable(ts->avdd);
> > +             dev_err(&ts->client->dev,
> > +                     "failed to enable vccio regulator: %d\n", error);
> > +             goto release_reset_gpio;
> > +     }
> > +
> > +     udelay(RAYDIUM_POWERON_DELAY_USEC);
> > +
> > +release_reset_gpio:
> > +     gpiod_set_value_cansleep(ts->reset_gpio, 0);
> > +
> > +     if (error)
> > +             return error;
> > +
> > +     msleep(RAYDIUM_RESET_DELAY_MSEC);
> > +
> > +     return 0;
> > +}
> > +
> > +static void raydium_i2c_power_off(void *_data)
> > +{
> > +     struct raydium_data *ts = _data;
> > +
> > +     if (!IS_ERR_OR_NULL(ts->reset_gpio)) {
> > +             gpiod_set_value_cansleep(ts->reset_gpio, 1);
> > +             regulator_disable(ts->vccio);
> > +             regulator_disable(ts->avdd);
> > +     }
> > +}
> > +
> > +static int raydium_i2c_probe(struct i2c_client *client,
> > +                         const struct i2c_device_id *id)
> > +{
> > +     union i2c_smbus_data dummy;
> > +     struct raydium_data *ts;
> > +     unsigned long irqflags;
> > +     int error;
> > +
> > +     if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
> > +             dev_err(&client->dev,
> > +                     "%s: i2c check functionality error\n",
> DEVICE_NAME);
> > +             return -ENXIO;
> > +     }
> > +
> > +     ts = devm_kzalloc(&client->dev, sizeof(struct raydium_data),
> > +             GFP_KERNEL);
> > +     if (!ts)
> > +             return -ENOMEM;
> > +
> > +     mutex_init(&ts->sysfs_mutex);
> > +     init_completion(&ts->cmd_done);
> > +
> > +     ts->client = client;
> > +     i2c_set_clientdata(client, ts);
> > +
> > +     ts->avdd = devm_regulator_get(&client->dev, "avdd");
> > +     if (IS_ERR(ts->avdd)) {
> > +             error = PTR_ERR(ts->avdd);
> > +             if (error != -EPROBE_DEFER)
> > +                     dev_err(&client->dev,
> > +                             "Failed to get 'avdd' regulator: %d\n",
> error);
> > +             return error;
> > +     }
> > +
> > +     ts->vccio = devm_regulator_get(&client->dev, "vccio");
> > +     if (IS_ERR(ts->vccio)) {
> > +             error = PTR_ERR(ts->vccio);
> > +             if (error != -EPROBE_DEFER)
> > +                     dev_err(&client->dev,
> > +                             "Failed to get 'vccio' regulator: %d\n",
> error);
> > +             return error;
> > +     }
> > +
> > +     ts->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
> > +             GPIOD_OUT_LOW);
> > +     if (IS_ERR(ts->reset_gpio)) {
> > +             error = PTR_ERR(ts->reset_gpio);
> > +             if (error != -EPROBE_DEFER) {
> > +                     dev_err(&client->dev,
> > +                             "failed to get reset gpio: %d\n", error);
> > +                     return error;
> > +             }
> > +     }
> > +
> > +     error = raydium_i2c_power_on(ts);
> > +     if (error)
> > +             return error;
> > +
> > +     error = devm_add_action(&client->dev, raydium_i2c_power_off, ts);
> > +     if (error) {
> > +             dev_err(&client->dev,
> > +                     "failed to install power off action: %d\n", error);
> > +             raydium_i2c_power_off(ts);
> > +             return error;
> > +     }
> > +
> > +     /* Make sure there is something at this address */
> > +     if (i2c_smbus_xfer(client->adapter, client->addr, 0,
> > +                     I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0) {
> > +             dev_err(&client->dev, "nothing at this address\n");
> > +             return -ENXIO;
> > +     }
> > +
> > +     error = raydium_i2c_initialize(ts);
> > +     if (error) {
> > +             dev_err(&client->dev, "failed to initialize: %d\n", error);
> > +             return error;
> > +     }
> > +
> > +     ts->input = devm_input_allocate_device(&client->dev);
> > +     if (!ts->input) {
> > +             dev_err(&client->dev, "Failed to allocate input device\n");
> > +             return -ENOMEM;
> > +     }
> > +
> > +     ts->input->name = "Raydium Touchscreen";
> > +     ts->input->id.bustype = BUS_I2C;
> > +
> > +     __set_bit(BTN_TOUCH, ts->input->keybit);
> > +     __set_bit(EV_ABS, ts->input->evbit);
> > +     __set_bit(EV_KEY, ts->input->evbit);
>
> These 3 __set_bits() are not needed, the calls below will take care of
> setting needed bits.
>
> > +
> > +     /* Multitouch input params setup */
> > +     input_set_abs_params(ts->input, ABS_MT_POSITION_X, 0,
> > +             ts->info.x_max, 0, 0);
> > +     input_set_abs_params(ts->input, ABS_MT_POSITION_Y,
> > +             0, ts->info.y_max, 0, 0);
> > +     input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
> > +     input_set_abs_params(ts->input, ABS_MT_PRESSURE, 0, 255, 0, 0);
> > +     input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->info.x_res);
> > +     input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->info.y_res);
> > +
> > +     input_set_drvdata(ts->input, ts);
> > +
> > +     error = input_mt_init_slots(ts->input, MAX_TOUCH_NUM,
> > +             INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
> > +     if (error) {
> > +             dev_err(&client->dev,
> > +                     "failed to initialize MT slots: %d\n", error);
> > +             return error;
> > +     }
> > +
> > +     error = input_register_device(ts->input);
> > +     if (error) {
> > +             dev_err(&client->dev,
> > +                     "unable to register input device: %d\n", error);
> > +             return error;
> > +     }
> > +
> > +     error = devm_request_threaded_irq(&client->dev, client->irq,
> > +                     NULL, raydium_i2c_irq, irqflags | IRQF_ONESHOT,
> > +                     client->name, ts);
> > +     if (error) {
> > +             dev_err(&client->dev, "Failed to register interrupt\n");
> > +             return error;
> > +     }
> > +
> > +     device_init_wakeup(&client->dev, true);
>
> Please remove and rely on I2C core to enable wakeup for the device.
> I know that is does not do that for ACPI-backed devices yet, but that is
> something that we need to change there, not in the driver.
>
> > +
> > +     error = sysfs_create_group(&client->dev.kobj,
> &raydium_attribute_group);
> > +     if (error) {
> > +             dev_err(&client->dev, "failed to create sysfs attributes:
> %d\n",
> > +                     error);
> > +             return error;
> > +     }
> > +
> > +     error = devm_add_action(&client->dev,
> > +                             raydium_i2c_remove_sysfs_group, ts);
> > +     if (error) {
> > +             raydium_i2c_remove_sysfs_group(ts);
> > +             dev_err(&client->dev,
> > +                     "Failed to add sysfs cleanup action: %d\n", error);
> > +             return error;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int raydium_i2c_remove(struct i2c_client *client)
> > +{
> > +     struct raydium_data *ts = i2c_get_clientdata(client);
> > +
> > +     input_unregister_device(ts->input);
>
> This call is not needed.
>
> > +
> > +     device_init_wakeup(&client->dev, false);
>
> If we rely on platform/i2c core to set up wakeup flag we do not need to
> reset it here.
>
> > +
> > +     mutex_destroy(&ts->sysfs_mutex);
>
> This is not needed. Because nothing in raydium_i2c_remove() is needed
> you can remove to altogether.
>
> > +
> > +     return 0;
> > +}
> > +
> > +static void __maybe_unused raydium_enter_sleep(struct i2c_client
> *client)
> > +{
> > +     static const u8 sleep_cmd[] = { 0x5A, 0xff, 0x00, 0x0f };
> > +     int error;
> > +
> > +     error = raydium_i2c_send(client, CMD_ENTER_SLEEP, (u8 *)sleep_cmd,
> > +             sizeof(sleep_cmd));
> > +     if (error)
> > +             dev_err(&client->dev,
> > +                     "Send sleep failed: %d\n", error);
> > +}
> > +
> > +static int __maybe_unused raydium_i2c_suspend(struct device *dev)
> > +{
> > +     struct i2c_client *client = to_i2c_client(dev);
> > +     struct raydium_data *ts = i2c_get_clientdata(client);
> > +
> > +     /* Command not support in BLDR recovery mode */
> > +     if (ts->boot_mode != RAYDIUM_TS_MAIN)
> > +             return -EBUSY;
> > +
> > +     disable_irq(client->irq);
> > +
> > +     if (device_may_wakeup(dev)) {
> > +             raydium_enter_sleep(client);
> > +
> > +             ts->wake_irq_enabled = (enable_irq_wake(client->irq) == 0);
> > +     } else {
> > +             raydium_i2c_power_off(ts);
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int __maybe_unused raydium_i2c_resume(struct device *dev)
> > +{
> > +     struct i2c_client *client = to_i2c_client(dev);
> > +     struct raydium_data *ts = i2c_get_clientdata(client);
> > +
> > +     if (device_may_wakeup(dev)) {
> > +             if (ts->wake_irq_enabled)
> > +                     disable_irq_wake(client->irq);
> > +             raydium_i2c_sw_reset(client);
> > +     } else {
> > +             raydium_i2c_power_on(ts);
> > +             raydium_i2c_initialize(ts);
> > +     }
> > +
> > +     enable_irq(client->irq);
> > +
> > +     return 0;
> > +}
> > +
> > +static SIMPLE_DEV_PM_OPS(raydium_i2c_pm_ops,
> > +                      raydium_i2c_suspend, raydium_i2c_resume);
> > +
> > +static const struct i2c_device_id raydium_i2c_id[] = {
> > +     { DEVICE_NAME, 0 },
> > +     { }
> > +};
> > +MODULE_DEVICE_TABLE(i2c, raydium_i2c_id);
> > +
> > +#ifdef CONFIG_ACPI
> > +static const struct acpi_device_id raydium_acpi_id[] = {
> > +     { "RAYD0001", 0 },
> > +     { }
> > +};
> > +MODULE_DEVICE_TABLE(acpi, raydium_acpi_id);
> > +#endif
> > +
> > +#ifdef CONFIG_OF
> > +static const struct of_device_id raydium_of_match[] = {
> > +     { .compatible = "raydium,rm32380",},
> > +     { /* sentinel */ }
> > +};
> > +MODULE_DEVICE_TABLE(of, raydium_of_match);
> > +#endif
> > +
> > +static struct i2c_driver raydium_i2c_driver = {
> > +     .probe = raydium_i2c_probe,
> > +     .remove = raydium_i2c_remove,
> > +     .id_table = raydium_i2c_id,
> > +     .driver = {
> > +             .name = "raydium_ts",
> > +             .pm = &raydium_i2c_pm_ops,
> > +             .acpi_match_table = ACPI_PTR(raydium_acpi_id),
> > +             .of_match_table = of_match_ptr(raydium_of_match),
> > +     },
> > +};
> > +
> > +module_i2c_driver(raydium_i2c_driver);
> > +
> > +MODULE_AUTHOR("Raydium");
> > +MODULE_DESCRIPTION("Raydium I2c Touchscreen driver");
> > +MODULE_LICENSE("GPL v2");
> > --
> > 2.1.2
> >
>
> Thanks.
>
> --
> Dmitry
>

[-- Attachment #2: Type: text/html, Size: 55066 bytes --]

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
       [not found] ` <1461319268-362-1-git-send-email-dan.huang-s3Ivl27awEzQT0dZR+AlfA@public.gmane.org>
@ 2016-04-22 22:50   ` Dmitry Torokhov
  2016-04-25  2:48     ` yajohn lin
  0 siblings, 1 reply; 42+ messages in thread
From: Dmitry Torokhov @ 2016-04-22 22:50 UTC (permalink / raw)
  To: dan.huang
  Cc: rydberg-Hk7bIW8heu4wFerOooGFRg,
	grant.likely-QSEj5FYQhm4dnm+yROfE0A,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, jeesw-iQTvn4YitUrQT0dZR+AlfA,
	bleung-F7+t8E8rja9g9hUCZPvPmw,
	jeffrey.lin-s3Ivl27awEzQT0dZR+AlfA,
	roger.yang-s3Ivl27awEzQT0dZR+AlfA, KP.li-s3Ivl27awEzQT0dZR+AlfA,
	albert.shieh-s3Ivl27awEzQT0dZR+AlfA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA

Hi Dan,

On Fri, Apr 22, 2016 at 06:01:08PM +0800, dan.huang wrote:
> From: "jeffrey.lin" <jeffrey.lin-s3Ivl27awEzQT0dZR+AlfA@public.gmane.org>
> 
> Raydium I2C touch driver.
> 
> Signed-off-by: jeffrey.lin <jeffrey.lin-s3Ivl27awEzQT0dZR+AlfA@public.gmane.org>

When you are sending a patch over you need to add your sign-off to it.

Also I am still getting compile errors/warnings:

  CC [M]  drivers/input/touchscreen/raydium_i2c_ts.o
drivers/input/touchscreen/raydium_i2c_ts.c: In function
‘raydium_i2c_probe’:
drivers/input/touchscreen/raydium_i2c_ts.c:1036:35: warning: ‘irqflags’
may be used uninitialized in this function [-Wmaybe-uninitialized]
  error = devm_request_threaded_irq(&client->dev, client->irq,

Also please mention what is changed between previous and new version so
I do not need to hunt for surprises.

> +/*I2C command */
> +#define CMD_BOOT_WRT		0x11
> +#define CMD_BOOT_ACK		0x22
> +#define CMD_BOOT_CHK		0x33
> +#define CMD_BOOT_READ		0x44
> +#define CMD_BOOT_WAIT_READY	0x1A
> +#define CMD_BOOT_PATH_READY	0x1B

I see that in this version the command codes are completely redone. Why
is this? I think there is general confusion as to the encoding of the
"header" for the command message little endian vs big endian) and
position of the register in the header.

Would you mind spelling out the format of command message, please?

> +#define BOOT_RDY		0xFF
> +#define CMD_QUERY_BANK		0x2B
> +#define CMD_DATA_BANK		0x4D
> +#define CMD_ENTER_SLEEP		0x4E
> +#define CMD_BANK_SWITCH		0xAA
> +
> +/* Touch relative info */
> +#define MAX_RETRIES		3
> +#define MAX_FW_UPDATE_RETRIES	30
> +#define MAX_TOUCH_NUM		10
> +#define MAX_PACKET_SIZE		32
> +#define BOOT_DELAY_MS	100
> +
> +#define RAYDIUM_FW_PAGESIZE		128
> +#define RAYDIUM_POWERON_DELAY_USEC	500
> +#define RAYDIUM_RESET_DELAY_MSEC	50
> +
> +#define ADDR_INDEX		0x03
> +#define DATA_INDEX		0x04
> +
> +#define HEADER_SIZE		4
> +
> +enum raydium_boot_mode {
> +	RAYDIUM_TS_MAIN = 0,
> +	RAYDIUM_TS_BLDR,
> +};
> +
> +enum raydium_abs_idx {
> +	POS_STATE = 0,/*1:touch, 0:no touch*/
> +	POS_X,
> +	POS_Y = 3,
> +	POS_PRESSURE,
> +	WIDTH_X,
> +	WIDTH_Y,
> +};
> +
> +struct raydium_info {
> +	u32 hw_ver;	/*device ver, __le32*/
> +	u8 main_ver;
> +	u8 sub_ver;
> +	u16 ft_ver;	/*test ver, __le16*/
> +	u8 x_num;
> +	u8 y_num;
> +	u16 x_max;	/*disp reso, __le16*/
> +	u16 y_max;	/*disp reso, __le16*/
> +	u8 x_res;		/* units/mm */
> +	u8 y_res;		/* units/mm */
> +};
> +
> +struct raydium_object {
> +	u32 data_bank_addr;
> +	u8 pkg_size;
> +	u8 tp_info_size;
> +};
> +
> +/* struct raydium_data - represents state of Raydium touchscreen device */
> +struct raydium_data {
> +	struct i2c_client *client;
> +	struct input_dev *input;
> +
> +	struct regulator *avdd;
> +	struct regulator *vccio;
> +	struct gpio_desc *reset_gpio;
> +
> +	u32 query_bank_info;
> +
> +	struct raydium_info info;
> +	struct raydium_object obj;
> +	enum raydium_boot_mode boot_mode;
> +
> +	struct mutex sysfs_mutex;
> +	struct completion cmd_done;
> +
> +	bool wake_irq_enabled;
> +};
> +
> +static int raydium_i2c_send(struct i2c_client *client,
> +	u8 addr, u8 *data, size_t len)
> +{
> +	u8 buf[MAX_PACKET_SIZE + 1], idx_i;
> +	u16 pkg_len, use_len;
> +	int tries = 0;
> +
> +	buf[0] = addr;
> +	use_len = len;
> +	use_len = 0;
> +
> +	while (use_len) {
> +		pkg_len = (use_len < MAX_PACKET_SIZE) ?
> +			use_len : MAX_PACKET_SIZE;
> +		memcpy(&buf[1], data + idx_i*MAX_PACKET_SIZE, pkg_len);
> +
> +		tries = 0;
> +		do {
> +			if (i2c_master_send(client, buf, pkg_len + 1)
> +				== (pkg_len + 1))
> +				break;
> +			msleep(20);
> +		} while (++tries < MAX_RETRIES);
> +		idx_i++;
> +		use_len = (use_len < MAX_PACKET_SIZE) ?
> +			0 : (use_len - MAX_PACKET_SIZE);
> +	}
> +
> +	dev_err(&client->dev, "%s: i2c send failed\n", __func__);
> +
> +	return -EIO;
> +}
> +
> +static int raydium_i2c_read(struct i2c_client *client,
> +	u8 addr, size_t len, void *data)
> +{
> +	struct i2c_msg xfer[2];
> +	int ret;
> +
> +	/* Write register */
> +	xfer[0].addr = client->addr;
> +	xfer[0].flags = 0;
> +	xfer[0].len = 1;
> +	xfer[0].buf = &addr;
> +
> +	/* Read data */
> +	xfer[1].addr = client->addr;
> +	xfer[1].flags = I2C_M_RD;
> +	xfer[1].len = len;
> +	xfer[1].buf = data;
> +
> +	ret = i2c_transfer(client->adapter, xfer, ARRAY_SIZE(xfer));
> +	if (ret < 0)
> +		return ret;
> +
> +	if (ret != ARRAY_SIZE(xfer))
> +		return -EIO;
> +
> +	return 0;
> +}
> +
> +static int raydium_i2c_read_message(struct i2c_client *client,
> +	u32 addr, size_t len, void *data)
> +{
> +	u16 pkg_size, use_len;
> +	u8 buf[HEADER_SIZE], idx_i;
> +	int error;
> +
> +	use_len = len;
> +	idx_i = 0;
> +	while (use_len > 0) {
> +		pkg_size = (use_len < MAX_PACKET_SIZE) ?
> +			use_len : MAX_PACKET_SIZE;
> +
> +		put_unaligned_be32(addr, buf);
> +
> +		/*set data bank*/
> +		error = raydium_i2c_send(client, CMD_BANK_SWITCH,
> +			(u8 *)buf, HEADER_SIZE);
> +		/*read potints data*/
> +		if (!error)
> +			error = raydium_i2c_read(client, buf[ADDR_INDEX],
> +				pkg_size,
> +				data + idx_i*MAX_PACKET_SIZE);
> +
> +		pkg_size += MAX_PACKET_SIZE;
> +		addr += MAX_PACKET_SIZE;
> +		use_len = (use_len < MAX_PACKET_SIZE) ?
> +			0 : (use_len - MAX_PACKET_SIZE);
> +		idx_i++;
> +	}
> +
> +	return error;
> +}
> +
> +static int raydium_i2c_send_message(struct i2c_client *client,
> +	size_t len, void *data)
> +{
> +	int error;
> +	__le32 cmd;
> +
> +	cmd = get_unaligned_le32(data);
> +
> +	/*set data bank*/
> +	error = raydium_i2c_send(client, CMD_BANK_SWITCH, (u8 *)&cmd,
> +		HEADER_SIZE);
> +
> +	/*send message*/
> +	if (!error)
> +		error = raydium_i2c_send(client, ((u8 *)data)[ADDR_INDEX],
> +			data + DATA_INDEX, len);
> +
> +	return error;
> +}
> +
> +static int raydium_i2c_sw_reset(struct i2c_client *client)
> +{
> +	static const u8 soft_rst_cmd[] = {0x40, 0x00, 0x00, 0x04, 0x01};
> +	int error;
> +
> +	error = raydium_i2c_send_message(client, ARRAY_SIZE(soft_rst_cmd),
> +		(void *)soft_rst_cmd);
> +	if (error) {
> +		dev_err(&client->dev, "software reset failed: %d\n", error);
> +		return error;
> +	}
> +
> +	msleep(RAYDIUM_RESET_DELAY_MSEC);
> +
> +	return 0;
> +}
> +
> +static int raydium_i2c_query_ts_info(struct raydium_data *ts)
> +{
> +	struct i2c_client *client = ts->client;
> +	int error, retry_cnt;
> +
> +	for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
> +		error = raydium_i2c_read(client, CMD_DATA_BANK,
> +			sizeof(ts->obj), (void *)&ts->obj);
> +			ts->obj.data_bank_addr =
> +				get_unaligned_le32(&ts->obj.data_bank_addr);
> +
> +		if (!error) {
> +			error = raydium_i2c_read(client, CMD_QUERY_BANK,
> +				sizeof(ts->query_bank_info),
> +				(void *)&ts->query_bank_info);
> +			if (!error) {
> +				error = raydium_i2c_read_message(client,
> +					ts->query_bank_info, sizeof(ts->info),
> +					(void *)&ts->info);
> +
> +				ts->info.hw_ver =
> +					get_unaligned_le32(&ts->info.hw_ver);
> +				ts->info.ft_ver =
> +					get_unaligned_le16(&ts->info.ft_ver);
> +				ts->info.x_max =
> +					get_unaligned_le16(&ts->info.x_max);
> +				ts->info.y_max =
> +					get_unaligned_le16(&ts->info.y_max);
> +				return 0;
> +			}
> +		}
> +	}
> +	dev_err(&client->dev, "Get touch data failed: %d\n", error);
> +
> +	return -EINVAL;
> +}
> +
> +static int raydium_i2c_fastboot(struct i2c_client *client)
> +{
> +	static const u8 boot_cmd[] = { 0x50, 0x00, 0x06, 0x20 };
> +	u8 buf[HEADER_SIZE];
> +	int error;
> +
> +	error = raydium_i2c_read_message(client,
> +		get_unaligned_be32(boot_cmd),
> +		sizeof(boot_cmd), buf);
> +
> +	if (!error) {
> +		if (buf[0] == RM_BOOT_BLDR) {
> +			dev_dbg(&client->dev, "boot in fastboot mode\n");
> +			return -EINVAL;
> +		}
> +		dev_dbg(&client->dev, "boot success -- 0x%x\n", client->addr);
> +		return 0;
> +	}
> +
> +	dev_err(&client->dev, "boot failed: %d\n", error);
> +
> +	return error;
> +}
> +
> +static int raydium_i2c_check_fw_status(struct raydium_data *ts)
> +{
> +	struct i2c_client *client = ts->client;
> +	static const u8 bl_area[] = {0x62, 0x6f, 0x6f, 0x74};
> +	static const u8 main_area[] = {0x66, 0x69, 0x72, 0x6d};
> +	u8 buf[HEADER_SIZE];
> +	int error;
> +
> +	error = raydium_i2c_read(client, CMD_BOOT_READ, HEADER_SIZE,
> +		(void *)buf);
> +	if (!error) {
> +		if (memcmp(buf, bl_area, sizeof(bl_area)))
> +			ts->boot_mode = RAYDIUM_TS_BLDR;
> +		else if (memcmp(buf, main_area, sizeof(main_area)))
> +			ts->boot_mode = RAYDIUM_TS_MAIN;
> +		else
> +			return -EINVAL;
> +
> +		return 0;
> +	}
> +
> +	dev_err(&client->dev, "check bl status failed: %d\n", error);
> +
> +	return error;
> +}
> +
> +static int raydium_i2c_initialize(struct raydium_data *ts)
> +{
> +	struct i2c_client *client = ts->client;
> +	int error, retry_cnt;
> +
> +	for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
> +		error = raydium_i2c_fastboot(client);
> +		if (error) {
> +			/* Continue initializing if it's the last try */
> +			if (retry_cnt < MAX_RETRIES - 1)
> +				continue;
> +		}
> +		/* Wait for Hello packet */
> +		msleep(BOOT_DELAY_MS);
> +
> +		error = raydium_i2c_check_fw_status(ts);
> +		if (error) {
> +			dev_err(&client->dev,
> +				"failed to read 'hello' packet: %d\n", error);
> +		}
> +	}
> +
> +	if (error)
> +		ts->boot_mode = RAYDIUM_TS_BLDR;
> +	else
> +		raydium_i2c_query_ts_info(ts);
> +
> +	return error;
> +}
> +
> +static int raydium_i2c_recv(struct i2c_client *client, u8 *buf, size_t count)
> +{
> +	int error = 0;
> +
> +	error = i2c_master_recv(client, buf, count);
> +
> +	if (error == count) {
> +		error = 0;
> +	} else if (error != count) {
> +		error = (error < 0) ? error : -EIO;
> +		dev_err(&client->dev, "i2c recv failed (%d)\n", error);
> +	}
> +
> +	return error;
> +}
> +
> +static int raydium_i2c_bl_chk_state(struct i2c_client *client, u8 state)
> +{
> +	static const u8 ack_ok[] = { 0xFF, 0x39, 0x30, 0x30, 0x54 };
> +	u8 rbuf[5];
> +	u8 retry;
> +	int error;
> +
> +	for (retry = 0; retry < MAX_FW_UPDATE_RETRIES; retry++) {
> +		if (state == CMD_BOOT_WAIT_READY) {
> +			error = raydium_i2c_recv(client, &rbuf[0], 1);
> +			if (!error) {
> +				if (rbuf[0] == BOOT_RDY)
> +					return 0;
> +			}
> +		} else if (state == CMD_BOOT_WAIT_READY) {

The condition on branches is the same (CMD_BOOT_WAIT_READY).

> +			error = raydium_i2c_recv(client, &rbuf[0],
> +				sizeof(ack_ok));
> +			if (!error) {
> +				if (memcmp(rbuf, ack_ok, sizeof(ack_ok)))
> +					return 0;
> +			}
> +		} else
> +			return -EINVAL;
> +
> +		msleep(20);
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static int raydium_i2c_wrt_object(struct i2c_client *client,
> +	u8 *data, size_t len, u8 state)
> +{
> +	int error = 0;
> +
> +	error = raydium_i2c_send(client, CMD_BOOT_WRT, data, len);
> +	if (error) {
> +		dev_err(&client->dev, "WRT obj command failed: %d\n", error);
> +		return error;
> +	}
> +
> +	error = raydium_i2c_send(client, CMD_BOOT_ACK, (u8 *)NULL, 0);
> +	if (error) {
> +		dev_err(&client->dev, "Ack obj command failed: %d\n", error);
> +		return error;
> +	}

I am looking at raydium_i2c_send() implementation and it seemd to be a
no-op with len == 0, what gives?

> +
> +	error = raydium_i2c_send(client, CMD_BOOT_CHK, (u8 *)NULL, 0);
> +	if (error) {
> +		dev_err(&client->dev, "Boot chk failed: %d\n", error);
> +		return error;
> +	}

Same here.

> +
> +	error = raydium_i2c_bl_chk_state(client, state);
> +	if (error) {
> +		dev_err(&client->dev, "boot trigger state failed: %d\n", error);
> +		return error;
> +	}
> +	return 0;
> +}
> +
> +static bool raydium_i2c_boot_trigger(struct i2c_client *client)
> +{
> +	int error;
> +	u8 u8_idx;
> +	static const u8 cmd[7][6] = {
> +			{0x08, 0x0C, 0x09, 0x00, 0x50, 0xD7},
> +			{0x08, 0x04, 0x09, 0x00, 0x50, 0xA5},
> +			{0x08, 0x04, 0x09, 0x00, 0x50, 0x00},
> +			{0x08, 0x04, 0x09, 0x00, 0x50, 0xA5},
> +			{0x08, 0x0C, 0x09, 0x00, 0x50, 0x00},
> +			{0x06, 0x01},
> +			{0x02, 0xA2},
> +		};
> +
> +	for (u8_idx = 0 ; u8_idx < 7 ; u8_idx++) {
> +		error = raydium_i2c_wrt_object(client, (u8 *)cmd[u8_idx],
> +			sizeof(cmd[u8_idx]), CMD_BOOT_WAIT_READY);

If you expect that for last 2 elements the sizeof(cmd[u8_idx]) will
return 2 you are mistaken, they all will be 6.

> +		if (error) {
> +			dev_err(&client->dev, "send boot trigger cmd failed: %d\n",
> +				error);
> +			return error;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int raydium_i2c_check_path(struct i2c_client *client)
> +{
> +	static const u8 cmd[] = {0x09, 0x00, 0x09, 0x00, 0x50, 0x10, 0x00};
> +	int error;
> +
> +	error = raydium_i2c_wrt_object(client, (u8 *)cmd, sizeof(cmd),
> +		CMD_BOOT_PATH_READY);
> +	if (error) {
> +		dev_err(&client->dev, "send chk path cmd fail: %d\n", error);
> +		return error;
> +	}
> +
> +	return error;
> +}
> +
> +static int raydium_i2c_enter_bl(struct i2c_client *client)
> +{
> +	static const u8 cal_cmd[] = {0x00, 0x01, 0x52};
> +	int error;
> +
> +	error = raydium_i2c_wrt_object(client, (u8 *)cal_cmd,
> +		sizeof(cal_cmd), CMD_BOOT_WAIT_READY);
> +	if (error) {
> +		dev_err(&client->dev, "send jump loader cmd fail: %d\n", error);
> +		return error;
> +	}
> +	return 0;
> +}
> +
> +static int raydium_i2c_leave_bl(struct i2c_client *client)
> +{
> +	static const u8 leave_cmd[] = {0x05, 0x00};
> +	int error;
> +
> +	error = raydium_i2c_wrt_object(client, (u8 *)leave_cmd,
> +		sizeof(leave_cmd), CMD_BOOT_WAIT_READY);
> +	if (error) {
> +		dev_err(&client->dev, "send leave bl cmd fail: %d\n", error);
> +		return error;
> +	}
> +
> +	return 0;
> +}
> +
> +static int raydium_i2c_write_checksum(struct i2c_client *client,
> +	u16 length, u16 checksum)
> +{
> +	u8 checksum_cmd[] = {0x00, 0x05, 0x6D, 0x00, 0x00, 0x00, 0x00};
> +	int error = 0;
> +
> +	checksum_cmd[3] = (u8)(length & 0xFF);
> +	checksum_cmd[4] = (u8)((length & 0xFF00) >> 8);
> +	checksum_cmd[5] = (u8)(checksum & 0xFF);
> +	checksum_cmd[6] = (u8)((checksum & 0xFF00) >> 8);
> +
> +	error = raydium_i2c_wrt_object(client, (u8 *)checksum_cmd,
> +		sizeof(checksum_cmd), CMD_BOOT_WAIT_READY);
> +	if (error) {
> +		dev_err(&client->dev, "send wrt checksum cmd fail: %d\n",
> +			error);
> +		return error;
> +	}
> +
> +	return 0;
> +}
> +
> +static int raydium_i2c_disable_watch_dog(struct i2c_client *client)
> +{
> +	static const u8 cmd[] = {0x0A, 0xAA};
> +	int error = 0;
> +
> +	error = raydium_i2c_wrt_object(client, (u8 *)cmd, sizeof(cmd),
> +		CMD_BOOT_WAIT_READY);
> +	if (error) {
> +		dev_err(&client->dev, "send disable watchdog cmd fail: %d\n",
> +			error);
> +		return error;
> +	}
> +
> +	return 0;
> +}
> +
> +static int raydium_i2c_fw_write_page(struct i2c_client *client,
> +				const void *page, size_t len)
> +{
> +	int error;
> +
> +	error = raydium_i2c_wrt_object(client,  (u8 *)page, len,
> +		CMD_BOOT_WAIT_READY);
> +	if (error) {
> +		dev_err(&client->dev, "send page wrt cmd failed: %d\n", error);
> +		return error;
> +	}
> +
> +	return error;
> +}
> +
> +static int raydium_i2c_do_update_firmware(struct raydium_data *ts,
> +					 const struct firmware *fw)
> +{
> +	struct i2c_client *client = ts->client;
> +	u8 u8_idx;
> +	u16 fw_length;
> +	u16 fw_checksum;
> +	u8 buf[RAYDIUM_FW_PAGESIZE + 2];
> +	int page, n_fw_pages;
> +	int error;
> +
> +	fw_length = (int)(fw->size);
> +	if (fw_length == 0) {
> +		dev_err(&client->dev, "Invalid firmware length\n");
> +		return -EINVAL;
> +	}
> +
> +	if (fw_length % RAYDIUM_FW_PAGESIZE)
> +		n_fw_pages = fw_length/RAYDIUM_FW_PAGESIZE;
> +	else
> +		n_fw_pages = fw_length/RAYDIUM_FW_PAGESIZE + 1;
> +
> +	error = raydium_i2c_check_fw_status(ts);
> +	if (error) {
> +		dev_err(&client->dev, "Unable to access IC %d\n", error);
> +		return error;
> +	}
> +
> +	if (ts->boot_mode == RAYDIUM_TS_MAIN) {
> +		error = raydium_i2c_enter_bl(client);
> +		if (error) {
> +			dev_err(&client->dev, "Unable jump to boot loader %d\n",
> +				error);
> +			return error;
> +		}
> +	}
> +
> +	error = raydium_i2c_disable_watch_dog(client);
> +	if (error) {
> +		dev_err(&client->dev, "send disable watchdog cmd fail, %d\n",
> +			error);
> +		return error;
> +	}
> +
> +	error = raydium_i2c_check_path(client);
> +	if (error) {
> +		dev_err(&client->dev, "send chk path fail, %d\n", error);
> +		return error;
> +	}
> +
> +	error = raydium_i2c_boot_trigger(client);
> +	if (error) {
> +		dev_err(&client->dev, "send boot trigger fail, %d\n", error);
> +		return error;
> +	}
> +
> +	fw_checksum = 0;
> +	for (page = 0 ; page < n_fw_pages ; page++) {
> +		memset(buf, 0xFF, sizeof(buf));
> +		buf[0] = 0x03;
> +		if (page == 0)
> +			buf[1] = 0x00;
> +
> +		memcpy(&buf[2], fw->data + page * RAYDIUM_FW_PAGESIZE,
> +			RAYDIUM_FW_PAGESIZE);
> +		/*calculate chksum*/
> +		for (u8_idx = 0; u8_idx < RAYDIUM_FW_PAGESIZE; u8_idx++)
> +			fw_checksum += buf[2 + u8_idx];
> +
> +		error = raydium_i2c_fw_write_page(client, (const void *)buf,
> +			sizeof(buf));
> +		if (error) {
> +			dev_err(&client->dev, "flash page write fail, %d\n",
> +				error);
> +			return error;
> +		}
> +
> +		msleep(20);
> +	}
> +
> +	error = raydium_i2c_leave_bl(client);
> +	if (error) {
> +		dev_err(&client->dev, "leave boot loader fail: %d\n", error);
> +		return error;
> +	}
> +
> +	msleep(BOOT_DELAY_MS);
> +
> +	error = raydium_i2c_check_fw_status(ts);
> +	if (error) {
> +		dev_err(&client->dev, "Unable to access IC %d\n", error);
> +		return error;
> +	}
> +
> +	if (ts->boot_mode == RAYDIUM_TS_MAIN) {
> +		error = raydium_i2c_write_checksum(client, fw_length,
> +			fw_checksum);
> +		if (error) {
> +			dev_err(&client->dev, "write checksum fail %d\n",
> +				error);
> +			return error;
> +		}
> +	} else {
> +		dev_err(&client->dev, "switch to main_fw fail %d\n", error);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int raydium_i2c_fw_update(struct raydium_data *ts)
> +{
> +	struct i2c_client *client = ts->client;
> +	const struct firmware *fw = NULL;
> +	const char *fn = "raydium.fw";
> +	int error;
> +
> +	error = request_firmware(&fw, fn, &client->dev);
> +	if (error) {
> +		dev_err(&client->dev, "Unable to open firmware %s\n", fn);
> +		return error;
> +	}
> +	/*disable irq*/
> +	disable_irq(client->irq);
> +
> +	error = raydium_i2c_do_update_firmware(ts, fw);
> +	if (error) {
> +		dev_err(&client->dev, "firmware update failed: %d\n", error);
> +		ts->boot_mode = RAYDIUM_TS_BLDR;
> +		goto out_enable_irq;
> +	}
> +
> +	error = raydium_i2c_initialize(ts);
> +	if (error) {
> +		dev_err(&client->dev,
> +			"failed to initialize device after firmware update: %d\n",
> +			error);
> +		ts->boot_mode = RAYDIUM_TS_BLDR;
> +		goto out_enable_irq;
> +	}
> +
> +	ts->boot_mode = RAYDIUM_TS_MAIN;
> +
> +out_enable_irq:
> +	enable_irq(client->irq);
> +	msleep(100);
> +
> +	return error;
> +}
> +
> +static void raydium_mt_event(struct raydium_data *ts)
> +{
> +	u8 data[MAX_PACKET_SIZE];
> +	int error, i;
> +	int x, y, f_state, pressure, wx, wy;
> +
> +	error = raydium_i2c_read_message(ts->client, ts->obj.data_bank_addr,
> +		ts->obj.pkg_size, (void *)data);
> +
> +	if (error < 0) {
> +		dev_err(&ts->client->dev, "%s: failed to read data: %d\n",
> +			__func__, error);
> +		return;
> +	}
> +
> +	for (i = 0; i < MAX_TOUCH_NUM; i++) {
> +		f_state = (data + i * ts->obj.tp_info_size)[POS_STATE];
> +		pressure = (data + i * ts->obj.tp_info_size)[POS_PRESSURE];
> +		wx = (data + i * ts->obj.tp_info_size)[WIDTH_X];
> +		wy = (data + i * ts->obj.tp_info_size)[WIDTH_Y];
> +
> +		input_mt_slot(ts->input, i);
> +		input_mt_report_slot_state(ts->input,
> +				MT_TOOL_FINGER, f_state != 0);
> +
> +		if (!f_state)
> +			continue;
> +		x = get_unaligned_le16(&data[i * ts->obj.tp_info_size + POS_X]);
> +		y = get_unaligned_le16(&data[i * ts->obj.tp_info_size + POS_Y]);
> +
> +		input_report_abs(ts->input, ABS_MT_POSITION_X, x);
> +		input_report_abs(ts->input, ABS_MT_POSITION_Y, y);
> +		input_report_abs(ts->input, ABS_MT_PRESSURE, pressure);
> +		input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR,
> +			max(wx, wy));
> +		input_report_abs(ts->input, ABS_MT_TOUCH_MINOR,
> +			min(wx, wy));
> +	}
> +
> +	input_mt_sync_frame(ts->input);
> +	input_sync(ts->input);
> +}
> +
> +static irqreturn_t raydium_i2c_irq(int irq, void *_dev)
> +{
> +	struct raydium_data *ts = _dev;
> +
> +	if (ts->boot_mode != RAYDIUM_TS_BLDR)
> +		raydium_mt_event(ts);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static ssize_t raydium_calibrate(struct device *dev,
> +				   struct device_attribute *attr,
> +				   const char *buf, size_t count)
> +{
> +	struct raydium_data *ts = dev_get_drvdata(dev);
> +	struct i2c_client *client = ts->client;
> +
> +	static const u8 cal_cmd[] = {0x00, 0x01, 0x9E};
> +	int error = 0;
> +
> +	error = raydium_i2c_wrt_object(client, (u8 *)cal_cmd, sizeof(cal_cmd),
> +		CMD_BOOT_WAIT_READY);
> +	if (error) {
> +		dev_err(&client->dev, "send chk path cmd fail: %d\n", error);
> +		return error;
> +	}
> +
> +	return error;
> +}
> +
> +static ssize_t write_update_fw(struct device *dev,
> +			struct device_attribute *attr,
> +			const char *buf, size_t count)
> +{
> +	struct raydium_data *ts = dev_get_drvdata(dev);
> +	int error;
> +
> +	error = mutex_lock_interruptible(&ts->sysfs_mutex);
> +	if (error)
> +		return error;
> +
> +	error = raydium_i2c_fw_update(ts);
> +	dev_dbg(dev, "firmware update result: %d\n", error);
> +
> +	mutex_unlock(&ts->sysfs_mutex);
> +	return error ?: count;
> +}
> +
> +static ssize_t raydium_bootmode_show(struct device *dev,
> +		struct device_attribute *attr, char *buf)
> +{
> +	struct raydium_data *ts = dev_get_drvdata(dev);
> +
> +	return sprintf(buf, "%s\n", ts->boot_mode == RAYDIUM_TS_MAIN ?
> +		"Normal" : "Recovery");
> +}
> +
> +static ssize_t raydium_fw_ver_show(struct device *dev,
> +			struct device_attribute *attr, char *buf)
> +{
> +	struct raydium_data *ts = dev_get_drvdata(dev);
> +
> +	return sprintf(buf, "%d.%d\n", ts->info.main_ver, ts->info.sub_ver);
> +}
> +
> +static ssize_t raydium_hw_ver_show(struct device *dev,
> +			struct device_attribute *attr, char *buf)
> +{
> +	struct raydium_data *ts = dev_get_drvdata(dev);
> +
> +	return sprintf(buf, "0x%04x\n", ts->info.hw_ver);

"%#04x\n"

> +}
> +
> +static DEVICE_ATTR(fw_version, S_IRUGO, raydium_fw_ver_show, NULL);
> +static DEVICE_ATTR(hw_version, S_IRUGO, raydium_hw_ver_show, NULL);
> +static DEVICE_ATTR(boot_mode, S_IRUGO, raydium_bootmode_show, NULL);
> +static DEVICE_ATTR(update_fw, S_IWUSR, NULL, write_update_fw);
> +static DEVICE_ATTR(calibrate, S_IWUSR, NULL, raydium_calibrate);
> +
> +static struct attribute *raydium_attributes[] = {
> +	&dev_attr_update_fw.attr,
> +	&dev_attr_boot_mode.attr,
> +	&dev_attr_fw_version.attr,
> +	&dev_attr_hw_version.attr,
> +	&dev_attr_calibrate.attr,
> +	NULL
> +};
> +
> +static struct attribute_group raydium_attribute_group = {
> +	.attrs = raydium_attributes,
> +};
> +
> +static void raydium_i2c_remove_sysfs_group(void *_data)
> +{
> +	struct raydium_data *ts = _data;
> +
> +	sysfs_remove_group(&ts->client->dev.kobj, &raydium_attribute_group);
> +}
> +
> +static int raydium_i2c_power_on(struct raydium_data *ts)
> +{
> +	int error;
> +
> +	if (IS_ERR_OR_NULL(ts->reset_gpio))
> +		return 0;
> +
> +	gpiod_set_value_cansleep(ts->reset_gpio, 1);
> +
> +	error = regulator_enable(ts->avdd);
> +	if (error) {
> +		dev_err(&ts->client->dev,
> +			"failed to enable avdd regulator: %d\n", error);
> +		goto release_reset_gpio;
> +	}
> +
> +	error = regulator_enable(ts->vccio);
> +	if (error) {
> +		regulator_disable(ts->avdd);
> +		dev_err(&ts->client->dev,
> +			"failed to enable vccio regulator: %d\n", error);
> +		goto release_reset_gpio;
> +	}
> +
> +	udelay(RAYDIUM_POWERON_DELAY_USEC);
> +
> +release_reset_gpio:
> +	gpiod_set_value_cansleep(ts->reset_gpio, 0);
> +
> +	if (error)
> +		return error;
> +
> +	msleep(RAYDIUM_RESET_DELAY_MSEC);
> +
> +	return 0;
> +}
> +
> +static void raydium_i2c_power_off(void *_data)
> +{
> +	struct raydium_data *ts = _data;
> +
> +	if (!IS_ERR_OR_NULL(ts->reset_gpio)) {
> +		gpiod_set_value_cansleep(ts->reset_gpio, 1);
> +		regulator_disable(ts->vccio);
> +		regulator_disable(ts->avdd);
> +	}
> +}
> +
> +static int raydium_i2c_probe(struct i2c_client *client,
> +			    const struct i2c_device_id *id)
> +{
> +	union i2c_smbus_data dummy;
> +	struct raydium_data *ts;
> +	unsigned long irqflags;
> +	int error;
> +
> +	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
> +		dev_err(&client->dev,
> +			"%s: i2c check functionality error\n", DEVICE_NAME);
> +		return -ENXIO;
> +	}
> +
> +	ts = devm_kzalloc(&client->dev, sizeof(struct raydium_data),
> +		GFP_KERNEL);
> +	if (!ts)
> +		return -ENOMEM;
> +
> +	mutex_init(&ts->sysfs_mutex);
> +	init_completion(&ts->cmd_done);
> +
> +	ts->client = client;
> +	i2c_set_clientdata(client, ts);
> +
> +	ts->avdd = devm_regulator_get(&client->dev, "avdd");
> +	if (IS_ERR(ts->avdd)) {
> +		error = PTR_ERR(ts->avdd);
> +		if (error != -EPROBE_DEFER)
> +			dev_err(&client->dev,
> +				"Failed to get 'avdd' regulator: %d\n", error);
> +		return error;
> +	}
> +
> +	ts->vccio = devm_regulator_get(&client->dev, "vccio");
> +	if (IS_ERR(ts->vccio)) {
> +		error = PTR_ERR(ts->vccio);
> +		if (error != -EPROBE_DEFER)
> +			dev_err(&client->dev,
> +				"Failed to get 'vccio' regulator: %d\n", error);
> +		return error;
> +	}
> +
> +	ts->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
> +		GPIOD_OUT_LOW);
> +	if (IS_ERR(ts->reset_gpio)) {
> +		error = PTR_ERR(ts->reset_gpio);
> +		if (error != -EPROBE_DEFER) {
> +			dev_err(&client->dev,
> +				"failed to get reset gpio: %d\n", error);
> +			return error;
> +		}
> +	}
> +
> +	error = raydium_i2c_power_on(ts);
> +	if (error)
> +		return error;
> +
> +	error = devm_add_action(&client->dev, raydium_i2c_power_off, ts);
> +	if (error) {
> +		dev_err(&client->dev,
> +			"failed to install power off action: %d\n", error);
> +		raydium_i2c_power_off(ts);
> +		return error;
> +	}
> +
> +	/* Make sure there is something at this address */
> +	if (i2c_smbus_xfer(client->adapter, client->addr, 0,
> +			I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0) {
> +		dev_err(&client->dev, "nothing at this address\n");
> +		return -ENXIO;
> +	}
> +
> +	error = raydium_i2c_initialize(ts);
> +	if (error) {
> +		dev_err(&client->dev, "failed to initialize: %d\n", error);
> +		return error;
> +	}
> +
> +	ts->input = devm_input_allocate_device(&client->dev);
> +	if (!ts->input) {
> +		dev_err(&client->dev, "Failed to allocate input device\n");
> +		return -ENOMEM;
> +	}
> +
> +	ts->input->name = "Raydium Touchscreen";
> +	ts->input->id.bustype = BUS_I2C;
> +
> +	__set_bit(BTN_TOUCH, ts->input->keybit);
> +	__set_bit(EV_ABS, ts->input->evbit);
> +	__set_bit(EV_KEY, ts->input->evbit);

These 3 __set_bits() are not needed, the calls below will take care of
setting needed bits.

> +
> +	/* Multitouch input params setup */
> +	input_set_abs_params(ts->input, ABS_MT_POSITION_X, 0,
> +		ts->info.x_max, 0, 0);
> +	input_set_abs_params(ts->input, ABS_MT_POSITION_Y,
> +		0, ts->info.y_max, 0, 0);
> +	input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
> +	input_set_abs_params(ts->input, ABS_MT_PRESSURE, 0, 255, 0, 0);
> +	input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->info.x_res);
> +	input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->info.y_res);
> +
> +	input_set_drvdata(ts->input, ts);
> +
> +	error = input_mt_init_slots(ts->input, MAX_TOUCH_NUM,
> +		INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
> +	if (error) {
> +		dev_err(&client->dev,
> +			"failed to initialize MT slots: %d\n", error);
> +		return error;
> +	}
> +
> +	error = input_register_device(ts->input);
> +	if (error) {
> +		dev_err(&client->dev,
> +			"unable to register input device: %d\n", error);
> +		return error;
> +	}
> +
> +	error = devm_request_threaded_irq(&client->dev, client->irq,
> +			NULL, raydium_i2c_irq, irqflags | IRQF_ONESHOT,
> +			client->name, ts);
> +	if (error) {
> +		dev_err(&client->dev, "Failed to register interrupt\n");
> +		return error;
> +	}
> +
> +	device_init_wakeup(&client->dev, true);

Please remove and rely on I2C core to enable wakeup for the device.
I know that is does not do that for ACPI-backed devices yet, but that is
something that we need to change there, not in the driver.

> +
> +	error = sysfs_create_group(&client->dev.kobj, &raydium_attribute_group);
> +	if (error) {
> +		dev_err(&client->dev, "failed to create sysfs attributes: %d\n",
> +			error);
> +		return error;
> +	}
> +
> +	error = devm_add_action(&client->dev,
> +				raydium_i2c_remove_sysfs_group, ts);
> +	if (error) {
> +		raydium_i2c_remove_sysfs_group(ts);
> +		dev_err(&client->dev,
> +			"Failed to add sysfs cleanup action: %d\n", error);
> +		return error;
> +	}
> +
> +	return 0;
> +}
> +
> +static int raydium_i2c_remove(struct i2c_client *client)
> +{
> +	struct raydium_data *ts = i2c_get_clientdata(client);
> +
> +	input_unregister_device(ts->input);

This call is not needed.

> +
> +	device_init_wakeup(&client->dev, false);

If we rely on platform/i2c core to set up wakeup flag we do not need to
reset it here.

> +
> +	mutex_destroy(&ts->sysfs_mutex);

This is not needed. Because nothing in raydium_i2c_remove() is needed
you can remove to altogether.

> +
> +	return 0;
> +}
> +
> +static void __maybe_unused raydium_enter_sleep(struct i2c_client *client)
> +{
> +	static const u8 sleep_cmd[] = { 0x5A, 0xff, 0x00, 0x0f };
> +	int error;
> +
> +	error = raydium_i2c_send(client, CMD_ENTER_SLEEP, (u8 *)sleep_cmd,
> +		sizeof(sleep_cmd));
> +	if (error)
> +		dev_err(&client->dev,
> +			"Send sleep failed: %d\n", error);
> +}
> +
> +static int __maybe_unused raydium_i2c_suspend(struct device *dev)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +	struct raydium_data *ts = i2c_get_clientdata(client);
> +
> +	/* Command not support in BLDR recovery mode */
> +	if (ts->boot_mode != RAYDIUM_TS_MAIN)
> +		return -EBUSY;
> +
> +	disable_irq(client->irq);
> +
> +	if (device_may_wakeup(dev)) {
> +		raydium_enter_sleep(client);
> +
> +		ts->wake_irq_enabled = (enable_irq_wake(client->irq) == 0);
> +	} else {
> +		raydium_i2c_power_off(ts);
> +	}
> +
> +	return 0;
> +}
> +
> +static int __maybe_unused raydium_i2c_resume(struct device *dev)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +	struct raydium_data *ts = i2c_get_clientdata(client);
> +
> +	if (device_may_wakeup(dev)) {
> +		if (ts->wake_irq_enabled)
> +			disable_irq_wake(client->irq);
> +		raydium_i2c_sw_reset(client);
> +	} else {
> +		raydium_i2c_power_on(ts);
> +		raydium_i2c_initialize(ts);
> +	}
> +
> +	enable_irq(client->irq);
> +
> +	return 0;
> +}
> +
> +static SIMPLE_DEV_PM_OPS(raydium_i2c_pm_ops,
> +			 raydium_i2c_suspend, raydium_i2c_resume);
> +
> +static const struct i2c_device_id raydium_i2c_id[] = {
> +	{ DEVICE_NAME, 0 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(i2c, raydium_i2c_id);
> +
> +#ifdef CONFIG_ACPI
> +static const struct acpi_device_id raydium_acpi_id[] = {
> +	{ "RAYD0001", 0 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(acpi, raydium_acpi_id);
> +#endif
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id raydium_of_match[] = {
> +	{ .compatible = "raydium,rm32380",},
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, raydium_of_match);
> +#endif
> +
> +static struct i2c_driver raydium_i2c_driver = {
> +	.probe = raydium_i2c_probe,
> +	.remove = raydium_i2c_remove,
> +	.id_table = raydium_i2c_id,
> +	.driver = {
> +		.name = "raydium_ts",
> +		.pm = &raydium_i2c_pm_ops,
> +		.acpi_match_table = ACPI_PTR(raydium_acpi_id),
> +		.of_match_table = of_match_ptr(raydium_of_match),
> +	},
> +};
> +
> +module_i2c_driver(raydium_i2c_driver);
> +
> +MODULE_AUTHOR("Raydium");
> +MODULE_DESCRIPTION("Raydium I2c Touchscreen driver");
> +MODULE_LICENSE("GPL v2");
> -- 
> 2.1.2
> 

Thanks.

-- 
Dmitry
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
@ 2016-04-22 10:01 dan.huang
       [not found] ` <1461319268-362-1-git-send-email-dan.huang-s3Ivl27awEzQT0dZR+AlfA@public.gmane.org>
  0 siblings, 1 reply; 42+ messages in thread
From: dan.huang @ 2016-04-22 10:01 UTC (permalink / raw)
  To: dmitry.torokhov, rydberg, grant.likely, robh+dt, jeesw, bleung
  Cc: jeffrey.lin, roger.yang, KP.li, albert.shieh, linux-kernel,
	linux-input, devicetree

From: "jeffrey.lin" <jeffrey.lin@rad-ic.com>

Raydium I2C touch driver.

Signed-off-by: jeffrey.lin <jeffrey.lin@rad-ic.com>
---
 drivers/input/touchscreen/Kconfig          |   12 +
 drivers/input/touchscreen/Makefile         |    1 +
 drivers/input/touchscreen/raydium_i2c_ts.c | 1172 ++++++++++++++++++++++++++++
 3 files changed, 1185 insertions(+)
 create mode 100644 drivers/input/touchscreen/raydium_i2c_ts.c

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 3f3f6ee..df0e2ed 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -915,6 +915,18 @@ config TOUCHSCREEN_PCAP
 	  To compile this driver as a module, choose M here: the
 	  module will be called pcap_ts.
 
+config TOUCHSCREEN_RM_TS
+	tristate "Raydium I2C Touchscreen"
+	depends on I2C
+	help
+	  Say Y here if you have Raydium series I2C touchscreen,
+	  such as RM32380,connected to your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called raydium_i2c_ts.
+
 config TOUCHSCREEN_ST1232
 	tristate "Sitronix ST1232 touchscreen controllers"
 	depends on I2C
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 4941f2d..99e08cf 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE)	+= usbtouchscreen.o
 obj-$(CONFIG_TOUCHSCREEN_PCAP)		+= pcap_ts.o
 obj-$(CONFIG_TOUCHSCREEN_PENMOUNT)	+= penmount.o
 obj-$(CONFIG_TOUCHSCREEN_PIXCIR)	+= pixcir_i2c_ts.o
+obj-$(CONFIG_TOUCHSCREEN_RM_TS)		+= raydium_i2c_ts.o
 obj-$(CONFIG_TOUCHSCREEN_S3C2410)	+= s3c2410_ts.o
 obj-$(CONFIG_TOUCHSCREEN_ST1232)	+= st1232.o
 obj-$(CONFIG_TOUCHSCREEN_STMPE)		+= stmpe-ts.o
diff --git a/drivers/input/touchscreen/raydium_i2c_ts.c b/drivers/input/touchscreen/raydium_i2c_ts.c
new file mode 100644
index 0000000..140970c
--- /dev/null
+++ b/drivers/input/touchscreen/raydium_i2c_ts.c
@@ -0,0 +1,1172 @@
+/*
+ * Raydium touchscreen I2C driver.
+ *
+ * Copyright (C) 2012-2014, Raydium Semiconductor Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Raydium reserves the right to make changes without further notice
+ * to the materials described herein. Raydium does not assume any
+ * liability arising out of the application described herein.
+ *
+ * Contact Raydium Semiconductor Corporation at www.rad-ic.com
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/firmware.h>
+#include <linux/input/mt.h>
+#include <linux/acpi.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+#include <asm/unaligned.h>
+#include <linux/gpio/consumer.h>
+
+/* Device, Driver information */
+#define DEVICE_NAME	"raydium_i2c"
+
+/* Slave I2C mode*/
+#define RM_BOOT_BLDR	0x02
+#define RM_BOOT_MAIN	0x03
+
+/*I2C command */
+#define CMD_BOOT_WRT		0x11
+#define CMD_BOOT_ACK		0x22
+#define CMD_BOOT_CHK		0x33
+#define CMD_BOOT_READ		0x44
+#define CMD_BOOT_WAIT_READY	0x1A
+#define CMD_BOOT_PATH_READY	0x1B
+#define BOOT_RDY		0xFF
+#define CMD_QUERY_BANK		0x2B
+#define CMD_DATA_BANK		0x4D
+#define CMD_ENTER_SLEEP		0x4E
+#define CMD_BANK_SWITCH		0xAA
+
+/* Touch relative info */
+#define MAX_RETRIES		3
+#define MAX_FW_UPDATE_RETRIES	30
+#define MAX_TOUCH_NUM		10
+#define MAX_PACKET_SIZE		32
+#define BOOT_DELAY_MS	100
+
+#define RAYDIUM_FW_PAGESIZE		128
+#define RAYDIUM_POWERON_DELAY_USEC	500
+#define RAYDIUM_RESET_DELAY_MSEC	50
+
+#define ADDR_INDEX		0x03
+#define DATA_INDEX		0x04
+
+#define HEADER_SIZE		4
+
+enum raydium_boot_mode {
+	RAYDIUM_TS_MAIN = 0,
+	RAYDIUM_TS_BLDR,
+};
+
+enum raydium_abs_idx {
+	POS_STATE = 0,/*1:touch, 0:no touch*/
+	POS_X,
+	POS_Y = 3,
+	POS_PRESSURE,
+	WIDTH_X,
+	WIDTH_Y,
+};
+
+struct raydium_info {
+	u32 hw_ver;	/*device ver, __le32*/
+	u8 main_ver;
+	u8 sub_ver;
+	u16 ft_ver;	/*test ver, __le16*/
+	u8 x_num;
+	u8 y_num;
+	u16 x_max;	/*disp reso, __le16*/
+	u16 y_max;	/*disp reso, __le16*/
+	u8 x_res;		/* units/mm */
+	u8 y_res;		/* units/mm */
+};
+
+struct raydium_object {
+	u32 data_bank_addr;
+	u8 pkg_size;
+	u8 tp_info_size;
+};
+
+/* struct raydium_data - represents state of Raydium touchscreen device */
+struct raydium_data {
+	struct i2c_client *client;
+	struct input_dev *input;
+
+	struct regulator *avdd;
+	struct regulator *vccio;
+	struct gpio_desc *reset_gpio;
+
+	u32 query_bank_info;
+
+	struct raydium_info info;
+	struct raydium_object obj;
+	enum raydium_boot_mode boot_mode;
+
+	struct mutex sysfs_mutex;
+	struct completion cmd_done;
+
+	bool wake_irq_enabled;
+};
+
+static int raydium_i2c_send(struct i2c_client *client,
+	u8 addr, u8 *data, size_t len)
+{
+	u8 buf[MAX_PACKET_SIZE + 1], idx_i;
+	u16 pkg_len, use_len;
+	int tries = 0;
+
+	buf[0] = addr;
+	use_len = len;
+	use_len = 0;
+
+	while (use_len) {
+		pkg_len = (use_len < MAX_PACKET_SIZE) ?
+			use_len : MAX_PACKET_SIZE;
+		memcpy(&buf[1], data + idx_i*MAX_PACKET_SIZE, pkg_len);
+
+		tries = 0;
+		do {
+			if (i2c_master_send(client, buf, pkg_len + 1)
+				== (pkg_len + 1))
+				break;
+			msleep(20);
+		} while (++tries < MAX_RETRIES);
+		idx_i++;
+		use_len = (use_len < MAX_PACKET_SIZE) ?
+			0 : (use_len - MAX_PACKET_SIZE);
+	}
+
+	dev_err(&client->dev, "%s: i2c send failed\n", __func__);
+
+	return -EIO;
+}
+
+static int raydium_i2c_read(struct i2c_client *client,
+	u8 addr, size_t len, void *data)
+{
+	struct i2c_msg xfer[2];
+	int ret;
+
+	/* Write register */
+	xfer[0].addr = client->addr;
+	xfer[0].flags = 0;
+	xfer[0].len = 1;
+	xfer[0].buf = &addr;
+
+	/* Read data */
+	xfer[1].addr = client->addr;
+	xfer[1].flags = I2C_M_RD;
+	xfer[1].len = len;
+	xfer[1].buf = data;
+
+	ret = i2c_transfer(client->adapter, xfer, ARRAY_SIZE(xfer));
+	if (ret < 0)
+		return ret;
+
+	if (ret != ARRAY_SIZE(xfer))
+		return -EIO;
+
+	return 0;
+}
+
+static int raydium_i2c_read_message(struct i2c_client *client,
+	u32 addr, size_t len, void *data)
+{
+	u16 pkg_size, use_len;
+	u8 buf[HEADER_SIZE], idx_i;
+	int error;
+
+	use_len = len;
+	idx_i = 0;
+	while (use_len > 0) {
+		pkg_size = (use_len < MAX_PACKET_SIZE) ?
+			use_len : MAX_PACKET_SIZE;
+
+		put_unaligned_be32(addr, buf);
+
+		/*set data bank*/
+		error = raydium_i2c_send(client, CMD_BANK_SWITCH,
+			(u8 *)buf, HEADER_SIZE);
+		/*read potints data*/
+		if (!error)
+			error = raydium_i2c_read(client, buf[ADDR_INDEX],
+				pkg_size,
+				data + idx_i*MAX_PACKET_SIZE);
+
+		pkg_size += MAX_PACKET_SIZE;
+		addr += MAX_PACKET_SIZE;
+		use_len = (use_len < MAX_PACKET_SIZE) ?
+			0 : (use_len - MAX_PACKET_SIZE);
+		idx_i++;
+	}
+
+	return error;
+}
+
+static int raydium_i2c_send_message(struct i2c_client *client,
+	size_t len, void *data)
+{
+	int error;
+	__le32 cmd;
+
+	cmd = get_unaligned_le32(data);
+
+	/*set data bank*/
+	error = raydium_i2c_send(client, CMD_BANK_SWITCH, (u8 *)&cmd,
+		HEADER_SIZE);
+
+	/*send message*/
+	if (!error)
+		error = raydium_i2c_send(client, ((u8 *)data)[ADDR_INDEX],
+			data + DATA_INDEX, len);
+
+	return error;
+}
+
+static int raydium_i2c_sw_reset(struct i2c_client *client)
+{
+	static const u8 soft_rst_cmd[] = {0x40, 0x00, 0x00, 0x04, 0x01};
+	int error;
+
+	error = raydium_i2c_send_message(client, ARRAY_SIZE(soft_rst_cmd),
+		(void *)soft_rst_cmd);
+	if (error) {
+		dev_err(&client->dev, "software reset failed: %d\n", error);
+		return error;
+	}
+
+	msleep(RAYDIUM_RESET_DELAY_MSEC);
+
+	return 0;
+}
+
+static int raydium_i2c_query_ts_info(struct raydium_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	int error, retry_cnt;
+
+	for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
+		error = raydium_i2c_read(client, CMD_DATA_BANK,
+			sizeof(ts->obj), (void *)&ts->obj);
+			ts->obj.data_bank_addr =
+				get_unaligned_le32(&ts->obj.data_bank_addr);
+
+		if (!error) {
+			error = raydium_i2c_read(client, CMD_QUERY_BANK,
+				sizeof(ts->query_bank_info),
+				(void *)&ts->query_bank_info);
+			if (!error) {
+				error = raydium_i2c_read_message(client,
+					ts->query_bank_info, sizeof(ts->info),
+					(void *)&ts->info);
+
+				ts->info.hw_ver =
+					get_unaligned_le32(&ts->info.hw_ver);
+				ts->info.ft_ver =
+					get_unaligned_le16(&ts->info.ft_ver);
+				ts->info.x_max =
+					get_unaligned_le16(&ts->info.x_max);
+				ts->info.y_max =
+					get_unaligned_le16(&ts->info.y_max);
+				return 0;
+			}
+		}
+	}
+	dev_err(&client->dev, "Get touch data failed: %d\n", error);
+
+	return -EINVAL;
+}
+
+static int raydium_i2c_fastboot(struct i2c_client *client)
+{
+	static const u8 boot_cmd[] = { 0x50, 0x00, 0x06, 0x20 };
+	u8 buf[HEADER_SIZE];
+	int error;
+
+	error = raydium_i2c_read_message(client,
+		get_unaligned_be32(boot_cmd),
+		sizeof(boot_cmd), buf);
+
+	if (!error) {
+		if (buf[0] == RM_BOOT_BLDR) {
+			dev_dbg(&client->dev, "boot in fastboot mode\n");
+			return -EINVAL;
+		}
+		dev_dbg(&client->dev, "boot success -- 0x%x\n", client->addr);
+		return 0;
+	}
+
+	dev_err(&client->dev, "boot failed: %d\n", error);
+
+	return error;
+}
+
+static int raydium_i2c_check_fw_status(struct raydium_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	static const u8 bl_area[] = {0x62, 0x6f, 0x6f, 0x74};
+	static const u8 main_area[] = {0x66, 0x69, 0x72, 0x6d};
+	u8 buf[HEADER_SIZE];
+	int error;
+
+	error = raydium_i2c_read(client, CMD_BOOT_READ, HEADER_SIZE,
+		(void *)buf);
+	if (!error) {
+		if (memcmp(buf, bl_area, sizeof(bl_area)))
+			ts->boot_mode = RAYDIUM_TS_BLDR;
+		else if (memcmp(buf, main_area, sizeof(main_area)))
+			ts->boot_mode = RAYDIUM_TS_MAIN;
+		else
+			return -EINVAL;
+
+		return 0;
+	}
+
+	dev_err(&client->dev, "check bl status failed: %d\n", error);
+
+	return error;
+}
+
+static int raydium_i2c_initialize(struct raydium_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	int error, retry_cnt;
+
+	for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
+		error = raydium_i2c_fastboot(client);
+		if (error) {
+			/* Continue initializing if it's the last try */
+			if (retry_cnt < MAX_RETRIES - 1)
+				continue;
+		}
+		/* Wait for Hello packet */
+		msleep(BOOT_DELAY_MS);
+
+		error = raydium_i2c_check_fw_status(ts);
+		if (error) {
+			dev_err(&client->dev,
+				"failed to read 'hello' packet: %d\n", error);
+		}
+	}
+
+	if (error)
+		ts->boot_mode = RAYDIUM_TS_BLDR;
+	else
+		raydium_i2c_query_ts_info(ts);
+
+	return error;
+}
+
+static int raydium_i2c_recv(struct i2c_client *client, u8 *buf, size_t count)
+{
+	int error = 0;
+
+	error = i2c_master_recv(client, buf, count);
+
+	if (error == count) {
+		error = 0;
+	} else if (error != count) {
+		error = (error < 0) ? error : -EIO;
+		dev_err(&client->dev, "i2c recv failed (%d)\n", error);
+	}
+
+	return error;
+}
+
+static int raydium_i2c_bl_chk_state(struct i2c_client *client, u8 state)
+{
+	static const u8 ack_ok[] = { 0xFF, 0x39, 0x30, 0x30, 0x54 };
+	u8 rbuf[5];
+	u8 retry;
+	int error;
+
+	for (retry = 0; retry < MAX_FW_UPDATE_RETRIES; retry++) {
+		if (state == CMD_BOOT_WAIT_READY) {
+			error = raydium_i2c_recv(client, &rbuf[0], 1);
+			if (!error) {
+				if (rbuf[0] == BOOT_RDY)
+					return 0;
+			}
+		} else if (state == CMD_BOOT_WAIT_READY) {
+			error = raydium_i2c_recv(client, &rbuf[0],
+				sizeof(ack_ok));
+			if (!error) {
+				if (memcmp(rbuf, ack_ok, sizeof(ack_ok)))
+					return 0;
+			}
+		} else
+			return -EINVAL;
+
+		msleep(20);
+	}
+
+	return -EINVAL;
+}
+
+static int raydium_i2c_wrt_object(struct i2c_client *client,
+	u8 *data, size_t len, u8 state)
+{
+	int error = 0;
+
+	error = raydium_i2c_send(client, CMD_BOOT_WRT, data, len);
+	if (error) {
+		dev_err(&client->dev, "WRT obj command failed: %d\n", error);
+		return error;
+	}
+
+	error = raydium_i2c_send(client, CMD_BOOT_ACK, (u8 *)NULL, 0);
+	if (error) {
+		dev_err(&client->dev, "Ack obj command failed: %d\n", error);
+		return error;
+	}
+
+	error = raydium_i2c_send(client, CMD_BOOT_CHK, (u8 *)NULL, 0);
+	if (error) {
+		dev_err(&client->dev, "Boot chk failed: %d\n", error);
+		return error;
+	}
+
+	error = raydium_i2c_bl_chk_state(client, state);
+	if (error) {
+		dev_err(&client->dev, "boot trigger state failed: %d\n", error);
+		return error;
+	}
+	return 0;
+}
+
+static bool raydium_i2c_boot_trigger(struct i2c_client *client)
+{
+	int error;
+	u8 u8_idx;
+	static const u8 cmd[7][6] = {
+			{0x08, 0x0C, 0x09, 0x00, 0x50, 0xD7},
+			{0x08, 0x04, 0x09, 0x00, 0x50, 0xA5},
+			{0x08, 0x04, 0x09, 0x00, 0x50, 0x00},
+			{0x08, 0x04, 0x09, 0x00, 0x50, 0xA5},
+			{0x08, 0x0C, 0x09, 0x00, 0x50, 0x00},
+			{0x06, 0x01},
+			{0x02, 0xA2},
+		};
+
+	for (u8_idx = 0 ; u8_idx < 7 ; u8_idx++) {
+		error = raydium_i2c_wrt_object(client, (u8 *)cmd[u8_idx],
+			sizeof(cmd[u8_idx]), CMD_BOOT_WAIT_READY);
+		if (error) {
+			dev_err(&client->dev, "send boot trigger cmd failed: %d\n",
+				error);
+			return error;
+		}
+	}
+
+	return 0;
+}
+
+static int raydium_i2c_check_path(struct i2c_client *client)
+{
+	static const u8 cmd[] = {0x09, 0x00, 0x09, 0x00, 0x50, 0x10, 0x00};
+	int error;
+
+	error = raydium_i2c_wrt_object(client, (u8 *)cmd, sizeof(cmd),
+		CMD_BOOT_PATH_READY);
+	if (error) {
+		dev_err(&client->dev, "send chk path cmd fail: %d\n", error);
+		return error;
+	}
+
+	return error;
+}
+
+static int raydium_i2c_enter_bl(struct i2c_client *client)
+{
+	static const u8 cal_cmd[] = {0x00, 0x01, 0x52};
+	int error;
+
+	error = raydium_i2c_wrt_object(client, (u8 *)cal_cmd,
+		sizeof(cal_cmd), CMD_BOOT_WAIT_READY);
+	if (error) {
+		dev_err(&client->dev, "send jump loader cmd fail: %d\n", error);
+		return error;
+	}
+	return 0;
+}
+
+static int raydium_i2c_leave_bl(struct i2c_client *client)
+{
+	static const u8 leave_cmd[] = {0x05, 0x00};
+	int error;
+
+	error = raydium_i2c_wrt_object(client, (u8 *)leave_cmd,
+		sizeof(leave_cmd), CMD_BOOT_WAIT_READY);
+	if (error) {
+		dev_err(&client->dev, "send leave bl cmd fail: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int raydium_i2c_write_checksum(struct i2c_client *client,
+	u16 length, u16 checksum)
+{
+	u8 checksum_cmd[] = {0x00, 0x05, 0x6D, 0x00, 0x00, 0x00, 0x00};
+	int error = 0;
+
+	checksum_cmd[3] = (u8)(length & 0xFF);
+	checksum_cmd[4] = (u8)((length & 0xFF00) >> 8);
+	checksum_cmd[5] = (u8)(checksum & 0xFF);
+	checksum_cmd[6] = (u8)((checksum & 0xFF00) >> 8);
+
+	error = raydium_i2c_wrt_object(client, (u8 *)checksum_cmd,
+		sizeof(checksum_cmd), CMD_BOOT_WAIT_READY);
+	if (error) {
+		dev_err(&client->dev, "send wrt checksum cmd fail: %d\n",
+			error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int raydium_i2c_disable_watch_dog(struct i2c_client *client)
+{
+	static const u8 cmd[] = {0x0A, 0xAA};
+	int error = 0;
+
+	error = raydium_i2c_wrt_object(client, (u8 *)cmd, sizeof(cmd),
+		CMD_BOOT_WAIT_READY);
+	if (error) {
+		dev_err(&client->dev, "send disable watchdog cmd fail: %d\n",
+			error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int raydium_i2c_fw_write_page(struct i2c_client *client,
+				const void *page, size_t len)
+{
+	int error;
+
+	error = raydium_i2c_wrt_object(client,  (u8 *)page, len,
+		CMD_BOOT_WAIT_READY);
+	if (error) {
+		dev_err(&client->dev, "send page wrt cmd failed: %d\n", error);
+		return error;
+	}
+
+	return error;
+}
+
+static int raydium_i2c_do_update_firmware(struct raydium_data *ts,
+					 const struct firmware *fw)
+{
+	struct i2c_client *client = ts->client;
+	u8 u8_idx;
+	u16 fw_length;
+	u16 fw_checksum;
+	u8 buf[RAYDIUM_FW_PAGESIZE + 2];
+	int page, n_fw_pages;
+	int error;
+
+	fw_length = (int)(fw->size);
+	if (fw_length == 0) {
+		dev_err(&client->dev, "Invalid firmware length\n");
+		return -EINVAL;
+	}
+
+	if (fw_length % RAYDIUM_FW_PAGESIZE)
+		n_fw_pages = fw_length/RAYDIUM_FW_PAGESIZE;
+	else
+		n_fw_pages = fw_length/RAYDIUM_FW_PAGESIZE + 1;
+
+	error = raydium_i2c_check_fw_status(ts);
+	if (error) {
+		dev_err(&client->dev, "Unable to access IC %d\n", error);
+		return error;
+	}
+
+	if (ts->boot_mode == RAYDIUM_TS_MAIN) {
+		error = raydium_i2c_enter_bl(client);
+		if (error) {
+			dev_err(&client->dev, "Unable jump to boot loader %d\n",
+				error);
+			return error;
+		}
+	}
+
+	error = raydium_i2c_disable_watch_dog(client);
+	if (error) {
+		dev_err(&client->dev, "send disable watchdog cmd fail, %d\n",
+			error);
+		return error;
+	}
+
+	error = raydium_i2c_check_path(client);
+	if (error) {
+		dev_err(&client->dev, "send chk path fail, %d\n", error);
+		return error;
+	}
+
+	error = raydium_i2c_boot_trigger(client);
+	if (error) {
+		dev_err(&client->dev, "send boot trigger fail, %d\n", error);
+		return error;
+	}
+
+	fw_checksum = 0;
+	for (page = 0 ; page < n_fw_pages ; page++) {
+		memset(buf, 0xFF, sizeof(buf));
+		buf[0] = 0x03;
+		if (page == 0)
+			buf[1] = 0x00;
+
+		memcpy(&buf[2], fw->data + page * RAYDIUM_FW_PAGESIZE,
+			RAYDIUM_FW_PAGESIZE);
+		/*calculate chksum*/
+		for (u8_idx = 0; u8_idx < RAYDIUM_FW_PAGESIZE; u8_idx++)
+			fw_checksum += buf[2 + u8_idx];
+
+		error = raydium_i2c_fw_write_page(client, (const void *)buf,
+			sizeof(buf));
+		if (error) {
+			dev_err(&client->dev, "flash page write fail, %d\n",
+				error);
+			return error;
+		}
+
+		msleep(20);
+	}
+
+	error = raydium_i2c_leave_bl(client);
+	if (error) {
+		dev_err(&client->dev, "leave boot loader fail: %d\n", error);
+		return error;
+	}
+
+	msleep(BOOT_DELAY_MS);
+
+	error = raydium_i2c_check_fw_status(ts);
+	if (error) {
+		dev_err(&client->dev, "Unable to access IC %d\n", error);
+		return error;
+	}
+
+	if (ts->boot_mode == RAYDIUM_TS_MAIN) {
+		error = raydium_i2c_write_checksum(client, fw_length,
+			fw_checksum);
+		if (error) {
+			dev_err(&client->dev, "write checksum fail %d\n",
+				error);
+			return error;
+		}
+	} else {
+		dev_err(&client->dev, "switch to main_fw fail %d\n", error);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int raydium_i2c_fw_update(struct raydium_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	const struct firmware *fw = NULL;
+	const char *fn = "raydium.fw";
+	int error;
+
+	error = request_firmware(&fw, fn, &client->dev);
+	if (error) {
+		dev_err(&client->dev, "Unable to open firmware %s\n", fn);
+		return error;
+	}
+	/*disable irq*/
+	disable_irq(client->irq);
+
+	error = raydium_i2c_do_update_firmware(ts, fw);
+	if (error) {
+		dev_err(&client->dev, "firmware update failed: %d\n", error);
+		ts->boot_mode = RAYDIUM_TS_BLDR;
+		goto out_enable_irq;
+	}
+
+	error = raydium_i2c_initialize(ts);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to initialize device after firmware update: %d\n",
+			error);
+		ts->boot_mode = RAYDIUM_TS_BLDR;
+		goto out_enable_irq;
+	}
+
+	ts->boot_mode = RAYDIUM_TS_MAIN;
+
+out_enable_irq:
+	enable_irq(client->irq);
+	msleep(100);
+
+	return error;
+}
+
+static void raydium_mt_event(struct raydium_data *ts)
+{
+	u8 data[MAX_PACKET_SIZE];
+	int error, i;
+	int x, y, f_state, pressure, wx, wy;
+
+	error = raydium_i2c_read_message(ts->client, ts->obj.data_bank_addr,
+		ts->obj.pkg_size, (void *)data);
+
+	if (error < 0) {
+		dev_err(&ts->client->dev, "%s: failed to read data: %d\n",
+			__func__, error);
+		return;
+	}
+
+	for (i = 0; i < MAX_TOUCH_NUM; i++) {
+		f_state = (data + i * ts->obj.tp_info_size)[POS_STATE];
+		pressure = (data + i * ts->obj.tp_info_size)[POS_PRESSURE];
+		wx = (data + i * ts->obj.tp_info_size)[WIDTH_X];
+		wy = (data + i * ts->obj.tp_info_size)[WIDTH_Y];
+
+		input_mt_slot(ts->input, i);
+		input_mt_report_slot_state(ts->input,
+				MT_TOOL_FINGER, f_state != 0);
+
+		if (!f_state)
+			continue;
+		x = get_unaligned_le16(&data[i * ts->obj.tp_info_size + POS_X]);
+		y = get_unaligned_le16(&data[i * ts->obj.tp_info_size + POS_Y]);
+
+		input_report_abs(ts->input, ABS_MT_POSITION_X, x);
+		input_report_abs(ts->input, ABS_MT_POSITION_Y, y);
+		input_report_abs(ts->input, ABS_MT_PRESSURE, pressure);
+		input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR,
+			max(wx, wy));
+		input_report_abs(ts->input, ABS_MT_TOUCH_MINOR,
+			min(wx, wy));
+	}
+
+	input_mt_sync_frame(ts->input);
+	input_sync(ts->input);
+}
+
+static irqreturn_t raydium_i2c_irq(int irq, void *_dev)
+{
+	struct raydium_data *ts = _dev;
+
+	if (ts->boot_mode != RAYDIUM_TS_BLDR)
+		raydium_mt_event(ts);
+
+	return IRQ_HANDLED;
+}
+
+static ssize_t raydium_calibrate(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t count)
+{
+	struct raydium_data *ts = dev_get_drvdata(dev);
+	struct i2c_client *client = ts->client;
+
+	static const u8 cal_cmd[] = {0x00, 0x01, 0x9E};
+	int error = 0;
+
+	error = raydium_i2c_wrt_object(client, (u8 *)cal_cmd, sizeof(cal_cmd),
+		CMD_BOOT_WAIT_READY);
+	if (error) {
+		dev_err(&client->dev, "send chk path cmd fail: %d\n", error);
+		return error;
+	}
+
+	return error;
+}
+
+static ssize_t write_update_fw(struct device *dev,
+			struct device_attribute *attr,
+			const char *buf, size_t count)
+{
+	struct raydium_data *ts = dev_get_drvdata(dev);
+	int error;
+
+	error = mutex_lock_interruptible(&ts->sysfs_mutex);
+	if (error)
+		return error;
+
+	error = raydium_i2c_fw_update(ts);
+	dev_dbg(dev, "firmware update result: %d\n", error);
+
+	mutex_unlock(&ts->sysfs_mutex);
+	return error ?: count;
+}
+
+static ssize_t raydium_bootmode_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct raydium_data *ts = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%s\n", ts->boot_mode == RAYDIUM_TS_MAIN ?
+		"Normal" : "Recovery");
+}
+
+static ssize_t raydium_fw_ver_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct raydium_data *ts = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%d.%d\n", ts->info.main_ver, ts->info.sub_ver);
+}
+
+static ssize_t raydium_hw_ver_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct raydium_data *ts = dev_get_drvdata(dev);
+
+	return sprintf(buf, "0x%04x\n", ts->info.hw_ver);
+}
+
+static DEVICE_ATTR(fw_version, S_IRUGO, raydium_fw_ver_show, NULL);
+static DEVICE_ATTR(hw_version, S_IRUGO, raydium_hw_ver_show, NULL);
+static DEVICE_ATTR(boot_mode, S_IRUGO, raydium_bootmode_show, NULL);
+static DEVICE_ATTR(update_fw, S_IWUSR, NULL, write_update_fw);
+static DEVICE_ATTR(calibrate, S_IWUSR, NULL, raydium_calibrate);
+
+static struct attribute *raydium_attributes[] = {
+	&dev_attr_update_fw.attr,
+	&dev_attr_boot_mode.attr,
+	&dev_attr_fw_version.attr,
+	&dev_attr_hw_version.attr,
+	&dev_attr_calibrate.attr,
+	NULL
+};
+
+static struct attribute_group raydium_attribute_group = {
+	.attrs = raydium_attributes,
+};
+
+static void raydium_i2c_remove_sysfs_group(void *_data)
+{
+	struct raydium_data *ts = _data;
+
+	sysfs_remove_group(&ts->client->dev.kobj, &raydium_attribute_group);
+}
+
+static int raydium_i2c_power_on(struct raydium_data *ts)
+{
+	int error;
+
+	if (IS_ERR_OR_NULL(ts->reset_gpio))
+		return 0;
+
+	gpiod_set_value_cansleep(ts->reset_gpio, 1);
+
+	error = regulator_enable(ts->avdd);
+	if (error) {
+		dev_err(&ts->client->dev,
+			"failed to enable avdd regulator: %d\n", error);
+		goto release_reset_gpio;
+	}
+
+	error = regulator_enable(ts->vccio);
+	if (error) {
+		regulator_disable(ts->avdd);
+		dev_err(&ts->client->dev,
+			"failed to enable vccio regulator: %d\n", error);
+		goto release_reset_gpio;
+	}
+
+	udelay(RAYDIUM_POWERON_DELAY_USEC);
+
+release_reset_gpio:
+	gpiod_set_value_cansleep(ts->reset_gpio, 0);
+
+	if (error)
+		return error;
+
+	msleep(RAYDIUM_RESET_DELAY_MSEC);
+
+	return 0;
+}
+
+static void raydium_i2c_power_off(void *_data)
+{
+	struct raydium_data *ts = _data;
+
+	if (!IS_ERR_OR_NULL(ts->reset_gpio)) {
+		gpiod_set_value_cansleep(ts->reset_gpio, 1);
+		regulator_disable(ts->vccio);
+		regulator_disable(ts->avdd);
+	}
+}
+
+static int raydium_i2c_probe(struct i2c_client *client,
+			    const struct i2c_device_id *id)
+{
+	union i2c_smbus_data dummy;
+	struct raydium_data *ts;
+	unsigned long irqflags;
+	int error;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		dev_err(&client->dev,
+			"%s: i2c check functionality error\n", DEVICE_NAME);
+		return -ENXIO;
+	}
+
+	ts = devm_kzalloc(&client->dev, sizeof(struct raydium_data),
+		GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+
+	mutex_init(&ts->sysfs_mutex);
+	init_completion(&ts->cmd_done);
+
+	ts->client = client;
+	i2c_set_clientdata(client, ts);
+
+	ts->avdd = devm_regulator_get(&client->dev, "avdd");
+	if (IS_ERR(ts->avdd)) {
+		error = PTR_ERR(ts->avdd);
+		if (error != -EPROBE_DEFER)
+			dev_err(&client->dev,
+				"Failed to get 'avdd' regulator: %d\n", error);
+		return error;
+	}
+
+	ts->vccio = devm_regulator_get(&client->dev, "vccio");
+	if (IS_ERR(ts->vccio)) {
+		error = PTR_ERR(ts->vccio);
+		if (error != -EPROBE_DEFER)
+			dev_err(&client->dev,
+				"Failed to get 'vccio' regulator: %d\n", error);
+		return error;
+	}
+
+	ts->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
+		GPIOD_OUT_LOW);
+	if (IS_ERR(ts->reset_gpio)) {
+		error = PTR_ERR(ts->reset_gpio);
+		if (error != -EPROBE_DEFER) {
+			dev_err(&client->dev,
+				"failed to get reset gpio: %d\n", error);
+			return error;
+		}
+	}
+
+	error = raydium_i2c_power_on(ts);
+	if (error)
+		return error;
+
+	error = devm_add_action(&client->dev, raydium_i2c_power_off, ts);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to install power off action: %d\n", error);
+		raydium_i2c_power_off(ts);
+		return error;
+	}
+
+	/* Make sure there is something at this address */
+	if (i2c_smbus_xfer(client->adapter, client->addr, 0,
+			I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0) {
+		dev_err(&client->dev, "nothing at this address\n");
+		return -ENXIO;
+	}
+
+	error = raydium_i2c_initialize(ts);
+	if (error) {
+		dev_err(&client->dev, "failed to initialize: %d\n", error);
+		return error;
+	}
+
+	ts->input = devm_input_allocate_device(&client->dev);
+	if (!ts->input) {
+		dev_err(&client->dev, "Failed to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	ts->input->name = "Raydium Touchscreen";
+	ts->input->id.bustype = BUS_I2C;
+
+	__set_bit(BTN_TOUCH, ts->input->keybit);
+	__set_bit(EV_ABS, ts->input->evbit);
+	__set_bit(EV_KEY, ts->input->evbit);
+
+	/* Multitouch input params setup */
+	input_set_abs_params(ts->input, ABS_MT_POSITION_X, 0,
+		ts->info.x_max, 0, 0);
+	input_set_abs_params(ts->input, ABS_MT_POSITION_Y,
+		0, ts->info.y_max, 0, 0);
+	input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+	input_set_abs_params(ts->input, ABS_MT_PRESSURE, 0, 255, 0, 0);
+	input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->info.x_res);
+	input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->info.y_res);
+
+	input_set_drvdata(ts->input, ts);
+
+	error = input_mt_init_slots(ts->input, MAX_TOUCH_NUM,
+		INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to initialize MT slots: %d\n", error);
+		return error;
+	}
+
+	error = input_register_device(ts->input);
+	if (error) {
+		dev_err(&client->dev,
+			"unable to register input device: %d\n", error);
+		return error;
+	}
+
+	error = devm_request_threaded_irq(&client->dev, client->irq,
+			NULL, raydium_i2c_irq, irqflags | IRQF_ONESHOT,
+			client->name, ts);
+	if (error) {
+		dev_err(&client->dev, "Failed to register interrupt\n");
+		return error;
+	}
+
+	device_init_wakeup(&client->dev, true);
+
+	error = sysfs_create_group(&client->dev.kobj, &raydium_attribute_group);
+	if (error) {
+		dev_err(&client->dev, "failed to create sysfs attributes: %d\n",
+			error);
+		return error;
+	}
+
+	error = devm_add_action(&client->dev,
+				raydium_i2c_remove_sysfs_group, ts);
+	if (error) {
+		raydium_i2c_remove_sysfs_group(ts);
+		dev_err(&client->dev,
+			"Failed to add sysfs cleanup action: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int raydium_i2c_remove(struct i2c_client *client)
+{
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	input_unregister_device(ts->input);
+
+	device_init_wakeup(&client->dev, false);
+
+	mutex_destroy(&ts->sysfs_mutex);
+
+	return 0;
+}
+
+static void __maybe_unused raydium_enter_sleep(struct i2c_client *client)
+{
+	static const u8 sleep_cmd[] = { 0x5A, 0xff, 0x00, 0x0f };
+	int error;
+
+	error = raydium_i2c_send(client, CMD_ENTER_SLEEP, (u8 *)sleep_cmd,
+		sizeof(sleep_cmd));
+	if (error)
+		dev_err(&client->dev,
+			"Send sleep failed: %d\n", error);
+}
+
+static int __maybe_unused raydium_i2c_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	/* Command not support in BLDR recovery mode */
+	if (ts->boot_mode != RAYDIUM_TS_MAIN)
+		return -EBUSY;
+
+	disable_irq(client->irq);
+
+	if (device_may_wakeup(dev)) {
+		raydium_enter_sleep(client);
+
+		ts->wake_irq_enabled = (enable_irq_wake(client->irq) == 0);
+	} else {
+		raydium_i2c_power_off(ts);
+	}
+
+	return 0;
+}
+
+static int __maybe_unused raydium_i2c_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	if (device_may_wakeup(dev)) {
+		if (ts->wake_irq_enabled)
+			disable_irq_wake(client->irq);
+		raydium_i2c_sw_reset(client);
+	} else {
+		raydium_i2c_power_on(ts);
+		raydium_i2c_initialize(ts);
+	}
+
+	enable_irq(client->irq);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(raydium_i2c_pm_ops,
+			 raydium_i2c_suspend, raydium_i2c_resume);
+
+static const struct i2c_device_id raydium_i2c_id[] = {
+	{ DEVICE_NAME, 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, raydium_i2c_id);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id raydium_acpi_id[] = {
+	{ "RAYD0001", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(acpi, raydium_acpi_id);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id raydium_of_match[] = {
+	{ .compatible = "raydium,rm32380",},
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, raydium_of_match);
+#endif
+
+static struct i2c_driver raydium_i2c_driver = {
+	.probe = raydium_i2c_probe,
+	.remove = raydium_i2c_remove,
+	.id_table = raydium_i2c_id,
+	.driver = {
+		.name = "raydium_ts",
+		.pm = &raydium_i2c_pm_ops,
+		.acpi_match_table = ACPI_PTR(raydium_acpi_id),
+		.of_match_table = of_match_ptr(raydium_of_match),
+	},
+};
+
+module_i2c_driver(raydium_i2c_driver);
+
+MODULE_AUTHOR("Raydium");
+MODULE_DESCRIPTION("Raydium I2c Touchscreen driver");
+MODULE_LICENSE("GPL v2");
-- 
2.1.2

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* RE: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2016-04-11  8:24 ` Dmitry Torokhov
  2016-04-11  9:57   ` Jeffrey Lin (林義章)
@ 2016-04-14  9:28   ` Jeffrey Lin (林義章)
  1 sibling, 0 replies; 42+ messages in thread
From: Jeffrey Lin (林義章) @ 2016-04-14  9:28 UTC (permalink / raw)
  To: Dmitry Torokhov, jeffrey.lin
  Cc: rydberg, grant.likely, robh+dt, jeesw, bleung, scott.liu,
	Roger Yang (楊鎮瑋),
	KP Li (李昆倍),
	Albert Shieh (謝欣瑋),
	linux-kernel, linux-input, devicetree

Hi Dmitry:
Our new submission now is in progress. Do you have any further suggestions for my submission and when can I submit my new update?

Thank you.

Best Regards
----------------------------------------------------------------------
Jeffrey Lin,林義章
瑞鼎科技
Raydium Semiconductor Corporation
Tel:(03)666-1818 Ext.4163
Fax:(03)666-1919

-----Original Message-----
From: Jeffrey Lin (林義章) 
Sent: Monday, April 11, 2016 5:57 PM
To: 'Dmitry Torokhov'; jeffrey.lin
Cc: rydberg@euromail.se; grant.likely@linaro.org; robh+dt@kernel.org; jeesw@melfas.com; bleung@chromium.org; scott.liu@emc.com.tw; Roger Yang (楊鎮瑋); KP Li (李昆倍); Albert Shieh (謝欣瑋); linux-kernel@vger.kernel.org; linux-input@vger.kernel.org; devicetree@vger.kernel.org
Subject: RE: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver

Hi Dmitry:
Thank you for your response.
	Actually, we didn't implement FW update function on the chromeos device, we just copy function from our previous android device. I'll fix that on the next version. Raydium controller doesn't have access bytes limit, but my previous android experimental device had 60 bytes limit.
	
About reformatting command structure, do you want me to reform all my commands as you said in the mail or just in case of fw update function? If not just in case of FW update, it's better to meet maximum buffer size, like "MAX_PACKET_SIZE".


Best Regards
----------------------------------------------------------------------
Jeffrey Lin,林義章
瑞鼎科技
Raydium Semiconductor Corporation
Tel:(03)666-1818 Ext.4163
Fax:(03)666-1919

-----Original Message-----
From: Dmitry Torokhov [mailto:dmitry.torokhov@gmail.com] 
Sent: Monday, April 11, 2016 4:24 PM
To: jeffrey.lin
Cc: rydberg@euromail.se; grant.likely@linaro.org; robh+dt@kernel.org; jeesw@melfas.com; bleung@chromium.org; scott.liu@emc.com.tw; Jeffrey Lin (林義章); Roger Yang (楊鎮瑋); KP Li (李昆倍); Albert Shieh (謝欣瑋); linux-kernel@vger.kernel.org; linux-input@vger.kernel.org; devicetree@vger.kernel.org
Subject: Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver

Hi Jeffrey,

On Fri, Mar 25, 2016 at 01:21:09PM +0800, jeffrey.lin wrote:
> +#define MAX_PACKET_SIZE		60
...
> +#define RAYDIUM_FW_PAGESIZE	128
...
> +
> +static int raydium_i2c_send(struct i2c_client *client,
> +	u8 addr, u8 *data, size_t len)
> +{
> +	u8 buf[MAX_PACKET_SIZE + 1];
> +	int tries = 0;
> +
> +	if (len > MAX_PACKET_SIZE)
> +		return -EINVAL;
...
> +static int raydium_i2c_fw_write_page(struct i2c_client *client,
> +				const void *page)
> +{
> +	static const u8 ack_ok[] = { 0x55, 0xAA };
> +	u8 buf[2];
> +	int retry;
> +	int error;
> +
> +	for (retry = 0; retry < MAX_FW_UPDATE_RETRIES; retry++) {
> +		error = raydium_i2c_send(client, CMD_BOOT_WRT,
> +			(u8 *)page, RAYDIUM_FW_PAGESIZE);
> +		if (error) {
> +			dev_err(&client->dev,
> +				"BLDR Write Page failed: %d\n", error);
> +			continue;
> +		}

Given the above definitions of MAX_PACKET_SIZE and RAYDIUM_FW_PAGESIZE I do not believe that firmware update is working.
What is the biggest buffer that can be sent to the device? Maybe we should allocate it dynamically?

I am also wondering about formatting command structure as byte sequence.
Would it be better to define it as:

struct raidium_cmd {
	u32 bank;
	int len;
	u8 cmd[RAIDIUM_MAX_CMD_LEN];
}

What is the longest command that the controller supports?

(Do not resubmit the driver yet, let's discuss first).

Thanks.

--
Dmitry

^ permalink raw reply	[flat|nested] 42+ messages in thread

* RE: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2016-04-11  8:24 ` Dmitry Torokhov
@ 2016-04-11  9:57   ` Jeffrey Lin (林義章)
  2016-04-14  9:28   ` Jeffrey Lin (林義章)
  1 sibling, 0 replies; 42+ messages in thread
From: Jeffrey Lin (林義章) @ 2016-04-11  9:57 UTC (permalink / raw)
  To: Dmitry Torokhov, jeffrey.lin
  Cc: rydberg, grant.likely, robh+dt, jeesw, bleung, scott.liu,
	Roger Yang (楊鎮瑋),
	KP Li (李昆倍),
	Albert Shieh (謝欣瑋),
	linux-kernel, linux-input, devicetree

Hi Dmitry:
Thank you for your response.
	Actually, we didn't implement FW update function on the chromeos device, we just copy function from our previous android device. I'll fix that on the next version. Raydium controller doesn't have access bytes limit, but my previous android experimental device had 60 bytes limit.
	
About reformatting command structure, do you want me to reform all my commands as you said in the mail or just in case of fw update function? If not just in case of FW update, it's better to meet maximum buffer size, like "MAX_PACKET_SIZE".


Best Regards
----------------------------------------------------------------------
Jeffrey Lin,林義章
瑞鼎科技
Raydium Semiconductor Corporation
Tel:(03)666-1818 Ext.4163
Fax:(03)666-1919

-----Original Message-----
From: Dmitry Torokhov [mailto:dmitry.torokhov@gmail.com] 
Sent: Monday, April 11, 2016 4:24 PM
To: jeffrey.lin
Cc: rydberg@euromail.se; grant.likely@linaro.org; robh+dt@kernel.org; jeesw@melfas.com; bleung@chromium.org; scott.liu@emc.com.tw; Jeffrey Lin (林義章); Roger Yang (楊鎮瑋); KP Li (李昆倍); Albert Shieh (謝欣瑋); linux-kernel@vger.kernel.org; linux-input@vger.kernel.org; devicetree@vger.kernel.org
Subject: Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver

Hi Jeffrey,

On Fri, Mar 25, 2016 at 01:21:09PM +0800, jeffrey.lin wrote:
> +#define MAX_PACKET_SIZE		60
...
> +#define RAYDIUM_FW_PAGESIZE	128
...
> +
> +static int raydium_i2c_send(struct i2c_client *client,
> +	u8 addr, u8 *data, size_t len)
> +{
> +	u8 buf[MAX_PACKET_SIZE + 1];
> +	int tries = 0;
> +
> +	if (len > MAX_PACKET_SIZE)
> +		return -EINVAL;
...
> +static int raydium_i2c_fw_write_page(struct i2c_client *client,
> +				const void *page)
> +{
> +	static const u8 ack_ok[] = { 0x55, 0xAA };
> +	u8 buf[2];
> +	int retry;
> +	int error;
> +
> +	for (retry = 0; retry < MAX_FW_UPDATE_RETRIES; retry++) {
> +		error = raydium_i2c_send(client, CMD_BOOT_WRT,
> +			(u8 *)page, RAYDIUM_FW_PAGESIZE);
> +		if (error) {
> +			dev_err(&client->dev,
> +				"BLDR Write Page failed: %d\n", error);
> +			continue;
> +		}

Given the above definitions of MAX_PACKET_SIZE and RAYDIUM_FW_PAGESIZE I do not believe that firmware update is working.
What is the biggest buffer that can be sent to the device? Maybe we should allocate it dynamically?

I am also wondering about formatting command structure as byte sequence.
Would it be better to define it as:

struct raidium_cmd {
	u32 bank;
	int len;
	u8 cmd[RAIDIUM_MAX_CMD_LEN];
}

What is the longest command that the controller supports?

(Do not resubmit the driver yet, let's discuss first).

Thanks.

--
Dmitry

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2016-03-25  5:21 jeffrey.lin
@ 2016-04-11  8:24 ` Dmitry Torokhov
  2016-04-11  9:57   ` Jeffrey Lin (林義章)
  2016-04-14  9:28   ` Jeffrey Lin (林義章)
  0 siblings, 2 replies; 42+ messages in thread
From: Dmitry Torokhov @ 2016-04-11  8:24 UTC (permalink / raw)
  To: jeffrey.lin
  Cc: rydberg, grant.likely, robh+dt, jeesw, bleung, scott.liu,
	jeffrey.lin, roger.yang, KP.li, albert.shieh, linux-kernel,
	linux-input, devicetree

Hi Jeffrey,

On Fri, Mar 25, 2016 at 01:21:09PM +0800, jeffrey.lin wrote:
> +#define MAX_PACKET_SIZE		60
...
> +#define RAYDIUM_FW_PAGESIZE	128
...
> +
> +static int raydium_i2c_send(struct i2c_client *client,
> +	u8 addr, u8 *data, size_t len)
> +{
> +	u8 buf[MAX_PACKET_SIZE + 1];
> +	int tries = 0;
> +
> +	if (len > MAX_PACKET_SIZE)
> +		return -EINVAL;
...
> +static int raydium_i2c_fw_write_page(struct i2c_client *client,
> +				const void *page)
> +{
> +	static const u8 ack_ok[] = { 0x55, 0xAA };
> +	u8 buf[2];
> +	int retry;
> +	int error;
> +
> +	for (retry = 0; retry < MAX_FW_UPDATE_RETRIES; retry++) {
> +		error = raydium_i2c_send(client, CMD_BOOT_WRT,
> +			(u8 *)page, RAYDIUM_FW_PAGESIZE);
> +		if (error) {
> +			dev_err(&client->dev,
> +				"BLDR Write Page failed: %d\n", error);
> +			continue;
> +		}

Given the above definitions of MAX_PACKET_SIZE and
RAYDIUM_FW_PAGESIZE I do not believe that firmware update is working.
What is the biggest buffer that can be sent to the device? Maybe we
should allocate it dynamically?

I am also wondering about formatting command structure as byte sequence.
Would it be better to define it as:

struct raidium_cmd {
	u32 bank;
	int len;
	u8 cmd[RAIDIUM_MAX_CMD_LEN];
}

What is the longest command that the controller supports?

(Do not resubmit the driver yet, let's discuss first).

Thanks.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
@ 2016-03-25  5:21 jeffrey.lin
  2016-04-11  8:24 ` Dmitry Torokhov
  0 siblings, 1 reply; 42+ messages in thread
From: jeffrey.lin @ 2016-03-25  5:21 UTC (permalink / raw)
  To: dmitry.torokhov, rydberg, grant.likely, robh+dt, jeesw, bleung,
	scott.liu
  Cc: jeffrey.lin, roger.yang, KP.li, albert.shieh, linux-kernel,
	linux-input, devicetree

Raydium I2C touch driver.

Signed-off-by: jeffrey.lin <jeffrey.lin@rad-ic.com>
---
 drivers/input/touchscreen/Kconfig          |  12 +
 drivers/input/touchscreen/Makefile         |   1 +
 drivers/input/touchscreen/raydium_i2c_ts.c | 932 +++++++++++++++++++++++++++++
 3 files changed, 945 insertions(+)
 create mode 100644 drivers/input/touchscreen/raydium_i2c_ts.c

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 3f3f6ee..df0e2ed 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -915,6 +915,18 @@ config TOUCHSCREEN_PCAP
 	  To compile this driver as a module, choose M here: the
 	  module will be called pcap_ts.
 
+config TOUCHSCREEN_RM_TS
+	tristate "Raydium I2C Touchscreen"
+	depends on I2C
+	help
+	  Say Y here if you have Raydium series I2C touchscreen,
+	  such as RM32380,connected to your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called raydium_i2c_ts.
+
 config TOUCHSCREEN_ST1232
 	tristate "Sitronix ST1232 touchscreen controllers"
 	depends on I2C
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 4941f2d..99e08cf 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE)	+= usbtouchscreen.o
 obj-$(CONFIG_TOUCHSCREEN_PCAP)		+= pcap_ts.o
 obj-$(CONFIG_TOUCHSCREEN_PENMOUNT)	+= penmount.o
 obj-$(CONFIG_TOUCHSCREEN_PIXCIR)	+= pixcir_i2c_ts.o
+obj-$(CONFIG_TOUCHSCREEN_RM_TS)		+= raydium_i2c_ts.o
 obj-$(CONFIG_TOUCHSCREEN_S3C2410)	+= s3c2410_ts.o
 obj-$(CONFIG_TOUCHSCREEN_ST1232)	+= st1232.o
 obj-$(CONFIG_TOUCHSCREEN_STMPE)		+= stmpe-ts.o
diff --git a/drivers/input/touchscreen/raydium_i2c_ts.c b/drivers/input/touchscreen/raydium_i2c_ts.c
new file mode 100644
index 0000000..e1ff49a
--- /dev/null
+++ b/drivers/input/touchscreen/raydium_i2c_ts.c
@@ -0,0 +1,932 @@
+/*
+ * Raydium touchscreen I2C driver.
+ *
+ * Copyright (C) 2012-2014, Raydium Semiconductor Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Raydium reserves the right to make changes without further notice
+ * to the materials described herein. Raydium does not assume any
+ * liability arising out of the application described herein.
+ *
+ * Contact Raydium Semiconductor Corporation at www.rad-ic.com
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/firmware.h>
+#include <linux/input/mt.h>
+#include <linux/acpi.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+#include <asm/unaligned.h>
+#include <linux/gpio/consumer.h>
+
+/* Device, Driver information */
+#define DEVICE_NAME	"rm_ts"
+
+/* Slave I2C mode*/
+#define RM_BOOT_BLDR	0x02
+#define RM_BOOT_MAIN	0x03
+
+/*I2C command */
+#define CMD_QUERY_BANK		0x2B
+#define CMD_DATA_BANK		0x4D
+#define CMD_ENTER_SLEEP		0x4E
+#define CMD_BOOT_ACK		0x0A
+#define CMD_BOOT_WRT		0x5B
+#define CMD_BOOT_CHK		0x0C
+#define CMD_BANK_SWITCH		0xAA
+
+/* Touch relative info */
+#define MAX_RETRIES		3
+#define MAX_FW_UPDATE_RETRIES	30
+#define MAX_TOUCH_NUM		10
+#define MAX_PACKET_SIZE		60
+#define BOOT_DELAY_MS	100
+
+#define RAYDIUM_FW_PAGESIZE	128
+#define RAYDIUM_POWERON_DELAY_USEC	500
+#define RAYDIUM_RESET_DELAY_MSEC	50
+
+#define ADDR_INDEX		0x03
+#define DATA_INDEX		0x04
+
+#define HEADER_SIZE		4
+
+enum raydium_boot_mode {
+	RAYDIUM_TS_MAIN = 0,
+	RAYDIUM_TS_BLDR,
+};
+
+enum raydium_abs_idx {
+	POS_STATE = 0,/*1:touch, 0:no touch*/
+	POS_X,
+	POS_Y = 3,
+	POS_PRESSURE,
+	WIDTH_X,
+	WIDTH_Y,
+};
+
+struct raydium_info {
+	u32 hw_ver;	/*device ver, __le32*/
+	u8 main_ver;
+	u8 sub_ver;
+	u16 ft_ver;	/*test ver, __le16*/
+	u8 x_num;
+	u8 y_num;
+	u16 x_max;	/*disp reso, __le16*/
+	u16 y_max;	/*disp reso, __le16*/
+	u8 x_res;		/* units/mm */
+	u8 y_res;		/* units/mm */
+};
+
+struct raydium_object {
+	u32 data_bank_addr;
+	u8 pkg_size;
+	u8 tp_info_size;
+};
+
+/* struct raydium_data - represents state of Raydium touchscreen device */
+struct raydium_data {
+	struct i2c_client *client;
+	struct input_dev *input;
+
+	struct regulator *avdd;
+	struct regulator *vccio;
+	struct gpio_desc *reset_gpio;
+
+	u32 query_bank_info;
+
+	struct raydium_info info;
+	struct raydium_object obj;
+	enum raydium_boot_mode boot_mode;
+
+	struct mutex sysfs_mutex;
+	struct completion cmd_done;
+
+	bool wake_irq_enabled;
+};
+
+static int raydium_i2c_send(struct i2c_client *client,
+	u8 addr, u8 *data, size_t len)
+{
+	u8 buf[MAX_PACKET_SIZE + 1];
+	int tries = 0;
+
+	if (len > MAX_PACKET_SIZE)
+		return -EINVAL;
+
+	buf[0] = addr;
+	memcpy(&buf[1], data, len);
+
+	do {
+		if (i2c_master_send(client, buf, len + 1) == (len + 1))
+			return 0;
+		msleep(20);
+	} while (++tries < MAX_RETRIES);
+
+	dev_err(&client->dev, "%s: i2c send failed\n", __func__);
+
+	return -EIO;
+}
+
+static int raydium_i2c_read(struct i2c_client *client,
+	u8 addr, size_t len, void *data)
+{
+	struct i2c_msg xfer[2];
+	int ret;
+
+	/* Write register */
+	xfer[0].addr = client->addr;
+	xfer[0].flags = 0;
+	xfer[0].len = 1;
+	xfer[0].buf = &addr;
+
+	/* Read data */
+	xfer[1].addr = client->addr;
+	xfer[1].flags = I2C_M_RD;
+	xfer[1].len = len;
+	xfer[1].buf = data;
+
+	ret = i2c_transfer(client->adapter, xfer, ARRAY_SIZE(xfer));
+	if (ret < 0)
+		return ret;
+
+	if (ret != ARRAY_SIZE(xfer))
+		return -EIO;
+
+	return 0;
+}
+
+static int raydium_i2c_read_message(struct i2c_client *client,
+	u32 addr, size_t len, void *data)
+{
+	u16 pkg_size, use_len;
+	u8 buf[HEADER_SIZE], idx_i;
+	int error;
+
+	use_len = len;
+	idx_i = 0;
+	while (use_len > 0) {
+		pkg_size = (use_len < MAX_PACKET_SIZE) ?
+			use_len : MAX_PACKET_SIZE;
+
+		put_unaligned_be32(addr, buf);
+
+		/*set data bank*/
+		error = raydium_i2c_send(client, CMD_BANK_SWITCH,
+			(u8 *)buf, HEADER_SIZE);
+		/*read potints data*/
+		if (!error)
+			error = raydium_i2c_read(client, buf[ADDR_INDEX],
+				pkg_size,
+				data + idx_i*MAX_PACKET_SIZE);
+
+		pkg_size += MAX_PACKET_SIZE;
+		addr += MAX_PACKET_SIZE;
+		use_len = (use_len < MAX_PACKET_SIZE) ?
+			0 : (use_len - MAX_PACKET_SIZE);
+		idx_i++;
+	}
+
+	return error;
+}
+
+static int raydium_i2c_send_message(struct i2c_client *client,
+	size_t len, void *data)
+{
+	int error;
+	__le32 cmd;
+
+	cmd = get_unaligned_le32(data);
+
+	/*set data bank*/
+	error = raydium_i2c_send(client, CMD_BANK_SWITCH, (u8 *)&cmd,
+		HEADER_SIZE);
+
+	/*send message*/
+	if (!error)
+		error = raydium_i2c_send(client, ((u8 *)data)[ADDR_INDEX],
+			data + DATA_INDEX, len);
+
+	return error;
+}
+
+static int raydium_i2c_sw_reset(struct i2c_client *client)
+{
+	static const u8 soft_rst_cmd[] = {0x40, 0x00, 0x00, 0x04, 0x01};
+	int error;
+
+	error = raydium_i2c_send_message(client, ARRAY_SIZE(soft_rst_cmd),
+		(void *)soft_rst_cmd);
+	if (error) {
+		dev_err(&client->dev, "software reset failed: %d\n", error);
+		return error;
+	}
+
+	msleep(RAYDIUM_RESET_DELAY_MSEC);
+
+	return 0;
+}
+
+static int raydium_i2c_query_ts_info(struct raydium_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	int error, retry_cnt;
+
+	for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
+		error = raydium_i2c_read(client, CMD_DATA_BANK,
+			sizeof(ts->obj), (void *)&ts->obj);
+			ts->obj.data_bank_addr =
+				get_unaligned_le32(&ts->obj.data_bank_addr);
+
+		if (!error) {
+			error = raydium_i2c_read(client, CMD_QUERY_BANK,
+				sizeof(ts->query_bank_info),
+				(void *)&ts->query_bank_info);
+			if (!error) {
+				error = raydium_i2c_read_message(client,
+					ts->query_bank_info, sizeof(ts->info),
+					(void *)&ts->info);
+
+				ts->info.hw_ver =
+					get_unaligned_le32(&ts->info.hw_ver);
+				ts->info.ft_ver =
+					get_unaligned_le16(&ts->info.ft_ver);
+				ts->info.x_max =
+					get_unaligned_le16(&ts->info.x_max);
+				ts->info.y_max =
+					get_unaligned_le16(&ts->info.y_max);
+
+				return 0;
+			}
+		}
+	}
+	dev_err(&client->dev, "Get touch data failed: %d\n", error);
+
+	return -EINVAL;
+}
+
+static int raydium_i2c_fastboot(struct i2c_client *client)
+{
+	static const u8 boot_cmd[] = { 0x50, 0x00, 0x06, 0x20 };
+	u8 buf[HEADER_SIZE];
+	int error;
+
+	error = raydium_i2c_read_message(client,
+		get_unaligned_be32(boot_cmd),
+		sizeof(boot_cmd), buf);
+
+	if (!error) {
+		if (buf[0] == RM_BOOT_BLDR) {
+			dev_dbg(&client->dev, "boot in fastboot mode\n");
+			return -EINVAL;
+		}
+		dev_dbg(&client->dev, "boot success -- 0x%x\n", client->addr);
+		return 0;
+	}
+
+	dev_err(&client->dev, "boot failed: %d\n", error);
+
+	return error;
+}
+
+static int raydium_i2c_initialize(struct raydium_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	int error, retry_cnt;
+	static const u8 recov_packet[] = { 0x04, 0x81 };
+	u8 buf[HEADER_SIZE];
+
+	for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
+		error = raydium_i2c_fastboot(client);
+		if (error) {
+			/* Continue initializing if it's the last try */
+			if (retry_cnt < MAX_RETRIES - 1)
+				continue;
+		}
+		/* Wait for Hello packet */
+		msleep(BOOT_DELAY_MS);
+
+		error = raydium_i2c_read(client, recov_packet[0], 1,
+			(void *)buf);
+		if (error) {
+			dev_err(&client->dev,
+				"failed to read 'hello' packet: %d\n", error);
+		} else if (buf[0] == recov_packet[1]) {
+			ts->boot_mode = RAYDIUM_TS_MAIN;
+			break;
+		}
+	}
+
+	if (error)
+		ts->boot_mode = RAYDIUM_TS_BLDR;
+	else
+		raydium_i2c_query_ts_info(ts);
+
+	return error;
+}
+
+static int raydium_i2c_fw_write_page(struct i2c_client *client,
+				const void *page)
+{
+	static const u8 ack_ok[] = { 0x55, 0xAA };
+	u8 buf[2];
+	int retry;
+	int error;
+
+	for (retry = 0; retry < MAX_FW_UPDATE_RETRIES; retry++) {
+		error = raydium_i2c_send(client, CMD_BOOT_WRT,
+			(u8 *)page, RAYDIUM_FW_PAGESIZE);
+		if (error) {
+			dev_err(&client->dev,
+				"BLDR Write Page failed: %d\n", error);
+			continue;
+		}
+
+		error = raydium_i2c_read(client, CMD_BOOT_CHK, sizeof(ack_ok),
+			(void *)buf);
+		if (error) {
+			dev_err(&client->dev,
+				"BLDR Ack read failed: %d\n", error);
+			return error;
+		}
+
+		if (!memcmp(buf, ack_ok, sizeof(ack_ok)))
+			return 0;
+
+		error = -EIO;
+		dev_err(&client->dev,
+			"BLDR Get Ack Error [%02x:%02x]\n", buf[0], buf[1]);
+	}
+
+	return error;
+}
+
+static int raydium_i2c_do_update_firmware(struct i2c_client *client,
+					 const struct firmware *fw,
+					 bool force)
+{
+	static const u8 boot_cmd[] = { 0x50, 0x00, 0x06, 0x20 , RM_BOOT_BLDR };
+	static const u8 main_cmd[] = { 0x50, 0x00, 0x06, 0x20, RM_BOOT_MAIN };
+	static const u8 boot_ack[] = { 0x55, 0xAA, 0x00, 0xFF };
+	u8 buf[HEADER_SIZE];
+	int page, n_fw_pages;
+	int error;
+
+	/* Recovery mode detection! */
+	if (!force) {
+		/* Start boot loader Procedure */
+		dev_dbg(&client->dev, "Normal BLDR procedure\n");
+		/* switch to mode */
+		error = raydium_i2c_send_message(client, 1, (void *)boot_cmd);
+		if (error)
+			dev_err(&client->dev, "failed to send boot cmd: %d\n",
+				error);
+		msleep(60);
+		raydium_i2c_sw_reset(client);
+		msleep(RAYDIUM_RESET_DELAY_MSEC);
+		error = raydium_i2c_send_message(client, 1, (void *)boot_cmd);
+	}
+
+	if (error) {
+		dev_err(&client->dev, "failed to enter fastboot mode: %d\n",
+			error);
+		return error;
+	}
+
+	msleep(20);
+
+	/* check fastboot state */
+	error = raydium_i2c_read(client, CMD_BOOT_ACK,
+		sizeof(boot_ack), (void *)buf);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to read boot ack: %d\n", error);
+		return error;
+	}
+
+	if (memcmp(buf, boot_ack, sizeof(boot_ack))) {
+		dev_err(&client->dev,
+			"failed to enter fastboot: %*ph (expected %*ph)\n",
+			(int)sizeof(buf), buf, (int)sizeof(boot_ack), boot_ack);
+		return -EIO;
+	}
+
+	dev_info(&client->dev, "successfully entered fastboot mode");
+
+	n_fw_pages = fw->size / RAYDIUM_FW_PAGESIZE;
+	dev_dbg(&client->dev, "BLDR Pages = %d\n", n_fw_pages);
+
+	for (page = 0; page < n_fw_pages; page++) {
+		error = raydium_i2c_fw_write_page(client,
+					fw->data + page * RAYDIUM_FW_PAGESIZE);
+		if (error) {
+			dev_err(&client->dev,
+				"failed to write FW page %d: %d\n",
+				page, error);
+			return error;
+		}
+	}
+	error = raydium_i2c_send_message(client, 1, (void *)main_cmd);
+	msleep(20);
+	raydium_i2c_sw_reset(client);
+	dev_info(&client->dev, "firmware update completed\n");
+
+	return 0;
+}
+
+static int raydium_i2c_fw_update(struct raydium_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	const struct firmware *fw;
+	char *fw_name;
+	int error;
+
+	fw_name = kasprintf(GFP_KERNEL, "raydium_i2c_%04x.bin",
+		ts->info.hw_ver & 0xFFFF);
+	if (!fw_name)
+		return -ENOMEM;
+
+	dev_info(&client->dev, "requesting fw name = %s\n", fw_name);
+	error = request_firmware(&fw, fw_name, &client->dev);
+	kfree(fw_name);
+	if (error) {
+		dev_err(&client->dev, "failed to request firmware: %d\n",
+			error);
+		return error;
+	}
+
+	if (fw->size % RAYDIUM_FW_PAGESIZE) {
+		dev_err(&client->dev, "invalid firmware length: %zu\n",
+			fw->size);
+		error = -EINVAL;
+		goto out;
+	}
+
+	disable_irq(client->irq);
+	error = raydium_i2c_do_update_firmware(client, fw,
+					ts->boot_mode == RAYDIUM_TS_BLDR);
+	if (error) {
+		dev_err(&client->dev, "firmware update failed: %d\n", error);
+		ts->boot_mode = RAYDIUM_TS_BLDR;
+		goto out_enable_irq;
+	}
+	error = raydium_i2c_initialize(ts);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to initialize device after firmware update: %d\n",
+			error);
+		ts->boot_mode = RAYDIUM_TS_BLDR;
+		goto out_enable_irq;
+	}
+
+	ts->boot_mode = RAYDIUM_TS_MAIN;
+
+out_enable_irq:
+	enable_irq(client->irq);
+	msleep(100);
+out:
+	release_firmware(fw);
+	return error;
+}
+
+static void raydium_mt_event(struct raydium_data *ts)
+{
+	u8 data[MAX_PACKET_SIZE];
+	int error, i;
+	int x, y, f_state, pressure, wx, wy;
+
+	error = raydium_i2c_read_message(ts->client, ts->obj.data_bank_addr,
+		ts->obj.pkg_size, (void *)data);
+
+	if (error < 0) {
+		dev_err(&ts->client->dev, "%s: failed to read data: %d\n",
+			__func__, error);
+		return;
+	}
+
+	for (i = 0; i < MAX_TOUCH_NUM; i++) {
+		f_state = (data + i * ts->obj.tp_info_size)[POS_STATE];
+		pressure = (data + i * ts->obj.tp_info_size)[POS_PRESSURE];
+		wx = (data + i * ts->obj.tp_info_size)[WIDTH_X];
+		wy = (data + i * ts->obj.tp_info_size)[WIDTH_Y];
+
+		input_mt_slot(ts->input, i);
+		input_mt_report_slot_state(ts->input,
+				MT_TOOL_FINGER, f_state != 0);
+
+		if (!f_state)
+			continue;
+		x = get_unaligned_le16(&data[i * ts->obj.tp_info_size + POS_X]);
+		y = get_unaligned_le16(&data[i * ts->obj.tp_info_size + POS_Y]);
+
+		input_report_abs(ts->input, ABS_MT_POSITION_X, x);
+		input_report_abs(ts->input, ABS_MT_POSITION_Y, y);
+		input_report_abs(ts->input, ABS_MT_PRESSURE, pressure);
+		input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR,
+			max(wx, wy));
+		input_report_abs(ts->input, ABS_MT_TOUCH_MINOR,
+			min(wx, wy));
+	}
+
+	input_mt_sync_frame(ts->input);
+	input_sync(ts->input);
+}
+
+static irqreturn_t raydium_i2c_irq(int irq, void *_dev)
+{
+	struct raydium_data *ts = _dev;
+
+	if (ts->boot_mode != RAYDIUM_TS_BLDR)
+		raydium_mt_event(ts);
+
+	return IRQ_HANDLED;
+}
+
+static ssize_t write_update_fw(struct device *dev,
+			struct device_attribute *attr,
+			const char *buf, size_t count)
+{
+	struct raydium_data *ts = dev_get_drvdata(dev);
+	int error;
+
+	error = mutex_lock_interruptible(&ts->sysfs_mutex);
+	if (error)
+		return error;
+
+	error = raydium_i2c_fw_update(ts);
+	dev_dbg(dev, "firmware update result: %d\n", error);
+
+	mutex_unlock(&ts->sysfs_mutex);
+	return error ?: count;
+}
+
+static ssize_t raydium_bootmode_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct raydium_data *ts = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%s\n", ts->boot_mode == RAYDIUM_TS_MAIN ?
+		"Normal" : "Recovery");
+}
+
+static ssize_t raydium_fw_ver_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct raydium_data *ts = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%d.%d\n", ts->info.main_ver, ts->info.sub_ver);
+}
+
+static ssize_t raydium_hw_ver_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct raydium_data *ts = dev_get_drvdata(dev);
+
+	return sprintf(buf, "0x%04x\n", ts->info.hw_ver);
+}
+
+static DEVICE_ATTR(fw_version, S_IRUGO, raydium_fw_ver_show, NULL);
+static DEVICE_ATTR(hw_version, S_IRUGO, raydium_hw_ver_show, NULL);
+static DEVICE_ATTR(boot_mode, S_IRUGO, raydium_bootmode_show, NULL);
+static DEVICE_ATTR(update_fw, S_IWUSR, NULL, write_update_fw);
+
+static struct attribute *raydium_attributes[] = {
+	&dev_attr_update_fw.attr,
+	&dev_attr_boot_mode.attr,
+	&dev_attr_fw_version.attr,
+	&dev_attr_hw_version.attr,
+	NULL
+};
+
+static struct attribute_group raydium_attribute_group = {
+	.attrs = raydium_attributes,
+};
+
+static void raydium_i2c_remove_sysfs_group(void *_data)
+{
+	struct raydium_data *ts = _data;
+
+	sysfs_remove_group(&ts->client->dev.kobj, &raydium_attribute_group);
+}
+
+static int raydium_i2c_power_on(struct raydium_data *ts)
+{
+	int error;
+
+	if (IS_ERR_OR_NULL(ts->reset_gpio))
+		return 0;
+
+	gpiod_set_value_cansleep(ts->reset_gpio, 1);
+
+	error = regulator_enable(ts->avdd);
+	if (error) {
+		dev_err(&ts->client->dev,
+			"failed to enable avdd regulator: %d\n", error);
+		goto release_reset_gpio;
+	}
+
+	error = regulator_enable(ts->vccio);
+	if (error) {
+		regulator_disable(ts->avdd);
+		dev_err(&ts->client->dev,
+			"failed to enable vccio regulator: %d\n", error);
+		goto release_reset_gpio;
+	}
+
+	udelay(RAYDIUM_POWERON_DELAY_USEC);
+
+release_reset_gpio:
+	gpiod_set_value_cansleep(ts->reset_gpio, 0);
+
+	if (error)
+		return error;
+
+	msleep(RAYDIUM_RESET_DELAY_MSEC);
+
+	return 0;
+}
+
+static void raydium_i2c_power_off(void *_data)
+{
+	struct raydium_data *ts = _data;
+
+	if (!IS_ERR_OR_NULL(ts->reset_gpio)) {
+		gpiod_set_value_cansleep(ts->reset_gpio, 1);
+		regulator_disable(ts->vccio);
+		regulator_disable(ts->avdd);
+	}
+}
+
+static int raydium_i2c_probe(struct i2c_client *client,
+			    const struct i2c_device_id *id)
+{
+	union i2c_smbus_data dummy;
+	struct raydium_data *ts;
+	unsigned long irqflags;
+	int error;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		dev_err(&client->dev,
+			"%s: i2c check functionality error\n", DEVICE_NAME);
+		return -ENXIO;
+	}
+
+	ts = devm_kzalloc(&client->dev, sizeof(struct raydium_data),
+		GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+
+	mutex_init(&ts->sysfs_mutex);
+	init_completion(&ts->cmd_done);
+
+	ts->client = client;
+	i2c_set_clientdata(client, ts);
+
+	ts->avdd = devm_regulator_get(&client->dev, "avdd");
+	if (IS_ERR(ts->avdd)) {
+		error = PTR_ERR(ts->avdd);
+		if (error != -EPROBE_DEFER)
+			dev_err(&client->dev,
+				"Failed to get 'avdd' regulator: %d\n", error);
+		return error;
+	}
+
+	ts->vccio = devm_regulator_get(&client->dev, "vccio");
+	if (IS_ERR(ts->vccio)) {
+		error = PTR_ERR(ts->vccio);
+		if (error != -EPROBE_DEFER)
+			dev_err(&client->dev,
+				"Failed to get 'vccio' regulator: %d\n", error);
+		return error;
+	}
+
+	ts->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
+		GPIOD_OUT_LOW);
+	if (IS_ERR(ts->reset_gpio)) {
+		error = PTR_ERR(ts->reset_gpio);
+		if (error != -EPROBE_DEFER) {
+			dev_err(&client->dev,
+				"failed to get reset gpio: %d\n", error);
+			return error;
+		}
+	}
+
+	error = raydium_i2c_power_on(ts);
+	if (error)
+		return error;
+
+	error = devm_add_action(&client->dev, raydium_i2c_power_off, ts);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to install power off action: %d\n", error);
+		raydium_i2c_power_off(ts);
+		return error;
+	}
+
+	/* Make sure there is something at this address */
+	if (i2c_smbus_xfer(client->adapter, client->addr, 0,
+			I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0) {
+		dev_err(&client->dev, "nothing at this address\n");
+		return -ENXIO;
+	}
+
+	error = raydium_i2c_initialize(ts);
+	if (error) {
+		dev_err(&client->dev, "failed to initialize: %d\n", error);
+		return error;
+	}
+
+	ts->input = devm_input_allocate_device(&client->dev);
+	if (!ts->input) {
+		dev_err(&client->dev, "Failed to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	ts->input->name = "Raydium Touchscreen";
+	ts->input->id.bustype = BUS_I2C;
+
+	__set_bit(BTN_TOUCH, ts->input->keybit);
+	__set_bit(EV_ABS, ts->input->evbit);
+	__set_bit(EV_KEY, ts->input->evbit);
+
+	/* Multitouch input params setup */
+	input_set_abs_params(ts->input, ABS_MT_POSITION_X, 0,
+		ts->info.x_max, 0, 0);
+	input_set_abs_params(ts->input, ABS_MT_POSITION_Y,
+		0, ts->info.y_max, 0, 0);
+	input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+	input_set_abs_params(ts->input, ABS_MT_PRESSURE, 0, 255, 0, 0);
+	input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->info.x_res);
+	input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->info.y_res);
+
+	input_mt_init_slots(ts->input, MAX_TOUCH_NUM, 0);
+
+	input_set_drvdata(ts->input, ts);
+
+	error = input_mt_init_slots(ts->input, MAX_TOUCH_NUM,
+		INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to initialize MT slots: %d\n", error);
+		return error;
+	}
+
+	error = input_register_device(ts->input);
+	if (error) {
+		dev_err(&client->dev,
+			"unable to register input device: %d\n", error);
+		return error;
+	}
+
+	error = devm_request_threaded_irq(&client->dev, client->irq,
+			NULL, raydium_i2c_irq, irqflags | IRQF_ONESHOT,
+			client->name, ts);
+	if (error) {
+		dev_err(&client->dev, "Failed to register interrupt\n");
+		return error;
+	}
+
+	device_init_wakeup(&client->dev, true);
+
+	error = sysfs_create_group(&client->dev.kobj, &raydium_attribute_group);
+	if (error) {
+		dev_err(&client->dev, "failed to create sysfs attributes: %d\n",
+			error);
+		return error;
+	}
+
+	error = devm_add_action(&client->dev,
+				raydium_i2c_remove_sysfs_group, ts);
+	if (error) {
+		raydium_i2c_remove_sysfs_group(ts);
+		dev_err(&client->dev,
+			"Failed to add sysfs cleanup action: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int raydium_i2c_remove(struct i2c_client *client)
+{
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	input_unregister_device(ts->input);
+
+	device_init_wakeup(&client->dev, false);
+
+	mutex_destroy(&ts->sysfs_mutex);
+
+	return 0;
+}
+
+static void __maybe_unused raydium_enter_sleep(struct i2c_client *client)
+{
+	static const u8 sleep_cmd[] = { 0x5A, 0xff, 0x00, 0x0f };
+	int error;
+
+	error = raydium_i2c_send(client, CMD_ENTER_SLEEP, (u8 *)sleep_cmd,
+		sizeof(sleep_cmd));
+	if (error)
+		dev_err(&client->dev,
+			"Send sleep failed: %d\n", error);
+}
+
+static int __maybe_unused raydium_i2c_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	/* Command not support in BLDR recovery mode */
+	if (ts->boot_mode != RAYDIUM_TS_MAIN)
+		return -EBUSY;
+
+	disable_irq(client->irq);
+
+	if (device_may_wakeup(dev)) {
+		raydium_enter_sleep(client);
+
+		ts->wake_irq_enabled = (enable_irq_wake(client->irq) == 0);
+	} else {
+		raydium_i2c_power_off(ts);
+	}
+
+	return 0;
+}
+
+static int __maybe_unused raydium_i2c_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	if (device_may_wakeup(dev)) {
+		if (ts->wake_irq_enabled)
+			disable_irq_wake(client->irq);
+		raydium_i2c_sw_reset(client);
+	} else {
+		raydium_i2c_power_on(ts);
+		raydium_i2c_initialize(ts);
+	}
+
+	enable_irq(client->irq);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(raydium_i2c_pm_ops,
+			 raydium_i2c_suspend, raydium_i2c_resume);
+
+static const struct i2c_device_id raydium_i2c_id[] = {
+	{ DEVICE_NAME, 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, raydium_i2c_id);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id raydium_acpi_id[] = {
+	{ "RAYD0001", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(acpi, raydium_acpi_id);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id raydium_of_match[] = {
+	{ .compatible = "raydium,rm32380",},
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, raydium_of_match);
+#endif
+
+static struct i2c_driver raydium_i2c_driver = {
+	.probe = raydium_i2c_probe,
+	.remove = raydium_i2c_remove,
+	.id_table = raydium_i2c_id,
+	.driver = {
+		.name = "raydium_ts",
+		.pm = &raydium_i2c_pm_ops,
+		.acpi_match_table = ACPI_PTR(raydium_acpi_id),
+		.of_match_table = of_match_ptr(raydium_of_match),
+	},
+};
+
+module_i2c_driver(raydium_i2c_driver);
+
+MODULE_AUTHOR("Raydium");
+MODULE_DESCRIPTION("Raydium I2c Touchscreen driver");
+MODULE_LICENSE("GPL v2");
-- 
2.1.2


^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
@ 2016-03-22  6:23 jeffrey.lin
  0 siblings, 0 replies; 42+ messages in thread
From: jeffrey.lin @ 2016-03-22  6:23 UTC (permalink / raw)
  To: dmitry.torokhov, rydberg, grant.likely, robh+dt, jeesw, bleung,
	scott.liu
  Cc: jeffrey.lin, roger.yang, KP.li, albert.shieh, linux-kernel,
	linux-input, devicetree

Raydium I2C touch driver.

Signed-off-by: jeffrey.lin <jeffrey.lin@rad-ic.com>
---
 drivers/input/touchscreen/Kconfig          |  13 +
 drivers/input/touchscreen/Makefile         |   1 +
 drivers/input/touchscreen/raydium_i2c_ts.c | 932 +++++++++++++++++++++++++++++
 3 files changed, 946 insertions(+)
 create mode 100644 drivers/input/touchscreen/raydium_i2c_ts.c

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 3f3f6ee..9adacf6 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -915,6 +915,19 @@ config TOUCHSCREEN_PCAP
 	  To compile this driver as a module, choose M here: the
 	  module will be called pcap_ts.
 
+config TOUCHSCREEN_RM_TS
+	tristate "Raydium I2C Touchscreen"
+	depends on I2C
+	help
+	  Say Y here if you have Raydium series I2C touchscreen,
+	  such as RM31100 , connected to your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called raydium_i2c_ts.
+
+
 config TOUCHSCREEN_ST1232
 	tristate "Sitronix ST1232 touchscreen controllers"
 	depends on I2C
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 4941f2d..99e08cf 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE)	+= usbtouchscreen.o
 obj-$(CONFIG_TOUCHSCREEN_PCAP)		+= pcap_ts.o
 obj-$(CONFIG_TOUCHSCREEN_PENMOUNT)	+= penmount.o
 obj-$(CONFIG_TOUCHSCREEN_PIXCIR)	+= pixcir_i2c_ts.o
+obj-$(CONFIG_TOUCHSCREEN_RM_TS)		+= raydium_i2c_ts.o
 obj-$(CONFIG_TOUCHSCREEN_S3C2410)	+= s3c2410_ts.o
 obj-$(CONFIG_TOUCHSCREEN_ST1232)	+= st1232.o
 obj-$(CONFIG_TOUCHSCREEN_STMPE)		+= stmpe-ts.o
diff --git a/drivers/input/touchscreen/raydium_i2c_ts.c b/drivers/input/touchscreen/raydium_i2c_ts.c
new file mode 100644
index 0000000..d4184e2
--- /dev/null
+++ b/drivers/input/touchscreen/raydium_i2c_ts.c
@@ -0,0 +1,932 @@
+/*
+ * Raydium touchscreen I2C driver.
+ *
+ * Copyright (C) 2012-2014, Raydium Semiconductor Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Raydium reserves the right to make changes without further notice
+ * to the materials described herein. Raydium does not assume any
+ * liability arising out of the application described herein.
+ *
+ * Contact Raydium Semiconductor Corporation at www.rad-ic.com
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/firmware.h>
+#include <linux/input/mt.h>
+#include <linux/acpi.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+#include <asm/unaligned.h>
+#include <linux/gpio/consumer.h>
+
+/* Device, Driver information */
+#define DEVICE_NAME	"rm3xxxx"
+
+/* Slave I2C mode*/
+#define RM_BOOT_BLDR	0x02
+#define RM_BOOT_MAIN	0x03
+
+/*I2C command */
+#define CMD_QUERY_BANK		0x2B
+#define CMD_DATA_BANK		0x4D
+#define CMD_ENTER_SLEEP		0x4E
+#define CMD_BOOT_ACK		0x0A
+#define CMD_BOOT_WRT		0x5B
+#define CMD_BOOT_CHK		0x0C
+#define CMD_BANK_SWITCH		0xAA
+
+/* Touch relative info */
+#define MAX_RETRIES		3
+#define MAX_FW_UPDATE_RETRIES	30
+#define MAX_TOUCH_NUM		10
+#define MAX_PACKET_SIZE		60
+#define BOOT_DELAY_MS	100
+
+#define RAYDIUM_FW_PAGESIZE	128
+#define RAYDIUM_POWERON_DELAY_USEC	500
+#define RAYDIUM_RESET_DELAY_MSEC	50
+
+#define ADDR_INDEX		0x03
+#define DATA_INDEX		0x04
+
+#define HEADER_SIZE		4
+
+enum raydium_boot_mode {
+	RAYDIUM_TS_MAIN = 0,
+	RAYDIUM_TS_BLDR,
+};
+
+enum raydium_abs_idx {
+	POS_STATE = 0,/*1:touch, 0:no touch*/
+	POS_X,
+	POS_Y = 3,
+	POS_PRESSURE,
+	WIDTH_X,
+	WIDTH_Y,
+};
+
+struct raydium_info {
+	u32 hw_ver;	/*device ver, __le32*/
+	u8 main_ver;
+	u8 sub_ver;
+	u16 ft_ver;	/*test ver, __le16*/
+	u8 x_num;
+	u8 y_num;
+	u16 x_max;	/*disp reso, __le16*/
+	u16 y_max;	/*disp reso, __le16*/
+	u8 x_res;		/* units/mm */
+	u8 y_res;		/* units/mm */
+};
+
+struct raydium_object {
+	u32 data_bank_addr;
+	u8 pkg_size;
+	u8 tp_info_size;
+};
+
+/* struct raydium_data - represents state of Raydium touchscreen device */
+struct raydium_data {
+	struct i2c_client *client;
+	struct input_dev *input;
+
+	struct regulator *avdd;
+	struct regulator *vccio;
+	struct gpio_desc *reset_gpio;
+
+	u32 query_bank_info;
+
+	struct raydium_info info;
+	struct raydium_object obj;
+	enum raydium_boot_mode boot_mode;
+
+	struct mutex sysfs_mutex;
+	struct completion cmd_done;
+
+	bool wake_irq_enabled;
+};
+
+static int raydium_i2c_send(struct i2c_client *client,
+	u8 addr, u8 *data, size_t len)
+{
+	u8 buf[MAX_PACKET_SIZE + 1];
+	int tries = 0;
+
+	if (len > MAX_PACKET_SIZE)
+		return -EINVAL;
+
+	buf[0] = addr;
+	memcpy(&buf[1], data, len);
+
+	do {
+		if (i2c_master_send(client, buf, len + 1) == (len + 1))
+			return 0;
+		msleep(20);
+	} while (++tries < MAX_RETRIES);
+
+	dev_err(&client->dev, "%s: i2c send failed\n", __func__);
+
+	return -EIO;
+}
+
+static int raydium_i2c_read(struct i2c_client *client,
+	u8 addr, size_t len, void *data)
+{
+	struct i2c_msg xfer[2];
+	int ret;
+
+	/* Write register */
+	xfer[0].addr = client->addr;
+	xfer[0].flags = 0;
+	xfer[0].len = 1;
+	xfer[0].buf = &addr;
+
+	/* Read data */
+	xfer[1].addr = client->addr;
+	xfer[1].flags = I2C_M_RD;
+	xfer[1].len = len;
+	xfer[1].buf = data;
+
+	ret = i2c_transfer(client->adapter, xfer, ARRAY_SIZE(xfer));
+	if (ret < 0)
+		return ret;
+
+	if (ret != ARRAY_SIZE(xfer))
+		return -EIO;
+
+	return 0;
+}
+
+static int raydium_i2c_read_message(struct i2c_client *client,
+	u32 addr, size_t len, void *data)
+{
+	u16 pkg_size, use_len;
+	u8 buf[HEADER_SIZE], idx_i;
+	int error;
+
+	use_len = len;
+	idx_i = 0;
+	while (use_len > 0) {
+		pkg_size = (use_len < MAX_PACKET_SIZE) ?
+			use_len : MAX_PACKET_SIZE;
+
+		put_unaligned_be32(addr, buf);
+
+		/*set data bank*/
+		error = raydium_i2c_send(client, CMD_BANK_SWITCH,
+			(u8 *)buf, HEADER_SIZE);
+		/*read potints data*/
+		if (!error)
+			error = raydium_i2c_read(client, buf[ADDR_INDEX],
+				pkg_size,
+				data + idx_i*MAX_PACKET_SIZE);
+
+		pkg_size += MAX_PACKET_SIZE;
+		addr += MAX_PACKET_SIZE;
+		use_len = (use_len < MAX_PACKET_SIZE) ?
+			0 : (use_len - MAX_PACKET_SIZE);
+		idx_i++;
+	}
+
+	return error;
+}
+
+static int raydium_i2c_send_message(struct i2c_client *client,
+	size_t len, void *data)
+{
+	int error;
+	__le32 cmd;
+
+	cmd = get_unaligned_le32(data);
+
+	/*set data bank*/
+	error = raydium_i2c_send(client, CMD_BANK_SWITCH, (u8 *)&cmd,
+		HEADER_SIZE);
+
+	/*send message*/
+	if (!error)
+		error = raydium_i2c_send(client, ((u8 *)data)[ADDR_INDEX],
+			data + DATA_INDEX, len);
+
+	return error;
+}
+
+static int raydium_i2c_sw_reset(struct i2c_client *client)
+{
+	static const u8 soft_rst_cmd[] = {0x40, 0x00, 0x00, 0x04, 0x01};
+	int error;
+
+	error = raydium_i2c_send_message(client, ARRAY_SIZE(soft_rst_cmd),
+		(void *)soft_rst_cmd);
+	if (error) {
+		dev_err(&client->dev, "software reset failed: %d\n", error);
+		return error;
+	}
+
+	msleep(RAYDIUM_RESET_DELAY_MSEC);
+
+	return 0;
+}
+
+static int raydium_i2c_query_ts_info(struct raydium_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	int error, retry_cnt;
+
+	for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
+		error = raydium_i2c_read(client, CMD_DATA_BANK,
+			sizeof(ts->obj), (void *)&ts->obj);
+			ts->obj.data_bank_addr =
+				get_unaligned_le32(&ts->obj.data_bank_addr);
+
+		if (!error) {
+			error = raydium_i2c_read(client, CMD_QUERY_BANK,
+				sizeof(ts->query_bank_info),
+				(void *)&ts->query_bank_info);
+			if (!error) {
+				error = raydium_i2c_read_message(client,
+					ts->query_bank_info, sizeof(ts->info),
+					(void *)&ts->info);
+
+				ts->info.hw_ver =
+					get_unaligned_le32(&ts->info.hw_ver);
+				ts->info.ft_ver =
+					get_unaligned_le16(&ts->info.ft_ver);
+				ts->info.x_max =
+					get_unaligned_le16(&ts->info.x_max);
+				ts->info.y_max =
+					get_unaligned_le16(&ts->info.y_max);
+
+				return 0;
+			}
+		}
+	}
+	dev_err(&client->dev, "Get touch data failed: %d\n", error);
+
+	return -EINVAL;
+}
+
+static int raydium_i2c_fastboot(struct i2c_client *client)
+{
+	static const u8 boot_cmd[] = { 0x50, 0x00, 0x06, 0x20 };
+	u8 buf[HEADER_SIZE];
+	int error;
+
+	error = raydium_i2c_read_message(client,
+		get_unaligned_be32(boot_cmd),
+		sizeof(boot_cmd), buf);
+
+	if (!error) {
+		if (buf[0] == RM_BOOT_BLDR) {
+			dev_dbg(&client->dev, "boot in fastboot mode\n");
+			return -EINVAL;
+		}
+		dev_dbg(&client->dev, "boot success -- 0x%x\n", client->addr);
+		return 0;
+	}
+
+	dev_err(&client->dev, "boot failed: %d\n", error);
+
+	return error;
+}
+
+static int raydium_i2c_initialize(struct raydium_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	int error, retry_cnt;
+	static const u8 recov_packet[] = { 0x04, 0x81 };
+	u8 buf[HEADER_SIZE];
+
+	for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
+		error = raydium_i2c_fastboot(client);
+		if (error) {
+			/* Continue initializing if it's the last try */
+			if (retry_cnt < MAX_RETRIES - 1)
+				continue;
+		}
+		/* Wait for Hello packet */
+		msleep(BOOT_DELAY_MS);
+
+		error = raydium_i2c_read(client, recov_packet[0], 1,
+			(void *)buf);
+		if (error) {
+			dev_err(&client->dev,
+				"failed to read 'hello' packet: %d\n", error);
+		} else if (buf[0] == recov_packet[1]) {
+			ts->boot_mode = RAYDIUM_TS_MAIN;
+			break;
+		}
+	}
+
+	if (error)
+		ts->boot_mode = RAYDIUM_TS_BLDR;
+	else
+		raydium_i2c_query_ts_info(ts);
+
+	return error;
+}
+
+static int raydium_i2c_fw_write_page(struct i2c_client *client,
+				const void *page)
+{
+	static const u8 ack_ok[] = { 0x55, 0xAA };
+	u8 buf[2];
+	int retry;
+	int error;
+
+	for (retry = 0; retry < MAX_FW_UPDATE_RETRIES; retry++) {
+		error = raydium_i2c_send(client, CMD_BOOT_WRT,
+			(u8 *)page, RAYDIUM_FW_PAGESIZE);
+		if (error) {
+			dev_err(&client->dev,
+				"BLDR Write Page failed: %d\n", error);
+			continue;
+		}
+
+		error = raydium_i2c_read(client, CMD_BOOT_CHK, sizeof(ack_ok),
+			(void *)buf);
+		if (error) {
+			dev_err(&client->dev,
+				"BLDR Ack read failed: %d\n", error);
+			return error;
+		}
+
+		if (!memcmp(buf, ack_ok, sizeof(ack_ok)))
+			return 0;
+
+		error = -EIO;
+		dev_err(&client->dev,
+			"BLDR Get Ack Error [%02x:%02x]\n", buf[0], buf[1]);
+	}
+
+	return error;
+}
+
+static int raydium_i2c_do_update_firmware(struct i2c_client *client,
+					 const struct firmware *fw,
+					 bool force)
+{
+	static const u8 boot_cmd[] = { 0x50, 0x00, 0x06, 0x20 , RM_BOOT_BLDR };
+	static const u8 main_cmd[] = { 0x50, 0x00, 0x06, 0x20, RM_BOOT_MAIN };
+	static const u8 boot_ack[] = { 0x55, 0xAA, 0x00, 0xFF };
+	u8 buf[HEADER_SIZE];
+	int page, n_fw_pages;
+	int error;
+
+	/* Recovery mode detection! */
+	if (!force) {
+		/* Start boot loader Procedure */
+		dev_dbg(&client->dev, "Normal BLDR procedure\n");
+		/* switch to mode */
+		error = raydium_i2c_send_message(client, 1, (void *)boot_cmd);
+		if (error)
+			dev_err(&client->dev, "failed to send boot cmd: %d\n",
+				error);
+		msleep(60);
+		raydium_i2c_sw_reset(client);
+		msleep(RAYDIUM_RESET_DELAY_MSEC);
+		error = raydium_i2c_send_message(client, 1, (void *)boot_cmd);
+	}
+
+	if (error) {
+		dev_err(&client->dev, "failed to enter fastboot mode: %d\n",
+			error);
+		return error;
+	}
+
+	msleep(20);
+
+	/* check fastboot state */
+	error = raydium_i2c_read(client, CMD_BOOT_ACK,
+		sizeof(boot_ack), (void *)buf);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to read boot ack: %d\n", error);
+		return error;
+	}
+
+	if (memcmp(buf, boot_ack, sizeof(boot_ack))) {
+		dev_err(&client->dev,
+			"failed to enter fastboot: %*ph (expected %*ph)\n",
+			(int)sizeof(buf), buf, (int)sizeof(boot_ack), boot_ack);
+		return -EIO;
+	}
+
+	dev_info(&client->dev, "successfully entered fastboot mode");
+
+	n_fw_pages = fw->size / RAYDIUM_FW_PAGESIZE;
+	dev_dbg(&client->dev, "BLDR Pages = %d\n", n_fw_pages);
+
+	for (page = 0; page < n_fw_pages; page++) {
+		error = raydium_i2c_fw_write_page(client,
+					fw->data + page * RAYDIUM_FW_PAGESIZE);
+		if (error) {
+			dev_err(&client->dev,
+				"failed to write FW page %d: %d\n",
+				page, error);
+			return error;
+		}
+	}
+	error = raydium_i2c_send_message(client, 1, (void *)main_cmd);
+	msleep(20);
+	raydium_i2c_sw_reset(client);
+	dev_info(&client->dev, "firmware update completed\n");
+
+	return 0;
+}
+
+static int raydium_i2c_fw_update(struct raydium_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	const struct firmware *fw;
+	char *fw_name;
+	int error;
+
+	fw_name = kasprintf(GFP_KERNEL, "raydium_i2c_%04x.bin",
+		ts->info.hw_ver & 0xFFFF);
+	if (!fw_name)
+		return -ENOMEM;
+
+	dev_info(&client->dev, "requesting fw name = %s\n", fw_name);
+	error = request_firmware(&fw, fw_name, &client->dev);
+	kfree(fw_name);
+	if (error) {
+		dev_err(&client->dev, "failed to request firmware: %d\n",
+			error);
+		return error;
+	}
+
+	if (fw->size % RAYDIUM_FW_PAGESIZE) {
+		dev_err(&client->dev, "invalid firmware length: %zu\n",
+			fw->size);
+		error = -EINVAL;
+		goto out;
+	}
+
+	disable_irq(client->irq);
+	error = raydium_i2c_do_update_firmware(client, fw,
+					ts->boot_mode == RAYDIUM_TS_BLDR);
+	if (error) {
+		dev_err(&client->dev, "firmware update failed: %d\n", error);
+		ts->boot_mode = RAYDIUM_TS_BLDR;
+		goto out_enable_irq;
+	}
+	error = raydium_i2c_initialize(ts);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to initialize device after firmware update: %d\n",
+			error);
+		ts->boot_mode = RAYDIUM_TS_BLDR;
+		goto out_enable_irq;
+	}
+
+	ts->boot_mode = RAYDIUM_TS_MAIN;
+
+out_enable_irq:
+	enable_irq(client->irq);
+	msleep(100);
+out:
+	release_firmware(fw);
+	return error;
+}
+
+static void raydium_mt_event(struct raydium_data *ts)
+{
+	u8 data[MAX_PACKET_SIZE];
+	int error, i;
+	int x, y, f_state, pressure, wx, wy;
+
+	error = raydium_i2c_read_message(ts->client, ts->obj.data_bank_addr,
+		ts->obj.pkg_size, (void *)data);
+
+	if (error < 0) {
+		dev_err(&ts->client->dev, "%s: failed to read data: %d\n",
+			__func__, error);
+		return;
+	}
+
+	for (i = 0; i < MAX_TOUCH_NUM; i++) {
+		f_state = (data + i * ts->obj.tp_info_size)[POS_STATE];
+		pressure = (data + i * ts->obj.tp_info_size)[POS_PRESSURE];
+		wx = (data + i * ts->obj.tp_info_size)[WIDTH_X];
+		wy = (data + i * ts->obj.tp_info_size)[WIDTH_Y];
+
+		input_mt_slot(ts->input, i);
+		input_mt_report_slot_state(ts->input,
+				MT_TOOL_FINGER, f_state != 0);
+
+		if (!f_state)
+			continue;
+		x = get_unaligned_le16(&data[i * ts->obj.tp_info_size + POS_X]);
+		y = get_unaligned_le16(&data[i * ts->obj.tp_info_size + POS_Y]);
+
+		input_report_abs(ts->input, ABS_MT_POSITION_X, x);
+		input_report_abs(ts->input, ABS_MT_POSITION_Y, y);
+		input_report_abs(ts->input, ABS_MT_PRESSURE, pressure);
+		input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR,
+			max(wx, wy));
+		input_report_abs(ts->input, ABS_MT_TOUCH_MINOR,
+			min(wx, wy));
+	}
+
+	input_mt_sync_frame(ts->input);
+	input_sync(ts->input);
+}
+
+static irqreturn_t raydium_i2c_irq(int irq, void *_dev)
+{
+	struct raydium_data *ts = _dev;
+
+	if (ts->boot_mode != RAYDIUM_TS_BLDR)
+		raydium_mt_event(ts);
+
+	return IRQ_HANDLED;
+}
+
+static ssize_t write_update_fw(struct device *dev,
+			struct device_attribute *attr,
+			const char *buf, size_t count)
+{
+	struct raydium_data *ts = dev_get_drvdata(dev);
+	int error;
+
+	error = mutex_lock_interruptible(&ts->sysfs_mutex);
+	if (error)
+		return error;
+
+	error = raydium_i2c_fw_update(ts);
+	dev_dbg(dev, "firmware update result: %d\n", error);
+
+	mutex_unlock(&ts->sysfs_mutex);
+	return error ?: count;
+}
+
+static ssize_t raydium_bootmode_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct raydium_data *ts = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%s\n", ts->boot_mode == RAYDIUM_TS_MAIN ?
+		"Normal" : "Recovery");
+}
+
+static ssize_t raydium_fw_ver_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct raydium_data *ts = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%d.%d\n", ts->info.main_ver, ts->info.sub_ver);
+}
+
+static ssize_t raydium_hw_ver_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct raydium_data *ts = dev_get_drvdata(dev);
+
+	return sprintf(buf, "0x%04x\n", ts->info.hw_ver);
+}
+
+static DEVICE_ATTR(fw_version, S_IRUGO, raydium_fw_ver_show, NULL);
+static DEVICE_ATTR(hw_version, S_IRUGO, raydium_hw_ver_show, NULL);
+static DEVICE_ATTR(boot_mode, S_IRUGO, raydium_bootmode_show, NULL);
+static DEVICE_ATTR(update_fw, S_IWUSR, NULL, write_update_fw);
+
+static struct attribute *raydium_attributes[] = {
+	&dev_attr_update_fw.attr,
+	&dev_attr_boot_mode.attr,
+	&dev_attr_fw_version.attr,
+	&dev_attr_hw_version.attr,
+	NULL
+};
+
+static struct attribute_group raydium_attribute_group = {
+	.attrs = raydium_attributes,
+};
+
+static void raydium_i2c_remove_sysfs_group(void *_data)
+{
+	struct raydium_data *ts = _data;
+
+	sysfs_remove_group(&ts->client->dev.kobj, &raydium_attribute_group);
+}
+
+static int raydium_i2c_power_on(struct raydium_data *ts)
+{
+	int error;
+
+	if (IS_ERR_OR_NULL(ts->reset_gpio))
+		return 0;
+
+	gpiod_set_value_cansleep(ts->reset_gpio, 1);
+
+	error = regulator_enable(ts->avdd);
+	if (error) {
+		dev_err(&ts->client->dev,
+			"failed to enable avdd regulator: %d\n", error);
+		goto release_reset_gpio;
+	}
+
+	error = regulator_enable(ts->vccio);
+	if (error) {
+		regulator_disable(ts->avdd);
+		dev_err(&ts->client->dev,
+			"failed to enable vccio regulator: %d\n", error);
+		goto release_reset_gpio;
+	}
+
+	udelay(RAYDIUM_POWERON_DELAY_USEC);
+
+release_reset_gpio:
+	gpiod_set_value_cansleep(ts->reset_gpio, 0);
+
+	if (error)
+		return error;
+
+	msleep(RAYDIUM_RESET_DELAY_MSEC);
+
+	return 0;
+}
+
+static void raydium_i2c_power_off(void *_data)
+{
+	struct raydium_data *ts = _data;
+
+	if (!IS_ERR_OR_NULL(ts->reset_gpio)) {
+		gpiod_set_value_cansleep(ts->reset_gpio, 1);
+		regulator_disable(ts->vccio);
+		regulator_disable(ts->avdd);
+	}
+}
+
+static int raydium_i2c_probe(struct i2c_client *client,
+			    const struct i2c_device_id *id)
+{
+	union i2c_smbus_data dummy;
+	struct raydium_data *ts;
+	unsigned long irqflags;
+	int error;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		dev_err(&client->dev,
+			"%s: i2c check functionality error\n", DEVICE_NAME);
+		return -ENXIO;
+	}
+
+	ts = devm_kzalloc(&client->dev, sizeof(struct raydium_data),
+		GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+
+	mutex_init(&ts->sysfs_mutex);
+	init_completion(&ts->cmd_done);
+
+	ts->client = client;
+	i2c_set_clientdata(client, ts);
+
+	ts->avdd = devm_regulator_get(&client->dev, "avdd");
+	if (IS_ERR(ts->avdd)) {
+		error = PTR_ERR(ts->avdd);
+		if (error != -EPROBE_DEFER)
+			dev_err(&client->dev,
+				"Failed to get 'avdd' regulator: %d\n", error);
+		return error;
+	}
+
+	ts->vccio = devm_regulator_get(&client->dev, "vccio");
+	if (IS_ERR(ts->vccio)) {
+		error = PTR_ERR(ts->vccio);
+		if (error != -EPROBE_DEFER)
+			dev_err(&client->dev,
+				"Failed to get 'vccio' regulator: %d\n", error);
+		return error;
+	}
+
+	ts->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
+		GPIOD_OUT_LOW);
+	if (IS_ERR(ts->reset_gpio)) {
+		error = PTR_ERR(ts->reset_gpio);
+		if (error != -EPROBE_DEFER) {
+			dev_err(&client->dev,
+				"failed to get reset gpio: %d\n", error);
+			return error;
+		}
+	}
+
+	error = raydium_i2c_power_on(ts);
+	if (error)
+		return error;
+
+	error = devm_add_action(&client->dev, raydium_i2c_power_off, ts);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to install power off action: %d\n", error);
+		raydium_i2c_power_off(ts);
+		return error;
+	}
+
+	/* Make sure there is something at this address */
+	if (i2c_smbus_xfer(client->adapter, client->addr, 0,
+			I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0) {
+		dev_err(&client->dev, "nothing at this address\n");
+		return -ENXIO;
+	}
+
+	error = raydium_i2c_initialize(ts);
+	if (error) {
+		dev_err(&client->dev, "failed to initialize: %d\n", error);
+		return error;
+	}
+
+	ts->input = devm_input_allocate_device(&client->dev);
+	if (!ts->input) {
+		dev_err(&client->dev, "Failed to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	ts->input->name = "Raydium Touchscreen";
+	ts->input->id.bustype = BUS_I2C;
+
+	__set_bit(BTN_TOUCH, ts->input->keybit);
+	__set_bit(EV_ABS, ts->input->evbit);
+	__set_bit(EV_KEY, ts->input->evbit);
+
+	/* Multitouch input params setup */
+	input_set_abs_params(ts->input, ABS_MT_POSITION_X, 0,
+		ts->info.x_max, 0, 0);
+	input_set_abs_params(ts->input, ABS_MT_POSITION_Y,
+		0, ts->info.y_max, 0, 0);
+	input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+	input_set_abs_params(ts->input, ABS_MT_PRESSURE, 0, 255, 0, 0);
+	input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->info.x_res);
+	input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->info.y_res);
+
+	input_mt_init_slots(ts->input, MAX_TOUCH_NUM, 0);
+
+	input_set_drvdata(ts->input, ts);
+
+	error = input_mt_init_slots(ts->input, MAX_TOUCH_NUM,
+		INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to initialize MT slots: %d\n", error);
+		return error;
+	}
+
+	error = input_register_device(ts->input);
+	if (error) {
+		dev_err(&client->dev,
+			"unable to register input device: %d\n", error);
+		return error;
+	}
+
+	error = devm_request_threaded_irq(&client->dev, client->irq,
+			NULL, raydium_i2c_irq, irqflags | IRQF_ONESHOT,
+			client->name, ts);
+	if (error) {
+		dev_err(&client->dev, "Failed to register interrupt\n");
+		return error;
+	}
+
+	device_init_wakeup(&client->dev, true);
+
+	error = sysfs_create_group(&client->dev.kobj, &raydium_attribute_group);
+	if (error) {
+		dev_err(&client->dev, "failed to create sysfs attributes: %d\n",
+			error);
+		return error;
+	}
+
+	error = devm_add_action(&client->dev,
+				raydium_i2c_remove_sysfs_group, ts);
+	if (error) {
+		raydium_i2c_remove_sysfs_group(ts);
+		dev_err(&client->dev,
+			"Failed to add sysfs cleanup action: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int raydium_i2c_remove(struct i2c_client *client)
+{
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	input_unregister_device(ts->input);
+
+	device_init_wakeup(&client->dev, false);
+
+	mutex_destroy(&ts->sysfs_mutex);
+
+	return 0;
+}
+
+static void __maybe_unused raydium_enter_sleep(struct i2c_client *client)
+{
+	static const u8 sleep_cmd[] = { 0x5A, 0xff, 0x00, 0x0f };
+	int error;
+
+	error = raydium_i2c_send(client, CMD_ENTER_SLEEP, (u8 *)sleep_cmd,
+		sizeof(sleep_cmd));
+	if (error)
+		dev_err(&client->dev,
+			"Send sleep failed: %d\n", error);
+}
+
+static int __maybe_unused raydium_i2c_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	/* Command not support in BLDR recovery mode */
+	if (ts->boot_mode != RAYDIUM_TS_MAIN)
+		return -EBUSY;
+
+	disable_irq(client->irq);
+
+	if (device_may_wakeup(dev)) {
+		raydium_enter_sleep(client);
+
+		ts->wake_irq_enabled = (enable_irq_wake(client->irq) == 0);
+	} else {
+		raydium_i2c_power_off(ts);
+	}
+
+	return 0;
+}
+
+static int __maybe_unused raydium_i2c_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	if (device_may_wakeup(dev)) {
+		if (ts->wake_irq_enabled)
+			disable_irq_wake(client->irq);
+		raydium_i2c_sw_reset(client);
+	} else {
+		raydium_i2c_power_on(ts);
+		raydium_i2c_initialize(ts);
+	}
+
+	enable_irq(client->irq);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(raydium_i2c_pm_ops,
+			 raydium_i2c_suspend, raydium_i2c_resume);
+
+static const struct i2c_device_id raydium_i2c_id[] = {
+	{ DEVICE_NAME, 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, raydium_i2c_id);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id raydium_acpi_id[] = {
+	{ "RAYD0001", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(acpi, raydium_acpi_id);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id raydium_of_match[] = {
+	{ .compatible = "raydium,rm3xxxx",},
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, raydium_of_match);
+#endif
+
+static struct i2c_driver raydium_i2c_driver = {
+	.probe = raydium_i2c_probe,
+	.remove = raydium_i2c_remove,
+	.id_table = raydium_i2c_id,
+	.driver = {
+		.name = "raydium_ts",
+		.pm = &raydium_i2c_pm_ops,
+		.acpi_match_table = ACPI_PTR(raydium_acpi_id),
+		.of_match_table = of_match_ptr(raydium_of_match),
+	},
+};
+
+module_i2c_driver(raydium_i2c_driver);
+
+MODULE_AUTHOR("Raydium");
+MODULE_DESCRIPTION("Raydium I2c Touchscreen driver");
+MODULE_LICENSE("GPL v2");
-- 
2.1.2


^ permalink raw reply related	[flat|nested] 42+ messages in thread

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2016-03-15  8:44 jeffrey.lin
@ 2016-03-18 21:04 ` Rob Herring
  0 siblings, 0 replies; 42+ messages in thread
From: Rob Herring @ 2016-03-18 21:04 UTC (permalink / raw)
  To: jeffrey.lin
  Cc: pawel.moll, mark.rutland, jc+devicetree, galak, treding,
	inki.dae, djkurtz, dusonlin, ajaykumar.rs, jeesw,
	dmitry.torokhov, rydberg, grant.likely, bleung, scott.liu,
	jeffrey.lin, roger.yang, KP.li, albert.shieh, linux-kernel,
	linux-input, devicetree

On Tue, Mar 15, 2016 at 04:44:17PM +0800, jeffrey.lin wrote:
> Raydium I2C touch driver.
> 
> Signed-off-by: jeffrey.lin <jeffrey.lin@rad-ic.com>
> ---
>  .../devicetree/bindings/input/raydium_i2c_ts.txt   |  23 +
>  .../devicetree/bindings/vendor-prefixes.txt        |   1 +

It is generally preferred to split bindings from driver changes.

>  drivers/input/touchscreen/Kconfig                  |  12 +
>  drivers/input/touchscreen/Makefile                 |   1 +
>  drivers/input/touchscreen/raydium_i2c_ts.c         | 932 +++++++++++++++++++++
>  5 files changed, 969 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/input/raydium_i2c_ts.txt
>  create mode 100644 drivers/input/touchscreen/raydium_i2c_ts.c
> 
> diff --git a/Documentation/devicetree/bindings/input/raydium_i2c_ts.txt b/Documentation/devicetree/bindings/input/raydium_i2c_ts.txt
> new file mode 100644
> index 0000000..329864d
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/input/raydium_i2c_ts.txt
> @@ -0,0 +1,23 @@
> +Raydium I2C touchscreen
> +
> +Required properties:
> +- compatible:
> +    "raydium,rm31100"
> +
> +- reg: The I2C address of the device
> +- interrupts: interrupt to which the chip is connected
> +    See ../interrupt-controller/interrupts.txt
> +Optional property:
> + - avdd-supply	: Analog power supply needed to power device
> + - vccio-supply: IO Power source
> +
> +Example:
> +
> +	raydium_i2c@0x39 {

touchscreen@39

Otherwise,

Acked-by: Rob Herring <robh@kernel.org>


> +		compatible = "raydium,rm31100";
> +		avdd-supply = <&pm8226_l19>;
> +		vccio-supply = <&pm8226_lvs1>;
> +		reg = <0x39>;
> +		interrupt-parent = <&gpio>;
> +		interrupts = <0x0 IRQ_TYPE_EDGE_FALLING>;
> +	};
> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
> index af49e0f..5b6224b 100644
> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
> @@ -126,6 +126,7 @@ radxa	Radxa
>  raidsonic	RaidSonic Technology GmbH
>  ralink	Mediatek/Ralink Technology Corp.
>  ramtron	Ramtron International
> +raydium Raydium Semiconductor Corp.
>  realtek Realtek Semiconductor Corp.
>  renesas	Renesas Electronics Corporation
>  ricoh	Ricoh Co. Ltd.


^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
@ 2016-03-15  8:44 jeffrey.lin
  2016-03-18 21:04 ` Rob Herring
  0 siblings, 1 reply; 42+ messages in thread
From: jeffrey.lin @ 2016-03-15  8:44 UTC (permalink / raw)
  To: pawel.moll, mark.rutland, jc+devicetree, galak, treding,
	inki.dae, djkurtz, dusonlin, ajaykumar.rs, jeesw,
	dmitry.torokhov, rydberg, grant.likely, robh+dt, bleung,
	scott.liu
  Cc: jeffrey.lin, roger.yang, KP.li, albert.shieh, linux-kernel,
	linux-input, devicetree

Raydium I2C touch driver.

Signed-off-by: jeffrey.lin <jeffrey.lin@rad-ic.com>
---
 .../devicetree/bindings/input/raydium_i2c_ts.txt   |  23 +
 .../devicetree/bindings/vendor-prefixes.txt        |   1 +
 drivers/input/touchscreen/Kconfig                  |  12 +
 drivers/input/touchscreen/Makefile                 |   1 +
 drivers/input/touchscreen/raydium_i2c_ts.c         | 932 +++++++++++++++++++++
 5 files changed, 969 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/raydium_i2c_ts.txt
 create mode 100644 drivers/input/touchscreen/raydium_i2c_ts.c

diff --git a/Documentation/devicetree/bindings/input/raydium_i2c_ts.txt b/Documentation/devicetree/bindings/input/raydium_i2c_ts.txt
new file mode 100644
index 0000000..329864d
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/raydium_i2c_ts.txt
@@ -0,0 +1,23 @@
+Raydium I2C touchscreen
+
+Required properties:
+- compatible:
+    "raydium,rm31100"
+
+- reg: The I2C address of the device
+- interrupts: interrupt to which the chip is connected
+    See ../interrupt-controller/interrupts.txt
+Optional property:
+ - avdd-supply	: Analog power supply needed to power device
+ - vccio-supply: IO Power source
+
+Example:
+
+	raydium_i2c@0x39 {
+		compatible = "raydium,rm31100";
+		avdd-supply = <&pm8226_l19>;
+		vccio-supply = <&pm8226_lvs1>;
+		reg = <0x39>;
+		interrupt-parent = <&gpio>;
+		interrupts = <0x0 IRQ_TYPE_EDGE_FALLING>;
+	};
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index af49e0f..5b6224b 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -126,6 +126,7 @@ radxa	Radxa
 raidsonic	RaidSonic Technology GmbH
 ralink	Mediatek/Ralink Technology Corp.
 ramtron	Ramtron International
+raydium Raydium Semiconductor Corp.
 realtek Realtek Semiconductor Corp.
 renesas	Renesas Electronics Corporation
 ricoh	Ricoh Co. Ltd.
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 3f3f6ee..ab28721 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -915,6 +915,18 @@ config TOUCHSCREEN_PCAP
 	  To compile this driver as a module, choose M here: the
 	  module will be called pcap_ts.
 
+config TOUCHSCREEN_RM_TS
+	tristate "Raydium I2C Touchscreen"
+	depends on I2C
+	help
+	  Say Y here if you have Raydium series I2C touchscreen,
+	  such as RM31100 , connected to your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called raydium_i2c_ts.
+
 config TOUCHSCREEN_ST1232
 	tristate "Sitronix ST1232 touchscreen controllers"
 	depends on I2C
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 4941f2d..99e08cf 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE)	+= usbtouchscreen.o
 obj-$(CONFIG_TOUCHSCREEN_PCAP)		+= pcap_ts.o
 obj-$(CONFIG_TOUCHSCREEN_PENMOUNT)	+= penmount.o
 obj-$(CONFIG_TOUCHSCREEN_PIXCIR)	+= pixcir_i2c_ts.o
+obj-$(CONFIG_TOUCHSCREEN_RM_TS)		+= raydium_i2c_ts.o
 obj-$(CONFIG_TOUCHSCREEN_S3C2410)	+= s3c2410_ts.o
 obj-$(CONFIG_TOUCHSCREEN_ST1232)	+= st1232.o
 obj-$(CONFIG_TOUCHSCREEN_STMPE)		+= stmpe-ts.o
diff --git a/drivers/input/touchscreen/raydium_i2c_ts.c b/drivers/input/touchscreen/raydium_i2c_ts.c
new file mode 100644
index 0000000..69a0388
--- /dev/null
+++ b/drivers/input/touchscreen/raydium_i2c_ts.c
@@ -0,0 +1,932 @@
+/*
+ * Raydium touchscreen I2C driver.
+ *
+ * Copyright (C) 2012-2014, Raydium Semiconductor Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Raydium reserves the right to make changes without further notice
+ * to the materials described herein. Raydium does not assume any
+ * liability arising out of the application described herein.
+ *
+ * Contact Raydium Semiconductor Corporation at www.rad-ic.com
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/firmware.h>
+#include <linux/input/mt.h>
+#include <linux/acpi.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+#include <asm/unaligned.h>
+#include <linux/gpio/consumer.h>
+
+/* Device, Driver information */
+#define DEVICE_NAME	"rm31100"
+
+/* Slave I2C mode*/
+#define RM_BOOT_BLDR	0x02
+#define RM_BOOT_MAIN	0x03
+
+/*I2C command */
+#define CMD_QUERY_BANK		0x2B
+#define CMD_DATA_BANK		0x4D
+#define CMD_ENTER_SLEEP		0x4E
+#define CMD_BOOT_ACK		0x0A
+#define CMD_BOOT_WRT		0x5B
+#define CMD_BOOT_CHK		0x0C
+#define CMD_BANK_SWITCH		0xAA
+
+/* Touch relative info */
+#define MAX_RETRIES		3
+#define MAX_FW_UPDATE_RETRIES	30
+#define MAX_TOUCH_NUM		10
+#define MAX_PACKET_SIZE		60
+#define BOOT_DELAY_MS	100
+
+#define RAYDIUM_FW_PAGESIZE	128
+#define RAYDIUM_POWERON_DELAY_USEC	500
+#define RAYDIUM_RESET_DELAY_MSEC	50
+
+#define ADDR_INDEX		0x03
+#define DATA_INDEX		0x04
+
+#define HEADER_SIZE		4
+
+enum raydium_boot_mode {
+	RAYDIUM_TS_MAIN = 0,
+	RAYDIUM_TS_BLDR,
+};
+
+enum raydium_abs_idx {
+	POS_STATE = 0,/*1:touch, 0:no touch*/
+	POS_X,
+	POS_Y = 3,
+	POS_PRESSURE,
+	WIDTH_X,
+	WIDTH_Y,
+};
+
+struct raydium_info {
+	u32 hw_ver;	/*device ver, __le32*/
+	u8 main_ver;
+	u8 sub_ver;
+	u16 ft_ver;	/*test ver, __le16*/
+	u8 x_num;
+	u8 y_num;
+	u16 x_max;	/*disp reso, __le16*/
+	u16 y_max;	/*disp reso, __le16*/
+	u8 x_res;		/* units/mm */
+	u8 y_res;		/* units/mm */
+};
+
+struct raydium_object {
+	u32 data_bank_addr;
+	u8 pkg_size;
+	u8 tp_info_size;
+};
+
+/* struct raydium_data - represents state of Raydium touchscreen device */
+struct raydium_data {
+	struct i2c_client *client;
+	struct input_dev *input;
+
+	struct regulator *avdd;
+	struct regulator *vccio;
+	struct gpio_desc *reset_gpio;
+
+	u32 query_bank_info;
+
+	struct raydium_info info;
+	struct raydium_object obj;
+	enum raydium_boot_mode boot_mode;
+
+	struct mutex sysfs_mutex;
+	struct completion cmd_done;
+
+	bool wake_irq_enabled;
+};
+
+static int raydium_i2c_send(struct i2c_client *client,
+	u8 addr, u8 *data, size_t len)
+{
+	u8 buf[MAX_PACKET_SIZE + 1];
+	int tries = 0;
+
+	if (len > MAX_PACKET_SIZE)
+		return -EINVAL;
+
+	buf[0] = addr;
+	memcpy(&buf[1], data, len);
+
+	do {
+		if (i2c_master_send(client, buf, len + 1) == (len + 1))
+			return 0;
+		msleep(20);
+	} while (++tries < MAX_RETRIES);
+
+	dev_err(&client->dev, "%s: i2c send failed\n", __func__);
+
+	return -EIO;
+}
+
+static int raydium_i2c_read(struct i2c_client *client,
+	u8 addr, size_t len, void *data)
+{
+	struct i2c_msg xfer[2];
+	int ret;
+
+	/* Write register */
+	xfer[0].addr = client->addr;
+	xfer[0].flags = 0;
+	xfer[0].len = 1;
+	xfer[0].buf = &addr;
+
+	/* Read data */
+	xfer[1].addr = client->addr;
+	xfer[1].flags = I2C_M_RD;
+	xfer[1].len = len;
+	xfer[1].buf = data;
+
+	ret = i2c_transfer(client->adapter, xfer, ARRAY_SIZE(xfer));
+	if (ret < 0)
+		return ret;
+
+	if (ret != ARRAY_SIZE(xfer))
+		return -EIO;
+
+	return 0;
+}
+
+static int raydium_i2c_read_message(struct i2c_client *client,
+	u32 addr, size_t len, void *data)
+{
+	u16 pkg_size, use_len;
+	u8 buf[HEADER_SIZE], idx_i;
+	int error;
+
+	use_len = len;
+	idx_i = 0;
+	while (use_len > 0) {
+		pkg_size = (use_len < MAX_PACKET_SIZE) ?
+			use_len : MAX_PACKET_SIZE;
+
+		put_unaligned_be32(addr, buf);
+
+		/*set data bank*/
+		error = raydium_i2c_send(client, CMD_BANK_SWITCH,
+			(u8 *)buf, HEADER_SIZE);
+		/*read potints data*/
+		if (!error)
+			error = raydium_i2c_read(client, buf[ADDR_INDEX],
+				pkg_size,
+				data + idx_i*MAX_PACKET_SIZE);
+
+		pkg_size += MAX_PACKET_SIZE;
+		addr += MAX_PACKET_SIZE;
+		use_len = (use_len < MAX_PACKET_SIZE) ?
+			0 : (use_len - MAX_PACKET_SIZE);
+		idx_i++;
+	}
+
+	return error;
+}
+
+static int raydium_i2c_send_message(struct i2c_client *client,
+	size_t len, void *data)
+{
+	int error;
+	__le32 cmd;
+
+	cmd = get_unaligned_le32(data);
+
+	/*set data bank*/
+	error = raydium_i2c_send(client, CMD_BANK_SWITCH, (u8 *)&cmd,
+		HEADER_SIZE);
+
+	/*send message*/
+	if (!error)
+		error = raydium_i2c_send(client, ((u8 *)data)[ADDR_INDEX],
+			data + DATA_INDEX, len);
+
+	return error;
+}
+
+static int raydium_i2c_sw_reset(struct i2c_client *client)
+{
+	static const u8 soft_rst_cmd[] = {0x40, 0x00, 0x00, 0x04, 0x01};
+	int error;
+
+	error = raydium_i2c_send_message(client, ARRAY_SIZE(soft_rst_cmd),
+		(void *)soft_rst_cmd);
+	if (error) {
+		dev_err(&client->dev, "software reset failed: %d\n", error);
+		return error;
+	}
+
+	msleep(RAYDIUM_RESET_DELAY_MSEC);
+
+	return 0;
+}
+
+static int raydium_i2c_query_ts_info(struct raydium_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	int error, retry_cnt;
+
+	for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
+		error = raydium_i2c_read(client, CMD_DATA_BANK,
+			sizeof(ts->obj), (void *)&ts->obj);
+			ts->obj.data_bank_addr =
+				get_unaligned_le32(&ts->obj.data_bank_addr);
+
+		if (!error) {
+			error = raydium_i2c_read(client, CMD_QUERY_BANK,
+				sizeof(ts->query_bank_info),
+				(void *)&ts->query_bank_info);
+			if (!error) {
+				error = raydium_i2c_read_message(client,
+					ts->query_bank_info, sizeof(ts->info),
+					(void *)&ts->info);
+
+				ts->info.hw_ver =
+					get_unaligned_le32(&ts->info.hw_ver);
+				ts->info.ft_ver =
+					get_unaligned_le16(&ts->info.ft_ver);
+				ts->info.x_max =
+					get_unaligned_le16(&ts->info.x_max);
+				ts->info.y_max =
+					get_unaligned_le16(&ts->info.y_max);
+
+				return 0;
+			}
+		}
+	}
+	dev_err(&client->dev, "Get touch data failed: %d\n", error);
+
+	return -EINVAL;
+}
+
+static int raydium_i2c_fastboot(struct i2c_client *client)
+{
+	static const u8 boot_cmd[] = { 0x50, 0x00, 0x06, 0x20 };
+	u8 buf[HEADER_SIZE];
+	int error;
+
+	error = raydium_i2c_read_message(client,
+		get_unaligned_be32(boot_cmd),
+		sizeof(boot_cmd), buf);
+
+	if (!error) {
+		if (buf[0] == RM_BOOT_BLDR) {
+			dev_dbg(&client->dev, "boot in fastboot mode\n");
+			return -EINVAL;
+		}
+		dev_dbg(&client->dev, "boot success -- 0x%x\n", client->addr);
+		return 0;
+	}
+
+	dev_err(&client->dev, "boot failed: %d\n", error);
+
+	return error;
+}
+
+static int raydium_i2c_initialize(struct raydium_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	int error, retry_cnt;
+	static const u8 recov_packet[] = { 0x04, 0x81 };
+	u8 buf[HEADER_SIZE];
+
+	for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
+		error = raydium_i2c_fastboot(client);
+		if (error) {
+			/* Continue initializing if it's the last try */
+			if (retry_cnt < MAX_RETRIES - 1)
+				continue;
+		}
+		/* Wait for Hello packet */
+		msleep(BOOT_DELAY_MS);
+
+		error = raydium_i2c_read(client, recov_packet[0], 1,
+			(void *)buf);
+		if (error) {
+			dev_err(&client->dev,
+				"failed to read 'hello' packet: %d\n", error);
+		} else if (buf[0] == recov_packet[1]) {
+			ts->boot_mode = RAYDIUM_TS_MAIN;
+			break;
+		}
+	}
+
+	if (error)
+		ts->boot_mode = RAYDIUM_TS_BLDR;
+	else
+		raydium_i2c_query_ts_info(ts);
+
+	return error;
+}
+
+static int raydium_i2c_fw_write_page(struct i2c_client *client,
+				const void *page)
+{
+	static const u8 ack_ok[] = { 0x55, 0xAA };
+	u8 buf[2];
+	int retry;
+	int error;
+
+	for (retry = 0; retry < MAX_FW_UPDATE_RETRIES; retry++) {
+		error = raydium_i2c_send(client, CMD_BOOT_WRT,
+			(u8 *)page, RAYDIUM_FW_PAGESIZE);
+		if (error) {
+			dev_err(&client->dev,
+				"BLDR Write Page failed: %d\n", error);
+			continue;
+		}
+
+		error = raydium_i2c_read(client, CMD_BOOT_CHK, sizeof(ack_ok),
+			(void *)buf);
+		if (error) {
+			dev_err(&client->dev,
+				"BLDR Ack read failed: %d\n", error);
+			return error;
+		}
+
+		if (!memcmp(buf, ack_ok, sizeof(ack_ok)))
+			return 0;
+
+		error = -EIO;
+		dev_err(&client->dev,
+			"BLDR Get Ack Error [%02x:%02x]\n", buf[0], buf[1]);
+	}
+
+	return error;
+}
+
+static int raydium_i2c_do_update_firmware(struct i2c_client *client,
+					 const struct firmware *fw,
+					 bool force)
+{
+	static const u8 boot_cmd[] = { 0x50, 0x00, 0x06, 0x20 , RM_BOOT_BLDR };
+	static const u8 main_cmd[] = { 0x50, 0x00, 0x06, 0x20, RM_BOOT_MAIN };
+	static const u8 boot_ack[] = { 0x55, 0xAA, 0x00, 0xFF };
+	u8 buf[HEADER_SIZE];
+	int page, n_fw_pages;
+	int error;
+
+	/* Recovery mode detection! */
+	if (!force) {
+		/* Start boot loader Procedure */
+		dev_dbg(&client->dev, "Normal BLDR procedure\n");
+		/* switch to mode */
+		error = raydium_i2c_send_message(client, 1, (void *)boot_cmd);
+		if (error)
+			dev_err(&client->dev, "failed to send boot cmd: %d\n",
+				error);
+		msleep(60);
+		raydium_i2c_sw_reset(client);
+		msleep(RAYDIUM_RESET_DELAY_MSEC);
+		error = raydium_i2c_send_message(client, 1, (void *)boot_cmd);
+	}
+
+	if (error) {
+		dev_err(&client->dev, "failed to enter fastboot mode: %d\n",
+			error);
+		return error;
+	}
+
+	msleep(20);
+
+	/* check fastboot state */
+	error = raydium_i2c_read(client, CMD_BOOT_ACK,
+		sizeof(boot_ack), (void *)buf);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to read boot ack: %d\n", error);
+		return error;
+	}
+
+	if (memcmp(buf, boot_ack, sizeof(boot_ack))) {
+		dev_err(&client->dev,
+			"failed to enter fastboot: %*ph (expected %*ph)\n",
+			(int)sizeof(buf), buf, (int)sizeof(boot_ack), boot_ack);
+		return -EIO;
+	}
+
+	dev_info(&client->dev, "successfully entered fastboot mode");
+
+	n_fw_pages = fw->size / RAYDIUM_FW_PAGESIZE;
+	dev_dbg(&client->dev, "BLDR Pages = %d\n", n_fw_pages);
+
+	for (page = 0; page < n_fw_pages; page++) {
+		error = raydium_i2c_fw_write_page(client,
+					fw->data + page * RAYDIUM_FW_PAGESIZE);
+		if (error) {
+			dev_err(&client->dev,
+				"failed to write FW page %d: %d\n",
+				page, error);
+			return error;
+		}
+	}
+	error = raydium_i2c_send_message(client, 1, (void *)main_cmd);
+	msleep(20);
+	raydium_i2c_sw_reset(client);
+	dev_info(&client->dev, "firmware update completed\n");
+
+	return 0;
+}
+
+static int raydium_i2c_fw_update(struct raydium_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	const struct firmware *fw;
+	char *fw_name;
+	int error;
+
+	fw_name = kasprintf(GFP_KERNEL, "raydium_i2c_%04x.bin",
+		ts->info.hw_ver & 0xFFFF);
+	if (!fw_name)
+		return -ENOMEM;
+
+	dev_info(&client->dev, "requesting fw name = %s\n", fw_name);
+	error = request_firmware(&fw, fw_name, &client->dev);
+	kfree(fw_name);
+	if (error) {
+		dev_err(&client->dev, "failed to request firmware: %d\n",
+			error);
+		return error;
+	}
+
+	if (fw->size % RAYDIUM_FW_PAGESIZE) {
+		dev_err(&client->dev, "invalid firmware length: %zu\n",
+			fw->size);
+		error = -EINVAL;
+		goto out;
+	}
+
+	disable_irq(client->irq);
+	error = raydium_i2c_do_update_firmware(client, fw,
+					ts->boot_mode == RAYDIUM_TS_BLDR);
+	if (error) {
+		dev_err(&client->dev, "firmware update failed: %d\n", error);
+		ts->boot_mode = RAYDIUM_TS_BLDR;
+		goto out_enable_irq;
+	}
+	error = raydium_i2c_initialize(ts);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to initialize device after firmware update: %d\n",
+			error);
+		ts->boot_mode = RAYDIUM_TS_BLDR;
+		goto out_enable_irq;
+	}
+
+	ts->boot_mode = RAYDIUM_TS_MAIN;
+
+out_enable_irq:
+	enable_irq(client->irq);
+	msleep(100);
+out:
+	release_firmware(fw);
+	return error;
+}
+
+static void raydium_mt_event(struct raydium_data *ts)
+{
+	u8 data[MAX_PACKET_SIZE];
+	int error, i;
+	int x, y, f_state, pressure, wx, wy;
+
+	error = raydium_i2c_read_message(ts->client, ts->obj.data_bank_addr,
+		ts->obj.pkg_size, (void *)data);
+
+	if (error < 0) {
+		dev_err(&ts->client->dev, "%s: failed to read data: %d\n",
+			__func__, error);
+		return;
+	}
+
+	for (i = 0; i < MAX_TOUCH_NUM; i++) {
+		f_state = (data + i * ts->obj.tp_info_size)[POS_STATE];
+		pressure = (data + i * ts->obj.tp_info_size)[POS_PRESSURE];
+		wx = (data + i * ts->obj.tp_info_size)[WIDTH_X];
+		wy = (data + i * ts->obj.tp_info_size)[WIDTH_Y];
+
+		input_mt_slot(ts->input, i);
+		input_mt_report_slot_state(ts->input,
+				MT_TOOL_FINGER, f_state != 0);
+
+		if (!f_state)
+			continue;
+		x = get_unaligned_le16(&data[i * ts->obj.tp_info_size + POS_X]);
+		y = get_unaligned_le16(&data[i * ts->obj.tp_info_size + POS_Y]);
+
+		input_report_abs(ts->input, ABS_MT_POSITION_X, x);
+		input_report_abs(ts->input, ABS_MT_POSITION_Y, y);
+		input_report_abs(ts->input, ABS_MT_PRESSURE, pressure);
+		input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR,
+			max(wx, wy));
+		input_report_abs(ts->input, ABS_MT_TOUCH_MINOR,
+			min(wx, wy));
+	}
+
+	input_mt_sync_frame(ts->input);
+	input_sync(ts->input);
+}
+
+static irqreturn_t raydium_i2c_irq(int irq, void *_dev)
+{
+	struct raydium_data *ts = _dev;
+
+	if (ts->boot_mode != RAYDIUM_TS_BLDR)
+		raydium_mt_event(ts);
+
+	return IRQ_HANDLED;
+}
+
+static ssize_t write_update_fw(struct device *dev,
+			struct device_attribute *attr,
+			const char *buf, size_t count)
+{
+	struct raydium_data *ts = dev_get_drvdata(dev);
+	int error;
+
+	error = mutex_lock_interruptible(&ts->sysfs_mutex);
+	if (error)
+		return error;
+
+	error = raydium_i2c_fw_update(ts);
+	dev_dbg(dev, "firmware update result: %d\n", error);
+
+	mutex_unlock(&ts->sysfs_mutex);
+	return error ?: count;
+}
+
+static ssize_t raydium_bootmode_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct raydium_data *ts = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%s\n", ts->boot_mode == RAYDIUM_TS_MAIN ?
+		"Normal" : "Recovery");
+}
+
+static ssize_t raydium_fw_ver_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct raydium_data *ts = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%d.%d\n", ts->info.main_ver, ts->info.sub_ver);
+}
+
+static ssize_t raydium_hw_ver_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct raydium_data *ts = dev_get_drvdata(dev);
+
+	return sprintf(buf, "0x%04x\n", ts->info.hw_ver);
+}
+
+static DEVICE_ATTR(fw_version, S_IRUGO, raydium_fw_ver_show, NULL);
+static DEVICE_ATTR(hw_version, S_IRUGO, raydium_hw_ver_show, NULL);
+static DEVICE_ATTR(boot_mode, S_IRUGO, raydium_bootmode_show, NULL);
+static DEVICE_ATTR(update_fw, S_IWUSR, NULL, write_update_fw);
+
+static struct attribute *raydium_attributes[] = {
+	&dev_attr_update_fw.attr,
+	&dev_attr_boot_mode.attr,
+	&dev_attr_fw_version.attr,
+	&dev_attr_hw_version.attr,
+	NULL
+};
+
+static struct attribute_group raydium_attribute_group = {
+	.attrs = raydium_attributes,
+};
+
+static void raydium_i2c_remove_sysfs_group(void *_data)
+{
+	struct raydium_data *ts = _data;
+
+	sysfs_remove_group(&ts->client->dev.kobj, &raydium_attribute_group);
+}
+
+static int raydium_i2c_power_on(struct raydium_data *ts)
+{
+	int error;
+
+	if (IS_ERR_OR_NULL(ts->reset_gpio))
+		return 0;
+
+	gpiod_set_value_cansleep(ts->reset_gpio, 1);
+
+	error = regulator_enable(ts->avdd);
+	if (error) {
+		dev_err(&ts->client->dev,
+			"failed to enable avdd regulator: %d\n", error);
+		goto release_reset_gpio;
+	}
+
+	error = regulator_enable(ts->vccio);
+	if (error) {
+		regulator_disable(ts->avdd);
+		dev_err(&ts->client->dev,
+			"failed to enable vccio regulator: %d\n", error);
+		goto release_reset_gpio;
+	}
+
+	udelay(RAYDIUM_POWERON_DELAY_USEC);
+
+release_reset_gpio:
+	gpiod_set_value_cansleep(ts->reset_gpio, 0);
+
+	if (error)
+		return error;
+
+	msleep(RAYDIUM_RESET_DELAY_MSEC);
+
+	return 0;
+}
+
+static void raydium_i2c_power_off(void *_data)
+{
+	struct raydium_data *ts = _data;
+
+	if (!IS_ERR_OR_NULL(ts->reset_gpio)) {
+		gpiod_set_value_cansleep(ts->reset_gpio, 1);
+		regulator_disable(ts->vccio);
+		regulator_disable(ts->avdd);
+	}
+}
+
+static int raydium_i2c_probe(struct i2c_client *client,
+			    const struct i2c_device_id *id)
+{
+	union i2c_smbus_data dummy;
+	struct raydium_data *ts;
+	unsigned long irqflags;
+	int error;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		dev_err(&client->dev,
+			"%s: i2c check functionality error\n", DEVICE_NAME);
+		return -ENXIO;
+	}
+
+	ts = devm_kzalloc(&client->dev, sizeof(struct raydium_data),
+		GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+
+	mutex_init(&ts->sysfs_mutex);
+	init_completion(&ts->cmd_done);
+
+	ts->client = client;
+	i2c_set_clientdata(client, ts);
+
+	ts->avdd = devm_regulator_get(&client->dev, "avdd");
+	if (IS_ERR(ts->avdd)) {
+		error = PTR_ERR(ts->avdd);
+		if (error != -EPROBE_DEFER)
+			dev_err(&client->dev,
+				"Failed to get 'avdd' regulator: %d\n", error);
+		return error;
+	}
+
+	ts->vccio = devm_regulator_get(&client->dev, "vccio");
+	if (IS_ERR(ts->vccio)) {
+		error = PTR_ERR(ts->vccio);
+		if (error != -EPROBE_DEFER)
+			dev_err(&client->dev,
+				"Failed to get 'vccio' regulator: %d\n", error);
+		return error;
+	}
+
+	ts->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
+		GPIOD_OUT_LOW);
+	if (IS_ERR(ts->reset_gpio)) {
+		error = PTR_ERR(ts->reset_gpio);
+		if (error != -EPROBE_DEFER) {
+			dev_err(&client->dev,
+				"failed to get reset gpio: %d\n", error);
+			return error;
+		}
+	}
+
+	error = raydium_i2c_power_on(ts);
+	if (error)
+		return error;
+
+	error = devm_add_action(&client->dev, raydium_i2c_power_off, ts);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to install power off action: %d\n", error);
+		raydium_i2c_power_off(ts);
+		return error;
+	}
+
+	/* Make sure there is something at this address */
+	if (i2c_smbus_xfer(client->adapter, client->addr, 0,
+			I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0) {
+		dev_err(&client->dev, "nothing at this address\n");
+		return -ENXIO;
+	}
+
+	error = raydium_i2c_initialize(ts);
+	if (error) {
+		dev_err(&client->dev, "failed to initialize: %d\n", error);
+		return error;
+	}
+
+	ts->input = devm_input_allocate_device(&client->dev);
+	if (!ts->input) {
+		dev_err(&client->dev, "Failed to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	ts->input->name = "Raydium Touchscreen";
+	ts->input->id.bustype = BUS_I2C;
+
+	__set_bit(BTN_TOUCH, ts->input->keybit);
+	__set_bit(EV_ABS, ts->input->evbit);
+	__set_bit(EV_KEY, ts->input->evbit);
+
+	/* Multitouch input params setup */
+	input_set_abs_params(ts->input, ABS_MT_POSITION_X, 0,
+		ts->info.x_max, 0, 0);
+	input_set_abs_params(ts->input, ABS_MT_POSITION_Y,
+		0, ts->info.y_max, 0, 0);
+	input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+	input_set_abs_params(ts->input, ABS_MT_PRESSURE, 0, 255, 0, 0);
+	input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->info.x_res);
+	input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->info.y_res);
+
+	input_mt_init_slots(ts->input, MAX_TOUCH_NUM, 0);
+
+	input_set_drvdata(ts->input, ts);
+
+	error = input_mt_init_slots(ts->input, MAX_TOUCH_NUM,
+		INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to initialize MT slots: %d\n", error);
+		return error;
+	}
+
+	error = input_register_device(ts->input);
+	if (error) {
+		dev_err(&client->dev,
+			"unable to register input device: %d\n", error);
+		return error;
+	}
+
+	error = devm_request_threaded_irq(&client->dev, client->irq,
+			NULL, raydium_i2c_irq, irqflags | IRQF_ONESHOT,
+			client->name, ts);
+	if (error) {
+		dev_err(&client->dev, "Failed to register interrupt\n");
+		return error;
+	}
+
+	device_init_wakeup(&client->dev, true);
+
+	error = sysfs_create_group(&client->dev.kobj, &raydium_attribute_group);
+	if (error) {
+		dev_err(&client->dev, "failed to create sysfs attributes: %d\n",
+			error);
+		return error;
+	}
+
+	error = devm_add_action(&client->dev,
+				raydium_i2c_remove_sysfs_group, ts);
+	if (error) {
+		raydium_i2c_remove_sysfs_group(ts);
+		dev_err(&client->dev,
+			"Failed to add sysfs cleanup action: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int raydium_i2c_remove(struct i2c_client *client)
+{
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	input_unregister_device(ts->input);
+
+	device_init_wakeup(&client->dev, false);
+
+	mutex_destroy(&ts->sysfs_mutex);
+
+	return 0;
+}
+
+static void __maybe_unused raydium_enter_sleep(struct i2c_client *client)
+{
+	static const u8 sleep_cmd[] = { 0x5A, 0xff, 0x00, 0x0f };
+	int error;
+
+	error = raydium_i2c_send(client, CMD_ENTER_SLEEP, (u8 *)sleep_cmd,
+		sizeof(sleep_cmd));
+	if (error)
+		dev_err(&client->dev,
+			"Send sleep failed: %d\n", error);
+}
+
+static int __maybe_unused raydium_i2c_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	/* Command not support in BLDR recovery mode */
+	if (ts->boot_mode != RAYDIUM_TS_MAIN)
+		return -EBUSY;
+
+	disable_irq(client->irq);
+
+	if (device_may_wakeup(dev)) {
+		raydium_enter_sleep(client);
+
+		ts->wake_irq_enabled = (enable_irq_wake(client->irq) == 0);
+	} else {
+		raydium_i2c_power_off(ts);
+	}
+
+	return 0;
+}
+
+static int __maybe_unused raydium_i2c_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	if (device_may_wakeup(dev)) {
+		if (ts->wake_irq_enabled)
+			disable_irq_wake(client->irq);
+		raydium_i2c_sw_reset(client);
+	} else {
+		raydium_i2c_power_on(ts);
+		raydium_i2c_initialize(ts);
+	}
+
+	enable_irq(client->irq);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(raydium_i2c_pm_ops,
+			 raydium_i2c_suspend, raydium_i2c_resume);
+
+static const struct i2c_device_id raydium_i2c_id[] = {
+	{ DEVICE_NAME, 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, raydium_i2c_id);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id raydium_acpi_id[] = {
+	{ "RAYD0001", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(acpi, raydium_acpi_id);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id raydium_of_match[] = {
+	{ .compatible = "raydium,rm31100",},
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, raydium_of_match);
+#endif
+
+static struct i2c_driver raydium_i2c_driver = {
+	.probe = raydium_i2c_probe,
+	.remove = raydium_i2c_remove,
+	.id_table = raydium_i2c_id,
+	.driver = {
+		.name = "raydium_ts",
+		.pm = &raydium_i2c_pm_ops,
+		.acpi_match_table = ACPI_PTR(raydium_acpi_id),
+		.of_match_table = of_match_ptr(raydium_of_match),
+	},
+};
+
+module_i2c_driver(raydium_i2c_driver);
+
+MODULE_AUTHOR("Raydium");
+MODULE_DESCRIPTION("Raydium I2c Touchscreen driver");
+MODULE_LICENSE("GPL v2");
-- 
2.1.2


^ permalink raw reply related	[flat|nested] 42+ messages in thread

* RE: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2016-03-10 18:47 ` Dmitry Torokhov
@ 2016-03-11  2:10   ` Jeffrey Lin (林義章)
  0 siblings, 0 replies; 42+ messages in thread
From: Jeffrey Lin (林義章) @ 2016-03-11  2:10 UTC (permalink / raw)
  To: Dmitry Torokhov, jeffrey.lin
  Cc: rydberg, grant.likely, robh+dt, jeesw, bleung, scott.liu,
	Roger Yang (楊鎮瑋),
	KP Li (李昆倍),
	Albert Shieh (謝欣瑋),
	linux-kernel, linux-input, devicetree

Hi Dmitry:
Thanks for your response. I'll fixed issues you remarked in the mail.

Best Regards
----------------------------------------------------------------------
Jeffrey Lin,林義章
瑞鼎科技
Raydium Semiconductor Corporation
Tel:(03)666-1818 Ext.4163
Fax:(03)666-1919

-----Original Message-----
From: Dmitry Torokhov [mailto:dmitry.torokhov@gmail.com] 
Sent: Friday, March 11, 2016 2:48 AM
To: jeffrey.lin
Cc: rydberg@euromail.se; grant.likely@linaro.org; robh+dt@kernel.org; jeesw@melfas.com; bleung@chromium.org; scott.liu@emc.com.tw; Jeffrey Lin (林義章); Roger Yang (楊鎮瑋); KP Li (李昆倍); Albert Shieh (謝欣瑋); linux-kernel@vger.kernel.org; linux-input@vger.kernel.org; devicetree@vger.kernel.org
Subject: Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver

Hi Jeffrey,

On Thu, Mar 03, 2016 at 02:42:11PM +0800, jeffrey.lin wrote:
> Raydium I2C touch driver.
> 
> Signed-off-by: jeffrey.lin <jeffrey.lin@rad-ic.com>
> ---
>  drivers/input/touchscreen/Kconfig          |  13 +
>  drivers/input/touchscreen/Makefile         |   1 +
>  drivers/input/touchscreen/raydium_i2c_ts.c | 953 
> +++++++++++++++++++++++++++++
>  3 files changed, 967 insertions(+)
>  create mode 100644 drivers/input/touchscreen/raydium_i2c_ts.c
> 
> diff --git a/drivers/input/touchscreen/Kconfig 
> b/drivers/input/touchscreen/Kconfig
> index 3f3f6ee..9adacf6 100644
> --- a/drivers/input/touchscreen/Kconfig
> +++ b/drivers/input/touchscreen/Kconfig
> @@ -915,6 +915,19 @@ config TOUCHSCREEN_PCAP
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called pcap_ts.
>  
> +config TOUCHSCREEN_RM_TS

Can we call it TOUCHSCREEN_RAYDIUM_RM31100? Do you have other models?
Maybe RM31XXX or similar?

Or TOUCHSCREEN_RAYDIUM_I2C?

I just want to make a general i2c driver for our several raydium models. So I think TOUCHSCREEN_RAYDIUM_I2C be better.
> +	tristate "Raydium I2C Touchscreen"
> +	depends on I2C
> +	help
> +	  Say Y here if you have Raydium series I2C touchscreen,
> +	  such as RM31100 , connected to your system.
> +
> +	  If unsure, say N.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called raydium_i2c_ts.
> +
> +

Extra empty line.

>  config TOUCHSCREEN_ST1232
>  	tristate "Sitronix ST1232 touchscreen controllers"
>  	depends on I2C
> diff --git a/drivers/input/touchscreen/Makefile 
> b/drivers/input/touchscreen/Makefile
> index 4941f2d..99e08cf 100644
> --- a/drivers/input/touchscreen/Makefile
> +++ b/drivers/input/touchscreen/Makefile
> @@ -55,6 +55,7 @@ obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE)	+= usbtouchscreen.o
>  obj-$(CONFIG_TOUCHSCREEN_PCAP)		+= pcap_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_PENMOUNT)	+= penmount.o
>  obj-$(CONFIG_TOUCHSCREEN_PIXCIR)	+= pixcir_i2c_ts.o
> +obj-$(CONFIG_TOUCHSCREEN_RM_TS)		+= raydium_i2c_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_S3C2410)	+= s3c2410_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_ST1232)	+= st1232.o
>  obj-$(CONFIG_TOUCHSCREEN_STMPE)		+= stmpe-ts.o
> diff --git a/drivers/input/touchscreen/raydium_i2c_ts.c 
> b/drivers/input/touchscreen/raydium_i2c_ts.c
> new file mode 100644
> index 0000000..7ba681e
> --- /dev/null
> +++ b/drivers/input/touchscreen/raydium_i2c_ts.c
> @@ -0,0 +1,953 @@
> +/*
> + * Raydium touchscreen I2C driver.
> + *
> + * Copyright (C) 2012-2014, Raydium Semiconductor Corporation.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * version 2, and only version 2, as published by the
> + * Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * Raydium reserves the right to make changes without further notice
> + * to the materials described herein. Raydium does not assume any
> + * liability arising out of the application described herein.
> + *
> + * Contact Raydium Semiconductor Corporation at www.rad-ic.com  */
> +
> +#include <linux/module.h>
> +#include <linux/input.h>
> +#include <linux/interrupt.h>
> +#include <linux/platform_device.h>
> +#include <linux/i2c.h>
> +#include <linux/delay.h>
> +#include <linux/uaccess.h>
> +#include <linux/buffer_head.h>

Why do you need this include?

> +#include <linux/slab.h>
> +#include <linux/firmware.h>
> +#include <linux/input/mt.h>
> +#include <linux/acpi.h>
> +#include <linux/of.h>
> +#include <linux/gpio.h>

I do not think you need this one.

> +#include <linux/regulator/consumer.h> #include <asm/unaligned.h> 
> +#include <linux/gpio/consumer.h>
> +
> +/* Device, Driver information */
> +#define DEVICE_NAME	"raydium_i2c"
> +
> +/* Slave I2C mode*/
> +#define RM_BOOT_BLDR	0x02
> +#define RM_BOOT_MAIN	0x03
> +
> +/*I2C command */
> +#define CMD_QUERY_BANK		0x2B
> +#define CMD_DATA_BANK		0x4D
> +#define CMD_ENTER_SLEEP		0x4E
> +#define CMD_BOOT_ACK		0x0A
> +#define CMD_BOOT_WRT		0x5B
> +#define CMD_BOOT_CHK		0x0C
> +#define CMD_BANK_SWITCH		0xAA
> +
> +/* Touch relative info */
> +#define MAX_RETRIES		3
> +#define MAX_FW_UPDATE_RETRIES	30
> +#define MAX_TOUCH_NUM		10
> +#define MAX_PACKET_SIZE		60
> +#define BOOT_DELAY_MS	100
> +
> +#define RAYDIUM_FW_PAGESIZE	128
> +#define RAYDIUM_POWERON_DELAY_USEC	500
> +#define RAYDIUM_RESET_DELAY_MSEC	50
> +
> +#define ADDR_INDEX		0x03
> +#define HEADER_SIZE		4
> +
> +enum raydium_boot_mode {
> +	RAYDIUM_TS_MAIN,
> +	RAYDIUM_TS_BLDR,
> +};
> +
> +struct raydium_info {
> +	u32 hw_ver;

This seems to be coming directly form the wire, you need to annotate it as little- or big-endian and use appropriate accessors to convert to CPU endianness.

Because of many CPU endianness are different, so next I'll use buffer or point to replace them and use le16(or le32)_to_cpu().
> +	u8 main_ver;
> +	u8 sub_ver;
> +	u16 ft_ver;

Same here.

> +	u8 x_num;
> +	u8 y_num;
> +	u16 x_max;
> +	u16 y_max;

And here.

> +	u8 x_res;		/* units/mm */
> +	u8 y_res;		/* units/mm */
> +};
> +
> +struct raydium_abs_info {
> +	u8 state;/*1:touch, 0:no touch*/
> +	u8 x_pos_lsb;
> +	u8 x_pos_msb;
> +	u8 y_pos_lsb;
> +	u8 y_pos_msb;
> +	u8 pressure;
> +	u8 x_width;
> +	u8 y_width;
> +};

I'd rather define offsets and use get_unaligned_le16() to fetch x/y data.

> +
> +struct raydium_object {
> +	u32 data_bank_addr;

This likely needs to be converted to cpu-endianness as well.

> +	u8 pkg_size;
> +};
> +
> +/* struct raydium_data - represents state of Raydium touchscreen 
> +device */ struct raydium_data {
> +	struct i2c_client *client;
> +	struct input_dev *input;
> +
> +	struct regulator *vcc33;
> +	struct regulator *vccio;
> +	struct gpio_desc *reset_gpio;
> +
> +	u32 query_bank_info;
> +
> +	struct raydium_info info;
> +	struct raydium_object obj;
> +	struct raydium_abs_info finger;

This is not used.

> +
> +	enum raydium_boot_mode boot_mode;
> +
> +	struct mutex sysfs_mutex;
> +
> +	u8 cmd_resp[HEADER_SIZE];
> +	struct completion cmd_done;
> +
> +	u8 buf[MAX_PACKET_SIZE];
> +
> +	bool wake_irq_enabled;
> +};
> +
> +static int raydium_i2c_send(struct i2c_client *client,
> +	u8 addr, u8 *data, size_t len)
> +{
> +	u8 buf[MAX_PACKET_SIZE + 1];
> +	int tries = 0;
> +
> +	if (len > MAX_PACKET_SIZE)
> +		return -EINVAL;
> +
> +	buf[0] = addr;
> +	memcpy(&buf[1], data, len);
> +
> +	do {
> +		if (i2c_master_send(client, buf, len + 1) == (len + 1))
> +			return 0;
> +		msleep(20);
> +	} while (++tries < MAX_RETRIES);
> +
> +	dev_err(&client->dev, "%s: i2c send failed\n", __func__);
> +
> +	return -EIO;
> +}
> +
> +static int raydium_i2c_read(struct i2c_client *client,
> +	u8 addr, size_t len, void *data)
> +{
> +	struct i2c_msg xfer[2];
> +
> +	/* Write register */
> +	xfer[0].addr = client->addr;
> +	xfer[0].flags = 0;
> +	xfer[0].len = 1;
> +	xfer[0].buf = &addr;
> +
> +	/* Read data */
> +	xfer[1].addr = client->addr;
> +	xfer[1].flags = I2C_M_RD;
> +	xfer[1].len = len;
> +	xfer[1].buf = data;
> +
> +	if (i2c_transfer(client->adapter, xfer, 2) == 2)

ARRAY_SIZE()

> +		return 0;
> +
> +	dev_err(&client->dev, "%s: i2c transfer failed\n", __func__);
> +
> +	return -EIO;

Do not clobber errors returned by i2c_transfer.

> +}
> +
> +static int raydium_i2c_read_message(struct i2c_client *client,
> +	u32 addr, size_t len, void *data)
> +{
> +	u16 pkg_size, use_len;
> +	u8 buf[HEADER_SIZE], idx_i, idx_j;
> +	int error;
> +
> +	use_len = len;
> +	idx_j = 0;
> +	while (use_len > 0) {
> +		pkg_size = (use_len < MAX_PACKET_SIZE) ?
> +			use_len : MAX_PACKET_SIZE;
> +		for (idx_i = 0; idx_i < HEADER_SIZE; idx_i++)
> +			buf[idx_i] = addr >> (HEADER_SIZE - 1 - idx_i)*8;
> +
> +		/*set data bank*/
> +		error = raydium_i2c_send(client, CMD_BANK_SWITCH,
> +			(u8 *)buf, HEADER_SIZE);
> +		/*read potints data*/
> +		if (!error)
> +			error = raydium_i2c_read(client, buf[ADDR_INDEX],
> +				pkg_size,
> +				(void *)(data + idx_j*MAX_PACKET_SIZE));

You do not need to convert to void * pointers.

> +
> +		pkg_size += MAX_PACKET_SIZE;
> +		addr += MAX_PACKET_SIZE;
> +		use_len = (use_len < MAX_PACKET_SIZE) ?
> +			0 : (use_len - MAX_PACKET_SIZE);
> +		idx_j++;
> +	}
> +
> +	return error;
> +}
> +
> +static int raydium_i2c_send_message(struct i2c_client *client,
> +	size_t len, void *data)
> +{
> +	int error;
> +	u8 buf[HEADER_SIZE], ii;
> +
> +	for (ii = 0; ii < HEADER_SIZE; ii++)
> +		buf[ii] = ((u8 *)data)[3 - ii];

This looks like cpu_to_be32().

> +	/*set data bank*/
> +	error = raydium_i2c_send(client, CMD_BANK_SWITCH, (u8 *)buf,
> +		HEADER_SIZE);
> +
> +	/*send message*/
> +	if (!error)
> +		error = raydium_i2c_send(client, buf[ADDR_INDEX], buf, len);
> +
> +	return error;
> +}
> +
> +static int raydium_i2c_sw_reset(struct i2c_client *client) {
> +	static const u8 soft_rst_cmd[] = { 0x01, 0x04, 0x00, 0x00, 0x40};
> +	int error;
> +
> +	error = raydium_i2c_send_message(client, 1, (void *)soft_rst_cmd);

This is wrong. You cast away constness. I also confused how it works.
You have 5 bytes of command, but I think you send first 4 and then fist
1 in raydium_i2c_send_message; you never get to the 5th byte.

Sorry. I sent the wrong length.

> +	if (error) {
> +		dev_err(&client->dev, "software reset failed: %d\n", error);
> +		return error;
> +	}
> +
> +	msleep(RAYDIUM_RESET_DELAY_MSEC);
> +
> +	return 0;
> +}
> +
> +static int raydium_i2c_query_ts_info(struct raydium_data *ts) {
> +	struct i2c_client *client = ts->client;
> +	int error, retry_cnt;
> +
> +	for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
> +		error = raydium_i2c_read(client, CMD_DATA_BANK,
> +			sizeof(ts->obj), (void *)&ts->obj);
> +		if (!error) {
> +			error = raydium_i2c_read(client, CMD_QUERY_BANK,
> +				sizeof(ts->query_bank_info),
> +				(void *)&ts->query_bank_info);
> +			if (!error) {
> +				error = raydium_i2c_read_message(client,
> +					ts->query_bank_info, sizeof(ts->info),
> +					(void *)&ts->info);
> +			}
> +
> +			if (!error)
> +				return 0;
> +		}
> +	}
> +	dev_err(&client->dev, "get data bank failed: %d\n", error);
> +
> +	return -EINVAL;
> +}
> +
> +static int raydium_i2c_fastboot(struct i2c_client *client) {
> +	static const u8 boot_cmd[] = { 0x20, 0x06, 0x00, 0x50 };
> +	u8 buf[HEADER_SIZE];
> +	int error;
> +
> +	error = raydium_i2c_read_message(client,
> +		get_unaligned_be32(boot_cmd),
> +		sizeof(boot_cmd), buf);
> +
> +	if (!error) {
> +		if (buf[0] == RM_BOOT_BLDR) {
> +			dev_dbg(&client->dev, "boot in fastboot mode\n");
> +			return -EINVAL;
> +		}
> +		dev_dbg(&client->dev, "boot success -- 0x%x\n", client->addr);
> +		return 0;
> +	}
> +
> +	dev_err(&client->dev, "boot failed: %d\n", error);
> +
> +	return error;
> +}
> +
> +static int raydium_i2c_initialize(struct raydium_data *ts) {
> +	struct i2c_client *client = ts->client;
> +	int error, retry_cnt;
> +	static const u8 recov_packet[] = { 0x04, 0x81 };
> +	u8 buf[HEADER_SIZE];
> +
> +	for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
> +		error = raydium_i2c_fastboot(client);
> +		if (error) {
> +			/* Continue initializing if it's the last try */
> +			if (retry_cnt < MAX_RETRIES - 1)
> +				continue;
> +		}
> +		/* Wait for Hello packet */
> +		msleep(BOOT_DELAY_MS);
> +
> +		error = raydium_i2c_read(client, recov_packet[0], 1,
> +			(void *)buf);
> +		if (error) {
> +			dev_err(&client->dev,
> +				"failed to read 'hello' packet: %d\n", error);
> +		} else if (buf[0] == recov_packet[1]) {
> +			ts->boot_mode = RAYDIUM_TS_MAIN;
> +			break;
> +		}
> +	}
> +
> +	if (error)
> +		ts->boot_mode = RAYDIUM_TS_BLDR;
> +	else
> +		raydium_i2c_query_ts_info(ts);
> +
> +	return error;
> +}
> +
> +static int raydium_i2c_fw_write_page(struct i2c_client *client,
> +				    const void *page)
> +{
> +	static const u8 ack_ok[] = { 0x55, 0xAA };
> +	u8 buf[2];
> +	int retry;
> +	int error;
> +
> +	for (retry = 0; retry < MAX_FW_UPDATE_RETRIES; retry++) {
> +		error = raydium_i2c_send(client, CMD_BOOT_WRT,
> +			(u8 *)page, RAYDIUM_FW_PAGESIZE);
> +		if (error) {
> +			dev_err(&client->dev,
> +				"BLDR Write Page failed: %d\n", error);
> +			continue;
> +		}
> +
> +		error = raydium_i2c_read(client, CMD_BOOT_CHK, sizeof(ack_ok),
> +			(void *)buf);
> +		if (error) {
> +			dev_err(&client->dev,
> +				"BLDR Ack read failed: %d\n", error);
> +			return error;
> +		}
> +
> +		if (!memcmp(buf, ack_ok, sizeof(ack_ok)))
> +			return 0;
> +
> +		error = -EIO;
> +		dev_err(&client->dev,
> +			"BLDR Get Ack Error [%02x:%02x]\n", buf[0], buf[1]);
> +	}
> +
> +	return error;
> +}
> +
> +static int raydium_i2c_do_update_firmware(struct i2c_client *client,
> +					 const struct firmware *fw,
> +					 bool force)
> +{
> +	static const u8 boot_cmd[] = { RM_BOOT_BLDR, 0x20, 0x06, 0x00, 0x50 };
> +	static const u8 main_cmd[] = { RM_BOOT_MAIN, 0x20, 0x06, 0x00, 0x50 };
> +	static const u8 boot_ack[] = { 0x55, 0xAA, 0x00, 0xFF };
> +	u8 buf[HEADER_SIZE];
> +	int page, n_fw_pages;
> +	int error;
> +
> +	/* Recovery mode detection! */
> +	if (!force) {
> +		/* Start boot loader Procedure */
> +		dev_dbg(&client->dev, "Normal BLDR procedure\n");
> +		/* switch to mode */
> +		error = raydium_i2c_send_message(client, 1, (void *)boot_cmd);
> +		if (error)
> +			dev_err(&client->dev, "failed to send boot cmd: %d\n",
> +				error);
> +		msleep(60);
> +		raydium_i2c_sw_reset(client);
> +		msleep(RAYDIUM_RESET_DELAY_MSEC);
> +		error = raydium_i2c_send_message(client, 1, (void *)boot_cmd);
> +	}
> +
> +	if (error) {
> +		dev_err(&client->dev, "failed to enter fastboot mode: %d\n",
> +			error);
> +		return error;
> +	}
> +
> +	msleep(20);
> +
> +	/* check fastboot state */
> +	error = raydium_i2c_read(client, CMD_BOOT_ACK,
> +		sizeof(boot_ack), (void *)buf);
> +	if (error) {
> +		dev_err(&client->dev,
> +			"failed to read boot ack: %d\n", error);
> +		return error;
> +	}
> +
> +	if (memcmp(buf, boot_ack, sizeof(boot_ack))) {
> +		dev_err(&client->dev,
> +			"failed to enter fastboot: %*ph (expected %*ph)\n",
> +			(int)sizeof(buf), buf, (int)sizeof(boot_ack), boot_ack);
> +		return -EIO;
> +	}
> +
> +	dev_info(&client->dev, "successfully entered fastboot mode");
> +
> +	n_fw_pages = fw->size / RAYDIUM_FW_PAGESIZE;
> +	dev_dbg(&client->dev, "BLDR Pages = %d\n", n_fw_pages);
> +
> +	for (page = 0; page < n_fw_pages; page++) {
> +		error = raydium_i2c_fw_write_page(client,
> +					fw->data + page * RAYDIUM_FW_PAGESIZE);
> +		if (error) {
> +			dev_err(&client->dev,
> +				"failed to write FW page %d: %d\n",
> +				page, error);
> +			return error;
> +		}
> +	}
> +	error = raydium_i2c_send_message(client, 1, (void *)main_cmd);
> +	msleep(20);
> +	raydium_i2c_sw_reset(client);
> +	dev_info(&client->dev, "firmware update completed\n");
> +
> +	return 0;
> +}
> +
> +static int raydium_i2c_fw_update(struct raydium_data *ts) {
> +	struct i2c_client *client = ts->client;
> +	const struct firmware *fw;
> +	char *fw_name;
> +	int error;
> +
> +	fw_name = kasprintf(GFP_KERNEL, "raydium_i2c_%04x.bin",
> +		ts->info.hw_ver & 0xFFFF);
> +	if (!fw_name)
> +		return -ENOMEM;
> +
> +	dev_info(&client->dev, "requesting fw name = %s\n", fw_name);
> +	error = request_firmware(&fw, fw_name, &client->dev);
> +	kfree(fw_name);
> +	if (error) {
> +		dev_err(&client->dev, "failed to request firmware: %d\n",
> +			error);
> +		return error;
> +	}
> +
> +	if (fw->size % RAYDIUM_FW_PAGESIZE) {
> +		dev_err(&client->dev, "invalid firmware length: %zu\n",
> +			fw->size);
> +		error = -EINVAL;
> +		goto out;
> +	}
> +
> +	disable_irq(client->irq);
> +	error = raydium_i2c_do_update_firmware(client, fw,
> +					ts->boot_mode == RAYDIUM_TS_BLDR);
> +	if (error) {
> +		dev_err(&client->dev, "firmware update failed: %d\n", error);
> +		ts->boot_mode = RAYDIUM_TS_BLDR;
> +		goto out_enable_irq;
> +	}
> +	error = raydium_i2c_initialize(ts);
> +	if (error) {
> +		dev_err(&client->dev,
> +			"failed to initialize device after firmware update: %d\n",
> +			error);
> +		ts->boot_mode = RAYDIUM_TS_BLDR;
> +		goto out_enable_irq;
> +	}
> +
> +	ts->boot_mode = RAYDIUM_TS_MAIN;
> +
> +out_enable_irq:
> +	enable_irq(client->irq);
> +	msleep(100);
> +out:
> +	release_firmware(fw);
> +	return error;
> +}
> +
> +static void raydium_mt_event(struct raydium_data *ts) {
> +	struct raydium_abs_info *data;
> +	int error, i, x, y;
> +	u8 f_state;
> +	u8 touch_count;
> +	u8 tp_info_size;
> +
> +	error = raydium_i2c_read_message(ts->client, ts->obj.data_bank_addr,
> +		ts->obj.pkg_size, (void *)&ts->buf);
> +
> +	if (error < 0) {
> +		dev_err(&ts->client->dev, "%s: failed to read data: %d\n",
> +			__func__, error);
> +		return;
> +	}
> +
> +	touch_count = 0;
> +	tp_info_size = sizeof(ts->finger);
> +
> +	for (i = 0; i < MAX_TOUCH_NUM; i++) {
> +		data = (struct raydium_abs_info *)(ts->buf + i * tp_info_size);
> +
> +		f_state = data->state & 0x03;
> +
> +		input_mt_slot(ts->input, i);
> +		input_mt_report_slot_state(ts->input,
> +				MT_TOOL_FINGER, f_state != 0);
> +
> +		if (!f_state)
> +			continue;
> +
> +		x = (data->x_pos_msb << 8) | (data->x_pos_lsb);
> +		y = (data->y_pos_msb << 8) | (data->y_pos_lsb);
> +
> +		input_report_key(ts->input, BTN_TOUCH, 1);
> +		input_report_key(ts->input, BTN_TOOL_FINGER, 1);

Drop these 2.

> +		input_report_abs(ts->input, ABS_MT_POSITION_X, x);
> +		input_report_abs(ts->input, ABS_MT_POSITION_Y, y);
> +		input_report_abs(ts->input, ABS_MT_PRESSURE, data->pressure);
> +		input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR,
> +			max(data->x_width, data->y_width));
> +		input_report_abs(ts->input, ABS_MT_TOUCH_MINOR,
> +			min(data->x_width, data->y_width));
> +		touch_count++;
> +	}
> +
> +	input_report_key(ts->input, BTN_TOUCH, touch_count > 0);


> +	input_report_key(ts->input, BTN_TOOL_FINGER, ts->input > 0);

What does ts->input > 0 means?

> +	input_mt_report_pointer_emulation(ts->input, false);

Please call input_mt_sync_frame() instead.

> +	input_sync(ts->input);
> +}
> +
> +static irqreturn_t raydium_i2c_irq(int irq, void *_dev) {
> +	struct raydium_data *ts = _dev;
> +
> +	if (ts->boot_mode != RAYDIUM_TS_BLDR)
> +		raydium_mt_event(ts);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static ssize_t write_update_fw(struct device *dev,
> +			struct device_attribute *attr,
> +			const char *buf, size_t count)
> +{
> +	struct raydium_data *ts = dev_get_drvdata(dev);
> +	int error;
> +
> +	error = mutex_lock_interruptible(&ts->sysfs_mutex);
> +	if (error)
> +		return error;
> +
> +	error = raydium_i2c_fw_update(ts);
> +	dev_dbg(dev, "firmware update result: %d\n", error);
> +
> +	mutex_unlock(&ts->sysfs_mutex);
> +	return error ?: count;
> +}
> +
> +static ssize_t raydium_bootmode_show(struct device *dev,
> +		struct device_attribute *attr, char *buf) {
> +	struct raydium_data *ts = dev_get_drvdata(dev);
> +
> +	return sprintf(buf, "%s\n", ts->boot_mode == RAYDIUM_TS_MAIN ?
> +		"Normal" : "Recovery");
> +}
> +
> +static ssize_t raydium_fw_ver_show(struct device *dev,
> +			struct device_attribute *attr, char *buf) {
> +	struct raydium_data *ts = dev_get_drvdata(dev);
> +
> +	return sprintf(buf, "Release Version %d.%d\n",
> +		ts->info.main_ver, ts->info.sub_ver);

Do not use strings in sysfs attributes. Just use %d.%d or, better yet, %02x.%02x.

> +}
> +
> +static ssize_t raydium_hw_ver_show(struct device *dev,
> +			struct device_attribute *attr, char *buf) {
> +	struct raydium_data *ts = dev_get_drvdata(dev);
> +
> +	return sprintf(buf, "Hardware version 0x%x\n",
> +		ts->info.hw_ver & 0xFFFF);

Same here, just use %04x.

> +}
> +
> +static DEVICE_ATTR(fw_version, S_IRUGO, raydium_fw_ver_show, NULL); 
> +static DEVICE_ATTR(hw_version, S_IRUGO, raydium_hw_ver_show, NULL); 
> +static DEVICE_ATTR(boot_mode, S_IRUGO, raydium_bootmode_show, NULL); 
> +static DEVICE_ATTR(update_fw, S_IWUSR, NULL, write_update_fw);
> +
> +static struct attribute *raydium_attributes[] = {
> +	&dev_attr_update_fw.attr,
> +	&dev_attr_boot_mode.attr,
> +	&dev_attr_fw_version.attr,
> +	&dev_attr_hw_version.attr,
> +	NULL
> +};
> +
> +static struct attribute_group raydium_attribute_group = {
> +	.attrs = raydium_attributes,
> +};
> +
> +static void raydium_i2c_remove_sysfs_group(void *_data) {
> +	struct raydium_data *ts = _data;
> +
> +	sysfs_remove_group(&ts->client->dev.kobj, &raydium_attribute_group); 
> +}
> +
> +static int raydium_i2c_power_on(struct raydium_data *ts) {
> +	int error;
> +
> +	if (IS_ERR_OR_NULL(ts->reset_gpio))
> +		return 0;
> +
> +	gpiod_set_value_cansleep(ts->reset_gpio, 1);
> +
> +	error = regulator_enable(ts->vcc33);
> +	if (error) {
> +		dev_err(&ts->client->dev,
> +			"failed to enable vcc33 regulator: %d\n", error);
> +		goto release_reset_gpio;
> +	}
> +
> +	error = regulator_enable(ts->vccio);
> +	if (error) {
> +		regulator_disable(ts->vcc33);
> +		dev_err(&ts->client->dev,
> +			"failed to enable vccio regulator: %d\n", error);
> +		goto release_reset_gpio;
> +	}
> +
> +	udelay(RAYDIUM_POWERON_DELAY_USEC);
> +
> +release_reset_gpio:
> +	gpiod_set_value_cansleep(ts->reset_gpio, 0);
> +
> +	if (error)
> +		return error;
> +
> +	msleep(RAYDIUM_RESET_DELAY_MSEC);
> +
> +	return 0;
> +}
> +
> +static void raydium_i2c_power_off(void *_data) {
> +	struct raydium_data *ts = _data;
> +
> +	if (!IS_ERR_OR_NULL(ts->reset_gpio)) {
> +		gpiod_set_value_cansleep(ts->reset_gpio, 1);
> +		regulator_disable(ts->vccio);
> +		regulator_disable(ts->vcc33);
> +	}
> +}
> +
> +static int raydium_i2c_probe(struct i2c_client *client,
> +			    const struct i2c_device_id *id) {
> +	union i2c_smbus_data dummy;
> +	struct raydium_data *ts;
> +	unsigned long irqflags;
> +	int error;
> +
> +	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
> +		dev_err(&client->dev,
> +			"%s: i2c check functionality error\n", DEVICE_NAME);
> +		return -ENXIO;
> +	}
> +
> +	ts = devm_kzalloc(&client->dev, sizeof(struct raydium_data),
> +		GFP_KERNEL);
> +	if (!ts)
> +		return -ENOMEM;
> +
> +	mutex_init(&ts->sysfs_mutex);
> +	init_completion(&ts->cmd_done);
> +
> +	ts->client = client;
> +	i2c_set_clientdata(client, ts);
> +
> +	ts->vcc33 = devm_regulator_get(&client->dev, "avdd");

If regulator is called avdd then I'd rather have ts->avdd as well.

> +	if (IS_ERR(ts->vcc33)) {
> +		error = PTR_ERR(ts->vcc33);
> +		if (error != -EPROBE_DEFER)
> +			dev_err(&client->dev,
> +				"Failed to get 'vcc33' regulator: %d\n", error);
> +		return error;
> +	}
> +
> +	ts->vccio = devm_regulator_get(&client->dev, "vdd");

ts->vccio -> ts->vdd please.

You also need to add binding documentation to Documentation/devicetree/bindings/input/...

> +	if (IS_ERR(ts->vccio)) {
> +		error = PTR_ERR(ts->vccio);
> +		if (error != -EPROBE_DEFER)
> +			dev_err(&client->dev,
> +				"Failed to get 'vccio' regulator: %d\n", error);
> +		return error;
> +	}
> +
> +

Extra empty line.

> +	ts->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
> +		GPIOD_OUT_LOW);
> +	if (IS_ERR(ts->reset_gpio)) {
> +		error = PTR_ERR(ts->reset_gpio);
> +		if (error != -EPROBE_DEFER) {
> +			dev_err(&client->dev,
> +				"failed to get reset gpio: %d\n", error);
> +			return error;
> +		}
> +	}
> +
> +	error = raydium_i2c_power_on(ts);
> +	if (error)
> +		return error;
> +
> +	error = devm_add_action(&client->dev, raydium_i2c_power_off, ts);
> +	if (error) {
> +		dev_err(&client->dev,
> +			"failed to install power off action: %d\n", error);
> +		raydium_i2c_power_off(ts);
> +		return error;
> +	}
> +
> +	/* Make sure there is something at this address */
> +	if (i2c_smbus_xfer(client->adapter, client->addr, 0,
> +			I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0) {
> +		dev_err(&client->dev, "nothing at this address\n");
> +		return -ENXIO;
> +	}
> +
> +	error = raydium_i2c_initialize(ts);
> +	if (error) {
> +		dev_err(&client->dev, "failed to initialize: %d\n", error);
> +		return error;
> +	}
> +
> +	ts->input = devm_input_allocate_device(&client->dev);
> +	if (!ts->input) {
> +		dev_err(&client->dev, "Failed to allocate input device\n");
> +		return -ENOMEM;
> +	}
> +
> +	ts->input->name = "Raydium Touchscreen";
> +	ts->input->id.bustype = BUS_I2C;
> +
> +	__set_bit(BTN_TOUCH, ts->input->keybit);
> +	__set_bit(EV_ABS, ts->input->evbit);
> +	__set_bit(EV_KEY, ts->input->evbit);
> +
> +	/* Single touch input params setup */
> +	input_set_abs_params(ts->input, ABS_X, 0, ts->info.x_max, 0, 0);
> +	input_set_abs_params(ts->input, ABS_Y, 0, ts->info.y_max, 0, 0);
> +	input_set_abs_params(ts->input, ABS_PRESSURE, 0, 255, 0, 0);
> +	input_abs_set_res(ts->input, ABS_X, ts->info.x_res);
> +	input_abs_set_res(ts->input, ABS_Y, ts->info.y_res);
> +
> +	/* Multitouch input params setup */
> +	error = input_mt_init_slots(ts->input, MAX_TOUCH_NUM,
> +				    INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
> +	if (error) {
> +		dev_err(&client->dev,
> +			"failed to initialize MT slots: %d\n", error);
> +		return error;
> +	}
> +
> +	input_set_abs_params(ts->input, ABS_MT_POSITION_X, 0,
> +		ts->info.x_max, 0, 0);
> +	input_set_abs_params(ts->input, ABS_MT_POSITION_Y,
> +		0, ts->info.y_max, 0, 0);
> +	input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
> +	input_set_abs_params(ts->input, ABS_MT_PRESSURE, 0, 255, 0, 0);
> +	input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->info.x_res);
> +	input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->info.y_res);
> +
> +	input_mt_init_slots(ts->input, MAX_TOUCH_NUM, 0);

You already called this once a few lines above. But what you actually want is to:

1. Set MT axies parameters.
2. Call  input_mt_init_slots(ts->input, MAX_TOUCH_NUM,
		INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED)
   so that it will finish MT initialization and will copy MT parameters
   over to ST.

> +
> +	input_set_drvdata(ts->input, ts);
> +
> +	error = input_register_device(ts->input);
> +	if (error) {
> +		dev_err(&client->dev,
> +			"unable to register input device: %d\n", error);
> +		return error;
> +	}
> +
> +	error = devm_request_threaded_irq(&client->dev, client->irq,
> +			NULL, raydium_i2c_irq, irqflags | IRQF_ONESHOT,
> +			client->name, ts);
> +	if (error) {
> +		dev_err(&client->dev, "Failed to register interrupt\n");
> +		return error;
> +	}
> +
> +	if (!client->dev.of_node)
> +		device_init_wakeup(&client->dev, true);

Please drop and instead make ACPI platform code mark i2c device as wakeup capable when registering it.

> +
> +	error = sysfs_create_group(&client->dev.kobj, &raydium_attribute_group);
> +	if (error) {
> +		dev_err(&client->dev, "failed to create sysfs attributes: %d\n",
> +			error);
> +		return error;
> +	}
> +
> +	error = devm_add_action(&client->dev,
> +				raydium_i2c_remove_sysfs_group, ts);
> +	if (error) {
> +		raydium_i2c_remove_sysfs_group(ts);
> +		dev_err(&client->dev,
> +			"Failed to add sysfs cleanup action: %d\n", error);
> +		return error;
> +	}
> +
> +	return 0;
> +}
> +
> +static int raydium_i2c_remove(struct i2c_client *client) {
> +	struct raydium_data *ts = i2c_get_clientdata(client);
> +
> +	if (ts->input)
> +		input_unregister_device(ts->input);

In what cases input device will not be present?

> +
> +	device_init_wakeup(&client->dev, false);
> +
> +	devm_free_irq(&client->dev, client->irq, ts);
> +
> +	mutex_destroy(&ts->sysfs_mutex);
> +
> +	devm_remove_action(&client->dev, raydium_i2c_remove_sysfs_group, 
> +ts);
> +
> +	devm_remove_action(&client->dev, raydium_i2c_power_off, ts);

The point of using devm_* API is not to free resources manually. Please remove devm_* calls from raydium_i2c_remove(), and see if the whole routine can be removed.

> +
> +	return 0;
> +}
> +
> +static void __maybe_unused raydium_enter_sleep(struct i2c_client 
> +*client) {
> +	static const u8 sleep_cmd[] = { 0x5A, 0xff, 0x00, 0x0f };
> +	int error;
> +
> +	error = raydium_i2c_send(client, CMD_ENTER_SLEEP, (u8 *)sleep_cmd,
> +		sizeof(sleep_cmd));
> +	if (error)
> +		dev_err(&client->dev,
> +			"Send sleep failed: %d\n", error); }
> +
> +static int __maybe_unused raydium_i2c_suspend(struct device *dev) {
> +	struct i2c_client *client = to_i2c_client(dev);
> +	struct raydium_data *ts = i2c_get_clientdata(client);
> +
> +	/* Command not support in BLDR recovery mode */
> +	if (ts->boot_mode != RAYDIUM_TS_MAIN)
> +		return -EBUSY;
> +
> +	disable_irq(client->irq);
> +
> +	if (device_may_wakeup(dev)) {
> +		raydium_enter_sleep(client);
> +
> +		ts->wake_irq_enabled = (enable_irq_wake(client->irq) == 0);
> +	} else {
> +		raydium_i2c_power_off(ts);
> +	}
> +
> +	return 0;
> +}
> +
> +static int __maybe_unused raydium_i2c_resume(struct device *dev) {
> +	struct i2c_client *client = to_i2c_client(dev);
> +	struct raydium_data *ts = i2c_get_clientdata(client);
> +
> +	if (device_may_wakeup(dev)) {
> +		if (ts->wake_irq_enabled)
> +			disable_irq_wake(client->irq);
> +		raydium_i2c_sw_reset(client);
> +	} else {
> +		raydium_i2c_power_on(ts);
> +		raydium_i2c_initialize(ts);
> +	}
> +
> +	enable_irq(client->irq);
> +
> +	return 0;
> +}
> +
> +static SIMPLE_DEV_PM_OPS(raydium_i2c_pm_ops,
> +			 raydium_i2c_suspend, raydium_i2c_resume);
> +
> +static const struct i2c_device_id raydium_i2c_id[] = {
> +	{ DEVICE_NAME, 0 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(i2c, raydium_i2c_id);
> +
> +#ifdef CONFIG_ACPI
> +static const struct acpi_device_id raydium_acpi_id[] = {
> +	{ "RMTS_0001", 0 },

As far as I know it is not a valid ACPI HID entry. In valid ACPI HIDs both vendor and product IDs are 4 characters long and consist of symbols in [A-Z0-9] range. This HID is 9 characters long and has invalid character (underscore).

What product uses this HID?

> +	{ }
> +};
> +MODULE_DEVICE_TABLE(acpi, raydium_acpi_id); #endif
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id raydium_of_match[] = {
> +	{ .compatible = "raydium,rm-ts",},

We also prefer real model number in OF binding, so raydium,rm31100 would be better. For module autoloading you also want to add rm31100 entry to raydium_i2c_id.

Please add "raydium" entry to
Documentation/devicetree/bindings/vendor-prefixes.txt

> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, raydium_of_match); #endif
> +
> +static struct i2c_driver raydium_i2c_driver = {
> +	.probe = raydium_i2c_probe,
> +	.remove = raydium_i2c_remove,
> +	.id_table = raydium_i2c_id,
> +	.driver = {
> +		.name = "raydium_ts",
> +		.pm = &raydium_i2c_pm_ops,
> +		.acpi_match_table = ACPI_PTR(raydium_acpi_id),
> +		.of_match_table = of_match_ptr(raydium_of_match),
> +	},
> +};
> +
> +module_i2c_driver(raydium_i2c_driver);
> +
> +MODULE_AUTHOR("Raydium");
> +MODULE_DESCRIPTION("Raydium I2c Touchscreen driver"); 
> +MODULE_LICENSE("GPL");

"GPL v2" since license notice at the top of the file specifies that only
v2 can be used.

Thanks.

--
Dmitry

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2016-03-03  6:42 jeffrey.lin
@ 2016-03-10 18:47 ` Dmitry Torokhov
  2016-03-11  2:10   ` Jeffrey Lin (林義章)
  0 siblings, 1 reply; 42+ messages in thread
From: Dmitry Torokhov @ 2016-03-10 18:47 UTC (permalink / raw)
  To: jeffrey.lin
  Cc: rydberg, grant.likely, robh+dt, jeesw, bleung, scott.liu,
	jeffrey.lin, roger.yang, KP.li, albert.shieh, linux-kernel,
	linux-input, devicetree

Hi Jeffrey,

On Thu, Mar 03, 2016 at 02:42:11PM +0800, jeffrey.lin wrote:
> Raydium I2C touch driver.
> 
> Signed-off-by: jeffrey.lin <jeffrey.lin@rad-ic.com>
> ---
>  drivers/input/touchscreen/Kconfig          |  13 +
>  drivers/input/touchscreen/Makefile         |   1 +
>  drivers/input/touchscreen/raydium_i2c_ts.c | 953 +++++++++++++++++++++++++++++
>  3 files changed, 967 insertions(+)
>  create mode 100644 drivers/input/touchscreen/raydium_i2c_ts.c
> 
> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> index 3f3f6ee..9adacf6 100644
> --- a/drivers/input/touchscreen/Kconfig
> +++ b/drivers/input/touchscreen/Kconfig
> @@ -915,6 +915,19 @@ config TOUCHSCREEN_PCAP
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called pcap_ts.
>  
> +config TOUCHSCREEN_RM_TS

Can we call it TOUCHSCREEN_RAYDIUM_RM31100? Do you have other models?
Maybe RM31XXX or similar?

Or TOUCHSCREEN_RAYDIUM_I2C?

> +	tristate "Raydium I2C Touchscreen"
> +	depends on I2C
> +	help
> +	  Say Y here if you have Raydium series I2C touchscreen,
> +	  such as RM31100 , connected to your system.
> +
> +	  If unsure, say N.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called raydium_i2c_ts.
> +
> +

Extra empty line.

>  config TOUCHSCREEN_ST1232
>  	tristate "Sitronix ST1232 touchscreen controllers"
>  	depends on I2C
> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
> index 4941f2d..99e08cf 100644
> --- a/drivers/input/touchscreen/Makefile
> +++ b/drivers/input/touchscreen/Makefile
> @@ -55,6 +55,7 @@ obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE)	+= usbtouchscreen.o
>  obj-$(CONFIG_TOUCHSCREEN_PCAP)		+= pcap_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_PENMOUNT)	+= penmount.o
>  obj-$(CONFIG_TOUCHSCREEN_PIXCIR)	+= pixcir_i2c_ts.o
> +obj-$(CONFIG_TOUCHSCREEN_RM_TS)		+= raydium_i2c_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_S3C2410)	+= s3c2410_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_ST1232)	+= st1232.o
>  obj-$(CONFIG_TOUCHSCREEN_STMPE)		+= stmpe-ts.o
> diff --git a/drivers/input/touchscreen/raydium_i2c_ts.c b/drivers/input/touchscreen/raydium_i2c_ts.c
> new file mode 100644
> index 0000000..7ba681e
> --- /dev/null
> +++ b/drivers/input/touchscreen/raydium_i2c_ts.c
> @@ -0,0 +1,953 @@
> +/*
> + * Raydium touchscreen I2C driver.
> + *
> + * Copyright (C) 2012-2014, Raydium Semiconductor Corporation.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * version 2, and only version 2, as published by the
> + * Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * Raydium reserves the right to make changes without further notice
> + * to the materials described herein. Raydium does not assume any
> + * liability arising out of the application described herein.
> + *
> + * Contact Raydium Semiconductor Corporation at www.rad-ic.com
> + */
> +
> +#include <linux/module.h>
> +#include <linux/input.h>
> +#include <linux/interrupt.h>
> +#include <linux/platform_device.h>
> +#include <linux/i2c.h>
> +#include <linux/delay.h>
> +#include <linux/uaccess.h>
> +#include <linux/buffer_head.h>

Why do you need this include?

> +#include <linux/slab.h>
> +#include <linux/firmware.h>
> +#include <linux/input/mt.h>
> +#include <linux/acpi.h>
> +#include <linux/of.h>
> +#include <linux/gpio.h>

I do not think you need this one.

> +#include <linux/regulator/consumer.h>
> +#include <asm/unaligned.h>
> +#include <linux/gpio/consumer.h>
> +
> +/* Device, Driver information */
> +#define DEVICE_NAME	"raydium_i2c"
> +
> +/* Slave I2C mode*/
> +#define RM_BOOT_BLDR	0x02
> +#define RM_BOOT_MAIN	0x03
> +
> +/*I2C command */
> +#define CMD_QUERY_BANK		0x2B
> +#define CMD_DATA_BANK		0x4D
> +#define CMD_ENTER_SLEEP		0x4E
> +#define CMD_BOOT_ACK		0x0A
> +#define CMD_BOOT_WRT		0x5B
> +#define CMD_BOOT_CHK		0x0C
> +#define CMD_BANK_SWITCH		0xAA
> +
> +/* Touch relative info */
> +#define MAX_RETRIES		3
> +#define MAX_FW_UPDATE_RETRIES	30
> +#define MAX_TOUCH_NUM		10
> +#define MAX_PACKET_SIZE		60
> +#define BOOT_DELAY_MS	100
> +
> +#define RAYDIUM_FW_PAGESIZE	128
> +#define RAYDIUM_POWERON_DELAY_USEC	500
> +#define RAYDIUM_RESET_DELAY_MSEC	50
> +
> +#define ADDR_INDEX		0x03
> +#define HEADER_SIZE		4
> +
> +enum raydium_boot_mode {
> +	RAYDIUM_TS_MAIN,
> +	RAYDIUM_TS_BLDR,
> +};
> +
> +struct raydium_info {
> +	u32 hw_ver;

This seems to be coming directly form the wire, you need to annotate it
as little- or big-endian and use appropriate accessors to convert to CPU
endianness.

> +	u8 main_ver;
> +	u8 sub_ver;
> +	u16 ft_ver;

Same here.

> +	u8 x_num;
> +	u8 y_num;
> +	u16 x_max;
> +	u16 y_max;

And here.

> +	u8 x_res;		/* units/mm */
> +	u8 y_res;		/* units/mm */
> +};
> +
> +struct raydium_abs_info {
> +	u8 state;/*1:touch, 0:no touch*/
> +	u8 x_pos_lsb;
> +	u8 x_pos_msb;
> +	u8 y_pos_lsb;
> +	u8 y_pos_msb;
> +	u8 pressure;
> +	u8 x_width;
> +	u8 y_width;
> +};

I'd rather define offsets and use get_unaligned_le16() to fetch x/y data.

> +
> +struct raydium_object {
> +	u32 data_bank_addr;

This likely needs to be converted to cpu-endianness as well.

> +	u8 pkg_size;
> +};
> +
> +/* struct raydium_data - represents state of Raydium touchscreen device */
> +struct raydium_data {
> +	struct i2c_client *client;
> +	struct input_dev *input;
> +
> +	struct regulator *vcc33;
> +	struct regulator *vccio;
> +	struct gpio_desc *reset_gpio;
> +
> +	u32 query_bank_info;
> +
> +	struct raydium_info info;
> +	struct raydium_object obj;
> +	struct raydium_abs_info finger;

This is not used.

> +
> +	enum raydium_boot_mode boot_mode;
> +
> +	struct mutex sysfs_mutex;
> +
> +	u8 cmd_resp[HEADER_SIZE];
> +	struct completion cmd_done;
> +
> +	u8 buf[MAX_PACKET_SIZE];
> +
> +	bool wake_irq_enabled;
> +};
> +
> +static int raydium_i2c_send(struct i2c_client *client,
> +	u8 addr, u8 *data, size_t len)
> +{
> +	u8 buf[MAX_PACKET_SIZE + 1];
> +	int tries = 0;
> +
> +	if (len > MAX_PACKET_SIZE)
> +		return -EINVAL;
> +
> +	buf[0] = addr;
> +	memcpy(&buf[1], data, len);
> +
> +	do {
> +		if (i2c_master_send(client, buf, len + 1) == (len + 1))
> +			return 0;
> +		msleep(20);
> +	} while (++tries < MAX_RETRIES);
> +
> +	dev_err(&client->dev, "%s: i2c send failed\n", __func__);
> +
> +	return -EIO;
> +}
> +
> +static int raydium_i2c_read(struct i2c_client *client,
> +	u8 addr, size_t len, void *data)
> +{
> +	struct i2c_msg xfer[2];
> +
> +	/* Write register */
> +	xfer[0].addr = client->addr;
> +	xfer[0].flags = 0;
> +	xfer[0].len = 1;
> +	xfer[0].buf = &addr;
> +
> +	/* Read data */
> +	xfer[1].addr = client->addr;
> +	xfer[1].flags = I2C_M_RD;
> +	xfer[1].len = len;
> +	xfer[1].buf = data;
> +
> +	if (i2c_transfer(client->adapter, xfer, 2) == 2)

ARRAY_SIZE()

> +		return 0;
> +
> +	dev_err(&client->dev, "%s: i2c transfer failed\n", __func__);
> +
> +	return -EIO;

Do not clobber errors returned by i2c_transfer.

> +}
> +
> +static int raydium_i2c_read_message(struct i2c_client *client,
> +	u32 addr, size_t len, void *data)
> +{
> +	u16 pkg_size, use_len;
> +	u8 buf[HEADER_SIZE], idx_i, idx_j;
> +	int error;
> +
> +	use_len = len;
> +	idx_j = 0;
> +	while (use_len > 0) {
> +		pkg_size = (use_len < MAX_PACKET_SIZE) ?
> +			use_len : MAX_PACKET_SIZE;
> +		for (idx_i = 0; idx_i < HEADER_SIZE; idx_i++)
> +			buf[idx_i] = addr >> (HEADER_SIZE - 1 - idx_i)*8;
> +
> +		/*set data bank*/
> +		error = raydium_i2c_send(client, CMD_BANK_SWITCH,
> +			(u8 *)buf, HEADER_SIZE);
> +		/*read potints data*/
> +		if (!error)
> +			error = raydium_i2c_read(client, buf[ADDR_INDEX],
> +				pkg_size,
> +				(void *)(data + idx_j*MAX_PACKET_SIZE));

You do not need to convert to void * pointers.

> +
> +		pkg_size += MAX_PACKET_SIZE;
> +		addr += MAX_PACKET_SIZE;
> +		use_len = (use_len < MAX_PACKET_SIZE) ?
> +			0 : (use_len - MAX_PACKET_SIZE);
> +		idx_j++;
> +	}
> +
> +	return error;
> +}
> +
> +static int raydium_i2c_send_message(struct i2c_client *client,
> +	size_t len, void *data)
> +{
> +	int error;
> +	u8 buf[HEADER_SIZE], ii;
> +
> +	for (ii = 0; ii < HEADER_SIZE; ii++)
> +		buf[ii] = ((u8 *)data)[3 - ii];

This looks like cpu_to_be32().

> +	/*set data bank*/
> +	error = raydium_i2c_send(client, CMD_BANK_SWITCH, (u8 *)buf,
> +		HEADER_SIZE);
> +
> +	/*send message*/
> +	if (!error)
> +		error = raydium_i2c_send(client, buf[ADDR_INDEX], buf, len);
> +
> +	return error;
> +}
> +
> +static int raydium_i2c_sw_reset(struct i2c_client *client)
> +{
> +	static const u8 soft_rst_cmd[] = { 0x01, 0x04, 0x00, 0x00, 0x40};
> +	int error;
> +
> +	error = raydium_i2c_send_message(client, 1, (void *)soft_rst_cmd);

This is wrong. You cast away constness. I also confused how it works.
You have 5 bytes of command, but I think you send first 4 and then fist
1 in raydium_i2c_send_message; you never get to the 5th byte.

> +	if (error) {
> +		dev_err(&client->dev, "software reset failed: %d\n", error);
> +		return error;
> +	}
> +
> +	msleep(RAYDIUM_RESET_DELAY_MSEC);
> +
> +	return 0;
> +}
> +
> +static int raydium_i2c_query_ts_info(struct raydium_data *ts)
> +{
> +	struct i2c_client *client = ts->client;
> +	int error, retry_cnt;
> +
> +	for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
> +		error = raydium_i2c_read(client, CMD_DATA_BANK,
> +			sizeof(ts->obj), (void *)&ts->obj);
> +		if (!error) {
> +			error = raydium_i2c_read(client, CMD_QUERY_BANK,
> +				sizeof(ts->query_bank_info),
> +				(void *)&ts->query_bank_info);
> +			if (!error) {
> +				error = raydium_i2c_read_message(client,
> +					ts->query_bank_info, sizeof(ts->info),
> +					(void *)&ts->info);
> +			}
> +
> +			if (!error)
> +				return 0;
> +		}
> +	}
> +	dev_err(&client->dev, "get data bank failed: %d\n", error);
> +
> +	return -EINVAL;
> +}
> +
> +static int raydium_i2c_fastboot(struct i2c_client *client)
> +{
> +	static const u8 boot_cmd[] = { 0x20, 0x06, 0x00, 0x50 };
> +	u8 buf[HEADER_SIZE];
> +	int error;
> +
> +	error = raydium_i2c_read_message(client,
> +		get_unaligned_be32(boot_cmd),
> +		sizeof(boot_cmd), buf);
> +
> +	if (!error) {
> +		if (buf[0] == RM_BOOT_BLDR) {
> +			dev_dbg(&client->dev, "boot in fastboot mode\n");
> +			return -EINVAL;
> +		}
> +		dev_dbg(&client->dev, "boot success -- 0x%x\n", client->addr);
> +		return 0;
> +	}
> +
> +	dev_err(&client->dev, "boot failed: %d\n", error);
> +
> +	return error;
> +}
> +
> +static int raydium_i2c_initialize(struct raydium_data *ts)
> +{
> +	struct i2c_client *client = ts->client;
> +	int error, retry_cnt;
> +	static const u8 recov_packet[] = { 0x04, 0x81 };
> +	u8 buf[HEADER_SIZE];
> +
> +	for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
> +		error = raydium_i2c_fastboot(client);
> +		if (error) {
> +			/* Continue initializing if it's the last try */
> +			if (retry_cnt < MAX_RETRIES - 1)
> +				continue;
> +		}
> +		/* Wait for Hello packet */
> +		msleep(BOOT_DELAY_MS);
> +
> +		error = raydium_i2c_read(client, recov_packet[0], 1,
> +			(void *)buf);
> +		if (error) {
> +			dev_err(&client->dev,
> +				"failed to read 'hello' packet: %d\n", error);
> +		} else if (buf[0] == recov_packet[1]) {
> +			ts->boot_mode = RAYDIUM_TS_MAIN;
> +			break;
> +		}
> +	}
> +
> +	if (error)
> +		ts->boot_mode = RAYDIUM_TS_BLDR;
> +	else
> +		raydium_i2c_query_ts_info(ts);
> +
> +	return error;
> +}
> +
> +static int raydium_i2c_fw_write_page(struct i2c_client *client,
> +				    const void *page)
> +{
> +	static const u8 ack_ok[] = { 0x55, 0xAA };
> +	u8 buf[2];
> +	int retry;
> +	int error;
> +
> +	for (retry = 0; retry < MAX_FW_UPDATE_RETRIES; retry++) {
> +		error = raydium_i2c_send(client, CMD_BOOT_WRT,
> +			(u8 *)page, RAYDIUM_FW_PAGESIZE);
> +		if (error) {
> +			dev_err(&client->dev,
> +				"BLDR Write Page failed: %d\n", error);
> +			continue;
> +		}
> +
> +		error = raydium_i2c_read(client, CMD_BOOT_CHK, sizeof(ack_ok),
> +			(void *)buf);
> +		if (error) {
> +			dev_err(&client->dev,
> +				"BLDR Ack read failed: %d\n", error);
> +			return error;
> +		}
> +
> +		if (!memcmp(buf, ack_ok, sizeof(ack_ok)))
> +			return 0;
> +
> +		error = -EIO;
> +		dev_err(&client->dev,
> +			"BLDR Get Ack Error [%02x:%02x]\n", buf[0], buf[1]);
> +	}
> +
> +	return error;
> +}
> +
> +static int raydium_i2c_do_update_firmware(struct i2c_client *client,
> +					 const struct firmware *fw,
> +					 bool force)
> +{
> +	static const u8 boot_cmd[] = { RM_BOOT_BLDR, 0x20, 0x06, 0x00, 0x50 };
> +	static const u8 main_cmd[] = { RM_BOOT_MAIN, 0x20, 0x06, 0x00, 0x50 };
> +	static const u8 boot_ack[] = { 0x55, 0xAA, 0x00, 0xFF };
> +	u8 buf[HEADER_SIZE];
> +	int page, n_fw_pages;
> +	int error;
> +
> +	/* Recovery mode detection! */
> +	if (!force) {
> +		/* Start boot loader Procedure */
> +		dev_dbg(&client->dev, "Normal BLDR procedure\n");
> +		/* switch to mode */
> +		error = raydium_i2c_send_message(client, 1, (void *)boot_cmd);
> +		if (error)
> +			dev_err(&client->dev, "failed to send boot cmd: %d\n",
> +				error);
> +		msleep(60);
> +		raydium_i2c_sw_reset(client);
> +		msleep(RAYDIUM_RESET_DELAY_MSEC);
> +		error = raydium_i2c_send_message(client, 1, (void *)boot_cmd);
> +	}
> +
> +	if (error) {
> +		dev_err(&client->dev, "failed to enter fastboot mode: %d\n",
> +			error);
> +		return error;
> +	}
> +
> +	msleep(20);
> +
> +	/* check fastboot state */
> +	error = raydium_i2c_read(client, CMD_BOOT_ACK,
> +		sizeof(boot_ack), (void *)buf);
> +	if (error) {
> +		dev_err(&client->dev,
> +			"failed to read boot ack: %d\n", error);
> +		return error;
> +	}
> +
> +	if (memcmp(buf, boot_ack, sizeof(boot_ack))) {
> +		dev_err(&client->dev,
> +			"failed to enter fastboot: %*ph (expected %*ph)\n",
> +			(int)sizeof(buf), buf, (int)sizeof(boot_ack), boot_ack);
> +		return -EIO;
> +	}
> +
> +	dev_info(&client->dev, "successfully entered fastboot mode");
> +
> +	n_fw_pages = fw->size / RAYDIUM_FW_PAGESIZE;
> +	dev_dbg(&client->dev, "BLDR Pages = %d\n", n_fw_pages);
> +
> +	for (page = 0; page < n_fw_pages; page++) {
> +		error = raydium_i2c_fw_write_page(client,
> +					fw->data + page * RAYDIUM_FW_PAGESIZE);
> +		if (error) {
> +			dev_err(&client->dev,
> +				"failed to write FW page %d: %d\n",
> +				page, error);
> +			return error;
> +		}
> +	}
> +	error = raydium_i2c_send_message(client, 1, (void *)main_cmd);
> +	msleep(20);
> +	raydium_i2c_sw_reset(client);
> +	dev_info(&client->dev, "firmware update completed\n");
> +
> +	return 0;
> +}
> +
> +static int raydium_i2c_fw_update(struct raydium_data *ts)
> +{
> +	struct i2c_client *client = ts->client;
> +	const struct firmware *fw;
> +	char *fw_name;
> +	int error;
> +
> +	fw_name = kasprintf(GFP_KERNEL, "raydium_i2c_%04x.bin",
> +		ts->info.hw_ver & 0xFFFF);
> +	if (!fw_name)
> +		return -ENOMEM;
> +
> +	dev_info(&client->dev, "requesting fw name = %s\n", fw_name);
> +	error = request_firmware(&fw, fw_name, &client->dev);
> +	kfree(fw_name);
> +	if (error) {
> +		dev_err(&client->dev, "failed to request firmware: %d\n",
> +			error);
> +		return error;
> +	}
> +
> +	if (fw->size % RAYDIUM_FW_PAGESIZE) {
> +		dev_err(&client->dev, "invalid firmware length: %zu\n",
> +			fw->size);
> +		error = -EINVAL;
> +		goto out;
> +	}
> +
> +	disable_irq(client->irq);
> +	error = raydium_i2c_do_update_firmware(client, fw,
> +					ts->boot_mode == RAYDIUM_TS_BLDR);
> +	if (error) {
> +		dev_err(&client->dev, "firmware update failed: %d\n", error);
> +		ts->boot_mode = RAYDIUM_TS_BLDR;
> +		goto out_enable_irq;
> +	}
> +	error = raydium_i2c_initialize(ts);
> +	if (error) {
> +		dev_err(&client->dev,
> +			"failed to initialize device after firmware update: %d\n",
> +			error);
> +		ts->boot_mode = RAYDIUM_TS_BLDR;
> +		goto out_enable_irq;
> +	}
> +
> +	ts->boot_mode = RAYDIUM_TS_MAIN;
> +
> +out_enable_irq:
> +	enable_irq(client->irq);
> +	msleep(100);
> +out:
> +	release_firmware(fw);
> +	return error;
> +}
> +
> +static void raydium_mt_event(struct raydium_data *ts)
> +{
> +	struct raydium_abs_info *data;
> +	int error, i, x, y;
> +	u8 f_state;
> +	u8 touch_count;
> +	u8 tp_info_size;
> +
> +	error = raydium_i2c_read_message(ts->client, ts->obj.data_bank_addr,
> +		ts->obj.pkg_size, (void *)&ts->buf);
> +
> +	if (error < 0) {
> +		dev_err(&ts->client->dev, "%s: failed to read data: %d\n",
> +			__func__, error);
> +		return;
> +	}
> +
> +	touch_count = 0;
> +	tp_info_size = sizeof(ts->finger);
> +
> +	for (i = 0; i < MAX_TOUCH_NUM; i++) {
> +		data = (struct raydium_abs_info *)(ts->buf + i * tp_info_size);
> +
> +		f_state = data->state & 0x03;
> +
> +		input_mt_slot(ts->input, i);
> +		input_mt_report_slot_state(ts->input,
> +				MT_TOOL_FINGER, f_state != 0);
> +
> +		if (!f_state)
> +			continue;
> +
> +		x = (data->x_pos_msb << 8) | (data->x_pos_lsb);
> +		y = (data->y_pos_msb << 8) | (data->y_pos_lsb);
> +
> +		input_report_key(ts->input, BTN_TOUCH, 1);
> +		input_report_key(ts->input, BTN_TOOL_FINGER, 1);

Drop these 2.

> +		input_report_abs(ts->input, ABS_MT_POSITION_X, x);
> +		input_report_abs(ts->input, ABS_MT_POSITION_Y, y);
> +		input_report_abs(ts->input, ABS_MT_PRESSURE, data->pressure);
> +		input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR,
> +			max(data->x_width, data->y_width));
> +		input_report_abs(ts->input, ABS_MT_TOUCH_MINOR,
> +			min(data->x_width, data->y_width));
> +		touch_count++;
> +	}
> +
> +	input_report_key(ts->input, BTN_TOUCH, touch_count > 0);


> +	input_report_key(ts->input, BTN_TOOL_FINGER, ts->input > 0);

What does ts->input > 0 means?

> +	input_mt_report_pointer_emulation(ts->input, false);

Please call input_mt_sync_frame() instead.

> +	input_sync(ts->input);
> +}
> +
> +static irqreturn_t raydium_i2c_irq(int irq, void *_dev)
> +{
> +	struct raydium_data *ts = _dev;
> +
> +	if (ts->boot_mode != RAYDIUM_TS_BLDR)
> +		raydium_mt_event(ts);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static ssize_t write_update_fw(struct device *dev,
> +			struct device_attribute *attr,
> +			const char *buf, size_t count)
> +{
> +	struct raydium_data *ts = dev_get_drvdata(dev);
> +	int error;
> +
> +	error = mutex_lock_interruptible(&ts->sysfs_mutex);
> +	if (error)
> +		return error;
> +
> +	error = raydium_i2c_fw_update(ts);
> +	dev_dbg(dev, "firmware update result: %d\n", error);
> +
> +	mutex_unlock(&ts->sysfs_mutex);
> +	return error ?: count;
> +}
> +
> +static ssize_t raydium_bootmode_show(struct device *dev,
> +		struct device_attribute *attr, char *buf)
> +{
> +	struct raydium_data *ts = dev_get_drvdata(dev);
> +
> +	return sprintf(buf, "%s\n", ts->boot_mode == RAYDIUM_TS_MAIN ?
> +		"Normal" : "Recovery");
> +}
> +
> +static ssize_t raydium_fw_ver_show(struct device *dev,
> +			struct device_attribute *attr, char *buf)
> +{
> +	struct raydium_data *ts = dev_get_drvdata(dev);
> +
> +	return sprintf(buf, "Release Version %d.%d\n",
> +		ts->info.main_ver, ts->info.sub_ver);

Do not use strings in sysfs attributes. Just use %d.%d or, better yet,
%02x.%02x.

> +}
> +
> +static ssize_t raydium_hw_ver_show(struct device *dev,
> +			struct device_attribute *attr, char *buf)
> +{
> +	struct raydium_data *ts = dev_get_drvdata(dev);
> +
> +	return sprintf(buf, "Hardware version 0x%x\n",
> +		ts->info.hw_ver & 0xFFFF);

Same here, just use %04x.

> +}
> +
> +static DEVICE_ATTR(fw_version, S_IRUGO, raydium_fw_ver_show, NULL);
> +static DEVICE_ATTR(hw_version, S_IRUGO, raydium_hw_ver_show, NULL);
> +static DEVICE_ATTR(boot_mode, S_IRUGO, raydium_bootmode_show, NULL);
> +static DEVICE_ATTR(update_fw, S_IWUSR, NULL, write_update_fw);
> +
> +static struct attribute *raydium_attributes[] = {
> +	&dev_attr_update_fw.attr,
> +	&dev_attr_boot_mode.attr,
> +	&dev_attr_fw_version.attr,
> +	&dev_attr_hw_version.attr,
> +	NULL
> +};
> +
> +static struct attribute_group raydium_attribute_group = {
> +	.attrs = raydium_attributes,
> +};
> +
> +static void raydium_i2c_remove_sysfs_group(void *_data)
> +{
> +	struct raydium_data *ts = _data;
> +
> +	sysfs_remove_group(&ts->client->dev.kobj, &raydium_attribute_group);
> +}
> +
> +static int raydium_i2c_power_on(struct raydium_data *ts)
> +{
> +	int error;
> +
> +	if (IS_ERR_OR_NULL(ts->reset_gpio))
> +		return 0;
> +
> +	gpiod_set_value_cansleep(ts->reset_gpio, 1);
> +
> +	error = regulator_enable(ts->vcc33);
> +	if (error) {
> +		dev_err(&ts->client->dev,
> +			"failed to enable vcc33 regulator: %d\n", error);
> +		goto release_reset_gpio;
> +	}
> +
> +	error = regulator_enable(ts->vccio);
> +	if (error) {
> +		regulator_disable(ts->vcc33);
> +		dev_err(&ts->client->dev,
> +			"failed to enable vccio regulator: %d\n", error);
> +		goto release_reset_gpio;
> +	}
> +
> +	udelay(RAYDIUM_POWERON_DELAY_USEC);
> +
> +release_reset_gpio:
> +	gpiod_set_value_cansleep(ts->reset_gpio, 0);
> +
> +	if (error)
> +		return error;
> +
> +	msleep(RAYDIUM_RESET_DELAY_MSEC);
> +
> +	return 0;
> +}
> +
> +static void raydium_i2c_power_off(void *_data)
> +{
> +	struct raydium_data *ts = _data;
> +
> +	if (!IS_ERR_OR_NULL(ts->reset_gpio)) {
> +		gpiod_set_value_cansleep(ts->reset_gpio, 1);
> +		regulator_disable(ts->vccio);
> +		regulator_disable(ts->vcc33);
> +	}
> +}
> +
> +static int raydium_i2c_probe(struct i2c_client *client,
> +			    const struct i2c_device_id *id)
> +{
> +	union i2c_smbus_data dummy;
> +	struct raydium_data *ts;
> +	unsigned long irqflags;
> +	int error;
> +
> +	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
> +		dev_err(&client->dev,
> +			"%s: i2c check functionality error\n", DEVICE_NAME);
> +		return -ENXIO;
> +	}
> +
> +	ts = devm_kzalloc(&client->dev, sizeof(struct raydium_data),
> +		GFP_KERNEL);
> +	if (!ts)
> +		return -ENOMEM;
> +
> +	mutex_init(&ts->sysfs_mutex);
> +	init_completion(&ts->cmd_done);
> +
> +	ts->client = client;
> +	i2c_set_clientdata(client, ts);
> +
> +	ts->vcc33 = devm_regulator_get(&client->dev, "avdd");

If regulator is called avdd then I'd rather have ts->avdd as well.

> +	if (IS_ERR(ts->vcc33)) {
> +		error = PTR_ERR(ts->vcc33);
> +		if (error != -EPROBE_DEFER)
> +			dev_err(&client->dev,
> +				"Failed to get 'vcc33' regulator: %d\n", error);
> +		return error;
> +	}
> +
> +	ts->vccio = devm_regulator_get(&client->dev, "vdd");

ts->vccio -> ts->vdd please.

You also need to add binding documentation to
Documentation/devicetree/bindings/input/...

> +	if (IS_ERR(ts->vccio)) {
> +		error = PTR_ERR(ts->vccio);
> +		if (error != -EPROBE_DEFER)
> +			dev_err(&client->dev,
> +				"Failed to get 'vccio' regulator: %d\n", error);
> +		return error;
> +	}
> +
> +

Extra empty line.

> +	ts->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
> +		GPIOD_OUT_LOW);
> +	if (IS_ERR(ts->reset_gpio)) {
> +		error = PTR_ERR(ts->reset_gpio);
> +		if (error != -EPROBE_DEFER) {
> +			dev_err(&client->dev,
> +				"failed to get reset gpio: %d\n", error);
> +			return error;
> +		}
> +	}
> +
> +	error = raydium_i2c_power_on(ts);
> +	if (error)
> +		return error;
> +
> +	error = devm_add_action(&client->dev, raydium_i2c_power_off, ts);
> +	if (error) {
> +		dev_err(&client->dev,
> +			"failed to install power off action: %d\n", error);
> +		raydium_i2c_power_off(ts);
> +		return error;
> +	}
> +
> +	/* Make sure there is something at this address */
> +	if (i2c_smbus_xfer(client->adapter, client->addr, 0,
> +			I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0) {
> +		dev_err(&client->dev, "nothing at this address\n");
> +		return -ENXIO;
> +	}
> +
> +	error = raydium_i2c_initialize(ts);
> +	if (error) {
> +		dev_err(&client->dev, "failed to initialize: %d\n", error);
> +		return error;
> +	}
> +
> +	ts->input = devm_input_allocate_device(&client->dev);
> +	if (!ts->input) {
> +		dev_err(&client->dev, "Failed to allocate input device\n");
> +		return -ENOMEM;
> +	}
> +
> +	ts->input->name = "Raydium Touchscreen";
> +	ts->input->id.bustype = BUS_I2C;
> +
> +	__set_bit(BTN_TOUCH, ts->input->keybit);
> +	__set_bit(EV_ABS, ts->input->evbit);
> +	__set_bit(EV_KEY, ts->input->evbit);
> +
> +	/* Single touch input params setup */
> +	input_set_abs_params(ts->input, ABS_X, 0, ts->info.x_max, 0, 0);
> +	input_set_abs_params(ts->input, ABS_Y, 0, ts->info.y_max, 0, 0);
> +	input_set_abs_params(ts->input, ABS_PRESSURE, 0, 255, 0, 0);
> +	input_abs_set_res(ts->input, ABS_X, ts->info.x_res);
> +	input_abs_set_res(ts->input, ABS_Y, ts->info.y_res);
> +
> +	/* Multitouch input params setup */
> +	error = input_mt_init_slots(ts->input, MAX_TOUCH_NUM,
> +				    INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
> +	if (error) {
> +		dev_err(&client->dev,
> +			"failed to initialize MT slots: %d\n", error);
> +		return error;
> +	}
> +
> +	input_set_abs_params(ts->input, ABS_MT_POSITION_X, 0,
> +		ts->info.x_max, 0, 0);
> +	input_set_abs_params(ts->input, ABS_MT_POSITION_Y,
> +		0, ts->info.y_max, 0, 0);
> +	input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
> +	input_set_abs_params(ts->input, ABS_MT_PRESSURE, 0, 255, 0, 0);
> +	input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->info.x_res);
> +	input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->info.y_res);
> +
> +	input_mt_init_slots(ts->input, MAX_TOUCH_NUM, 0);

You already called this once a few lines above. But what you actually
want is to:

1. Set MT axies parameters.
2. Call  input_mt_init_slots(ts->input, MAX_TOUCH_NUM,
		INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED)
   so that it will finish MT initialization and will copy MT parameters
   over to ST.

> +
> +	input_set_drvdata(ts->input, ts);
> +
> +	error = input_register_device(ts->input);
> +	if (error) {
> +		dev_err(&client->dev,
> +			"unable to register input device: %d\n", error);
> +		return error;
> +	}
> +
> +	error = devm_request_threaded_irq(&client->dev, client->irq,
> +			NULL, raydium_i2c_irq, irqflags | IRQF_ONESHOT,
> +			client->name, ts);
> +	if (error) {
> +		dev_err(&client->dev, "Failed to register interrupt\n");
> +		return error;
> +	}
> +
> +	if (!client->dev.of_node)
> +		device_init_wakeup(&client->dev, true);

Please drop and instead make ACPI platform code mark i2c device as
wakeup capable when registering it.

> +
> +	error = sysfs_create_group(&client->dev.kobj, &raydium_attribute_group);
> +	if (error) {
> +		dev_err(&client->dev, "failed to create sysfs attributes: %d\n",
> +			error);
> +		return error;
> +	}
> +
> +	error = devm_add_action(&client->dev,
> +				raydium_i2c_remove_sysfs_group, ts);
> +	if (error) {
> +		raydium_i2c_remove_sysfs_group(ts);
> +		dev_err(&client->dev,
> +			"Failed to add sysfs cleanup action: %d\n", error);
> +		return error;
> +	}
> +
> +	return 0;
> +}
> +
> +static int raydium_i2c_remove(struct i2c_client *client)
> +{
> +	struct raydium_data *ts = i2c_get_clientdata(client);
> +
> +	if (ts->input)
> +		input_unregister_device(ts->input);

In what cases input device will not be present?

> +
> +	device_init_wakeup(&client->dev, false);
> +
> +	devm_free_irq(&client->dev, client->irq, ts);
> +
> +	mutex_destroy(&ts->sysfs_mutex);
> +
> +	devm_remove_action(&client->dev, raydium_i2c_remove_sysfs_group, ts);
> +
> +	devm_remove_action(&client->dev, raydium_i2c_power_off, ts);

The point of using devm_* API is not to free resources manually. Please
remove devm_* calls from raydium_i2c_remove(), and see if the whole
routine can be removed.

> +
> +	return 0;
> +}
> +
> +static void __maybe_unused raydium_enter_sleep(struct i2c_client *client)
> +{
> +	static const u8 sleep_cmd[] = { 0x5A, 0xff, 0x00, 0x0f };
> +	int error;
> +
> +	error = raydium_i2c_send(client, CMD_ENTER_SLEEP, (u8 *)sleep_cmd,
> +		sizeof(sleep_cmd));
> +	if (error)
> +		dev_err(&client->dev,
> +			"Send sleep failed: %d\n", error);
> +}
> +
> +static int __maybe_unused raydium_i2c_suspend(struct device *dev)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +	struct raydium_data *ts = i2c_get_clientdata(client);
> +
> +	/* Command not support in BLDR recovery mode */
> +	if (ts->boot_mode != RAYDIUM_TS_MAIN)
> +		return -EBUSY;
> +
> +	disable_irq(client->irq);
> +
> +	if (device_may_wakeup(dev)) {
> +		raydium_enter_sleep(client);
> +
> +		ts->wake_irq_enabled = (enable_irq_wake(client->irq) == 0);
> +	} else {
> +		raydium_i2c_power_off(ts);
> +	}
> +
> +	return 0;
> +}
> +
> +static int __maybe_unused raydium_i2c_resume(struct device *dev)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +	struct raydium_data *ts = i2c_get_clientdata(client);
> +
> +	if (device_may_wakeup(dev)) {
> +		if (ts->wake_irq_enabled)
> +			disable_irq_wake(client->irq);
> +		raydium_i2c_sw_reset(client);
> +	} else {
> +		raydium_i2c_power_on(ts);
> +		raydium_i2c_initialize(ts);
> +	}
> +
> +	enable_irq(client->irq);
> +
> +	return 0;
> +}
> +
> +static SIMPLE_DEV_PM_OPS(raydium_i2c_pm_ops,
> +			 raydium_i2c_suspend, raydium_i2c_resume);
> +
> +static const struct i2c_device_id raydium_i2c_id[] = {
> +	{ DEVICE_NAME, 0 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(i2c, raydium_i2c_id);
> +
> +#ifdef CONFIG_ACPI
> +static const struct acpi_device_id raydium_acpi_id[] = {
> +	{ "RMTS_0001", 0 },

As far as I know it is not a valid ACPI HID entry. In valid ACPI HIDs
both vendor and product IDs are 4 characters long and consist of symbols
in [A-Z0-9] range. This HID is 9 characters long and has invalid
character (underscore).

What product uses this HID?

> +	{ }
> +};
> +MODULE_DEVICE_TABLE(acpi, raydium_acpi_id);
> +#endif
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id raydium_of_match[] = {
> +	{ .compatible = "raydium,rm-ts",},

We also prefer real model number in OF binding, so raydium,rm31100 would
be better. For module autoloading you also want to add rm31100 entry to
raydium_i2c_id.

Please add "raydium" entry to
Documentation/devicetree/bindings/vendor-prefixes.txt

> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, raydium_of_match);
> +#endif
> +
> +static struct i2c_driver raydium_i2c_driver = {
> +	.probe = raydium_i2c_probe,
> +	.remove = raydium_i2c_remove,
> +	.id_table = raydium_i2c_id,
> +	.driver = {
> +		.name = "raydium_ts",
> +		.pm = &raydium_i2c_pm_ops,
> +		.acpi_match_table = ACPI_PTR(raydium_acpi_id),
> +		.of_match_table = of_match_ptr(raydium_of_match),
> +	},
> +};
> +
> +module_i2c_driver(raydium_i2c_driver);
> +
> +MODULE_AUTHOR("Raydium");
> +MODULE_DESCRIPTION("Raydium I2c Touchscreen driver");
> +MODULE_LICENSE("GPL");

"GPL v2" since license notice at the top of the file specifies that only
v2 can be used.

Thanks.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
@ 2016-03-03  6:42 jeffrey.lin
  2016-03-10 18:47 ` Dmitry Torokhov
  0 siblings, 1 reply; 42+ messages in thread
From: jeffrey.lin @ 2016-03-03  6:42 UTC (permalink / raw)
  To: dmitry.torokhov, rydberg, grant.likely, robh+dt, jeesw, bleung,
	scott.liu
  Cc: jeffrey.lin, roger.yang, KP.li, albert.shieh, linux-kernel,
	linux-input, devicetree

Raydium I2C touch driver.

Signed-off-by: jeffrey.lin <jeffrey.lin@rad-ic.com>
---
 drivers/input/touchscreen/Kconfig          |  13 +
 drivers/input/touchscreen/Makefile         |   1 +
 drivers/input/touchscreen/raydium_i2c_ts.c | 953 +++++++++++++++++++++++++++++
 3 files changed, 967 insertions(+)
 create mode 100644 drivers/input/touchscreen/raydium_i2c_ts.c

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 3f3f6ee..9adacf6 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -915,6 +915,19 @@ config TOUCHSCREEN_PCAP
 	  To compile this driver as a module, choose M here: the
 	  module will be called pcap_ts.
 
+config TOUCHSCREEN_RM_TS
+	tristate "Raydium I2C Touchscreen"
+	depends on I2C
+	help
+	  Say Y here if you have Raydium series I2C touchscreen,
+	  such as RM31100 , connected to your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called raydium_i2c_ts.
+
+
 config TOUCHSCREEN_ST1232
 	tristate "Sitronix ST1232 touchscreen controllers"
 	depends on I2C
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 4941f2d..99e08cf 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE)	+= usbtouchscreen.o
 obj-$(CONFIG_TOUCHSCREEN_PCAP)		+= pcap_ts.o
 obj-$(CONFIG_TOUCHSCREEN_PENMOUNT)	+= penmount.o
 obj-$(CONFIG_TOUCHSCREEN_PIXCIR)	+= pixcir_i2c_ts.o
+obj-$(CONFIG_TOUCHSCREEN_RM_TS)		+= raydium_i2c_ts.o
 obj-$(CONFIG_TOUCHSCREEN_S3C2410)	+= s3c2410_ts.o
 obj-$(CONFIG_TOUCHSCREEN_ST1232)	+= st1232.o
 obj-$(CONFIG_TOUCHSCREEN_STMPE)		+= stmpe-ts.o
diff --git a/drivers/input/touchscreen/raydium_i2c_ts.c b/drivers/input/touchscreen/raydium_i2c_ts.c
new file mode 100644
index 0000000..7ba681e
--- /dev/null
+++ b/drivers/input/touchscreen/raydium_i2c_ts.c
@@ -0,0 +1,953 @@
+/*
+ * Raydium touchscreen I2C driver.
+ *
+ * Copyright (C) 2012-2014, Raydium Semiconductor Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Raydium reserves the right to make changes without further notice
+ * to the materials described herein. Raydium does not assume any
+ * liability arising out of the application described herein.
+ *
+ * Contact Raydium Semiconductor Corporation at www.rad-ic.com
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include <linux/buffer_head.h>
+#include <linux/slab.h>
+#include <linux/firmware.h>
+#include <linux/input/mt.h>
+#include <linux/acpi.h>
+#include <linux/of.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <asm/unaligned.h>
+#include <linux/gpio/consumer.h>
+
+/* Device, Driver information */
+#define DEVICE_NAME	"raydium_i2c"
+
+/* Slave I2C mode*/
+#define RM_BOOT_BLDR	0x02
+#define RM_BOOT_MAIN	0x03
+
+/*I2C command */
+#define CMD_QUERY_BANK		0x2B
+#define CMD_DATA_BANK		0x4D
+#define CMD_ENTER_SLEEP		0x4E
+#define CMD_BOOT_ACK		0x0A
+#define CMD_BOOT_WRT		0x5B
+#define CMD_BOOT_CHK		0x0C
+#define CMD_BANK_SWITCH		0xAA
+
+/* Touch relative info */
+#define MAX_RETRIES		3
+#define MAX_FW_UPDATE_RETRIES	30
+#define MAX_TOUCH_NUM		10
+#define MAX_PACKET_SIZE		60
+#define BOOT_DELAY_MS	100
+
+#define RAYDIUM_FW_PAGESIZE	128
+#define RAYDIUM_POWERON_DELAY_USEC	500
+#define RAYDIUM_RESET_DELAY_MSEC	50
+
+#define ADDR_INDEX		0x03
+#define HEADER_SIZE		4
+
+enum raydium_boot_mode {
+	RAYDIUM_TS_MAIN,
+	RAYDIUM_TS_BLDR,
+};
+
+struct raydium_info {
+	u32 hw_ver;
+	u8 main_ver;
+	u8 sub_ver;
+	u16 ft_ver;
+	u8 x_num;
+	u8 y_num;
+	u16 x_max;
+	u16 y_max;
+	u8 x_res;		/* units/mm */
+	u8 y_res;		/* units/mm */
+};
+
+struct raydium_abs_info {
+	u8 state;/*1:touch, 0:no touch*/
+	u8 x_pos_lsb;
+	u8 x_pos_msb;
+	u8 y_pos_lsb;
+	u8 y_pos_msb;
+	u8 pressure;
+	u8 x_width;
+	u8 y_width;
+};
+
+struct raydium_object {
+	u32 data_bank_addr;
+	u8 pkg_size;
+};
+
+/* struct raydium_data - represents state of Raydium touchscreen device */
+struct raydium_data {
+	struct i2c_client *client;
+	struct input_dev *input;
+
+	struct regulator *vcc33;
+	struct regulator *vccio;
+	struct gpio_desc *reset_gpio;
+
+	u32 query_bank_info;
+
+	struct raydium_info info;
+	struct raydium_object obj;
+	struct raydium_abs_info finger;
+
+	enum raydium_boot_mode boot_mode;
+
+	struct mutex sysfs_mutex;
+
+	u8 cmd_resp[HEADER_SIZE];
+	struct completion cmd_done;
+
+	u8 buf[MAX_PACKET_SIZE];
+
+	bool wake_irq_enabled;
+};
+
+static int raydium_i2c_send(struct i2c_client *client,
+	u8 addr, u8 *data, size_t len)
+{
+	u8 buf[MAX_PACKET_SIZE + 1];
+	int tries = 0;
+
+	if (len > MAX_PACKET_SIZE)
+		return -EINVAL;
+
+	buf[0] = addr;
+	memcpy(&buf[1], data, len);
+
+	do {
+		if (i2c_master_send(client, buf, len + 1) == (len + 1))
+			return 0;
+		msleep(20);
+	} while (++tries < MAX_RETRIES);
+
+	dev_err(&client->dev, "%s: i2c send failed\n", __func__);
+
+	return -EIO;
+}
+
+static int raydium_i2c_read(struct i2c_client *client,
+	u8 addr, size_t len, void *data)
+{
+	struct i2c_msg xfer[2];
+
+	/* Write register */
+	xfer[0].addr = client->addr;
+	xfer[0].flags = 0;
+	xfer[0].len = 1;
+	xfer[0].buf = &addr;
+
+	/* Read data */
+	xfer[1].addr = client->addr;
+	xfer[1].flags = I2C_M_RD;
+	xfer[1].len = len;
+	xfer[1].buf = data;
+
+	if (i2c_transfer(client->adapter, xfer, 2) == 2)
+		return 0;
+
+	dev_err(&client->dev, "%s: i2c transfer failed\n", __func__);
+
+	return -EIO;
+}
+
+static int raydium_i2c_read_message(struct i2c_client *client,
+	u32 addr, size_t len, void *data)
+{
+	u16 pkg_size, use_len;
+	u8 buf[HEADER_SIZE], idx_i, idx_j;
+	int error;
+
+	use_len = len;
+	idx_j = 0;
+	while (use_len > 0) {
+		pkg_size = (use_len < MAX_PACKET_SIZE) ?
+			use_len : MAX_PACKET_SIZE;
+		for (idx_i = 0; idx_i < HEADER_SIZE; idx_i++)
+			buf[idx_i] = addr >> (HEADER_SIZE - 1 - idx_i)*8;
+
+		/*set data bank*/
+		error = raydium_i2c_send(client, CMD_BANK_SWITCH,
+			(u8 *)buf, HEADER_SIZE);
+		/*read potints data*/
+		if (!error)
+			error = raydium_i2c_read(client, buf[ADDR_INDEX],
+				pkg_size,
+				(void *)(data + idx_j*MAX_PACKET_SIZE));
+
+		pkg_size += MAX_PACKET_SIZE;
+		addr += MAX_PACKET_SIZE;
+		use_len = (use_len < MAX_PACKET_SIZE) ?
+			0 : (use_len - MAX_PACKET_SIZE);
+		idx_j++;
+	}
+
+	return error;
+}
+
+static int raydium_i2c_send_message(struct i2c_client *client,
+	size_t len, void *data)
+{
+	int error;
+	u8 buf[HEADER_SIZE], ii;
+
+	for (ii = 0; ii < HEADER_SIZE; ii++)
+		buf[ii] = ((u8 *)data)[3 - ii];
+	/*set data bank*/
+	error = raydium_i2c_send(client, CMD_BANK_SWITCH, (u8 *)buf,
+		HEADER_SIZE);
+
+	/*send message*/
+	if (!error)
+		error = raydium_i2c_send(client, buf[ADDR_INDEX], buf, len);
+
+	return error;
+}
+
+static int raydium_i2c_sw_reset(struct i2c_client *client)
+{
+	static const u8 soft_rst_cmd[] = { 0x01, 0x04, 0x00, 0x00, 0x40};
+	int error;
+
+	error = raydium_i2c_send_message(client, 1, (void *)soft_rst_cmd);
+	if (error) {
+		dev_err(&client->dev, "software reset failed: %d\n", error);
+		return error;
+	}
+
+	msleep(RAYDIUM_RESET_DELAY_MSEC);
+
+	return 0;
+}
+
+static int raydium_i2c_query_ts_info(struct raydium_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	int error, retry_cnt;
+
+	for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
+		error = raydium_i2c_read(client, CMD_DATA_BANK,
+			sizeof(ts->obj), (void *)&ts->obj);
+		if (!error) {
+			error = raydium_i2c_read(client, CMD_QUERY_BANK,
+				sizeof(ts->query_bank_info),
+				(void *)&ts->query_bank_info);
+			if (!error) {
+				error = raydium_i2c_read_message(client,
+					ts->query_bank_info, sizeof(ts->info),
+					(void *)&ts->info);
+			}
+
+			if (!error)
+				return 0;
+		}
+	}
+	dev_err(&client->dev, "get data bank failed: %d\n", error);
+
+	return -EINVAL;
+}
+
+static int raydium_i2c_fastboot(struct i2c_client *client)
+{
+	static const u8 boot_cmd[] = { 0x20, 0x06, 0x00, 0x50 };
+	u8 buf[HEADER_SIZE];
+	int error;
+
+	error = raydium_i2c_read_message(client,
+		get_unaligned_be32(boot_cmd),
+		sizeof(boot_cmd), buf);
+
+	if (!error) {
+		if (buf[0] == RM_BOOT_BLDR) {
+			dev_dbg(&client->dev, "boot in fastboot mode\n");
+			return -EINVAL;
+		}
+		dev_dbg(&client->dev, "boot success -- 0x%x\n", client->addr);
+		return 0;
+	}
+
+	dev_err(&client->dev, "boot failed: %d\n", error);
+
+	return error;
+}
+
+static int raydium_i2c_initialize(struct raydium_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	int error, retry_cnt;
+	static const u8 recov_packet[] = { 0x04, 0x81 };
+	u8 buf[HEADER_SIZE];
+
+	for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
+		error = raydium_i2c_fastboot(client);
+		if (error) {
+			/* Continue initializing if it's the last try */
+			if (retry_cnt < MAX_RETRIES - 1)
+				continue;
+		}
+		/* Wait for Hello packet */
+		msleep(BOOT_DELAY_MS);
+
+		error = raydium_i2c_read(client, recov_packet[0], 1,
+			(void *)buf);
+		if (error) {
+			dev_err(&client->dev,
+				"failed to read 'hello' packet: %d\n", error);
+		} else if (buf[0] == recov_packet[1]) {
+			ts->boot_mode = RAYDIUM_TS_MAIN;
+			break;
+		}
+	}
+
+	if (error)
+		ts->boot_mode = RAYDIUM_TS_BLDR;
+	else
+		raydium_i2c_query_ts_info(ts);
+
+	return error;
+}
+
+static int raydium_i2c_fw_write_page(struct i2c_client *client,
+				    const void *page)
+{
+	static const u8 ack_ok[] = { 0x55, 0xAA };
+	u8 buf[2];
+	int retry;
+	int error;
+
+	for (retry = 0; retry < MAX_FW_UPDATE_RETRIES; retry++) {
+		error = raydium_i2c_send(client, CMD_BOOT_WRT,
+			(u8 *)page, RAYDIUM_FW_PAGESIZE);
+		if (error) {
+			dev_err(&client->dev,
+				"BLDR Write Page failed: %d\n", error);
+			continue;
+		}
+
+		error = raydium_i2c_read(client, CMD_BOOT_CHK, sizeof(ack_ok),
+			(void *)buf);
+		if (error) {
+			dev_err(&client->dev,
+				"BLDR Ack read failed: %d\n", error);
+			return error;
+		}
+
+		if (!memcmp(buf, ack_ok, sizeof(ack_ok)))
+			return 0;
+
+		error = -EIO;
+		dev_err(&client->dev,
+			"BLDR Get Ack Error [%02x:%02x]\n", buf[0], buf[1]);
+	}
+
+	return error;
+}
+
+static int raydium_i2c_do_update_firmware(struct i2c_client *client,
+					 const struct firmware *fw,
+					 bool force)
+{
+	static const u8 boot_cmd[] = { RM_BOOT_BLDR, 0x20, 0x06, 0x00, 0x50 };
+	static const u8 main_cmd[] = { RM_BOOT_MAIN, 0x20, 0x06, 0x00, 0x50 };
+	static const u8 boot_ack[] = { 0x55, 0xAA, 0x00, 0xFF };
+	u8 buf[HEADER_SIZE];
+	int page, n_fw_pages;
+	int error;
+
+	/* Recovery mode detection! */
+	if (!force) {
+		/* Start boot loader Procedure */
+		dev_dbg(&client->dev, "Normal BLDR procedure\n");
+		/* switch to mode */
+		error = raydium_i2c_send_message(client, 1, (void *)boot_cmd);
+		if (error)
+			dev_err(&client->dev, "failed to send boot cmd: %d\n",
+				error);
+		msleep(60);
+		raydium_i2c_sw_reset(client);
+		msleep(RAYDIUM_RESET_DELAY_MSEC);
+		error = raydium_i2c_send_message(client, 1, (void *)boot_cmd);
+	}
+
+	if (error) {
+		dev_err(&client->dev, "failed to enter fastboot mode: %d\n",
+			error);
+		return error;
+	}
+
+	msleep(20);
+
+	/* check fastboot state */
+	error = raydium_i2c_read(client, CMD_BOOT_ACK,
+		sizeof(boot_ack), (void *)buf);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to read boot ack: %d\n", error);
+		return error;
+	}
+
+	if (memcmp(buf, boot_ack, sizeof(boot_ack))) {
+		dev_err(&client->dev,
+			"failed to enter fastboot: %*ph (expected %*ph)\n",
+			(int)sizeof(buf), buf, (int)sizeof(boot_ack), boot_ack);
+		return -EIO;
+	}
+
+	dev_info(&client->dev, "successfully entered fastboot mode");
+
+	n_fw_pages = fw->size / RAYDIUM_FW_PAGESIZE;
+	dev_dbg(&client->dev, "BLDR Pages = %d\n", n_fw_pages);
+
+	for (page = 0; page < n_fw_pages; page++) {
+		error = raydium_i2c_fw_write_page(client,
+					fw->data + page * RAYDIUM_FW_PAGESIZE);
+		if (error) {
+			dev_err(&client->dev,
+				"failed to write FW page %d: %d\n",
+				page, error);
+			return error;
+		}
+	}
+	error = raydium_i2c_send_message(client, 1, (void *)main_cmd);
+	msleep(20);
+	raydium_i2c_sw_reset(client);
+	dev_info(&client->dev, "firmware update completed\n");
+
+	return 0;
+}
+
+static int raydium_i2c_fw_update(struct raydium_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	const struct firmware *fw;
+	char *fw_name;
+	int error;
+
+	fw_name = kasprintf(GFP_KERNEL, "raydium_i2c_%04x.bin",
+		ts->info.hw_ver & 0xFFFF);
+	if (!fw_name)
+		return -ENOMEM;
+
+	dev_info(&client->dev, "requesting fw name = %s\n", fw_name);
+	error = request_firmware(&fw, fw_name, &client->dev);
+	kfree(fw_name);
+	if (error) {
+		dev_err(&client->dev, "failed to request firmware: %d\n",
+			error);
+		return error;
+	}
+
+	if (fw->size % RAYDIUM_FW_PAGESIZE) {
+		dev_err(&client->dev, "invalid firmware length: %zu\n",
+			fw->size);
+		error = -EINVAL;
+		goto out;
+	}
+
+	disable_irq(client->irq);
+	error = raydium_i2c_do_update_firmware(client, fw,
+					ts->boot_mode == RAYDIUM_TS_BLDR);
+	if (error) {
+		dev_err(&client->dev, "firmware update failed: %d\n", error);
+		ts->boot_mode = RAYDIUM_TS_BLDR;
+		goto out_enable_irq;
+	}
+	error = raydium_i2c_initialize(ts);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to initialize device after firmware update: %d\n",
+			error);
+		ts->boot_mode = RAYDIUM_TS_BLDR;
+		goto out_enable_irq;
+	}
+
+	ts->boot_mode = RAYDIUM_TS_MAIN;
+
+out_enable_irq:
+	enable_irq(client->irq);
+	msleep(100);
+out:
+	release_firmware(fw);
+	return error;
+}
+
+static void raydium_mt_event(struct raydium_data *ts)
+{
+	struct raydium_abs_info *data;
+	int error, i, x, y;
+	u8 f_state;
+	u8 touch_count;
+	u8 tp_info_size;
+
+	error = raydium_i2c_read_message(ts->client, ts->obj.data_bank_addr,
+		ts->obj.pkg_size, (void *)&ts->buf);
+
+	if (error < 0) {
+		dev_err(&ts->client->dev, "%s: failed to read data: %d\n",
+			__func__, error);
+		return;
+	}
+
+	touch_count = 0;
+	tp_info_size = sizeof(ts->finger);
+
+	for (i = 0; i < MAX_TOUCH_NUM; i++) {
+		data = (struct raydium_abs_info *)(ts->buf + i * tp_info_size);
+
+		f_state = data->state & 0x03;
+
+		input_mt_slot(ts->input, i);
+		input_mt_report_slot_state(ts->input,
+				MT_TOOL_FINGER, f_state != 0);
+
+		if (!f_state)
+			continue;
+
+		x = (data->x_pos_msb << 8) | (data->x_pos_lsb);
+		y = (data->y_pos_msb << 8) | (data->y_pos_lsb);
+
+		input_report_key(ts->input, BTN_TOUCH, 1);
+		input_report_key(ts->input, BTN_TOOL_FINGER, 1);
+		input_report_abs(ts->input, ABS_MT_POSITION_X, x);
+		input_report_abs(ts->input, ABS_MT_POSITION_Y, y);
+		input_report_abs(ts->input, ABS_MT_PRESSURE, data->pressure);
+		input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR,
+			max(data->x_width, data->y_width));
+		input_report_abs(ts->input, ABS_MT_TOUCH_MINOR,
+			min(data->x_width, data->y_width));
+		touch_count++;
+	}
+
+	input_report_key(ts->input, BTN_TOUCH, touch_count > 0);
+	input_report_key(ts->input, BTN_TOOL_FINGER, ts->input > 0);
+	input_mt_report_pointer_emulation(ts->input, false);
+	input_sync(ts->input);
+}
+
+static irqreturn_t raydium_i2c_irq(int irq, void *_dev)
+{
+	struct raydium_data *ts = _dev;
+
+	if (ts->boot_mode != RAYDIUM_TS_BLDR)
+		raydium_mt_event(ts);
+
+	return IRQ_HANDLED;
+}
+
+static ssize_t write_update_fw(struct device *dev,
+			struct device_attribute *attr,
+			const char *buf, size_t count)
+{
+	struct raydium_data *ts = dev_get_drvdata(dev);
+	int error;
+
+	error = mutex_lock_interruptible(&ts->sysfs_mutex);
+	if (error)
+		return error;
+
+	error = raydium_i2c_fw_update(ts);
+	dev_dbg(dev, "firmware update result: %d\n", error);
+
+	mutex_unlock(&ts->sysfs_mutex);
+	return error ?: count;
+}
+
+static ssize_t raydium_bootmode_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct raydium_data *ts = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%s\n", ts->boot_mode == RAYDIUM_TS_MAIN ?
+		"Normal" : "Recovery");
+}
+
+static ssize_t raydium_fw_ver_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct raydium_data *ts = dev_get_drvdata(dev);
+
+	return sprintf(buf, "Release Version %d.%d\n",
+		ts->info.main_ver, ts->info.sub_ver);
+}
+
+static ssize_t raydium_hw_ver_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct raydium_data *ts = dev_get_drvdata(dev);
+
+	return sprintf(buf, "Hardware version 0x%x\n",
+		ts->info.hw_ver & 0xFFFF);
+}
+
+static DEVICE_ATTR(fw_version, S_IRUGO, raydium_fw_ver_show, NULL);
+static DEVICE_ATTR(hw_version, S_IRUGO, raydium_hw_ver_show, NULL);
+static DEVICE_ATTR(boot_mode, S_IRUGO, raydium_bootmode_show, NULL);
+static DEVICE_ATTR(update_fw, S_IWUSR, NULL, write_update_fw);
+
+static struct attribute *raydium_attributes[] = {
+	&dev_attr_update_fw.attr,
+	&dev_attr_boot_mode.attr,
+	&dev_attr_fw_version.attr,
+	&dev_attr_hw_version.attr,
+	NULL
+};
+
+static struct attribute_group raydium_attribute_group = {
+	.attrs = raydium_attributes,
+};
+
+static void raydium_i2c_remove_sysfs_group(void *_data)
+{
+	struct raydium_data *ts = _data;
+
+	sysfs_remove_group(&ts->client->dev.kobj, &raydium_attribute_group);
+}
+
+static int raydium_i2c_power_on(struct raydium_data *ts)
+{
+	int error;
+
+	if (IS_ERR_OR_NULL(ts->reset_gpio))
+		return 0;
+
+	gpiod_set_value_cansleep(ts->reset_gpio, 1);
+
+	error = regulator_enable(ts->vcc33);
+	if (error) {
+		dev_err(&ts->client->dev,
+			"failed to enable vcc33 regulator: %d\n", error);
+		goto release_reset_gpio;
+	}
+
+	error = regulator_enable(ts->vccio);
+	if (error) {
+		regulator_disable(ts->vcc33);
+		dev_err(&ts->client->dev,
+			"failed to enable vccio regulator: %d\n", error);
+		goto release_reset_gpio;
+	}
+
+	udelay(RAYDIUM_POWERON_DELAY_USEC);
+
+release_reset_gpio:
+	gpiod_set_value_cansleep(ts->reset_gpio, 0);
+
+	if (error)
+		return error;
+
+	msleep(RAYDIUM_RESET_DELAY_MSEC);
+
+	return 0;
+}
+
+static void raydium_i2c_power_off(void *_data)
+{
+	struct raydium_data *ts = _data;
+
+	if (!IS_ERR_OR_NULL(ts->reset_gpio)) {
+		gpiod_set_value_cansleep(ts->reset_gpio, 1);
+		regulator_disable(ts->vccio);
+		regulator_disable(ts->vcc33);
+	}
+}
+
+static int raydium_i2c_probe(struct i2c_client *client,
+			    const struct i2c_device_id *id)
+{
+	union i2c_smbus_data dummy;
+	struct raydium_data *ts;
+	unsigned long irqflags;
+	int error;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		dev_err(&client->dev,
+			"%s: i2c check functionality error\n", DEVICE_NAME);
+		return -ENXIO;
+	}
+
+	ts = devm_kzalloc(&client->dev, sizeof(struct raydium_data),
+		GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+
+	mutex_init(&ts->sysfs_mutex);
+	init_completion(&ts->cmd_done);
+
+	ts->client = client;
+	i2c_set_clientdata(client, ts);
+
+	ts->vcc33 = devm_regulator_get(&client->dev, "avdd");
+	if (IS_ERR(ts->vcc33)) {
+		error = PTR_ERR(ts->vcc33);
+		if (error != -EPROBE_DEFER)
+			dev_err(&client->dev,
+				"Failed to get 'vcc33' regulator: %d\n", error);
+		return error;
+	}
+
+	ts->vccio = devm_regulator_get(&client->dev, "vdd");
+	if (IS_ERR(ts->vccio)) {
+		error = PTR_ERR(ts->vccio);
+		if (error != -EPROBE_DEFER)
+			dev_err(&client->dev,
+				"Failed to get 'vccio' regulator: %d\n", error);
+		return error;
+	}
+
+
+	ts->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
+		GPIOD_OUT_LOW);
+	if (IS_ERR(ts->reset_gpio)) {
+		error = PTR_ERR(ts->reset_gpio);
+		if (error != -EPROBE_DEFER) {
+			dev_err(&client->dev,
+				"failed to get reset gpio: %d\n", error);
+			return error;
+		}
+	}
+
+	error = raydium_i2c_power_on(ts);
+	if (error)
+		return error;
+
+	error = devm_add_action(&client->dev, raydium_i2c_power_off, ts);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to install power off action: %d\n", error);
+		raydium_i2c_power_off(ts);
+		return error;
+	}
+
+	/* Make sure there is something at this address */
+	if (i2c_smbus_xfer(client->adapter, client->addr, 0,
+			I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0) {
+		dev_err(&client->dev, "nothing at this address\n");
+		return -ENXIO;
+	}
+
+	error = raydium_i2c_initialize(ts);
+	if (error) {
+		dev_err(&client->dev, "failed to initialize: %d\n", error);
+		return error;
+	}
+
+	ts->input = devm_input_allocate_device(&client->dev);
+	if (!ts->input) {
+		dev_err(&client->dev, "Failed to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	ts->input->name = "Raydium Touchscreen";
+	ts->input->id.bustype = BUS_I2C;
+
+	__set_bit(BTN_TOUCH, ts->input->keybit);
+	__set_bit(EV_ABS, ts->input->evbit);
+	__set_bit(EV_KEY, ts->input->evbit);
+
+	/* Single touch input params setup */
+	input_set_abs_params(ts->input, ABS_X, 0, ts->info.x_max, 0, 0);
+	input_set_abs_params(ts->input, ABS_Y, 0, ts->info.y_max, 0, 0);
+	input_set_abs_params(ts->input, ABS_PRESSURE, 0, 255, 0, 0);
+	input_abs_set_res(ts->input, ABS_X, ts->info.x_res);
+	input_abs_set_res(ts->input, ABS_Y, ts->info.y_res);
+
+	/* Multitouch input params setup */
+	error = input_mt_init_slots(ts->input, MAX_TOUCH_NUM,
+				    INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to initialize MT slots: %d\n", error);
+		return error;
+	}
+
+	input_set_abs_params(ts->input, ABS_MT_POSITION_X, 0,
+		ts->info.x_max, 0, 0);
+	input_set_abs_params(ts->input, ABS_MT_POSITION_Y,
+		0, ts->info.y_max, 0, 0);
+	input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+	input_set_abs_params(ts->input, ABS_MT_PRESSURE, 0, 255, 0, 0);
+	input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->info.x_res);
+	input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->info.y_res);
+
+	input_mt_init_slots(ts->input, MAX_TOUCH_NUM, 0);
+
+	input_set_drvdata(ts->input, ts);
+
+	error = input_register_device(ts->input);
+	if (error) {
+		dev_err(&client->dev,
+			"unable to register input device: %d\n", error);
+		return error;
+	}
+
+	error = devm_request_threaded_irq(&client->dev, client->irq,
+			NULL, raydium_i2c_irq, irqflags | IRQF_ONESHOT,
+			client->name, ts);
+	if (error) {
+		dev_err(&client->dev, "Failed to register interrupt\n");
+		return error;
+	}
+
+	if (!client->dev.of_node)
+		device_init_wakeup(&client->dev, true);
+
+	error = sysfs_create_group(&client->dev.kobj, &raydium_attribute_group);
+	if (error) {
+		dev_err(&client->dev, "failed to create sysfs attributes: %d\n",
+			error);
+		return error;
+	}
+
+	error = devm_add_action(&client->dev,
+				raydium_i2c_remove_sysfs_group, ts);
+	if (error) {
+		raydium_i2c_remove_sysfs_group(ts);
+		dev_err(&client->dev,
+			"Failed to add sysfs cleanup action: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int raydium_i2c_remove(struct i2c_client *client)
+{
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	if (ts->input)
+		input_unregister_device(ts->input);
+
+	device_init_wakeup(&client->dev, false);
+
+	devm_free_irq(&client->dev, client->irq, ts);
+
+	mutex_destroy(&ts->sysfs_mutex);
+
+	devm_remove_action(&client->dev, raydium_i2c_remove_sysfs_group, ts);
+
+	devm_remove_action(&client->dev, raydium_i2c_power_off, ts);
+
+	return 0;
+}
+
+static void __maybe_unused raydium_enter_sleep(struct i2c_client *client)
+{
+	static const u8 sleep_cmd[] = { 0x5A, 0xff, 0x00, 0x0f };
+	int error;
+
+	error = raydium_i2c_send(client, CMD_ENTER_SLEEP, (u8 *)sleep_cmd,
+		sizeof(sleep_cmd));
+	if (error)
+		dev_err(&client->dev,
+			"Send sleep failed: %d\n", error);
+}
+
+static int __maybe_unused raydium_i2c_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	/* Command not support in BLDR recovery mode */
+	if (ts->boot_mode != RAYDIUM_TS_MAIN)
+		return -EBUSY;
+
+	disable_irq(client->irq);
+
+	if (device_may_wakeup(dev)) {
+		raydium_enter_sleep(client);
+
+		ts->wake_irq_enabled = (enable_irq_wake(client->irq) == 0);
+	} else {
+		raydium_i2c_power_off(ts);
+	}
+
+	return 0;
+}
+
+static int __maybe_unused raydium_i2c_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	if (device_may_wakeup(dev)) {
+		if (ts->wake_irq_enabled)
+			disable_irq_wake(client->irq);
+		raydium_i2c_sw_reset(client);
+	} else {
+		raydium_i2c_power_on(ts);
+		raydium_i2c_initialize(ts);
+	}
+
+	enable_irq(client->irq);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(raydium_i2c_pm_ops,
+			 raydium_i2c_suspend, raydium_i2c_resume);
+
+static const struct i2c_device_id raydium_i2c_id[] = {
+	{ DEVICE_NAME, 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, raydium_i2c_id);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id raydium_acpi_id[] = {
+	{ "RMTS_0001", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(acpi, raydium_acpi_id);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id raydium_of_match[] = {
+	{ .compatible = "raydium,rm-ts",},
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, raydium_of_match);
+#endif
+
+static struct i2c_driver raydium_i2c_driver = {
+	.probe = raydium_i2c_probe,
+	.remove = raydium_i2c_remove,
+	.id_table = raydium_i2c_id,
+	.driver = {
+		.name = "raydium_ts",
+		.pm = &raydium_i2c_pm_ops,
+		.acpi_match_table = ACPI_PTR(raydium_acpi_id),
+		.of_match_table = of_match_ptr(raydium_of_match),
+	},
+};
+
+module_i2c_driver(raydium_i2c_driver);
+
+MODULE_AUTHOR("Raydium");
+MODULE_DESCRIPTION("Raydium I2c Touchscreen driver");
+MODULE_LICENSE("GPL");
-- 
2.1.2


^ permalink raw reply related	[flat|nested] 42+ messages in thread

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2016-03-03  3:14   ` Jeffrey Lin (林義章)
@ 2016-03-03  3:55     ` Joe Perches
  0 siblings, 0 replies; 42+ messages in thread
From: Joe Perches @ 2016-03-03  3:55 UTC (permalink / raw)
  To: Jeffrey Lin (林義章),
	jeffrey.lin, dmitry.torokhov, rydberg, grant.likely, robh+dt,
	jeesw, bleung, scott.liu
  Cc: Roger Yang (楊鎮瑋),
	KP Li (李昆倍),
	Albert Shieh (謝欣瑋),
	linux-kernel, linux-input, devicetree

On Thu, 2016-03-03 at 03:14 +0000, Jeffrey Lin (林義章) wrote:
> Hi Joe:
> Thank you for your response. I'll follow your comments in next version.

OK, btw: the below looks very odd

> > +static int raydium_i2c_send_message(struct i2c_client *client,
> > +	size_t len, void *data)
> > +{
> > +	int error;
> > +	u8 buf[HEADER_SIZE], ii;
> > +
> > +	for (ii = 0; ii < HEADER_SIZE; ii++)
> > +		buf[ii] = ((u8 *)data)[3 - ii];

It intermixes a #define of 4 with a hard declaration of 3
to reverse copy a byte array of length HEADER_SIZE.

Maybe use:
	for (ii = 0; ii < HEADER_SIZE; ii++)
		buf[ii] = ((u8 *)data)[HEADER_SIZE - 1 - ii]

^ permalink raw reply	[flat|nested] 42+ messages in thread

* RE: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2016-03-03  2:44 ` Joe Perches
@ 2016-03-03  3:14   ` Jeffrey Lin (林義章)
  2016-03-03  3:55     ` Joe Perches
  0 siblings, 1 reply; 42+ messages in thread
From: Jeffrey Lin (林義章) @ 2016-03-03  3:14 UTC (permalink / raw)
  To: Joe Perches, jeffrey.lin, dmitry.torokhov, rydberg, grant.likely,
	robh+dt, jeesw, bleung, scott.liu
  Cc: Roger Yang (楊鎮瑋),
	KP Li (李昆倍),
	Albert Shieh (謝欣瑋),
	linux-kernel, linux-input, devicetree

Hi Joe:
Thank you for your response. I'll follow your comments in next version.

Best Regards
----------------------------------------------------------------------
Jeffrey Lin,林義章
瑞鼎科技
Raydium Semiconductor Corporation
Tel:(03)666-1818 Ext.4163
Fax:(03)666-1919

-----Original Message-----
From: Joe Perches [mailto:joe@perches.com] 
Sent: Thursday, March 03, 2016 10:44 AM
To: jeffrey.lin; dmitry.torokhov@gmail.com; rydberg@euromail.se; grant.likely@linaro.org; robh+dt@kernel.org; jeesw@melfas.com; bleung@chromium.org; scott.liu@emc.com.tw
Cc: Jeffrey Lin (林義章); Roger Yang (楊鎮瑋); KP Li (李昆倍); Albert Shieh (謝欣瑋); linux-kernel@vger.kernel.org; linux-input@vger.kernel.org; devicetree@vger.kernel.org
Subject: Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver

On Thu, 2016-03-03 at 10:29 +0800, jeffrey.lin wrote:
> This patch is porting Raydium I2C touch driver. Developer can enable raydium touch driver by modifying define "CONFIG_TOUCHSCREEN_RM_TS".

trivial comments:

> diff --git a/drivers/input/touchscreen/raydium_i2c_ts.c b/drivers/input/touchscreen/raydium_i2c_ts.c
[]
> +static int raydium_i2c_send(struct i2c_client *client,
> +	u8 addr, u8 *data, size_t len)
> +{
> +	u8 buf[MAX_PACKET_SIZE + 1];
> +	int i, tries = 0;
> +
> +	if (len > MAX_PACKET_SIZE)
> +		return -EINVAL;
> +
> +	buf[0] = addr & 0xff;

Seems pointless to use & 0xff when both
buf and addr are u8

> +	for (i = 0; i < len; i++)
> +		buf[i + 1] = *data++;

memcpy.  Maybe:

	buf[0] = addr;
	memcpy(&buf[1], data, len);

> +static int raydium_i2c_send_message(struct i2c_client *client,
> +	size_t len, void *data)
> +{
> +	int error;
> +	u8 buf[HEADER_SIZE], ii;
> +
> +	for (ii = 0; ii < HEADER_SIZE; ii++)
> +		buf[ii] = ((u8 *)data)[3 - ii];
> +	/*set data bank*/
> +	error = raydium_i2c_send(client, CMD_BANK_SWITCH,
> +		(u8 *)buf, HEADER_SIZE);
> +
> +	/*send messange*/

typo, message

> +	if (!error)
> +		error = raydium_i2c_send(client, buf[ADDR_INDEX], buf, len);
> +
> +	return error;
> +}
> +
> +static int raydium_i2c_sw_reset(struct i2c_client *client)
> +{
> +	const u8 soft_rst_cmd[] = { 0x01, 0x04, 0x00, 0x00, 0x40};

static

> +	int error;
> +
> +	error = raydium_i2c_send_message(client,
> +			1, (void *)soft_rst_cmd);

could be better on a single line

> +static int raydium_i2c_fastboot(struct i2c_client *client)
> +{
> +	const u8 boot_cmd[] = { 0x20, 0x06, 0x00, 0x50 };
> +	u8 buf[HEADER_SIZE];
> +	int error;
> +
> +	error = raydium_i2c_read_message(client,
> +		get_unaligned_be32(boot_cmd),
> +		sizeof(boot_cmd),
> +		buf);
> +
> +	if (error) {
> +		dev_err(&client->dev, "boot failed: %d\n", error);
> +		return error;
> +	} else if (buf[0] == RM_BOOT_BLDR) {

unnecessary else

> +		dev_dbg(&client->dev, "boot in fastboot mode\n");
> +		return -EINVAL;
> +	}
> +
> +	dev_dbg(&client->dev, "boot success -- 0x%x\n", client->addr);
> +	return 0;
> +}
> +
> +static int raydium_i2c_initialize(struct raydium_data *ts)
> +{
> +	struct i2c_client *client = ts->client;
> +	int error, retry_cnt;
> +	const u8 recov_packet[] = { 0x04, 0x81 };

static and etc...

generally, it'd be nice to align to open parenthesis and
to more maximally fill lines to 80 chars instead of using
relatively short line lengths and multiple line statements.

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2016-03-03  2:29 jeffrey.lin
@ 2016-03-03  2:44 ` Joe Perches
  2016-03-03  3:14   ` Jeffrey Lin (林義章)
  0 siblings, 1 reply; 42+ messages in thread
From: Joe Perches @ 2016-03-03  2:44 UTC (permalink / raw)
  To: jeffrey.lin, dmitry.torokhov, rydberg, grant.likely, robh+dt,
	jeesw, bleung, scott.liu
  Cc: jeffrey.lin, roger.yang, KP.li, albert.shieh, linux-kernel,
	linux-input, devicetree

On Thu, 2016-03-03 at 10:29 +0800, jeffrey.lin wrote:
> This patch is porting Raydium I2C touch driver. Developer can enable raydium touch driver by modifying define "CONFIG_TOUCHSCREEN_RM_TS".

trivial comments:

> diff --git a/drivers/input/touchscreen/raydium_i2c_ts.c b/drivers/input/touchscreen/raydium_i2c_ts.c
[]
> +static int raydium_i2c_send(struct i2c_client *client,
> +	u8 addr, u8 *data, size_t len)
> +{
> +	u8 buf[MAX_PACKET_SIZE + 1];
> +	int i, tries = 0;
> +
> +	if (len > MAX_PACKET_SIZE)
> +		return -EINVAL;
> +
> +	buf[0] = addr & 0xff;

Seems pointless to use & 0xff when both
buf and addr are u8

> +	for (i = 0; i < len; i++)
> +		buf[i + 1] = *data++;

memcpy.  Maybe:

	buf[0] = addr;
	memcpy(&buf[1], data, len);

> +static int raydium_i2c_send_message(struct i2c_client *client,
> +	size_t len, void *data)
> +{
> +	int error;
> +	u8 buf[HEADER_SIZE], ii;
> +
> +	for (ii = 0; ii < HEADER_SIZE; ii++)
> +		buf[ii] = ((u8 *)data)[3 - ii];
> +	/*set data bank*/
> +	error = raydium_i2c_send(client, CMD_BANK_SWITCH,
> +		(u8 *)buf, HEADER_SIZE);
> +
> +	/*send messange*/

typo, message

> +	if (!error)
> +		error = raydium_i2c_send(client, buf[ADDR_INDEX], buf, len);
> +
> +	return error;
> +}
> +
> +static int raydium_i2c_sw_reset(struct i2c_client *client)
> +{
> +	const u8 soft_rst_cmd[] = { 0x01, 0x04, 0x00, 0x00, 0x40};

static

> +	int error;
> +
> +	error = raydium_i2c_send_message(client,
> +			1, (void *)soft_rst_cmd);

could be better on a single line

> +static int raydium_i2c_fastboot(struct i2c_client *client)
> +{
> +	const u8 boot_cmd[] = { 0x20, 0x06, 0x00, 0x50 };
> +	u8 buf[HEADER_SIZE];
> +	int error;
> +
> +	error = raydium_i2c_read_message(client,
> +		get_unaligned_be32(boot_cmd),
> +		sizeof(boot_cmd),
> +		buf);
> +
> +	if (error) {
> +		dev_err(&client->dev, "boot failed: %d\n", error);
> +		return error;
> +	} else if (buf[0] == RM_BOOT_BLDR) {

unnecessary else

> +		dev_dbg(&client->dev, "boot in fastboot mode\n");
> +		return -EINVAL;
> +	}
> +
> +	dev_dbg(&client->dev, "boot success -- 0x%x\n", client->addr);
> +	return 0;
> +}
> +
> +static int raydium_i2c_initialize(struct raydium_data *ts)
> +{
> +	struct i2c_client *client = ts->client;
> +	int error, retry_cnt;
> +	const u8 recov_packet[] = { 0x04, 0x81 };

static and etc...

generally, it'd be nice to align to open parenthesis and
to more maximally fill lines to 80 chars instead of using
relatively short line lengths and multiple line statements.

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
@ 2016-03-03  2:29 jeffrey.lin
  2016-03-03  2:44 ` Joe Perches
  0 siblings, 1 reply; 42+ messages in thread
From: jeffrey.lin @ 2016-03-03  2:29 UTC (permalink / raw)
  To: dmitry.torokhov, rydberg, grant.likely, robh+dt, jeesw, bleung,
	scott.liu
  Cc: jeffrey.lin, roger.yang, KP.li, albert.shieh, linux-kernel,
	linux-input, devicetree

This patch is porting Raydium I2C touch driver. Developer can enable raydium touch driver by modifying define "CONFIG_TOUCHSCREEN_RM_TS".

Signed-off-by: jeffrey.lin <jeffrey.lin@rad-ic.com>
---
 drivers/input/touchscreen/Kconfig          |  12 +
 drivers/input/touchscreen/Makefile         |   1 +
 drivers/input/touchscreen/raydium_i2c_ts.c | 977 +++++++++++++++++++++++++++++
 3 files changed, 990 insertions(+)
 create mode 100644 drivers/input/touchscreen/raydium_i2c_ts.c

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 3f3f6ee..ab28721 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -915,6 +915,18 @@ config TOUCHSCREEN_PCAP
 	  To compile this driver as a module, choose M here: the
 	  module will be called pcap_ts.
 
+config TOUCHSCREEN_RM_TS
+	tristate "Raydium I2C Touchscreen"
+	depends on I2C
+	help
+	  Say Y here if you have Raydium series I2C touchscreen,
+	  such as RM31100 , connected to your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called raydium_i2c_ts.
+
 config TOUCHSCREEN_ST1232
 	tristate "Sitronix ST1232 touchscreen controllers"
 	depends on I2C
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 4941f2d..99e08cf 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE)	+= usbtouchscreen.o
 obj-$(CONFIG_TOUCHSCREEN_PCAP)		+= pcap_ts.o
 obj-$(CONFIG_TOUCHSCREEN_PENMOUNT)	+= penmount.o
 obj-$(CONFIG_TOUCHSCREEN_PIXCIR)	+= pixcir_i2c_ts.o
+obj-$(CONFIG_TOUCHSCREEN_RM_TS)		+= raydium_i2c_ts.o
 obj-$(CONFIG_TOUCHSCREEN_S3C2410)	+= s3c2410_ts.o
 obj-$(CONFIG_TOUCHSCREEN_ST1232)	+= st1232.o
 obj-$(CONFIG_TOUCHSCREEN_STMPE)		+= stmpe-ts.o
diff --git a/drivers/input/touchscreen/raydium_i2c_ts.c b/drivers/input/touchscreen/raydium_i2c_ts.c
new file mode 100644
index 0000000..fa65c26
--- /dev/null
+++ b/drivers/input/touchscreen/raydium_i2c_ts.c
@@ -0,0 +1,977 @@
+/*
+ * Raydium touchscreen I2C driver.
+ *
+ * Copyright (C) 2012-2014, Raydium Semiconductor Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Raydium reserves the right to make changes without further notice
+ * to the materials described herein. Raydium does not assume any
+ * liability arising out of the application described herein.
+ *
+ * Contact Raydium Semiconductor Corporation at www.rad-ic.com
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include <linux/buffer_head.h>
+#include <linux/slab.h>
+#include <linux/firmware.h>
+#include <linux/input/mt.h>
+#include <linux/acpi.h>
+#include <linux/of.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <asm/unaligned.h>
+#include <linux/gpio/consumer.h>
+
+/* Device, Driver information */
+#define DEVICE_NAME	"raydium_i2c"
+
+/* Slave I2C mode*/
+#define RM_BOOT_BLDR	0x02
+#define RM_BOOT_MAIN	0x03
+
+/*I2C command */
+#define CMD_QUERY_BANK		0x2B
+#define CMD_DATA_BANK		0x4D
+#define CMD_ENTER_SLEEP		0x4E
+#define CMD_BOOT_ACK		0x0A
+#define CMD_BOOT_WRT		0x5B
+#define CMD_BOOT_CHK		0x0C
+#define CMD_BANK_SWITCH		0xAA
+
+/* Touch relative info */
+#define MAX_RETRIES		3
+#define MAX_FW_UPDATE_RETRIES	30
+#define MAX_TOUCH_NUM		10
+#define MAX_PACKET_SIZE		60
+#define BOOT_DELAY_MS	100
+
+#define RAYDIUM_FW_PAGESIZE	128
+#define RAYDIUM_POWERON_DELAY_USEC	500
+#define RAYDIUM_RESET_DELAY_MSEC	50
+
+#define ADDR_INDEX		0x03
+#define HEADER_SIZE		4
+
+enum raydium_boot_mode {
+	RAYDIUM_TS_MAIN,
+	RAYDIUM_TS_BLDR,
+};
+
+struct raydium_info {
+	u32 hw_ver;
+	u8 main_ver;
+	u8 sub_ver;
+	u16 ft_ver;
+	u8 x_num;
+	u8 y_num;
+	u16 x_max;
+	u16 y_max;
+	u8 x_res;		/* units/mm */
+	u8 y_res;		/* units/mm */
+};
+
+struct raydium_abs_info {
+	u8 state;/*1:touch, 0:no touch*/
+	u8 x_pos_lsb;
+	u8 x_pos_msb;
+	u8 y_pos_lsb;
+	u8 y_pos_msb;
+	u8 pressure;
+	u8 x_width;
+	u8 y_width;
+};
+
+struct raydium_object {
+	u32 data_bank_addr;
+	u8 pkg_size;
+};
+
+/* struct raydium_data - represents state of Raydium touchscreen device */
+struct raydium_data {
+	struct i2c_client *client;
+	struct input_dev *input;
+
+	struct regulator *vcc33;
+	struct regulator *vccio;
+	struct gpio_desc *reset_gpio;
+
+	u32 query_bank_info;
+
+	struct raydium_info info;
+	struct raydium_object obj;
+	struct raydium_abs_info finger;
+
+	enum raydium_boot_mode boot_mode;
+
+	struct mutex sysfs_mutex;
+
+	u8 cmd_resp[HEADER_SIZE];
+	struct completion cmd_done;
+
+	u8 buf[MAX_PACKET_SIZE];
+
+	bool wake_irq_enabled;
+};
+
+static int raydium_i2c_send(struct i2c_client *client,
+	u8 addr, u8 *data, size_t len)
+{
+	u8 buf[MAX_PACKET_SIZE + 1];
+	int i, tries = 0;
+
+	if (len > MAX_PACKET_SIZE)
+		return -EINVAL;
+
+	buf[0] = addr & 0xff;
+	for (i = 0; i < len; i++)
+		buf[i + 1] = *data++;
+
+	do {
+		if (i2c_master_send(client, buf, len + 1) == (len + 1))
+			return 0;
+		msleep(20);
+	} while (++tries < MAX_RETRIES);
+
+	dev_err(&client->dev, "%s: i2c send failed\n", __func__);
+
+	return -EIO;
+}
+
+static int raydium_i2c_read(struct i2c_client *client,
+	u8 addr, size_t len, void *data)
+{
+	struct i2c_msg xfer[2];
+
+	/* Write register */
+	xfer[0].addr = client->addr;
+	xfer[0].flags = 0;
+	xfer[0].len = 1;
+	xfer[0].buf = &addr;
+
+	/* Read data */
+	xfer[1].addr = client->addr;
+	xfer[1].flags = I2C_M_RD;
+	xfer[1].len = len;
+	xfer[1].buf = data;
+
+	if (i2c_transfer(client->adapter, xfer, 2) == 2)
+		return 0;
+
+	dev_err(&client->dev, "%s: i2c transfer failed\n", __func__);
+
+	return -EIO;
+}
+
+static int raydium_i2c_read_message(struct i2c_client *client,
+	u32 addr, size_t len, void *data)
+{
+	u16 pkg_size, use_len;
+	u8 buf[HEADER_SIZE], idx_i, idx_j;
+	int error;
+
+	use_len = len;
+	idx_j = 0;
+	while (use_len > 0) {
+		pkg_size = (use_len < MAX_PACKET_SIZE) ?
+			use_len : MAX_PACKET_SIZE;
+		for (idx_i = 0; idx_i < HEADER_SIZE; idx_i++)
+			buf[idx_i] = addr >> (3 - idx_i)*8;
+
+		/*set data bank*/
+		error = raydium_i2c_send(client, CMD_BANK_SWITCH,
+			(u8 *)buf, HEADER_SIZE);
+		/*read potints data*/
+		if (!error)
+			error = raydium_i2c_read(client,
+				buf[ADDR_INDEX],
+				pkg_size,
+				(void *)(data + idx_j*MAX_PACKET_SIZE));
+
+		pkg_size += MAX_PACKET_SIZE;
+		addr += MAX_PACKET_SIZE;
+		use_len = (use_len < MAX_PACKET_SIZE) ?
+			0 : (use_len - MAX_PACKET_SIZE);
+		idx_j++;
+	}
+
+	return error;
+}
+
+static int raydium_i2c_send_message(struct i2c_client *client,
+	size_t len, void *data)
+{
+	int error;
+	u8 buf[HEADER_SIZE], ii;
+
+	for (ii = 0; ii < HEADER_SIZE; ii++)
+		buf[ii] = ((u8 *)data)[3 - ii];
+	/*set data bank*/
+	error = raydium_i2c_send(client, CMD_BANK_SWITCH,
+		(u8 *)buf, HEADER_SIZE);
+
+	/*send messange*/
+	if (!error)
+		error = raydium_i2c_send(client, buf[ADDR_INDEX], buf, len);
+
+	return error;
+}
+
+static int raydium_i2c_sw_reset(struct i2c_client *client)
+{
+	const u8 soft_rst_cmd[] = { 0x01, 0x04, 0x00, 0x00, 0x40};
+	int error;
+
+	error = raydium_i2c_send_message(client,
+			1, (void *)soft_rst_cmd);
+	if (error) {
+		dev_err(&client->dev, "software reset failed: %d\n", error);
+		return error;
+	}
+
+	msleep(RAYDIUM_RESET_DELAY_MSEC);
+
+	return 0;
+}
+
+static int raydium_i2c_query_ts_info(struct raydium_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	int error, retry_cnt;
+
+	for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
+		error = raydium_i2c_read(client, CMD_DATA_BANK,
+			sizeof(ts->obj), (void *)&ts->obj);
+		if (error)
+			dev_err(&client->dev, "get data bank failed: %d\n",
+			error);
+		else {
+			error = raydium_i2c_read(client, CMD_QUERY_BANK,
+				sizeof(ts->query_bank_info),
+				(void *)&ts->query_bank_info);
+			if (!error) {
+				error = raydium_i2c_read_message(client,
+					ts->query_bank_info,
+					sizeof(ts->info),
+					(void *)&ts->info);
+			}
+		}
+		if (!error)
+			return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int raydium_i2c_fastboot(struct i2c_client *client)
+{
+	const u8 boot_cmd[] = { 0x20, 0x06, 0x00, 0x50 };
+	u8 buf[HEADER_SIZE];
+	int error;
+
+	error = raydium_i2c_read_message(client,
+		get_unaligned_be32(boot_cmd),
+		sizeof(boot_cmd),
+		buf);
+
+	if (error) {
+		dev_err(&client->dev, "boot failed: %d\n", error);
+		return error;
+	} else if (buf[0] == RM_BOOT_BLDR) {
+		dev_dbg(&client->dev, "boot in fastboot mode\n");
+		return -EINVAL;
+	}
+
+	dev_dbg(&client->dev, "boot success -- 0x%x\n", client->addr);
+	return 0;
+}
+
+static int raydium_i2c_initialize(struct raydium_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	int error, retry_cnt;
+	const u8 recov_packet[] = { 0x04, 0x81 };
+	u8 buf[HEADER_SIZE];
+
+	for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
+		error = raydium_i2c_fastboot(client);
+		if (error) {
+			/* Continue initializing if it's the last try */
+			if (retry_cnt < MAX_RETRIES - 1)
+				continue;
+		}
+		/* Wait for Hello packet */
+		msleep(BOOT_DELAY_MS);
+
+		error = raydium_i2c_read(client, recov_packet[0],
+			1, (void *)buf);
+		if (error) {
+			dev_err(&client->dev,
+				"failed to read 'hello' packet: %d\n", error);
+		} else if (buf[0] == recov_packet[1]) {
+			ts->boot_mode = RAYDIUM_TS_MAIN;
+			break;
+		}
+	}
+
+	if (error)
+		ts->boot_mode = RAYDIUM_TS_BLDR;
+	else
+		raydium_i2c_query_ts_info(ts);
+
+	return error;
+}
+
+static int raydium_i2c_fw_write_page(struct i2c_client *client,
+				    const void *page)
+{
+	const u8 ack_ok[] = { 0x55, 0xAA };
+	u8 buf[2];
+	int retry;
+	int error;
+
+	for (retry = 0; retry < MAX_FW_UPDATE_RETRIES; retry++) {
+		error = raydium_i2c_send(client, CMD_BOOT_WRT,
+			(u8 *)page, RAYDIUM_FW_PAGESIZE);
+		if (error) {
+			dev_err(&client->dev,
+				"BLDR Write Page failed: %d\n", error);
+			continue;
+		}
+
+		error = raydium_i2c_read(client, CMD_BOOT_CHK,
+			sizeof(ack_ok), (void *)buf);
+		if (error) {
+			dev_err(&client->dev,
+				"BLDR Ack read failed: %d\n", error);
+			return error;
+		}
+
+		if (!memcmp(buf, ack_ok, sizeof(ack_ok)))
+			return 0;
+
+		error = -EIO;
+		dev_err(&client->dev,
+			"BLDR Get Ack Error [%02x:%02x]\n",
+			buf[0], buf[1]);
+	}
+
+	return error;
+}
+
+static int raydium_i2c_do_update_firmware(struct i2c_client *client,
+					 const struct firmware *fw,
+					 bool force)
+{
+	const u8 boot_cmd[] = { RM_BOOT_BLDR, 0x20, 0x06, 0x00, 0x50 };
+	const u8 main_cmd[] = { RM_BOOT_MAIN, 0x20, 0x06, 0x00, 0x50 };
+	const u8 boot_ack[] = { 0x55, 0xAA, 0x00, 0xFF };
+	u8 buf[HEADER_SIZE];
+	int page, n_fw_pages;
+	int error;
+
+	/* Recovery mode detection! */
+	if (!force) {
+		/* Start boot loader Procedure */
+		dev_dbg(&client->dev, "Normal BLDR procedure\n");
+		/* switch to mode */
+		error = raydium_i2c_send_message(client, 1, (void *)boot_cmd);
+		if (error)
+			dev_err(&client->dev, "failed to send boot cmd: %d\n",
+				error);
+		msleep(60);
+		raydium_i2c_sw_reset(client);
+		msleep(RAYDIUM_RESET_DELAY_MSEC);
+		error = raydium_i2c_send_message(client, 1, (void *)boot_cmd);
+	}
+
+	if (error) {
+		dev_err(&client->dev, "failed to enter fastboot mode: %d\n",
+			error);
+		return error;
+	}
+
+	msleep(20);
+
+	/* check fastboot state */
+	error = raydium_i2c_read(client, CMD_BOOT_ACK,
+		sizeof(boot_ack), (void *)buf);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to read boot ack: %d\n",
+			error);
+		return error;
+	}
+
+	if (memcmp(buf, boot_ack, sizeof(boot_ack))) {
+		dev_err(&client->dev,
+			"failed to enter fastboot: %*ph (expected %*ph)\n",
+			(int)sizeof(buf), buf, (int)sizeof(boot_ack), boot_ack);
+		return -EIO;
+	}
+
+	dev_info(&client->dev, "successfully entered fastboot mode");
+
+	n_fw_pages = fw->size / RAYDIUM_FW_PAGESIZE;
+	dev_dbg(&client->dev, "BLDR Pages = %d\n", n_fw_pages);
+
+	for (page = 0; page < n_fw_pages; page++) {
+		error = raydium_i2c_fw_write_page(client,
+					fw->data + page * RAYDIUM_FW_PAGESIZE);
+		if (error) {
+			dev_err(&client->dev,
+				"failed to write FW page %d: %d\n",
+				page, error);
+			return error;
+		}
+	}
+	error = raydium_i2c_send_message(client, 1, (void *)main_cmd);
+	msleep(20);
+	raydium_i2c_sw_reset(client);
+	dev_info(&client->dev, "firmware update completed\n");
+
+	return 0;
+}
+
+static int raydium_i2c_fw_update(struct raydium_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	const struct firmware *fw;
+	char *fw_name;
+	int error;
+
+	fw_name = kasprintf(GFP_KERNEL, "raydium_i2c_%04x.bin",
+		ts->info.hw_ver & 0xFFFF);
+	if (!fw_name)
+		return -ENOMEM;
+
+	dev_info(&client->dev, "requesting fw name = %s\n", fw_name);
+	error = request_firmware(&fw, fw_name, &client->dev);
+	kfree(fw_name);
+	if (error) {
+		dev_err(&client->dev, "failed to request firmware: %d\n",
+			error);
+		return error;
+	}
+
+	if (fw->size % RAYDIUM_FW_PAGESIZE) {
+		dev_err(&client->dev, "invalid firmware length: %zu\n",
+			fw->size);
+		error = -EINVAL;
+		goto out;
+	}
+
+	disable_irq(client->irq);
+	error = raydium_i2c_do_update_firmware(client, fw,
+					ts->boot_mode == RAYDIUM_TS_BLDR);
+	if (error) {
+		dev_err(&client->dev, "firmware update failed: %d\n", error);
+		ts->boot_mode = RAYDIUM_TS_BLDR;
+		goto out_enable_irq;
+	}
+	error = raydium_i2c_initialize(ts);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to initialize device after firmware update: %d\n",
+			error);
+		ts->boot_mode = RAYDIUM_TS_BLDR;
+		goto out_enable_irq;
+	}
+
+	ts->boot_mode = RAYDIUM_TS_MAIN;
+
+out_enable_irq:
+	enable_irq(client->irq);
+	msleep(100);
+out:
+	release_firmware(fw);
+	return error;
+}
+
+static void raydium_mt_event(struct raydium_data *ts)
+{
+	struct raydium_abs_info *data;
+	int error, i, x, y;
+	u8 f_state;
+	u8 touch_count;
+	u8 tp_info_size;
+
+	error = raydium_i2c_read_message(ts->client, ts->obj.data_bank_addr,
+		ts->obj.pkg_size, (void *)&ts->buf);
+
+	if (error < 0) {
+		dev_err(&ts->client->dev, "%s: failed to read data: %d\n",
+			__func__, error);
+		return;
+	}
+
+	touch_count = 0;
+	tp_info_size = sizeof(ts->finger);
+
+	for (i = 0; i < MAX_TOUCH_NUM; i++) {
+		data = (struct raydium_abs_info *)(ts->buf + i * tp_info_size);
+
+		f_state = data->state & 0x03;
+
+		input_mt_slot(ts->input, i);
+		input_mt_report_slot_state(ts->input,
+				MT_TOOL_FINGER, f_state != 0);
+
+		if (!f_state)
+			continue;
+
+		x = (data->x_pos_msb << 8) | (data->x_pos_lsb);
+		y = (data->y_pos_msb << 8) | (data->y_pos_lsb);
+
+		input_report_key(ts->input,
+			BTN_TOUCH, 1);
+		input_report_key(ts->input,
+			BTN_TOOL_FINGER, 1);
+		input_report_abs(ts->input,
+			ABS_MT_POSITION_X, x);
+		input_report_abs(ts->input,
+			ABS_MT_POSITION_Y, y);
+		input_report_abs(ts->input,
+			ABS_MT_PRESSURE, data->pressure);
+		input_report_abs(ts->input,
+			ABS_MT_TOUCH_MAJOR,
+			max(data->x_width, data->y_width));
+		input_report_abs(ts->input,
+			ABS_MT_TOUCH_MINOR,
+			min(data->x_width, data->y_width));
+		touch_count++;
+	}
+
+	input_report_key(ts->input,
+		BTN_TOUCH, touch_count > 0);
+	input_report_key(ts->input,
+		BTN_TOOL_FINGER, ts->input > 0);
+	input_mt_report_pointer_emulation(ts->input, false);
+	input_sync(ts->input);
+}
+
+static irqreturn_t raydium_i2c_irq(int irq, void *_dev)
+{
+	struct raydium_data *ts = _dev;
+
+	if (ts->boot_mode != RAYDIUM_TS_BLDR)
+		raydium_mt_event(ts);
+
+	return IRQ_HANDLED;
+}
+
+static ssize_t write_update_fw(struct device *dev,
+			struct device_attribute *attr,
+			const char *buf, size_t count)
+{
+	struct raydium_data *ts = dev_get_drvdata(dev);
+	int error;
+
+	error = mutex_lock_interruptible(&ts->sysfs_mutex);
+	if (error)
+		return error;
+
+	error = raydium_i2c_fw_update(ts);
+	dev_dbg(dev, "firmware update result: %d\n", error);
+
+	mutex_unlock(&ts->sysfs_mutex);
+	return error ?: count;
+}
+
+static ssize_t raydium_bootmode_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct raydium_data *ts = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%s\n",
+		ts->boot_mode == RAYDIUM_TS_MAIN ?
+		"Normal" : "Recovery");
+}
+
+static ssize_t raydium_fw_ver_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct raydium_data *ts = dev_get_drvdata(dev);
+
+	return sprintf(buf, "Release Version %d.%d\n",
+		ts->info.main_ver,
+		ts->info.sub_ver);
+}
+
+static ssize_t raydium_hw_ver_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct raydium_data *ts = dev_get_drvdata(dev);
+
+	return sprintf(buf, "Hardware version 0x%x\n",
+		ts->info.hw_ver & 0xFFFF);
+}
+
+static DEVICE_ATTR(fw_version, S_IRUGO, raydium_fw_ver_show, NULL);
+static DEVICE_ATTR(hw_version, S_IRUGO, raydium_hw_ver_show, NULL);
+static DEVICE_ATTR(boot_mode, S_IRUGO, raydium_bootmode_show, NULL);
+static DEVICE_ATTR(update_fw, S_IWUSR, NULL, write_update_fw);
+
+static struct attribute *raydium_attributes[] = {
+	&dev_attr_update_fw.attr,
+	&dev_attr_boot_mode.attr,
+	&dev_attr_fw_version.attr,
+	&dev_attr_hw_version.attr,
+	NULL
+};
+
+static struct attribute_group raydium_attribute_group = {
+	.attrs = raydium_attributes,
+};
+
+static void raydium_i2c_remove_sysfs_group(void *_data)
+{
+	struct raydium_data *ts = _data;
+
+	sysfs_remove_group(&ts->client->dev.kobj, &raydium_attribute_group);
+}
+
+static int raydium_i2c_power_on(struct raydium_data *ts)
+{
+	int error;
+
+	if (IS_ERR_OR_NULL(ts->reset_gpio))
+		return 0;
+
+	gpiod_set_value_cansleep(ts->reset_gpio, 1);
+
+	error = regulator_enable(ts->vcc33);
+	if (error) {
+		dev_err(&ts->client->dev,
+			"failed to enable vcc33 regulator: %d\n",
+			error);
+		goto release_reset_gpio;
+	}
+
+	error = regulator_enable(ts->vccio);
+	if (error) {
+		dev_err(&ts->client->dev,
+			"failed to enable vccio regulator: %d\n",
+			error);
+		regulator_disable(ts->vcc33);
+		goto release_reset_gpio;
+	}
+
+	udelay(RAYDIUM_POWERON_DELAY_USEC);
+
+release_reset_gpio:
+	gpiod_set_value_cansleep(ts->reset_gpio, 0);
+
+	if (error)
+		return error;
+
+	msleep(RAYDIUM_RESET_DELAY_MSEC);
+
+	return 0;
+}
+
+static void raydium_i2c_power_off(void *_data)
+{
+	struct raydium_data *ts = _data;
+
+	if (!IS_ERR_OR_NULL(ts->reset_gpio)) {
+		gpiod_set_value_cansleep(ts->reset_gpio, 1);
+		regulator_disable(ts->vccio);
+		regulator_disable(ts->vcc33);
+	}
+}
+
+static int raydium_i2c_probe(struct i2c_client *client,
+			    const struct i2c_device_id *id)
+{
+	union i2c_smbus_data dummy;
+	struct raydium_data *ts;
+	unsigned long irqflags;
+	int error;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		dev_err(&client->dev,
+			"%s: i2c check functionality error\n", DEVICE_NAME);
+		return -ENXIO;
+	}
+
+	ts = devm_kzalloc(&client->dev,
+		sizeof(struct raydium_data), GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+
+	mutex_init(&ts->sysfs_mutex);
+	init_completion(&ts->cmd_done);
+
+	ts->client = client;
+	i2c_set_clientdata(client, ts);
+
+	ts->vcc33 = devm_regulator_get(&client->dev, "avdd");
+	if (IS_ERR(ts->vcc33)) {
+		error = PTR_ERR(ts->vcc33);
+		if (error != -EPROBE_DEFER)
+			dev_err(&client->dev,
+				"Failed to get 'vcc33' regulator: %d\n",
+				error);
+		return error;
+	}
+
+	ts->vccio = devm_regulator_get(&client->dev, "vdd");
+	if (IS_ERR(ts->vccio)) {
+		error = PTR_ERR(ts->vccio);
+		if (error != -EPROBE_DEFER)
+			dev_err(&client->dev,
+				"Failed to get 'vccio' regulator: %d\n",
+				error);
+		return error;
+	}
+
+
+	ts->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
+		GPIOD_OUT_LOW);
+	if (IS_ERR(ts->reset_gpio)) {
+		error = PTR_ERR(ts->reset_gpio);
+		if (error != -EPROBE_DEFER) {
+			dev_err(&client->dev,
+				"failed to get reset gpio: %d\n",
+				error);
+			return error;
+		}
+	}
+
+	error = raydium_i2c_power_on(ts);
+	if (error)
+		return error;
+
+	error = devm_add_action(&client->dev, raydium_i2c_power_off, ts);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to install power off action: %d\n", error);
+		raydium_i2c_power_off(ts);
+		return error;
+	}
+
+	/* Make sure there is something at this address */
+	if (i2c_smbus_xfer(client->adapter, client->addr, 0,
+			   I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0) {
+		dev_err(&client->dev, "nothing at this address\n");
+		return -ENXIO;
+	}
+
+	error = raydium_i2c_initialize(ts);
+	if (error) {
+		dev_err(&client->dev, "failed to initialize: %d\n", error);
+		return error;
+	}
+
+	ts->input = devm_input_allocate_device(&client->dev);
+	if (!ts->input) {
+		dev_err(&client->dev, "Failed to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	ts->input->name = "Raydium Touchscreen";
+	ts->input->id.bustype = BUS_I2C;
+
+	__set_bit(BTN_TOUCH, ts->input->keybit);
+	__set_bit(EV_ABS, ts->input->evbit);
+	__set_bit(EV_KEY, ts->input->evbit);
+
+	/* Single touch input params setup */
+	input_set_abs_params(ts->input, ABS_X, 0, ts->info.x_max, 0, 0);
+	input_set_abs_params(ts->input, ABS_Y, 0, ts->info.y_max, 0, 0);
+	input_set_abs_params(ts->input, ABS_PRESSURE, 0, 255, 0, 0);
+	input_abs_set_res(ts->input, ABS_X, ts->info.x_res);
+	input_abs_set_res(ts->input, ABS_Y, ts->info.y_res);
+
+	/* Multitouch input params setup */
+	error = input_mt_init_slots(ts->input, MAX_TOUCH_NUM,
+				    INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to initialize MT slots: %d\n", error);
+		return error;
+	}
+
+	input_set_abs_params(ts->input, ABS_MT_POSITION_X, 0,
+		ts->info.x_max, 0, 0);
+	input_set_abs_params(ts->input, ABS_MT_POSITION_Y,
+		0, ts->info.y_max, 0, 0);
+	input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+	input_set_abs_params(ts->input, ABS_MT_PRESSURE, 0, 255, 0, 0);
+	input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->info.x_res);
+	input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->info.y_res);
+
+	input_mt_init_slots(ts->input, MAX_TOUCH_NUM, 0);
+
+	input_set_drvdata(ts->input, ts);
+
+	error = input_register_device(ts->input);
+	if (error) {
+		dev_err(&client->dev,
+			"unable to register input device: %d\n", error);
+		return error;
+	}
+
+	error = devm_request_threaded_irq(&client->dev, client->irq,
+					  NULL, raydium_i2c_irq,
+					  irqflags | IRQF_ONESHOT,
+					  client->name, ts);
+	if (error) {
+		dev_err(&client->dev, "Failed to register interrupt\n");
+		return error;
+	}
+
+	if (!client->dev.of_node)
+		device_init_wakeup(&client->dev, true);
+
+	error = sysfs_create_group(&client->dev.kobj, &raydium_attribute_group);
+	if (error) {
+		dev_err(&client->dev, "failed to create sysfs attributes: %d\n",
+			error);
+		return error;
+	}
+
+	error = devm_add_action(&client->dev,
+				raydium_i2c_remove_sysfs_group, ts);
+	if (error) {
+		raydium_i2c_remove_sysfs_group(ts);
+		dev_err(&client->dev,
+			"Failed to add sysfs cleanup action: %d\n",
+			error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int raydium_i2c_remove(struct i2c_client *client)
+{
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	if (ts->input)
+		input_unregister_device(ts->input);
+
+	device_init_wakeup(&client->dev, false);
+
+	devm_free_irq(&client->dev, client->irq, ts);
+
+	mutex_destroy(&ts->sysfs_mutex);
+
+	devm_remove_action(&client->dev, raydium_i2c_remove_sysfs_group, ts);
+
+	devm_remove_action(&client->dev, raydium_i2c_power_off, ts);
+
+	return 0;
+}
+
+static void __maybe_unused raydium_enter_sleep(struct i2c_client *client)
+{
+	const u8 sleep_cmd[] = { 0x5A, 0xff, 0x00, 0x0f };
+	int error;
+
+	error = raydium_i2c_send(client, CMD_ENTER_SLEEP,
+			(u8 *)sleep_cmd, sizeof(sleep_cmd));
+	if (error)
+		dev_err(&client->dev,
+			"Send sleep failed: %d\n", error);
+}
+
+static int __maybe_unused raydium_i2c_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	/* Command not support in BLDR recovery mode */
+	if (ts->boot_mode != RAYDIUM_TS_MAIN)
+		return -EBUSY;
+
+	disable_irq(client->irq);
+
+	if (device_may_wakeup(dev)) {
+		raydium_enter_sleep(client);
+
+		ts->wake_irq_enabled = (enable_irq_wake(client->irq) == 0);
+	} else {
+		raydium_i2c_power_off(ts);
+	}
+
+	return 0;
+}
+
+static int __maybe_unused raydium_i2c_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	if (device_may_wakeup(dev)) {
+		if (ts->wake_irq_enabled)
+			disable_irq_wake(client->irq);
+		raydium_i2c_sw_reset(client);
+	} else {
+		raydium_i2c_power_on(ts);
+		raydium_i2c_initialize(ts);
+	}
+
+	enable_irq(client->irq);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(raydium_i2c_pm_ops,
+			 raydium_i2c_suspend, raydium_i2c_resume);
+
+static const struct i2c_device_id raydium_i2c_id[] = {
+	{ DEVICE_NAME, 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, raydium_i2c_id);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id raydium_acpi_id[] = {
+	{ "RMTS_0001", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(acpi, raydium_acpi_id);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id raydium_of_match[] = {
+	{ .compatible = "raydium,rm-ts",},
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, raydium_of_match);
+#endif
+
+static struct i2c_driver raydium_i2c_driver = {
+	.probe = raydium_i2c_probe,
+	.remove = raydium_i2c_remove,
+	.id_table = raydium_i2c_id,
+	.driver = {
+		.name = "raydium_ts",
+		.pm = &raydium_i2c_pm_ops,
+		.acpi_match_table = ACPI_PTR(raydium_acpi_id),
+		.of_match_table = of_match_ptr(raydium_of_match),
+	},
+};
+
+module_i2c_driver(raydium_i2c_driver);
+
+MODULE_AUTHOR("Raydium");
+MODULE_DESCRIPTION("Raydium I2c Touchscreen driver");
+MODULE_LICENSE("GPL");
-- 
2.1.2


^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
@ 2016-02-23  8:11 jeffrey.lin
  0 siblings, 0 replies; 42+ messages in thread
From: jeffrey.lin @ 2016-02-23  8:11 UTC (permalink / raw)
  To: dmitry.torokhov, rydberg, grant.likely, robh+dt, jeesw, bleung,
	scott.liu
  Cc: jeffrey.lin, roger.yang, KP.li, linux-kernel, linux-input, devicetree

This patch is porting Raydium I2C touch driver. Developer can enable raydium touch driver by modifying define "CONFIG_TOUCHSCREEN_RM_TS".

Signed-off-by: jeffrey.lin <jeffrey.lin@rad-ic.com>
---
 drivers/input/touchscreen/Kconfig          |  12 +
 drivers/input/touchscreen/Makefile         |   1 +
 drivers/input/touchscreen/raydium_i2c_ts.c | 973 +++++++++++++++++++++++++++++
 3 files changed, 986 insertions(+)
 create mode 100644 drivers/input/touchscreen/raydium_i2c_ts.c

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 3f3f6ee..ab28721 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -915,6 +915,18 @@ config TOUCHSCREEN_PCAP
 	  To compile this driver as a module, choose M here: the
 	  module will be called pcap_ts.
 
+config TOUCHSCREEN_RM_TS
+	tristate "Raydium I2C Touchscreen"
+	depends on I2C
+	help
+	  Say Y here if you have Raydium series I2C touchscreen,
+	  such as RM31100 , connected to your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called raydium_i2c_ts.
+
 config TOUCHSCREEN_ST1232
 	tristate "Sitronix ST1232 touchscreen controllers"
 	depends on I2C
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 4941f2d..99e08cf 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE)	+= usbtouchscreen.o
 obj-$(CONFIG_TOUCHSCREEN_PCAP)		+= pcap_ts.o
 obj-$(CONFIG_TOUCHSCREEN_PENMOUNT)	+= penmount.o
 obj-$(CONFIG_TOUCHSCREEN_PIXCIR)	+= pixcir_i2c_ts.o
+obj-$(CONFIG_TOUCHSCREEN_RM_TS)		+= raydium_i2c_ts.o
 obj-$(CONFIG_TOUCHSCREEN_S3C2410)	+= s3c2410_ts.o
 obj-$(CONFIG_TOUCHSCREEN_ST1232)	+= st1232.o
 obj-$(CONFIG_TOUCHSCREEN_STMPE)		+= stmpe-ts.o
diff --git a/drivers/input/touchscreen/raydium_i2c_ts.c b/drivers/input/touchscreen/raydium_i2c_ts.c
new file mode 100644
index 0000000..738b2fd
--- /dev/null
+++ b/drivers/input/touchscreen/raydium_i2c_ts.c
@@ -0,0 +1,973 @@
+/*
+ * Raydium touchscreen I2C driver.
+ *
+ * Copyright (C) 2012-2014, Raydium Semiconductor Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Raydium reserves the right to make changes without further notice
+ * to the materials described herein. Raydium does not assume any
+ * liability arising out of the application described herein.
+ *
+ * Contact Raydium Semiconductor Corporation at www.rad-ic.com
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include <linux/buffer_head.h>
+#include <linux/slab.h>
+#include <linux/firmware.h>
+#include <linux/input/mt.h>
+#include <linux/acpi.h>
+#include <linux/of.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <asm/unaligned.h>
+#include <linux/gpio/consumer.h>
+
+/* Device, Driver information */
+#define DEVICE_NAME	"raydium_i2c"
+
+/* Slave I2C mode*/
+#define RM_BOOT_BLDR	0x02
+#define RM_BOOT_MAIN	0x03
+
+/*I2C command */
+#define CMD_QUERY_BANK		0x2B
+#define CMD_DATA_BANK		0x4D
+#define CMD_ENTER_SLEEP		0x4E
+#define CMD_BOOT_ACK		0x0A
+#define CMD_BOOT_WRT		0x5B
+#define CMD_BOOT_CHK		0x0C
+#define CMD_BANK_SWITCH		0xAA
+
+/* Touch relative info */
+#define MAX_RETRIES		3
+#define MAX_FW_UPDATE_RETRIES	30
+#define MAX_TOUCH_NUM		10
+#define MAX_PACKET_SIZE		128
+#define BOOT_DELAY_MS	100
+
+#define RAYDIUM_FW_PAGESIZE	128
+#define RAYDIUM_POWERON_DELAY_USEC	500
+#define RAYDIUM_RESET_DELAY_MSEC	50
+
+#define ADDR_INDEX		0x03
+#define HEADER_SIZE		4
+
+enum raydium_boot_mode {
+	RAYDIUM_TS_MAIN,
+	RAYDIUM_TS_BLDR,
+};
+
+struct raydium_info {
+	u32 hw_ver;
+	u8 main_ver;
+	u8 sub_ver;
+	u16 ft_ver;
+	u8 x_num;
+	u8 y_num;
+	u16 x_max;
+	u16 y_max;
+	u8 x_res;		/* units/mm */
+	u8 y_res;		/* units/mm */
+};
+
+struct raydium_abs_info {
+	u8 state;/*1:touch, 0:no touch*/
+	u8 x_pos_lsb;
+	u8 x_pos_msb;
+	u8 y_pos_lsb;
+	u8 y_pos_msb;
+	u8 pressure;
+	u8 x_width;
+	u8 y_width;
+};
+
+struct raydium_object {
+	u32 data_bank_addr;
+	u8 pkg_size;
+};
+
+/* struct raydium_data - represents state of Raydium touchscreen device */
+struct raydium_data {
+	struct i2c_client *client;
+	struct input_dev *input;
+
+	struct regulator *vcc33;
+	struct regulator *vccio;
+	struct gpio_desc *reset_gpio;
+
+	u32 query_bank_info;
+
+	struct raydium_info info;
+	struct raydium_object obj;
+	struct raydium_abs_info finger;
+
+	enum raydium_boot_mode boot_mode;
+
+	struct mutex sysfs_mutex;
+
+	u8 cmd_resp[HEADER_SIZE];
+	struct completion cmd_done;
+
+	u8 buf[MAX_PACKET_SIZE];
+
+	bool wake_irq_enabled;
+};
+
+static int raydium_i2c_send(struct i2c_client *client,
+	u8 addr, u8 *data, size_t len)
+{
+	u8 buf[MAX_PACKET_SIZE + 1];
+	int i, tries = 0;
+
+	if (len > MAX_PACKET_SIZE)
+		return -EINVAL;
+
+	buf[0] = addr & 0xff;
+	for (i = 0; i < len; i++)
+		buf[i + 1] = *data++;
+
+	do {
+		if (i2c_master_send(client, buf, len + 1) == (len + 1))
+			return 0;
+		msleep(20);
+	} while (++tries < MAX_RETRIES);
+
+	dev_err(&client->dev, "%s: i2c send failed\n", __func__);
+
+	return -EIO;
+}
+
+static int raydium_i2c_read(struct i2c_client *client,
+	u8 addr, size_t len, void *data)
+{
+	struct i2c_msg xfer[2];
+
+	/* Write register */
+	xfer[0].addr = client->addr;
+	xfer[0].flags = 0;
+	xfer[0].len = 1;
+	xfer[0].buf = &addr;
+
+	/* Read data */
+	xfer[1].addr = client->addr;
+	xfer[1].flags = I2C_M_RD;
+	xfer[1].len = len;
+	xfer[1].buf = data;
+
+	if (i2c_transfer(client->adapter, xfer, 2) == 2)
+		return 0;
+
+	dev_err(&client->dev, "%s: i2c transfer failed\n", __func__);
+
+	return -EIO;
+}
+
+static int raydium_i2c_read_message(struct raydium_data *ts,
+	u32 addr, size_t len, void *data)
+{
+	int error;
+	u8 buf[HEADER_SIZE], ii;
+
+	for (ii = 0; ii < HEADER_SIZE; ii++)
+		buf[ii] = addr >> (3 - ii)*8;
+
+	/*set data bank*/
+	error = raydium_i2c_send(ts->client, CMD_BANK_SWITCH,
+		(u8 *)buf, HEADER_SIZE);
+	/*read potints data*/
+	if (!error)
+		error = raydium_i2c_read(ts->client,
+			buf[ADDR_INDEX],
+			len,
+			(void *)data);
+
+	return error;
+}
+
+static int raydium_i2c_send_message(struct i2c_client *client,
+	size_t len, void *data)
+{
+	int error;
+	u8 buf[HEADER_SIZE], ii;
+
+	for (ii = 0; ii < HEADER_SIZE; ii++)
+		buf[ii] = ((u8 *)data)[3 - ii];
+	/*set data bank*/
+	error = raydium_i2c_send(client, CMD_BANK_SWITCH,
+		(u8 *)buf, HEADER_SIZE);
+
+	/*send messange*/
+	if (!error)
+		error = raydium_i2c_send(client, buf[ADDR_INDEX], buf, len);
+
+	return error;
+}
+
+static int raydium_i2c_sw_reset(struct i2c_client *client)
+{
+	const u8 soft_rst_cmd[] = { 0x01, 0x04, 0x00, 0x00, 0x40};
+	int error;
+
+	error = raydium_i2c_send_message(client,
+			1, (void *)soft_rst_cmd);
+	if (error) {
+		dev_err(&client->dev, "software reset failed: %d\n", error);
+		return error;
+	}
+
+	msleep(RAYDIUM_RESET_DELAY_MSEC);
+
+	return 0;
+}
+
+static int raydium_i2c_query_ts_info(struct raydium_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	int error, retry_cnt;
+
+	for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
+		error = raydium_i2c_read(client, CMD_DATA_BANK,
+			sizeof(ts->obj), (void *)&ts->obj);
+		if (error)
+			dev_err(&client->dev, "get data bank failed: %d\n",
+			error);
+		else {
+			error = raydium_i2c_read(client, CMD_QUERY_BANK,
+				sizeof(ts->query_bank_info),
+				(void *)&ts->query_bank_info);
+			if (!error) {
+				error = raydium_i2c_read_message(ts,
+					ts->query_bank_info,
+					sizeof(ts->info),
+					(void *)&ts->info);
+			}
+		}
+		if (!error)
+			return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int raydium_i2c_fastboot(struct raydium_data *ts)
+{
+	const u8 boot_cmd[] = { 0x20, 0x06, 0x00, 0x50 };
+	u8 buf[HEADER_SIZE];
+	int error;
+
+	error = raydium_i2c_read_message(ts,
+		get_unaligned_be32(boot_cmd),
+		sizeof(boot_cmd),
+		buf);
+
+	if (error) {
+		dev_err(&ts->client->dev, "boot failed: %d\n", error);
+		return error;
+	} else if (buf[0] == RM_BOOT_BLDR) {
+		dev_dbg(&ts->client->dev, "boot in fastboot mode\n");
+		return -EINVAL;
+	}
+
+	dev_dbg(&ts->client->dev, "boot success -- 0x%x\n", ts->client->addr);
+	return 0;
+}
+
+static int raydium_i2c_initialize(struct raydium_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	int error, retry_cnt;
+	const u8 recov_packet[] = { 0x04, 0x81 };
+	u8 buf[HEADER_SIZE];
+
+	for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
+		error = raydium_i2c_fastboot(ts);
+		if (error) {
+			/* Continue initializing if it's the last try */
+			if (retry_cnt < MAX_RETRIES - 1)
+				continue;
+		}
+		/* Wait for Hello packet */
+		msleep(BOOT_DELAY_MS);
+
+		error = raydium_i2c_read(client, recov_packet[0],
+			1, (void *)buf);
+		if (error) {
+			dev_err(&client->dev,
+				"failed to read 'hello' packet: %d\n", error);
+		} else if (buf[0] == recov_packet[1]) {
+			ts->boot_mode = RAYDIUM_TS_MAIN;
+			break;
+		}
+	}
+
+	if (error)
+		ts->boot_mode = RAYDIUM_TS_BLDR;
+	else
+		raydium_i2c_query_ts_info(ts);
+
+	return error;
+}
+
+static int raydium_i2c_fw_write_page(struct i2c_client *client,
+				    const void *page)
+{
+	const u8 ack_ok[] = { 0x55, 0xAA };
+	u8 buf[2];
+	int retry;
+	int error;
+
+	for (retry = 0; retry < MAX_FW_UPDATE_RETRIES; retry++) {
+		error = raydium_i2c_send(client, CMD_BOOT_WRT,
+			(u8 *)page, RAYDIUM_FW_PAGESIZE);
+		if (error) {
+			dev_err(&client->dev,
+				"BLDR Write Page failed: %d\n", error);
+			continue;
+		}
+
+		error = raydium_i2c_read(client, CMD_BOOT_CHK,
+			sizeof(ack_ok), (void *)buf);
+		if (error) {
+			dev_err(&client->dev,
+				"BLDR Ack read failed: %d\n", error);
+			return error;
+		}
+
+		if (!memcmp(buf, ack_ok, sizeof(ack_ok)))
+			return 0;
+
+		error = -EIO;
+		dev_err(&client->dev,
+			"BLDR Get Ack Error [%02x:%02x]\n",
+			buf[0], buf[1]);
+	}
+
+	return error;
+}
+
+static int raydium_i2c_do_update_firmware(struct i2c_client *client,
+					 const struct firmware *fw,
+					 bool force)
+{
+	const u8 boot_cmd[] = { RM_BOOT_BLDR, 0x20, 0x06, 0x00, 0x50 };
+	const u8 main_cmd[] = { RM_BOOT_MAIN, 0x20, 0x06, 0x00, 0x50 };
+	const u8 boot_ack[] = { 0x55, 0xAA, 0x00, 0xFF };
+	u8 buf[HEADER_SIZE];
+	int page, n_fw_pages;
+	int error;
+
+	/* Recovery mode detection! */
+	if (!force) {
+		/* Start boot loader Procedure */
+		dev_dbg(&client->dev, "Normal BLDR procedure\n");
+		/* switch to mode */
+		error = raydium_i2c_send_message(client, 1, (void *)boot_cmd);
+		if (error)
+			dev_err(&client->dev, "failed to send boot cmd: %d\n",
+				error);
+		msleep(60);
+		raydium_i2c_sw_reset(client);
+		msleep(RAYDIUM_RESET_DELAY_MSEC);
+		error = raydium_i2c_send_message(client, 1, (void *)boot_cmd);
+	}
+
+	if (error) {
+		dev_err(&client->dev, "failed to enter fastboot mode: %d\n",
+			error);
+		return error;
+	}
+
+	msleep(20);
+
+	/* check fastboot state */
+	error = raydium_i2c_read(client, CMD_BOOT_ACK,
+		sizeof(boot_ack), (void *)buf);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to read boot ack: %d\n",
+			error);
+		return error;
+	}
+
+	if (memcmp(buf, boot_ack, sizeof(boot_ack))) {
+		dev_err(&client->dev,
+			"failed to enter fastboot: %*ph (expected %*ph)\n",
+			(int)sizeof(buf), buf, (int)sizeof(boot_ack), boot_ack);
+		return -EIO;
+	}
+
+	dev_info(&client->dev, "successfully entered fastboot mode");
+
+	n_fw_pages = fw->size / RAYDIUM_FW_PAGESIZE;
+	dev_dbg(&client->dev, "BLDR Pages = %d\n", n_fw_pages);
+
+	for (page = 0; page < n_fw_pages; page++) {
+		error = raydium_i2c_fw_write_page(client,
+					fw->data + page * RAYDIUM_FW_PAGESIZE);
+		if (error) {
+			dev_err(&client->dev,
+				"failed to write FW page %d: %d\n",
+				page, error);
+			return error;
+		}
+	}
+	error = raydium_i2c_send_message(client, 1, (void *)main_cmd);
+	msleep(20);
+	raydium_i2c_sw_reset(client);
+	dev_info(&client->dev, "firmware update completed\n");
+
+	return 0;
+}
+
+static int raydium_i2c_fw_update(struct raydium_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	const struct firmware *fw;
+	char *fw_name;
+	int error;
+
+	fw_name = kasprintf(GFP_KERNEL, "raydium_i2c_%04x.bin",
+		ts->info.hw_ver & 0xFFFF);
+	if (!fw_name)
+		return -ENOMEM;
+
+	dev_info(&client->dev, "requesting fw name = %s\n", fw_name);
+	error = request_firmware(&fw, fw_name, &client->dev);
+	kfree(fw_name);
+	if (error) {
+		dev_err(&client->dev, "failed to request firmware: %d\n",
+			error);
+		return error;
+	}
+
+	if (fw->size % RAYDIUM_FW_PAGESIZE) {
+		dev_err(&client->dev, "invalid firmware length: %zu\n",
+			fw->size);
+		error = -EINVAL;
+		goto out;
+	}
+
+	disable_irq(client->irq);
+	error = raydium_i2c_do_update_firmware(client, fw,
+					ts->boot_mode == RAYDIUM_TS_BLDR);
+	if (error) {
+		dev_err(&client->dev, "firmware update failed: %d\n", error);
+		ts->boot_mode = RAYDIUM_TS_BLDR;
+		goto out_enable_irq;
+	}
+	error = raydium_i2c_initialize(ts);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to initialize device after firmware update: %d\n",
+			error);
+		ts->boot_mode = RAYDIUM_TS_BLDR;
+		goto out_enable_irq;
+	}
+
+	ts->boot_mode = RAYDIUM_TS_MAIN;
+
+out_enable_irq:
+	enable_irq(client->irq);
+	msleep(100);
+out:
+	release_firmware(fw);
+	return error;
+}
+
+static void raydium_mt_event(struct raydium_data *ts)
+{
+	struct raydium_abs_info *data;
+	int error, i, x, y;
+	u8 f_state;
+	u8 touch_count;
+	u8 tp_info_size;
+
+	error = raydium_i2c_read_message(ts, ts->obj.data_bank_addr,
+		ts->obj.pkg_size, (void *)&ts->buf);
+
+	if (error < 0) {
+		dev_err(&ts->client->dev, "%s: failed to read data: %d\n",
+			__func__, error);
+		return;
+	}
+
+	touch_count = 0;
+	tp_info_size = sizeof(ts->finger);
+
+	for (i = 0; i < MAX_TOUCH_NUM; i++) {
+		data = (struct raydium_abs_info *)(ts->buf + i * tp_info_size);
+
+		f_state = data->state & 0x03;
+
+		input_mt_slot(ts->input, i);
+		input_mt_report_slot_state(ts->input,
+				MT_TOOL_FINGER, f_state != 0);
+
+		if (!f_state)
+			continue;
+
+			x = (data->x_pos_msb << 8) | (data->x_pos_lsb);
+			y = (data->y_pos_msb << 8) | (data->y_pos_lsb);
+
+			input_report_key(ts->input,
+				BTN_TOUCH, 1);
+			input_report_key(ts->input,
+				BTN_TOOL_FINGER, 1);
+			input_report_abs(ts->input,
+				ABS_MT_POSITION_X, x);
+			input_report_abs(ts->input,
+				ABS_MT_POSITION_Y, y);
+			input_report_abs(ts->input,
+				ABS_MT_PRESSURE, data->pressure);
+			input_report_abs(ts->input,
+				ABS_MT_TOUCH_MAJOR,
+				max(data->x_width, data->y_width));
+			input_report_abs(ts->input,
+				ABS_MT_TOUCH_MINOR,
+				min(data->x_width, data->y_width));
+			touch_count++;
+	}
+
+	input_report_key(ts->input,
+			BTN_TOUCH, touch_count > 0);
+	input_report_key(ts->input,
+			BTN_TOOL_FINGER, ts->input > 0);
+	input_mt_report_pointer_emulation(ts->input, false);
+	input_sync(ts->input);
+}
+
+static irqreturn_t raydium_i2c_irq(int irq, void *_dev)
+{
+	struct raydium_data *ts = _dev;
+
+	if (ts->boot_mode != RAYDIUM_TS_BLDR)
+		raydium_mt_event(ts);
+
+	return IRQ_HANDLED;
+}
+
+static ssize_t write_update_fw(struct device *dev,
+			       struct device_attribute *attr,
+			       const char *buf, size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
+	int error;
+
+	error = mutex_lock_interruptible(&ts->sysfs_mutex);
+	if (error)
+		return error;
+
+	error = raydium_i2c_fw_update(ts);
+	dev_dbg(dev, "firmware update result: %d\n", error);
+
+	mutex_unlock(&ts->sysfs_mutex);
+	return error ?: count;
+}
+
+static ssize_t show_boot_mode(struct device *dev,
+			     struct device_attribute *attr, char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	return sprintf(buf, "%s\n",
+		       ts->boot_mode == RAYDIUM_TS_MAIN ?
+				"Normal" : "Recovery");
+}
+
+static ssize_t raydium_fw_ver_show(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	return sprintf(buf, "Release Version %d.%d\n",
+		ts->info.main_ver,
+		ts->info.sub_ver);
+}
+
+static ssize_t raydium_hw_ver_show(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	return sprintf(buf, "Hardware version 0x%x\n",
+		ts->info.hw_ver & 0xFFFF);
+}
+static DEVICE_ATTR(fw_version, S_IRUGO, raydium_fw_ver_show, NULL);
+static DEVICE_ATTR(hw_version, S_IRUGO, raydium_hw_ver_show, NULL);
+static DEVICE_ATTR(boot_mode, S_IRUGO, show_boot_mode, NULL);
+static DEVICE_ATTR(update_fw, S_IWUSR, NULL, write_update_fw);
+
+static struct attribute *raydium_attributes[] = {
+	&dev_attr_update_fw.attr,
+	&dev_attr_boot_mode.attr,
+	&dev_attr_fw_version.attr,
+	&dev_attr_hw_version.attr,
+	NULL
+};
+
+static struct attribute_group raydium_attribute_group = {
+	.attrs = raydium_attributes,
+};
+
+static void raydium_i2c_remove_sysfs_group(void *_data)
+{
+	struct raydium_data *ts = _data;
+
+	sysfs_remove_group(&ts->client->dev.kobj, &raydium_attribute_group);
+}
+
+static int raydium_i2c_power_on(struct raydium_data *ts)
+{
+	int error;
+
+	/*
+	 * If we do not have reset gpio assume platform firmware
+	 * controls regulators and does power them on for us.
+	 */
+	if (IS_ERR_OR_NULL(ts->reset_gpio))
+		return 0;
+
+	gpiod_set_value_cansleep(ts->reset_gpio, 1);
+
+	error = regulator_enable(ts->vcc33);
+	if (error) {
+		dev_err(&ts->client->dev,
+			"failed to enable vcc33 regulator: %d\n",
+			error);
+		goto release_reset_gpio;
+	}
+
+	error = regulator_enable(ts->vccio);
+	if (error) {
+		dev_err(&ts->client->dev,
+			"failed to enable vccio regulator: %d\n",
+			error);
+		regulator_disable(ts->vcc33);
+		goto release_reset_gpio;
+	}
+
+	udelay(RAYDIUM_POWERON_DELAY_USEC);
+
+release_reset_gpio:
+	gpiod_set_value_cansleep(ts->reset_gpio, 0);
+
+	if (error)
+		return error;
+
+	msleep(RAYDIUM_RESET_DELAY_MSEC);
+
+	return 0;
+}
+
+static void raydium_i2c_power_off(void *_data)
+{
+	struct raydium_data *ts = _data;
+
+	if (!IS_ERR_OR_NULL(ts->reset_gpio)) {
+		gpiod_set_value_cansleep(ts->reset_gpio, 1);
+		regulator_disable(ts->vccio);
+		regulator_disable(ts->vcc33);
+	}
+}
+
+static int raydium_i2c_probe(struct i2c_client *client,
+			    const struct i2c_device_id *id)
+{
+	union i2c_smbus_data dummy;
+	struct raydium_data *ts;
+	unsigned long irqflags;
+	int error;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		dev_err(&client->dev,
+			"%s: i2c check functionality error\n", DEVICE_NAME);
+		return -ENXIO;
+	}
+
+	ts = devm_kzalloc(&client->dev,
+		sizeof(struct raydium_data), GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+
+	mutex_init(&ts->sysfs_mutex);
+	init_completion(&ts->cmd_done);
+
+	ts->client = client;
+	i2c_set_clientdata(client, ts);
+
+	ts->vcc33 = devm_regulator_get(&client->dev, "avdd");
+	if (IS_ERR(ts->vcc33)) {
+		error = PTR_ERR(ts->vcc33);
+		if (error != -EPROBE_DEFER)
+			dev_err(&client->dev,
+				"Failed to get 'vcc33' regulator: %d\n",
+				error);
+		return error;
+	}
+
+	ts->vccio = devm_regulator_get(&client->dev, "vdd");
+	if (IS_ERR(ts->vccio)) {
+		error = PTR_ERR(ts->vccio);
+		if (error != -EPROBE_DEFER)
+			dev_err(&client->dev,
+				"Failed to get 'vccio' regulator: %d\n",
+				error);
+		return error;
+	}
+
+
+	ts->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
+		GPIOD_OUT_LOW);
+	if (IS_ERR(ts->reset_gpio)) {
+		error = PTR_ERR(ts->reset_gpio);
+		if (error != -EPROBE_DEFER) {
+			dev_err(&client->dev,
+				"failed to get reset gpio: %d\n",
+				error);
+			return error;
+		}
+	}
+
+	error = raydium_i2c_power_on(ts);
+	if (error)
+		return error;
+
+	error = devm_add_action(&client->dev, raydium_i2c_power_off, ts);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to install power off action: %d\n", error);
+		raydium_i2c_power_off(ts);
+		return error;
+	}
+
+	/* Make sure there is something at this address */
+	if (i2c_smbus_xfer(client->adapter, client->addr, 0,
+			   I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0) {
+		dev_err(&client->dev, "nothing at this address\n");
+		return -ENXIO;
+	}
+
+	error = raydium_i2c_initialize(ts);
+	if (error) {
+		dev_err(&client->dev, "failed to initialize: %d\n", error);
+		return error;
+	}
+
+	ts->input = devm_input_allocate_device(&client->dev);
+	if (!ts->input) {
+		dev_err(&client->dev, "Failed to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	ts->input->name = "Raydium Touchscreen";
+	ts->input->id.bustype = BUS_I2C;
+
+	__set_bit(BTN_TOUCH, ts->input->keybit);
+	__set_bit(EV_ABS, ts->input->evbit);
+	__set_bit(EV_KEY, ts->input->evbit);
+
+	/* Single touch input params setup */
+	input_set_abs_params(ts->input, ABS_X, 0, ts->info.x_max, 0, 0);
+	input_set_abs_params(ts->input, ABS_Y, 0, ts->info.y_max, 0, 0);
+	input_set_abs_params(ts->input, ABS_PRESSURE, 0, 255, 0, 0);
+	input_abs_set_res(ts->input, ABS_X, ts->info.x_res);
+	input_abs_set_res(ts->input, ABS_Y, ts->info.y_res);
+
+	/* Multitouch input params setup */
+	error = input_mt_init_slots(ts->input, MAX_TOUCH_NUM,
+				    INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to initialize MT slots: %d\n", error);
+		return error;
+	}
+
+	input_set_abs_params(ts->input, ABS_MT_POSITION_X, 0,
+		ts->info.x_max, 0, 0);
+	input_set_abs_params(ts->input, ABS_MT_POSITION_Y,
+		0, ts->info.y_max, 0, 0);
+	input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+	input_set_abs_params(ts->input, ABS_MT_PRESSURE, 0, 255, 0, 0);
+	input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->info.x_res);
+	input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->info.y_res);
+
+	input_mt_init_slots(ts->input, MAX_TOUCH_NUM, 0);
+
+	input_set_drvdata(ts->input, ts);
+
+	error = input_register_device(ts->input);
+	if (error) {
+		dev_err(&client->dev,
+			"unable to register input device: %d\n", error);
+		return error;
+	}
+		dev_err(&client->dev,
+			"#Success to register input device: %d\n", error);
+
+	error = devm_request_threaded_irq(&client->dev, client->irq,
+					  NULL, raydium_i2c_irq,
+					  irqflags | IRQF_ONESHOT,
+					  client->name, ts);
+	if (error) {
+		dev_err(&client->dev, "Failed to register interrupt\n");
+		return error;
+	}
+
+	if (!client->dev.of_node)
+		device_init_wakeup(&client->dev, true);
+
+	error = sysfs_create_group(&client->dev.kobj, &raydium_attribute_group);
+	if (error) {
+		dev_err(&client->dev, "failed to create sysfs attributes: %d\n",
+			error);
+		return error;
+	}
+
+	error = devm_add_action(&client->dev,
+				raydium_i2c_remove_sysfs_group, ts);
+	if (error) {
+		raydium_i2c_remove_sysfs_group(ts);
+		dev_err(&client->dev,
+			"Failed to add sysfs cleanup action: %d\n",
+			error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int raydium_i2c_remove(struct i2c_client *client)
+{
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	if (ts->input)
+		input_unregister_device(ts->input);
+
+	device_init_wakeup(&client->dev, false);
+
+	devm_free_irq(&client->dev, client->irq, ts);
+
+	mutex_destroy(&ts->sysfs_mutex);
+
+	devm_remove_action(&client->dev, raydium_i2c_remove_sysfs_group, ts);
+
+	devm_remove_action(&client->dev, raydium_i2c_power_off, ts);
+
+	return 0;
+}
+
+static void __maybe_unused raydium_enter_sleep(struct i2c_client *client)
+{
+	const u8 sleep_cmd[] = { 0x5A, 0xff, 0x00, 0x0f };
+	int error;
+
+	error = raydium_i2c_send(client, CMD_ENTER_SLEEP,
+			(u8 *)sleep_cmd, sizeof(sleep_cmd));
+	if (error)
+		dev_err(&client->dev,
+			"Send sleep failed: %d\n", error);
+}
+
+static int __maybe_unused raydium_i2c_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	/* Command not support in BLDR recovery mode */
+	if (ts->boot_mode != RAYDIUM_TS_MAIN)
+		return -EBUSY;
+
+	disable_irq(client->irq);
+
+	if (device_may_wakeup(dev)) {
+		raydium_enter_sleep(client);
+
+		ts->wake_irq_enabled = (enable_irq_wake(client->irq) == 0);
+	} else {
+		raydium_i2c_power_off(ts);
+	}
+
+	return 0;
+}
+
+static int __maybe_unused raydium_i2c_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct raydium_data *ts = i2c_get_clientdata(client);
+
+	if (device_may_wakeup(dev)) {
+		if (ts->wake_irq_enabled)
+			disable_irq_wake(client->irq);
+		raydium_i2c_sw_reset(client);
+	} else {
+		raydium_i2c_power_on(ts);
+		raydium_i2c_initialize(ts);
+	}
+
+	enable_irq(client->irq);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(raydium_i2c_pm_ops,
+			 raydium_i2c_suspend, raydium_i2c_resume);
+
+static const struct i2c_device_id raydium_i2c_id[] = {
+	{ DEVICE_NAME, 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, raydium_i2c_id);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id raydium_acpi_id[] = {
+	{ "RMTS_0001", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(acpi, raydium_acpi_id);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id raydium_of_match[] = {
+	{ .compatible = "raydium,rm-ts",},
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, raydium_of_match);
+#endif
+
+static struct i2c_driver raydium_i2c_driver = {
+	.probe = raydium_i2c_probe,
+	.remove = raydium_i2c_remove,
+	.id_table = raydium_i2c_id,
+	.driver = {
+		.name = "raydium_ts",
+		.pm = &raydium_i2c_pm_ops,
+		.acpi_match_table = ACPI_PTR(raydium_acpi_id),
+		.of_match_table = of_match_ptr(raydium_of_match),
+	},
+};
+
+module_i2c_driver(raydium_i2c_driver);
+
+MODULE_AUTHOR("Raydium");
+MODULE_DESCRIPTION("Raydium I2c Touchscreen driver");
+MODULE_LICENSE("GPL");
-- 
2.1.2


^ permalink raw reply related	[flat|nested] 42+ messages in thread

end of thread, other threads:[~2016-05-27 16:34 UTC | newest]

Thread overview: 42+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-04-29  9:45 [PATCH] driver: input :touchscreen : add Raydium I2C touch driver jeffrey.lin
2016-05-04 22:58 ` Dmitry Torokhov
2016-05-05 10:23   ` Jeffrey Lin (林義章)
2016-05-05 10:25   ` Jeffrey Lin (林義章)
2016-05-06  8:24 ` jeffrey.lin
2016-05-11 16:04 ` jeffrey.lin
     [not found] ` <1461923113-426-1-git-send-email-jeffrey.lin-s3Ivl27awEzQT0dZR+AlfA@public.gmane.org>
2016-05-13  4:18   ` Dmitry Torokhov
2016-05-13 17:08     ` jeffrey.lin
2016-05-16 15:46     ` jeffrey.lin
     [not found]       ` <1463413611-367-1-git-send-email-jeffrey.lin-s3Ivl27awEzQT0dZR+AlfA@public.gmane.org>
2016-05-16 16:41         ` Dmitry Torokhov
2016-05-16 16:43           ` Dmitry Torokhov
2016-05-16 15:57     ` jeffrey.lin
2016-05-17 16:07     ` jeffrey.lin
     [not found]       ` <1463501223-20723-1-git-send-email-jeffrey.lin-s3Ivl27awEzQT0dZR+AlfA@public.gmane.org>
2016-05-21 18:18         ` Dmitry Torokhov
2016-05-22  9:32     ` jeffrey.lin
2016-05-22 22:37       ` Dmitry Torokhov
2016-05-22  9:35     ` jeffrey.lin
2016-05-23 14:43     ` jeffrey.lin
     [not found]       ` <1464014633-20829-1-git-send-email-jeffrey.lin-s3Ivl27awEzQT0dZR+AlfA@public.gmane.org>
2016-05-23 16:27         ` Dmitry Torokhov
2016-05-24  9:31     ` jeffrey.lin
     [not found]       ` <1464082301-11539-1-git-send-email-jeffrey.lin-s3Ivl27awEzQT0dZR+AlfA@public.gmane.org>
2016-05-27 16:34         ` Dmitry Torokhov
  -- strict thread matches above, loose matches on Subject: below --
2016-05-24  9:05 jeffrey.lin
2016-05-17 15:34 jeffrey.lin
2016-05-11 13:51 jeffrey.lin
2016-04-22 10:01 dan.huang
     [not found] ` <1461319268-362-1-git-send-email-dan.huang-s3Ivl27awEzQT0dZR+AlfA@public.gmane.org>
2016-04-22 22:50   ` Dmitry Torokhov
2016-04-25  2:48     ` yajohn lin
2016-03-25  5:21 jeffrey.lin
2016-04-11  8:24 ` Dmitry Torokhov
2016-04-11  9:57   ` Jeffrey Lin (林義章)
2016-04-14  9:28   ` Jeffrey Lin (林義章)
2016-03-22  6:23 jeffrey.lin
2016-03-15  8:44 jeffrey.lin
2016-03-18 21:04 ` Rob Herring
2016-03-03  6:42 jeffrey.lin
2016-03-10 18:47 ` Dmitry Torokhov
2016-03-11  2:10   ` Jeffrey Lin (林義章)
2016-03-03  2:29 jeffrey.lin
2016-03-03  2:44 ` Joe Perches
2016-03-03  3:14   ` Jeffrey Lin (林義章)
2016-03-03  3:55     ` Joe Perches
2016-02-23  8:11 jeffrey.lin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).