linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [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; 58+ 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] 58+ messages in thread

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2016-03-03  6:42 [PATCH] driver: input :touchscreen : add Raydium I2C touch driver jeffrey.lin
@ 2016-03-10 18:47 ` Dmitry Torokhov
  2016-03-11  2:10   ` Jeffrey Lin (林義章)
  0 siblings, 1 reply; 58+ 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] 58+ 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; 58+ 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] 58+ messages in thread

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2016-05-24  9:31   ` jeffrey.lin
@ 2016-05-27 16:34     ` Dmitry Torokhov
  0 siblings, 0 replies; 58+ messages in thread
From: Dmitry Torokhov @ 2016-05-27 16:34 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 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

^ permalink raw reply	[flat|nested] 58+ 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
  2016-05-27 16:34     ` Dmitry Torokhov
  7 siblings, 1 reply; 58+ 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] 58+ messages in thread

* [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
@ 2016-05-24  9:05 jeffrey.lin
  0 siblings, 0 replies; 58+ 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] 58+ messages in thread

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2016-05-23 14:43   ` jeffrey.lin
@ 2016-05-23 16:27     ` Dmitry Torokhov
  0 siblings, 0 replies; 58+ messages in thread
From: Dmitry Torokhov @ 2016-05-23 16:27 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 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

^ permalink raw reply	[flat|nested] 58+ 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
  2016-05-23 16:27     ` Dmitry Torokhov
  2016-05-24  9:31   ` jeffrey.lin
  7 siblings, 1 reply; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ messages in thread

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2016-05-17 16:07   ` jeffrey.lin
@ 2016-05-21 18:18     ` Dmitry Torokhov
  0 siblings, 0 replies; 58+ messages in thread
From: Dmitry Torokhov @ 2016-05-21 18:18 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 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

^ permalink raw reply	[flat|nested] 58+ 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
  2016-05-21 18:18     ` Dmitry Torokhov
  2016-05-22  9:32   ` jeffrey.lin
                     ` (3 subsequent siblings)
  7 siblings, 1 reply; 58+ 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] 58+ messages in thread

* [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
@ 2016-05-17 15:34 jeffrey.lin
  0 siblings, 0 replies; 58+ 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] 58+ 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; 58+ 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] 58+ messages in thread

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2016-05-16 15:46   ` jeffrey.lin
@ 2016-05-16 16:41     ` Dmitry Torokhov
  2016-05-16 16:43       ` Dmitry Torokhov
  0 siblings, 1 reply; 58+ messages in thread
From: Dmitry Torokhov @ 2016-05-16 16:41 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 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

^ permalink raw reply	[flat|nested] 58+ 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; 58+ 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] 58+ 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 16:41     ` Dmitry Torokhov
  2016-05-16 15:57   ` jeffrey.lin
                     ` (5 subsequent siblings)
  7 siblings, 1 reply; 58+ messages in thread
From: jeffrey.lin @ 2016-05-16 15:46 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 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);
>}

^ permalink raw reply	[flat|nested] 58+ 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; 58+ 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] 58+ messages in thread

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2016-04-29  9:45 jeffrey.lin
                   ` (2 preceding siblings ...)
  2016-05-11 16:04 ` jeffrey.lin
@ 2016-05-13  4:18 ` Dmitry Torokhov
  2016-05-13 17:08   ` jeffrey.lin
                     ` (7 more replies)
  3 siblings, 8 replies; 58+ messages in thread
From: Dmitry Torokhov @ 2016-05-13  4:18 UTC (permalink / raw)
  To: jeffrey.lin
  Cc: 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.
> 
> Signed-off-by: jeffrey.lin <jeffrey.lin@rad-ic.com>

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@gmail.com>

Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
 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");

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

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2016-04-29  9:45 jeffrey.lin
  2016-05-04 22:58 ` Dmitry Torokhov
  2016-05-06  8:24 ` jeffrey.lin
@ 2016-05-11 16:04 ` jeffrey.lin
  2016-05-13  4:18 ` Dmitry Torokhov
  3 siblings, 0 replies; 58+ 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] 58+ messages in thread

* [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
@ 2016-05-11 13:51 jeffrey.lin
  0 siblings, 0 replies; 58+ 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] 58+ messages in thread

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2016-04-29  9:45 jeffrey.lin
  2016-05-04 22:58 ` Dmitry Torokhov
@ 2016-05-06  8:24 ` jeffrey.lin
  2016-05-11 16:04 ` jeffrey.lin
  2016-05-13  4:18 ` Dmitry Torokhov
  3 siblings, 0 replies; 58+ 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] 58+ messages in thread

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2016-04-29  9:45 jeffrey.lin
@ 2016-05-04 22:58 ` Dmitry Torokhov
  2016-05-06  8:24 ` jeffrey.lin
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 58+ 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] 58+ messages in thread

* [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; 58+ 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] 58+ messages in thread

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

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

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

* [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
@ 2016-04-22 10:01 dan.huang
  2016-04-22 22:50 ` Dmitry Torokhov
  0 siblings, 1 reply; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ messages in thread

* [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
@ 2016-03-22  6:23 jeffrey.lin
  0 siblings, 0 replies; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ messages in thread

* [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
@ 2016-02-23  8:11 jeffrey.lin
  0 siblings, 0 replies; 58+ 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] 58+ messages in thread

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2016-01-21  4:02 Jeffrey Lin
@ 2016-01-21  9:37 ` Dmitry Torokhov
  0 siblings, 0 replies; 58+ messages in thread
From: Dmitry Torokhov @ 2016-01-21  9:37 UTC (permalink / raw)
  To: Jeffrey Lin
  Cc: rydberg, dianders, scott.liu, jeffrey.lin, roger.yang, KP.li,
	linux-kernel, linux-input

Hi Jeffrey,

On Thu, Jan 21, 2016 at 12:02:58PM +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".

Thank you for making changes, please see my comments below.

> 
> Signed-off-by: jeffrey.lin <jeffrey.lin@rad-ic.com>
> ---
>  drivers/input/touchscreen/Kconfig      |  12 +
>  drivers/input/touchscreen/Makefile     |   1 +
>  drivers/input/touchscreen/rm31100_ts.c | 747 +++++++++++++++++++++++++++++++++
>  3 files changed, 760 insertions(+)
>  create mode 100644 drivers/input/touchscreen/rm31100_ts.c
> 
> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> index c0659eb..ea7e259 100644
> --- a/drivers/input/touchscreen/Kconfig
> +++ b/drivers/input/touchscreen/Kconfig
> @@ -974,4 +974,16 @@ config TOUCHSCREEN_ZFORCE
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called zforce_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 rm31100_ts.
> +
>  endif
> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
> index 944e5b3..1a11974 100644
> --- a/drivers/input/touchscreen/Makefile
> +++ b/drivers/input/touchscreen/Makefile
> @@ -80,3 +80,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE)	+= zylonite-wm97xx.o
>  obj-$(CONFIG_TOUCHSCREEN_W90X900)	+= w90p910_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_TPS6507X)	+= tps6507x-ts.o
>  obj-$(CONFIG_TOUCHSCREEN_ZFORCE)	+= zforce_ts.o
> +obj-$(CONFIG_TOUCHSCREEN_RM_TS)		+= rm31100_ts.o


Please add new entries to makefile in alphabetical order.

> diff --git a/drivers/input/touchscreen/rm31100_ts.c b/drivers/input/touchscreen/rm31100_ts.c
> new file mode 100644
> index 0000000..b1b8087
> --- /dev/null
> +++ b/drivers/input/touchscreen/rm31100_ts.c
> @@ -0,0 +1,747 @@
> +/*
> + * Raydium RM31100_ts touchscreen 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/async.h>

You are not using this anymore.

> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/i2c.h>
> +#include <linux/input.h>
> +#include <linux/slab.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/mutex.h>
> +#include <linux/delay.h>
> +#include <linux/uaccess.h>
> +#include <asm/unaligned.h>
> +#include <linux/input/mt.h>
> +#include <linux/regulator/consumer.h>
> +
> +#define rm31100	0x0
> +#define rm3110x	0x1
> +
> +#define INVALID_DATA	0xff
> +#define MAX_REPORT_TOUCHED_POINTS	10
> +
> +#define I2C_CLIENT_ADDR         0x39
> +#define I2C_DMA_CLIENT_ADDR     0x5A
> +
> +struct rm31100_ts_data {
> +	u8 x_index;
> +	u8 y_index;
> +	u8 z_index;
> +	u8 id_index;
> +	u8 touch_index;
> +	u8 data_reg;
> +	u8 status_reg;
> +	u8 data_size;
> +	u8 touch_bytes;
> +	u8 update_data;
> +	u8 touch_meta_data;
> +	u8 finger_size;
> +};
> +
> +struct rm3110x_ts_platform_data {
> +	u32 dis_min_x; /* display resoltion ABS min*/
> +	u32 dis_max_x;/* display resoltion ABS max*/
> +	u32 dis_min_y;
> +	u32 dis_max_y;
> +	u32 res_x; /* TS resolution unit*/
> +	u32 res_y;
> +	u32 swap_xy;
> +	u8 nfingers;

I believe I already asked, why do we have number of fingers in platform
data?

> +	bool wakeup;
> +};
> +
> +static struct rm31100_ts_data devices[] = {
> +	[0] = {
> +		.x_index = 2,
> +		.y_index = 4,
> +		.z_index = 6,
> +		.id_index = 1,
> +		.data_reg = 0x1,
> +		.status_reg = 0,
> +		.update_data = 0x0,
> +		.touch_bytes = 6,
> +		.touch_meta_data = 1,
> +		.finger_size = 70,
> +	},
> +	[1] = {
> +		.x_index = 2,
> +		.y_index = 4,
> +		.z_index = 6,
> +		.id_index = 1,
> +		.data_reg = 0x0,
> +		.status_reg = 0,
> +		.update_data = 0x0,
> +		.touch_bytes = 6,
> +		.touch_meta_data = 1,
> +		.finger_size = 70,
> +	},

Please attach the instances of this structure as driver_data to the
platform ids instead of using indices.

> +};
> +
> +struct rm31100_ts {
> +	struct i2c_client *client;
> +	struct input_dev *input;
> +	struct regulator *dvdd;
> +	struct regulator *avdd;
> +	struct gpio_desc *resout_gpio;
> +	struct rm3110x_ts_platform_data *pdata;
> +	struct rm31100_ts_data *dd;
> +	u8 *touch_data;
> +	u8 device_id;
> +	bool is_suspended;

I do not understand the use of this member.

> +	struct mutex access_lock;

You do not seem to be using this.

> +	u32 pen_irq;
> +	u8 fw_version;
> +	u8 u8_sub_version;
> +};
> +
> +static int rm31100_ts_write_reg_u8(struct i2c_client *client, u8 reg, u8 val)
> +{
> +	int data;
> +
> +	data = i2c_smbus_write_byte_data(client, reg, val);
> +	if (data < 0)
> +		dev_err(&client->dev, "error %d in writing reg 0x%x\n",
> +						 data, reg);
> +
> +	return data;
> +}
> +
> +static int rm31100_ts_read_reg_u8(struct i2c_client *client, u8 reg)
> +{
> +	int data;
> +
> +	data = i2c_smbus_read_byte_data(client, reg);
> +	if (data < 0)
> +		dev_err(&client->dev, "error %d in reading reg 0x%x\n",
> +						 data, reg);
> +
> +	return data;
> +}
> +
> +static int rm31100_ts_read(struct i2c_client *client, u8 reg, u8 *buf, int num)
> +{
> +	struct i2c_msg xfer_msg[2];
> +
> +	xfer_msg[0].addr = client->addr;
> +	xfer_msg[0].len = 1;
> +	xfer_msg[0].flags = 0;
> +	xfer_msg[0].buf = &reg;
> +
> +	xfer_msg[1].addr = client->addr;
> +	xfer_msg[1].len = num;
> +	xfer_msg[1].flags = I2C_M_RD;
> +	xfer_msg[1].buf = buf;
> +
> +	return i2c_transfer(client->adapter, xfer_msg, num);
> +}
> +
> +static int rm31100_ts_write(struct i2c_client *client, u8 *buf, int num)
> +{
> +	struct i2c_msg xfer_msg[1];
> +
> +	xfer_msg[0].addr = client->addr;
> +	xfer_msg[0].len = num;
> +	xfer_msg[0].flags = 0;
> +	xfer_msg[0].buf = buf;
> +
> +	return i2c_transfer(client->adapter, xfer_msg, num);
> +}
> +
> +static ssize_t rm_fw_version_show(struct device *dev,
> +				      struct device_attribute *attr,
> +				      char *buf)
> +{
> +	struct rm31100_ts *info = dev_get_drvdata(dev);
> +
> +	return sprintf(buf, "Release Version %d.%0d\n",
> +		info->fw_version,
> +		info->u8_sub_version);
> +}
> +
> +static DEVICE_ATTR(fw_version, S_IRUGO, rm_fw_version_show, NULL);
> +
> +static struct attribute *rm_ts_attributes[] = {
> +	&dev_attr_fw_version.attr,
> +	NULL
> +};
> +
> +static const struct attribute_group rm_ts_attr_group = {
> +	.attrs = rm_ts_attributes,
> +};
> +
> +static void report_data(struct rm31100_ts *dev, u16 x, u16 y,
> +	u8 pressure, u8 id)
> +{
> +	struct input_dev *input_dev = dev->input;
> +
> +	if (dev->pdata->swap_xy)
> +		swap(x, y);
> +
> +	input_mt_slot(input_dev, id);
> +	input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, true);
> +	input_report_abs(input_dev, ABS_MT_POSITION_X, x);
> +	input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
> +	input_report_abs(input_dev, ABS_MT_PRESSURE, pressure);
> +	input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, dev->dd->finger_size);
> +}
> +
> +static void process_rm31100_data(struct rm31100_ts *ts)
> +{
> +	u8 id, pressure, touches, i;
> +	u16 x, y;
> +
> +	touches = ts->touch_data[ts->dd->touch_index];
> +
> +	if (touches > 0) {
> +		for (i = 0; i < touches; i++) {
> +			id = ts->touch_data[i * ts->dd->touch_bytes +
> +				ts->dd->id_index];
> +			pressure = ts->touch_data[i * ts->dd->touch_bytes +
> +				ts->dd->z_index];
> +			x = get_unaligned_le16(&(ts->touch_data[i *
> +				ts->dd->touch_bytes + ts->dd->x_index]));
> +			y = get_unaligned_le16(&(ts->touch_data[i *
> +				ts->dd->touch_bytes + ts->dd->y_index]));
> +			report_data(ts, x, y, pressure, id);
> +		}
> +	} else
> +		input_mt_sync(ts->input);
> +
> +	input_mt_report_pointer_emulation(ts->input, true);
> +	input_sync(ts->input);
> +}
> +
> +static int rm31100_ts_xy_worker(struct rm31100_ts *work)
> +{
> +	int rc;
> +	struct rm31100_ts *ts = work;
> +
> +	/* read data from DATA_REG */
> +	rc = rm31100_ts_read(ts->client, ts->dd->data_reg, ts->touch_data,
> +	ts->dd->data_size);
> +
> +	if (rc < 0) {
> +		dev_err(&ts->client->dev, "read failed\n");
> +		goto schedule;
> +	}
> +
> +	if (ts->touch_data[ts->dd->touch_index] == INVALID_DATA)
> +		goto schedule;
> +
> +	/* write to STATUS_REG to release lock */
> +	rc = rm31100_ts_write_reg_u8(ts->client,
> +		ts->dd->status_reg, ts->dd->update_data);
> +	if (rc < 0) {
> +		dev_err(&ts->client->dev, "write failed, try once more\n");
> +
> +		rc = rm31100_ts_write_reg_u8(ts->client,
> +			ts->dd->status_reg, ts->dd->update_data);
> +		if (rc < 0)
> +			dev_err(&ts->client->dev, "write failed, exiting\n");
> +	}
> +
> +	process_rm31100_data(ts);
> +schedule:
> +	return rc;
> +}
> +
> +static irqreturn_t rm31100_ts_irq(int irq, void *dev_id)
> +{
> +	struct rm31100_ts *ts = dev_id;
> +	struct device *dev = &ts->client->dev;
> +	int rc;
> +
> +	rc = rm31100_ts_xy_worker(ts);
> +	if (rc < 0) {
> +		dev_err(dev, "Handling message fails in IRQ, %d.\n",
> +			rc);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int rm_ts_power_on(struct rm31100_ts *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->resout_gpio))
> +		return 0;
> +
> +	gpiod_set_value_cansleep(ts->resout_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->dvdd);
> +	if (error) {
> +		dev_err(&ts->client->dev,
> +			"failed to enable dvdd regulator: %d\n",
> +			error);
> +		regulator_disable(ts->dvdd);
> +		goto release_reset_gpio;
> +	}
> +
> +release_reset_gpio:
> +	gpiod_set_value_cansleep(ts->resout_gpio, 0);
> +	if (error)
> +		return error;
> +
> +	msleep(20);
> +
> +	return 0;
> +}
> +
> +static void rm_ts_power_off(void *_data)
> +{
> +	struct rm31100_ts *data = _data;
> +
> +	if (!IS_ERR_OR_NULL(data->resout_gpio)) {
> +		/*
> +		 * Activate reset gpio to prevent leakage through the
> +		 * pin once we shut off power to the controller.
> +		 */
> +		gpiod_set_value_cansleep(data->resout_gpio, 1);
> +		regulator_disable(data->avdd);
> +		regulator_disable(data->dvdd);
> +	}
> +}
> +
> +static int rm31100_ts_init_ts(struct i2c_client *client, struct rm31100_ts *ts)
> +{
> +	/*struct input_dev *input_device;*/
> +	/*int rc = 0;*/
> +
> +	ts->dd = &devices[ts->device_id];
> +
> +	if (!ts->pdata->nfingers) {
> +		dev_err(&client->dev, "Touches information not specified\n");
> +		return -EINVAL;
> +	}
> +
> +	if (ts->device_id == rm3110x) {
> +		if (ts->pdata->nfingers > 2) {
> +			dev_err(&client->dev, "Touches >=1 & <= 2\n");
> +			return -EINVAL;
> +		}
> +		ts->dd->data_size = ts->dd->touch_bytes;
> +		ts->dd->touch_index = 0x0;
> +	} else if (ts->device_id == rm31100) {
> +		if (ts->pdata->nfingers > 10) {
> +			dev_err(&client->dev, "Touches >=1 & <= 10\n");
> +			return -EINVAL;
> +		}
> +		ts->dd->data_size = ts->pdata->nfingers * ts->dd->touch_bytes +
> +						ts->dd->touch_meta_data;
> +		ts->dd->touch_index = 0x0;
> +	}
> +	/* w001 */
> +	else {
> +		ts->dd->data_size = ts->pdata->nfingers * ts->dd->touch_bytes +
> +		ts->dd->touch_meta_data;
> +		ts->dd->touch_index = 0x0;
> +	}
> +
> +	ts->touch_data = kzalloc(ts->dd->data_size, GFP_KERNEL);
> +	if (!ts->touch_data)
> +		return -ENOMEM;
> +
> +	return 0;
> +}
> +
> +static void __maybe_unused rm_enter_sleep(struct rm31100_ts *data)
> +{
> +	const u8 sleep_cmd[] = { 0x5A, 0xff, 0x00, 0x0f };
> +	int rc;
> +
> +	rc = rm31100_ts_write(data->client, (u8 *)sleep_cmd,
> +		ARRAY_SIZE(sleep_cmd));
> +	if (rc)
> +		dev_err(&data->client->dev,
> +			"Send sleep failed: %d\n", rc);
> +}
> +
> +static int __maybe_unused rm31100_ts_suspend(struct device *dev)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +	struct rm31100_ts *ts = i2c_get_clientdata(client);
> +
> +	disable_irq(ts->pen_irq);
> +
> +	if (device_may_wakeup(dev)) {
> +		rm_enter_sleep(ts);
> +		/* mark suspend flag */
> +		ts->is_suspended = (enable_irq_wake(ts->pen_irq) == 0);
> +	} else
> +		rm_ts_power_off(ts);
> +
> +	return 0;
> +}
> +
> +static int __maybe_unused rm31100_ts_resume(struct device *dev)
> +{
> +	struct rm31100_ts *ts = dev_get_drvdata(dev);
> +
> +	int rc = 0;
> +
> +	if (device_may_wakeup(dev)) {
> +		disable_irq_wake(ts->pen_irq);
> +
> +		ts->is_suspended = false;
> +	} else {
> +		rc = rm_ts_power_on(ts);
> +		if (rc) {
> +			dev_err(dev, "unable to resume\n");
> +			return rc;
> +		}
> +
> +		enable_irq(ts->pen_irq);
> +
> +		/* Clear the status register of the TS controller */
> +		rc = rm31100_ts_write_reg_u8(ts->client,
> +			ts->dd->status_reg, ts->dd->update_data);
> +		if (rc < 0) {
> +			dev_err(&ts->client->dev,
> +				"write failed, try once more\n");
> +
> +			rc = rm31100_ts_write_reg_u8(ts->client,
> +				ts->dd->status_reg,
> +				ts->dd->update_data);
> +			if (rc < 0)
> +				dev_err(&ts->client->dev,
> +					"write failed, exiting\n");
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static void rm_ts_remove_sysfs_group(void *_data)
> +{
> +	struct rm31100_ts *ts = _data;
> +
> +	sysfs_remove_group(&ts->client->dev.kobj, &rm_ts_attr_group);
> +}
> +
> +static SIMPLE_DEV_PM_OPS(rm31100_ts_pm_ops,
> +			 rm31100_ts_suspend, rm31100_ts_resume);
> +
> +static int rm_input_dev_create(struct rm31100_ts *ts)
> +{
> +	struct input_dev *input_device;
> +	int rc = 0;
> +
> +	input_device = input_allocate_device();

devm_input_allocate_device().

> +	if (!input_device) {
> +		rc = -ENOMEM;
> +		goto error_alloc_dev;
> +	}
> +	ts->input = input_device;
> +
> +	input_device->name = "raydium_ts";
> +	input_device->id.bustype = BUS_I2C;
> +	input_device->dev.parent = &ts->client->dev;
> +	input_set_drvdata(input_device, ts);
> +
> +	__set_bit(BTN_TOUCH, input_device->keybit);
> +	__set_bit(EV_ABS, input_device->evbit);
> +	__set_bit(EV_KEY, input_device->evbit);
> +
> +	/* For single touch */
> +	input_set_abs_params(input_device, ABS_X,
> +			     ts->pdata->dis_min_x, ts->pdata->dis_max_x, 0, 0);
> +	input_set_abs_params(input_device, ABS_Y,
> +			     ts->pdata->dis_min_x, ts->pdata->dis_max_y, 0, 0);
> +	input_set_abs_params(input_device, ABS_PRESSURE,
> +			     0, 255, 0, 0);
> +	input_abs_set_res(input_device, ABS_X, ts->pdata->res_x);
> +	input_abs_set_res(input_device, ABS_Y, ts->pdata->res_y);
> +
> +	/* Multitouch input params setup */
> +	rc = input_mt_init_slots(input_device,
> +		MAX_REPORT_TOUCHED_POINTS,
> +		INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
> +	if (rc)
> +		goto error_unreg_device;
> +
> +	input_set_abs_params(input_device, ABS_MT_POSITION_X,
> +		ts->pdata->dis_min_x, ts->pdata->dis_max_x, 0, 0);
> +	input_set_abs_params(input_device, ABS_MT_POSITION_Y,
> +		ts->pdata->dis_min_y, ts->pdata->dis_max_y, 0, 0);
> +	input_set_abs_params(input_device, ABS_MT_PRESSURE,
> +		0, 0xFF, 0, 0);
> +	input_set_abs_params(input_device, ABS_MT_TOUCH_MAJOR,
> +		0, 0xFF, 0, 0);
> +	input_abs_set_res(input_device, ABS_MT_POSITION_X, ts->pdata->res_x);
> +	input_abs_set_res(input_device, ABS_MT_POSITION_Y, ts->pdata->res_y);
> +
> +	rc = input_register_device(input_device);
> +	if (rc)
> +		goto error_unreg_device;
> +
> +	return 0;
> +
> +error_unreg_device:
> +	input_free_device(input_device);
> +error_alloc_dev:
> +	ts->input = NULL;
> +	return rc;
> +}
> +
> +static int rm31100_initialize(struct i2c_client *client)
> +{
> +	struct rm31100_ts *ts = i2c_get_clientdata(client);
> +	int rc = 0, temp_reg;
> +
> +	/* read one byte to make sure i2c device exists */
> +	if (ts->device_id == rm3110x)
> +		temp_reg = 0x01;
> +	else if (ts->device_id == rm31100)
> +		temp_reg = 0x00;
> +	else
> +		temp_reg = 0x05;
> +
> +	rc = rm31100_ts_read_reg_u8(client, temp_reg);
> +	if (rc < 0) {
> +		dev_err(&client->dev, "i2c sanity check failed\n");
> +		return rc;
> +	}
> +
> +	rc = rm31100_ts_init_ts(client, ts);
> +	if (rc < 0) {
> +		dev_err(&client->dev, "rm31100_ts init failed\n");
> +		return rc;
> +	}
> +
> +	return 0;
> +}
> +
> +static int rm31100_ts_probe(struct i2c_client *client,
> +			const struct i2c_device_id *id)
> +{
> +	struct rm31100_ts *ts;
> +	struct rm3110x_ts_platform_data *pdata = client->dev.platform_data;
> +	int rc;

Please call this variable "error".

> +	union i2c_smbus_data dummy;
> +
> +	ts = devm_kzalloc(&client->dev, sizeof(struct rm31100_ts), GFP_KERNEL);
> +

Stray empty line.

> +	if (!ts)
> +		return -ENOMEM;
> +
> +	if (!i2c_check_functionality(client->adapter,
> +		I2C_FUNC_SMBUS_READ_WORD_DATA)) {
> +		dev_err(&client->dev, "I2C functionality not supported\n");
> +		rc = -EIO;
> +		goto error_touch_data_alloc;
> +	}
> +
> +	ts->client = client;
> +	ts->pdata = pdata;
> +	i2c_set_clientdata(client, ts);
> +	ts->device_id = id->driver_data;
> +
> +	ts->is_suspended = false;
> +
> +	mutex_init(&ts->access_lock);
> +
> +	ts->avdd = devm_regulator_get(&client->dev, "avdd");
> +	if (IS_ERR(ts->avdd)) {
> +		rc = PTR_ERR(ts->avdd);
> +		if (rc != -EPROBE_DEFER)
> +			dev_err(&client->dev,
> +				"Failed to get 'avdd' regulator: %d\n",
> +				rc);
> +		return rc;
> +	}
> +
> +	ts->dvdd = devm_regulator_get(&client->dev, "dvdd");
> +	if (IS_ERR(ts->dvdd)) {
> +		rc = PTR_ERR(ts->dvdd);
> +		if (rc != -EPROBE_DEFER)
> +			dev_err(&client->dev,
> +				"Failed to get 'dvdd' regulator: %d\n",
> +				rc);
> +		return rc;
> +	}

I took a peek at the data sheet and it looks like the chip uses 3 power
supplies, and they have different names from avdd/dvdd. We should be
using the names form the data sheet.

> +
> +	ts->resout_gpio = devm_gpiod_get_optional(&client->dev,
> +		"ts_reset", 0);
> +
> +	if (IS_ERR(ts->resout_gpio)) {
> +		rc = PTR_ERR(ts->resout_gpio);
> +		/*
> +		 * On Chromebooks vendors like to source touch panels from
> +		 * different vendors, but they are connected to the same
> +		 * regulators/GPIO pin. The drivers also use asynchronous
> +		 * probing, which means that more than one driver will
> +		 * attempt to claim the reset line. If we find it busy,
> +		 * let's try again later.
> +		 */
> +		if (rc == -EBUSY) {
> +			dev_info(&client->dev,
> +				 "reset gpio is busy, deferring probe\n");
> +			return -EPROBE_DEFER;
> +		}

As I said in my other email this is Chrome OS specific and doe snot
belong in mainline. Simply return error here, do not try to convert busy
into deferral.

> +
> +		if (rc == -EPROBE_DEFER)
> +			return -EPROBE_DEFER;
> +
> +		if (rc != -ENOENT && rc != -ENOSYS) {
> +			dev_err(&client->dev,
> +				"failed to get reset gpio: %d\n",
> +				rc);
> +			return rc;
> +		}
> +	}
> +
> +	rc = rm_ts_power_on(ts);
> +	if (rc)
> +		return rc;
> +
> +	rc = devm_add_action(&client->dev, rm_ts_power_off, ts);
> +	if (rc) {
> +		dev_err(&client->dev,
> +			"failed to install power off action: %d\n", rc);
> +		rm_ts_power_off(ts);
> +		return rc;
> +	}
> +
> +	/* 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)
> +		return -ENODEV;
> +
> +	rc = rm31100_initialize(client);
> +	if (rc < 0) {
> +		dev_err(&client->dev, "probe failed! unbind device.\n");
> +		return rc;
> +	}
> +
> +	rc = rm_input_dev_create(ts);
> +	if (rc) {
> +		dev_err(&client->dev, "%s crated failed, %d\n", __func__, rc);
> +		return rc;
> +	}
> +
> +	rc = devm_request_threaded_irq(&client->dev, ts->pen_irq,
> +					NULL, rm31100_ts_irq,
> +					IRQF_TRIGGER_RISING | IRQF_ONESHOT,
> +					client->name, ts);

Where is ts->pen_irq assignment? I do not believe you tested this version
of the driver.

> +	if (rc) {
> +		dev_err(&client->dev, "Failed to register interrupt\n");
> +		return rc;
> +	}
> +
> +	rc = sysfs_create_group(&client->dev.kobj, &rm_ts_attr_group);
> +	if (rc) {
> +		dev_err(&client->dev, "failed to create sysfs attributes: %d\n",
> +			rc);
> +		return rc;
> +	}
> +
> +	rc = devm_add_action(&client->dev,
> +				rm_ts_remove_sysfs_group, ts);
> +	if (rc) {
> +		rm_ts_remove_sysfs_group(ts);
> +		dev_err(&client->dev,
> +			"Failed to add sysfs cleanup action: %d\n",
> +			rc);
> +		return rc;
> +	}
> +	return 0;
> +
> +error_touch_data_alloc:
> +	return rc;
> +}
> +
> +static int rm31100_ts_remove(struct i2c_client *client)
> +{
> +	struct rm31100_ts *ts = i2c_get_clientdata(client);
> +
> +	device_init_wakeup(&client->dev, 0);
> +
> +	devm_free_irq(&client->dev, ts->pen_irq, ts);
> +
> +	input_unregister_device(ts->input);
> +
> +	mutex_destroy(&ts->access_lock);
> +
> +	rm_ts_power_off(ts);

You installed power of ass devm action, why do you also callit manually.

> +
> +	kfree(ts->touch_data);
> +	kfree(ts);

This was allocated by devm, you can't release with kfree.

> +
> +	return 0;
> +}
> +
> +static const struct i2c_device_id rm31100_ts_id[] = {
> +	{"RM31100", 0},
> +	{"RM3110x", 1},
> +	{}
> +};
> +MODULE_DEVICE_TABLE(i2c, rm31100_ts_id);
> +
> +
> +static struct i2c_driver rm31100_ts_driver = {
> +	.probe = rm31100_ts_probe,
> +	.remove = rm31100_ts_remove,
> +	.id_table = rm31100_ts_id,
> +	.driver = {
> +		.name = "raydium_ts",
> +	},
> +};
> +
> +static int __init rm31100_ts_init(void)
> +{
> +	int rc;
> +
> +	rc = i2c_add_driver(&rm31100_ts_driver);
> +
> +	return rc;
> +}
> +/* Making this as late init to avoid power fluctuations
> + * during LCD initialization.
> + */
> +module_init(rm31100_ts_init);
> +
> +static void __exit rm31100_ts_exit(void)
> +{
> +	i2c_del_driver(&rm31100_ts_driver);
> +}
> +module_exit(rm31100_ts_exit);

module_i2c_driver() please instead of boilerplate above.

> +
> +MODULE_LICENSE("GPL");

"GPL v2"

> +MODULE_DESCRIPTION("rm31100-rm3110x touchscreen controller driver");
> +MODULE_AUTHOR("Raydium");
> +MODULE_ALIAS("platform:rm31100_ts");
> -- 
> 2.1.2
> 

Thanks.

-- 
Dmitry

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

* [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
@ 2016-01-21  4:02 Jeffrey Lin
  2016-01-21  9:37 ` Dmitry Torokhov
  0 siblings, 1 reply; 58+ messages in thread
From: Jeffrey Lin @ 2016-01-21  4:02 UTC (permalink / raw)
  To: dmitry.torokhov, rydberg, dianders, scott.liu
  Cc: jeffrey.lin, roger.yang, KP.li, linux-kernel, linux-input

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/rm31100_ts.c | 747 +++++++++++++++++++++++++++++++++
 3 files changed, 760 insertions(+)
 create mode 100644 drivers/input/touchscreen/rm31100_ts.c

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index c0659eb..ea7e259 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -974,4 +974,16 @@ config TOUCHSCREEN_ZFORCE
 	  To compile this driver as a module, choose M here: the
 	  module will be called zforce_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 rm31100_ts.
+
 endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 944e5b3..1a11974 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -80,3 +80,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE)	+= zylonite-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_W90X900)	+= w90p910_ts.o
 obj-$(CONFIG_TOUCHSCREEN_TPS6507X)	+= tps6507x-ts.o
 obj-$(CONFIG_TOUCHSCREEN_ZFORCE)	+= zforce_ts.o
+obj-$(CONFIG_TOUCHSCREEN_RM_TS)		+= rm31100_ts.o
diff --git a/drivers/input/touchscreen/rm31100_ts.c b/drivers/input/touchscreen/rm31100_ts.c
new file mode 100644
index 0000000..b1b8087
--- /dev/null
+++ b/drivers/input/touchscreen/rm31100_ts.c
@@ -0,0 +1,747 @@
+/*
+ * Raydium RM31100_ts touchscreen 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/async.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio/consumer.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include <asm/unaligned.h>
+#include <linux/input/mt.h>
+#include <linux/regulator/consumer.h>
+
+#define rm31100	0x0
+#define rm3110x	0x1
+
+#define INVALID_DATA	0xff
+#define MAX_REPORT_TOUCHED_POINTS	10
+
+#define I2C_CLIENT_ADDR         0x39
+#define I2C_DMA_CLIENT_ADDR     0x5A
+
+struct rm31100_ts_data {
+	u8 x_index;
+	u8 y_index;
+	u8 z_index;
+	u8 id_index;
+	u8 touch_index;
+	u8 data_reg;
+	u8 status_reg;
+	u8 data_size;
+	u8 touch_bytes;
+	u8 update_data;
+	u8 touch_meta_data;
+	u8 finger_size;
+};
+
+struct rm3110x_ts_platform_data {
+	u32 dis_min_x; /* display resoltion ABS min*/
+	u32 dis_max_x;/* display resoltion ABS max*/
+	u32 dis_min_y;
+	u32 dis_max_y;
+	u32 res_x; /* TS resolution unit*/
+	u32 res_y;
+	u32 swap_xy;
+	u8 nfingers;
+	bool wakeup;
+};
+
+static struct rm31100_ts_data devices[] = {
+	[0] = {
+		.x_index = 2,
+		.y_index = 4,
+		.z_index = 6,
+		.id_index = 1,
+		.data_reg = 0x1,
+		.status_reg = 0,
+		.update_data = 0x0,
+		.touch_bytes = 6,
+		.touch_meta_data = 1,
+		.finger_size = 70,
+	},
+	[1] = {
+		.x_index = 2,
+		.y_index = 4,
+		.z_index = 6,
+		.id_index = 1,
+		.data_reg = 0x0,
+		.status_reg = 0,
+		.update_data = 0x0,
+		.touch_bytes = 6,
+		.touch_meta_data = 1,
+		.finger_size = 70,
+	},
+};
+
+struct rm31100_ts {
+	struct i2c_client *client;
+	struct input_dev *input;
+	struct regulator *dvdd;
+	struct regulator *avdd;
+	struct gpio_desc *resout_gpio;
+	struct rm3110x_ts_platform_data *pdata;
+	struct rm31100_ts_data *dd;
+	u8 *touch_data;
+	u8 device_id;
+	bool is_suspended;
+	struct mutex access_lock;
+	u32 pen_irq;
+	u8 fw_version;
+	u8 u8_sub_version;
+};
+
+static int rm31100_ts_write_reg_u8(struct i2c_client *client, u8 reg, u8 val)
+{
+	int data;
+
+	data = i2c_smbus_write_byte_data(client, reg, val);
+	if (data < 0)
+		dev_err(&client->dev, "error %d in writing reg 0x%x\n",
+						 data, reg);
+
+	return data;
+}
+
+static int rm31100_ts_read_reg_u8(struct i2c_client *client, u8 reg)
+{
+	int data;
+
+	data = i2c_smbus_read_byte_data(client, reg);
+	if (data < 0)
+		dev_err(&client->dev, "error %d in reading reg 0x%x\n",
+						 data, reg);
+
+	return data;
+}
+
+static int rm31100_ts_read(struct i2c_client *client, u8 reg, u8 *buf, int num)
+{
+	struct i2c_msg xfer_msg[2];
+
+	xfer_msg[0].addr = client->addr;
+	xfer_msg[0].len = 1;
+	xfer_msg[0].flags = 0;
+	xfer_msg[0].buf = &reg;
+
+	xfer_msg[1].addr = client->addr;
+	xfer_msg[1].len = num;
+	xfer_msg[1].flags = I2C_M_RD;
+	xfer_msg[1].buf = buf;
+
+	return i2c_transfer(client->adapter, xfer_msg, num);
+}
+
+static int rm31100_ts_write(struct i2c_client *client, u8 *buf, int num)
+{
+	struct i2c_msg xfer_msg[1];
+
+	xfer_msg[0].addr = client->addr;
+	xfer_msg[0].len = num;
+	xfer_msg[0].flags = 0;
+	xfer_msg[0].buf = buf;
+
+	return i2c_transfer(client->adapter, xfer_msg, num);
+}
+
+static ssize_t rm_fw_version_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf)
+{
+	struct rm31100_ts *info = dev_get_drvdata(dev);
+
+	return sprintf(buf, "Release Version %d.%0d\n",
+		info->fw_version,
+		info->u8_sub_version);
+}
+
+static DEVICE_ATTR(fw_version, S_IRUGO, rm_fw_version_show, NULL);
+
+static struct attribute *rm_ts_attributes[] = {
+	&dev_attr_fw_version.attr,
+	NULL
+};
+
+static const struct attribute_group rm_ts_attr_group = {
+	.attrs = rm_ts_attributes,
+};
+
+static void report_data(struct rm31100_ts *dev, u16 x, u16 y,
+	u8 pressure, u8 id)
+{
+	struct input_dev *input_dev = dev->input;
+
+	if (dev->pdata->swap_xy)
+		swap(x, y);
+
+	input_mt_slot(input_dev, id);
+	input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, true);
+	input_report_abs(input_dev, ABS_MT_POSITION_X, x);
+	input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
+	input_report_abs(input_dev, ABS_MT_PRESSURE, pressure);
+	input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, dev->dd->finger_size);
+}
+
+static void process_rm31100_data(struct rm31100_ts *ts)
+{
+	u8 id, pressure, touches, i;
+	u16 x, y;
+
+	touches = ts->touch_data[ts->dd->touch_index];
+
+	if (touches > 0) {
+		for (i = 0; i < touches; i++) {
+			id = ts->touch_data[i * ts->dd->touch_bytes +
+				ts->dd->id_index];
+			pressure = ts->touch_data[i * ts->dd->touch_bytes +
+				ts->dd->z_index];
+			x = get_unaligned_le16(&(ts->touch_data[i *
+				ts->dd->touch_bytes + ts->dd->x_index]));
+			y = get_unaligned_le16(&(ts->touch_data[i *
+				ts->dd->touch_bytes + ts->dd->y_index]));
+			report_data(ts, x, y, pressure, id);
+		}
+	} else
+		input_mt_sync(ts->input);
+
+	input_mt_report_pointer_emulation(ts->input, true);
+	input_sync(ts->input);
+}
+
+static int rm31100_ts_xy_worker(struct rm31100_ts *work)
+{
+	int rc;
+	struct rm31100_ts *ts = work;
+
+	/* read data from DATA_REG */
+	rc = rm31100_ts_read(ts->client, ts->dd->data_reg, ts->touch_data,
+	ts->dd->data_size);
+
+	if (rc < 0) {
+		dev_err(&ts->client->dev, "read failed\n");
+		goto schedule;
+	}
+
+	if (ts->touch_data[ts->dd->touch_index] == INVALID_DATA)
+		goto schedule;
+
+	/* write to STATUS_REG to release lock */
+	rc = rm31100_ts_write_reg_u8(ts->client,
+		ts->dd->status_reg, ts->dd->update_data);
+	if (rc < 0) {
+		dev_err(&ts->client->dev, "write failed, try once more\n");
+
+		rc = rm31100_ts_write_reg_u8(ts->client,
+			ts->dd->status_reg, ts->dd->update_data);
+		if (rc < 0)
+			dev_err(&ts->client->dev, "write failed, exiting\n");
+	}
+
+	process_rm31100_data(ts);
+schedule:
+	return rc;
+}
+
+static irqreturn_t rm31100_ts_irq(int irq, void *dev_id)
+{
+	struct rm31100_ts *ts = dev_id;
+	struct device *dev = &ts->client->dev;
+	int rc;
+
+	rc = rm31100_ts_xy_worker(ts);
+	if (rc < 0) {
+		dev_err(dev, "Handling message fails in IRQ, %d.\n",
+			rc);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int rm_ts_power_on(struct rm31100_ts *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->resout_gpio))
+		return 0;
+
+	gpiod_set_value_cansleep(ts->resout_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->dvdd);
+	if (error) {
+		dev_err(&ts->client->dev,
+			"failed to enable dvdd regulator: %d\n",
+			error);
+		regulator_disable(ts->dvdd);
+		goto release_reset_gpio;
+	}
+
+release_reset_gpio:
+	gpiod_set_value_cansleep(ts->resout_gpio, 0);
+	if (error)
+		return error;
+
+	msleep(20);
+
+	return 0;
+}
+
+static void rm_ts_power_off(void *_data)
+{
+	struct rm31100_ts *data = _data;
+
+	if (!IS_ERR_OR_NULL(data->resout_gpio)) {
+		/*
+		 * Activate reset gpio to prevent leakage through the
+		 * pin once we shut off power to the controller.
+		 */
+		gpiod_set_value_cansleep(data->resout_gpio, 1);
+		regulator_disable(data->avdd);
+		regulator_disable(data->dvdd);
+	}
+}
+
+static int rm31100_ts_init_ts(struct i2c_client *client, struct rm31100_ts *ts)
+{
+	/*struct input_dev *input_device;*/
+	/*int rc = 0;*/
+
+	ts->dd = &devices[ts->device_id];
+
+	if (!ts->pdata->nfingers) {
+		dev_err(&client->dev, "Touches information not specified\n");
+		return -EINVAL;
+	}
+
+	if (ts->device_id == rm3110x) {
+		if (ts->pdata->nfingers > 2) {
+			dev_err(&client->dev, "Touches >=1 & <= 2\n");
+			return -EINVAL;
+		}
+		ts->dd->data_size = ts->dd->touch_bytes;
+		ts->dd->touch_index = 0x0;
+	} else if (ts->device_id == rm31100) {
+		if (ts->pdata->nfingers > 10) {
+			dev_err(&client->dev, "Touches >=1 & <= 10\n");
+			return -EINVAL;
+		}
+		ts->dd->data_size = ts->pdata->nfingers * ts->dd->touch_bytes +
+						ts->dd->touch_meta_data;
+		ts->dd->touch_index = 0x0;
+	}
+	/* w001 */
+	else {
+		ts->dd->data_size = ts->pdata->nfingers * ts->dd->touch_bytes +
+		ts->dd->touch_meta_data;
+		ts->dd->touch_index = 0x0;
+	}
+
+	ts->touch_data = kzalloc(ts->dd->data_size, GFP_KERNEL);
+	if (!ts->touch_data)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void __maybe_unused rm_enter_sleep(struct rm31100_ts *data)
+{
+	const u8 sleep_cmd[] = { 0x5A, 0xff, 0x00, 0x0f };
+	int rc;
+
+	rc = rm31100_ts_write(data->client, (u8 *)sleep_cmd,
+		ARRAY_SIZE(sleep_cmd));
+	if (rc)
+		dev_err(&data->client->dev,
+			"Send sleep failed: %d\n", rc);
+}
+
+static int __maybe_unused rm31100_ts_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct rm31100_ts *ts = i2c_get_clientdata(client);
+
+	disable_irq(ts->pen_irq);
+
+	if (device_may_wakeup(dev)) {
+		rm_enter_sleep(ts);
+		/* mark suspend flag */
+		ts->is_suspended = (enable_irq_wake(ts->pen_irq) == 0);
+	} else
+		rm_ts_power_off(ts);
+
+	return 0;
+}
+
+static int __maybe_unused rm31100_ts_resume(struct device *dev)
+{
+	struct rm31100_ts *ts = dev_get_drvdata(dev);
+
+	int rc = 0;
+
+	if (device_may_wakeup(dev)) {
+		disable_irq_wake(ts->pen_irq);
+
+		ts->is_suspended = false;
+	} else {
+		rc = rm_ts_power_on(ts);
+		if (rc) {
+			dev_err(dev, "unable to resume\n");
+			return rc;
+		}
+
+		enable_irq(ts->pen_irq);
+
+		/* Clear the status register of the TS controller */
+		rc = rm31100_ts_write_reg_u8(ts->client,
+			ts->dd->status_reg, ts->dd->update_data);
+		if (rc < 0) {
+			dev_err(&ts->client->dev,
+				"write failed, try once more\n");
+
+			rc = rm31100_ts_write_reg_u8(ts->client,
+				ts->dd->status_reg,
+				ts->dd->update_data);
+			if (rc < 0)
+				dev_err(&ts->client->dev,
+					"write failed, exiting\n");
+		}
+	}
+
+	return 0;
+}
+
+static void rm_ts_remove_sysfs_group(void *_data)
+{
+	struct rm31100_ts *ts = _data;
+
+	sysfs_remove_group(&ts->client->dev.kobj, &rm_ts_attr_group);
+}
+
+static SIMPLE_DEV_PM_OPS(rm31100_ts_pm_ops,
+			 rm31100_ts_suspend, rm31100_ts_resume);
+
+static int rm_input_dev_create(struct rm31100_ts *ts)
+{
+	struct input_dev *input_device;
+	int rc = 0;
+
+	input_device = input_allocate_device();
+	if (!input_device) {
+		rc = -ENOMEM;
+		goto error_alloc_dev;
+	}
+	ts->input = input_device;
+
+	input_device->name = "raydium_ts";
+	input_device->id.bustype = BUS_I2C;
+	input_device->dev.parent = &ts->client->dev;
+	input_set_drvdata(input_device, ts);
+
+	__set_bit(BTN_TOUCH, input_device->keybit);
+	__set_bit(EV_ABS, input_device->evbit);
+	__set_bit(EV_KEY, input_device->evbit);
+
+	/* For single touch */
+	input_set_abs_params(input_device, ABS_X,
+			     ts->pdata->dis_min_x, ts->pdata->dis_max_x, 0, 0);
+	input_set_abs_params(input_device, ABS_Y,
+			     ts->pdata->dis_min_x, ts->pdata->dis_max_y, 0, 0);
+	input_set_abs_params(input_device, ABS_PRESSURE,
+			     0, 255, 0, 0);
+	input_abs_set_res(input_device, ABS_X, ts->pdata->res_x);
+	input_abs_set_res(input_device, ABS_Y, ts->pdata->res_y);
+
+	/* Multitouch input params setup */
+	rc = input_mt_init_slots(input_device,
+		MAX_REPORT_TOUCHED_POINTS,
+		INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+	if (rc)
+		goto error_unreg_device;
+
+	input_set_abs_params(input_device, ABS_MT_POSITION_X,
+		ts->pdata->dis_min_x, ts->pdata->dis_max_x, 0, 0);
+	input_set_abs_params(input_device, ABS_MT_POSITION_Y,
+		ts->pdata->dis_min_y, ts->pdata->dis_max_y, 0, 0);
+	input_set_abs_params(input_device, ABS_MT_PRESSURE,
+		0, 0xFF, 0, 0);
+	input_set_abs_params(input_device, ABS_MT_TOUCH_MAJOR,
+		0, 0xFF, 0, 0);
+	input_abs_set_res(input_device, ABS_MT_POSITION_X, ts->pdata->res_x);
+	input_abs_set_res(input_device, ABS_MT_POSITION_Y, ts->pdata->res_y);
+
+	rc = input_register_device(input_device);
+	if (rc)
+		goto error_unreg_device;
+
+	return 0;
+
+error_unreg_device:
+	input_free_device(input_device);
+error_alloc_dev:
+	ts->input = NULL;
+	return rc;
+}
+
+static int rm31100_initialize(struct i2c_client *client)
+{
+	struct rm31100_ts *ts = i2c_get_clientdata(client);
+	int rc = 0, temp_reg;
+
+	/* read one byte to make sure i2c device exists */
+	if (ts->device_id == rm3110x)
+		temp_reg = 0x01;
+	else if (ts->device_id == rm31100)
+		temp_reg = 0x00;
+	else
+		temp_reg = 0x05;
+
+	rc = rm31100_ts_read_reg_u8(client, temp_reg);
+	if (rc < 0) {
+		dev_err(&client->dev, "i2c sanity check failed\n");
+		return rc;
+	}
+
+	rc = rm31100_ts_init_ts(client, ts);
+	if (rc < 0) {
+		dev_err(&client->dev, "rm31100_ts init failed\n");
+		return rc;
+	}
+
+	return 0;
+}
+
+static int rm31100_ts_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct rm31100_ts *ts;
+	struct rm3110x_ts_platform_data *pdata = client->dev.platform_data;
+	int rc;
+	union i2c_smbus_data dummy;
+
+	ts = devm_kzalloc(&client->dev, sizeof(struct rm31100_ts), GFP_KERNEL);
+
+	if (!ts)
+		return -ENOMEM;
+
+	if (!i2c_check_functionality(client->adapter,
+		I2C_FUNC_SMBUS_READ_WORD_DATA)) {
+		dev_err(&client->dev, "I2C functionality not supported\n");
+		rc = -EIO;
+		goto error_touch_data_alloc;
+	}
+
+	ts->client = client;
+	ts->pdata = pdata;
+	i2c_set_clientdata(client, ts);
+	ts->device_id = id->driver_data;
+
+	ts->is_suspended = false;
+
+	mutex_init(&ts->access_lock);
+
+	ts->avdd = devm_regulator_get(&client->dev, "avdd");
+	if (IS_ERR(ts->avdd)) {
+		rc = PTR_ERR(ts->avdd);
+		if (rc != -EPROBE_DEFER)
+			dev_err(&client->dev,
+				"Failed to get 'avdd' regulator: %d\n",
+				rc);
+		return rc;
+	}
+
+	ts->dvdd = devm_regulator_get(&client->dev, "dvdd");
+	if (IS_ERR(ts->dvdd)) {
+		rc = PTR_ERR(ts->dvdd);
+		if (rc != -EPROBE_DEFER)
+			dev_err(&client->dev,
+				"Failed to get 'dvdd' regulator: %d\n",
+				rc);
+		return rc;
+	}
+
+	ts->resout_gpio = devm_gpiod_get_optional(&client->dev,
+		"ts_reset", 0);
+
+	if (IS_ERR(ts->resout_gpio)) {
+		rc = PTR_ERR(ts->resout_gpio);
+		/*
+		 * On Chromebooks vendors like to source touch panels from
+		 * different vendors, but they are connected to the same
+		 * regulators/GPIO pin. The drivers also use asynchronous
+		 * probing, which means that more than one driver will
+		 * attempt to claim the reset line. If we find it busy,
+		 * let's try again later.
+		 */
+		if (rc == -EBUSY) {
+			dev_info(&client->dev,
+				 "reset gpio is busy, deferring probe\n");
+			return -EPROBE_DEFER;
+		}
+
+		if (rc == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+
+		if (rc != -ENOENT && rc != -ENOSYS) {
+			dev_err(&client->dev,
+				"failed to get reset gpio: %d\n",
+				rc);
+			return rc;
+		}
+	}
+
+	rc = rm_ts_power_on(ts);
+	if (rc)
+		return rc;
+
+	rc = devm_add_action(&client->dev, rm_ts_power_off, ts);
+	if (rc) {
+		dev_err(&client->dev,
+			"failed to install power off action: %d\n", rc);
+		rm_ts_power_off(ts);
+		return rc;
+	}
+
+	/* 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)
+		return -ENODEV;
+
+	rc = rm31100_initialize(client);
+	if (rc < 0) {
+		dev_err(&client->dev, "probe failed! unbind device.\n");
+		return rc;
+	}
+
+	rc = rm_input_dev_create(ts);
+	if (rc) {
+		dev_err(&client->dev, "%s crated failed, %d\n", __func__, rc);
+		return rc;
+	}
+
+	rc = devm_request_threaded_irq(&client->dev, ts->pen_irq,
+					NULL, rm31100_ts_irq,
+					IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+					client->name, ts);
+	if (rc) {
+		dev_err(&client->dev, "Failed to register interrupt\n");
+		return rc;
+	}
+
+	rc = sysfs_create_group(&client->dev.kobj, &rm_ts_attr_group);
+	if (rc) {
+		dev_err(&client->dev, "failed to create sysfs attributes: %d\n",
+			rc);
+		return rc;
+	}
+
+	rc = devm_add_action(&client->dev,
+				rm_ts_remove_sysfs_group, ts);
+	if (rc) {
+		rm_ts_remove_sysfs_group(ts);
+		dev_err(&client->dev,
+			"Failed to add sysfs cleanup action: %d\n",
+			rc);
+		return rc;
+	}
+	return 0;
+
+error_touch_data_alloc:
+	return rc;
+}
+
+static int rm31100_ts_remove(struct i2c_client *client)
+{
+	struct rm31100_ts *ts = i2c_get_clientdata(client);
+
+	device_init_wakeup(&client->dev, 0);
+
+	devm_free_irq(&client->dev, ts->pen_irq, ts);
+
+	input_unregister_device(ts->input);
+
+	mutex_destroy(&ts->access_lock);
+
+	rm_ts_power_off(ts);
+
+	kfree(ts->touch_data);
+	kfree(ts);
+
+	return 0;
+}
+
+static const struct i2c_device_id rm31100_ts_id[] = {
+	{"RM31100", 0},
+	{"RM3110x", 1},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, rm31100_ts_id);
+
+
+static struct i2c_driver rm31100_ts_driver = {
+	.probe = rm31100_ts_probe,
+	.remove = rm31100_ts_remove,
+	.id_table = rm31100_ts_id,
+	.driver = {
+		.name = "raydium_ts",
+	},
+};
+
+static int __init rm31100_ts_init(void)
+{
+	int rc;
+
+	rc = i2c_add_driver(&rm31100_ts_driver);
+
+	return rc;
+}
+/* Making this as late init to avoid power fluctuations
+ * during LCD initialization.
+ */
+module_init(rm31100_ts_init);
+
+static void __exit rm31100_ts_exit(void)
+{
+	i2c_del_driver(&rm31100_ts_driver);
+}
+module_exit(rm31100_ts_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("rm31100-rm3110x touchscreen controller driver");
+MODULE_AUTHOR("Raydium");
+MODULE_ALIAS("platform:rm31100_ts");
-- 
2.1.2

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

* [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
@ 2016-01-15  3:30 Jeffrey Lin
  0 siblings, 0 replies; 58+ messages in thread
From: Jeffrey Lin @ 2016-01-15  3:30 UTC (permalink / raw)
  To: dmitry.torokhov, rydberg, dianders, bleung, scott.liu
  Cc: jeffrey.lin, roger.yang, KP.li, linux-kernel, linux-input

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      |  11 +
 drivers/input/touchscreen/Makefile     |   1 +
 drivers/input/touchscreen/rm31100_ts.c | 784 +++++++++++++++++++++++++++++++++
 3 files changed, 796 insertions(+)
 create mode 100644 drivers/input/touchscreen/rm31100_ts.c

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 0f13e52..dcaf7e2 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -955,4 +955,15 @@ config TOUCHSCREEN_ZFORCE
 	  To compile this driver as a module, choose M here: the
 	  module will be called zforce_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 rm31100_ts.
 endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 687d5a7..3220f66 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -78,3 +78,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE)	+= zylonite-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_W90X900)	+= w90p910_ts.o
 obj-$(CONFIG_TOUCHSCREEN_TPS6507X)	+= tps6507x-ts.o
 obj-$(CONFIG_TOUCHSCREEN_ZFORCE)	+= zforce_ts.o
+obj-$(CONFIG_TOUCHSCREEN_RM_TS)		+= rm31100_ts.o
diff --git a/drivers/input/touchscreen/rm31100_ts.c b/drivers/input/touchscreen/rm31100_ts.c
new file mode 100644
index 0000000..0fc70f5
--- /dev/null
+++ b/drivers/input/touchscreen/rm31100_ts.c
@@ -0,0 +1,784 @@
+/*
+ * Raydium RM31100_ts touchscreen 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/async.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio/consumer.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include <asm/unaligned.h>
+#include <linux/input/mt.h>
+#include <linux/regulator/consumer.h>
+
+#define rm31100	0x0
+#define rm3110x	0x1
+
+#define INVALID_DATA	0xff
+#define MAX_REPORT_TOUCHED_POINTS	10
+
+#define I2C_CLIENT_ADDR         0x39
+#define I2C_DMA_CLIENT_ADDR     0x5A
+
+struct rm31100_ts_data {
+	u8 x_index;
+	u8 y_index;
+	u8 z_index;
+	u8 id_index;
+	u8 touch_index;
+	u8 data_reg;
+	u8 status_reg;
+	u8 data_size;
+	u8 touch_bytes;
+	u8 update_data;
+	u8 touch_meta_data;
+	u8 finger_size;
+};
+
+struct rm3110x_ts_platform_data {
+	u32 dis_min_x; /* display resoltion ABS min*/
+	u32 dis_max_x;/* display resoltion ABS max*/
+	u32 dis_min_y;
+	u32 dis_max_y;
+	u32 res_x; /* TS resolution unit*/
+	u32 res_y;
+	u32 swap_xy;
+	u8 nfingers;
+	bool wakeup;
+};
+
+static struct rm31100_ts_data devices[] = {
+	[0] = {
+		.x_index = 2,
+		.y_index = 4,
+		.z_index = 6,
+		.id_index = 1,
+		.data_reg = 0x1,
+		.status_reg = 0,
+		.update_data = 0x0,
+		.touch_bytes = 6,
+		.touch_meta_data = 1,
+		.finger_size = 70,
+	},
+	[1] = {
+		.x_index = 2,
+		.y_index = 4,
+		.z_index = 6,
+		.id_index = 1,
+		.data_reg = 0x0,
+		.status_reg = 0,
+		.update_data = 0x0,
+		.touch_bytes = 6,
+		.touch_meta_data = 1,
+		.finger_size = 70,
+	},
+};
+
+struct rm31100_ts {
+	struct i2c_client *client;
+	struct input_dev *input;
+	struct regulator *dvdd;
+	struct regulator *avdd;
+	struct gpio_desc *resout_gpio;
+	struct rm3110x_ts_platform_data *pdata;
+	struct rm31100_ts_data *dd;
+	u8 *touch_data;
+	u8 device_id;
+	bool is_suspended;
+	struct mutex access_lock;
+	u32 pen_irq;
+	u8 fw_version;
+	u8 u8_sub_version;
+};
+
+static int rm31100_ts_write_reg_u8(struct i2c_client *client, u8 reg, u8 val)
+{
+	int data;
+
+	data = i2c_smbus_write_byte_data(client, reg, val);
+	if (data < 0)
+		dev_err(&client->dev, "error %d in writing reg 0x%x\n",
+						 data, reg);
+
+	return data;
+}
+
+static int rm31100_ts_read_reg_u8(struct i2c_client *client, u8 reg)
+{
+	int data;
+
+	data = i2c_smbus_read_byte_data(client, reg);
+	if (data < 0)
+		dev_err(&client->dev, "error %d in reading reg 0x%x\n",
+						 data, reg);
+
+	return data;
+}
+
+static int rm31100_ts_read(struct i2c_client *client, u8 reg, u8 *buf, int num)
+{
+	struct i2c_msg xfer_msg[2];
+
+	xfer_msg[0].addr = client->addr;
+	xfer_msg[0].len = 1;
+	xfer_msg[0].flags = 0;
+	xfer_msg[0].buf = &reg;
+
+	xfer_msg[1].addr = client->addr;
+	xfer_msg[1].len = num;
+	xfer_msg[1].flags = I2C_M_RD;
+	xfer_msg[1].buf = buf;
+
+	return i2c_transfer(client->adapter, xfer_msg, num);
+}
+
+static int rm31100_ts_write(struct i2c_client *client, u8 *buf, int num)
+{
+	struct i2c_msg xfer_msg[1];
+
+	xfer_msg[0].addr = client->addr;
+	xfer_msg[0].len = num;
+	xfer_msg[0].flags = 0;
+	xfer_msg[0].buf = buf;
+
+	return i2c_transfer(client->adapter, xfer_msg, num);
+}
+
+static ssize_t rm_fw_version_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf)
+{
+	struct rm31100_ts *info = dev_get_drvdata(dev);
+	return sprintf(buf, "Release Version %d.%0d\n",
+		info->fw_version,
+		info->u8_sub_version);
+}
+
+static DEVICE_ATTR(fw_version, S_IRUGO, rm_fw_version_show, NULL);
+
+static struct attribute *rm_ts_attributes[] = {
+	&dev_attr_fw_version.attr,
+	NULL
+};
+
+static const struct attribute_group rm_ts_attr_group = {
+	.attrs = rm_ts_attributes,
+};
+
+static void report_data(struct rm31100_ts *dev, u16 x, u16 y,
+	u8 pressure, u8 id)
+{
+	struct input_dev *input_dev = dev->input;
+	if (dev->pdata->swap_xy)
+		swap(x, y);
+
+	input_mt_slot(input_dev, id);
+	input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, true);
+	input_report_abs(input_dev, ABS_MT_POSITION_X, x);
+	input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
+	input_report_abs(input_dev, ABS_MT_PRESSURE, pressure);
+	input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, dev->dd->finger_size);
+}
+
+static void process_rm31100_data(struct rm31100_ts *ts)
+{
+	u8 id, pressure, touches, i;
+	u16 x, y;
+
+	touches = ts->touch_data[ts->dd->touch_index];
+
+	if (touches > 0) {
+		for (i = 0; i < touches; i++) {
+			id = ts->touch_data[i * ts->dd->touch_bytes +
+				ts->dd->id_index];
+			pressure = ts->touch_data[i * ts->dd->touch_bytes +
+				ts->dd->z_index];
+			x = get_unaligned_le16(&(ts->touch_data[i *
+				ts->dd->touch_bytes + ts->dd->x_index]));
+			y = get_unaligned_le16(&(ts->touch_data[i *
+				ts->dd->touch_bytes + ts->dd->y_index]));
+			report_data(ts, x, y, pressure, id);
+		}
+	} else
+		input_mt_sync(ts->input);
+
+	input_mt_report_pointer_emulation(ts->input, true);
+	input_sync(ts->input);
+}
+
+static int rm31100_ts_xy_worker(struct rm31100_ts *work)
+{
+	int rc;
+	struct rm31100_ts *ts = work;
+
+	/* read data from DATA_REG */
+	rc = rm31100_ts_read(ts->client, ts->dd->data_reg, ts->touch_data,
+	ts->dd->data_size);
+
+	if (rc < 0) {
+		dev_err(&ts->client->dev, "read failed\n");
+		goto schedule;
+	}
+
+	if (ts->touch_data[ts->dd->touch_index] == INVALID_DATA)
+		goto schedule;
+
+	/* write to STATUS_REG to release lock */
+	rc = rm31100_ts_write_reg_u8(ts->client,
+		ts->dd->status_reg, ts->dd->update_data);
+	if (rc < 0) {
+		dev_err(&ts->client->dev, "write failed, try once more\n");
+
+		rc = rm31100_ts_write_reg_u8(ts->client,
+			ts->dd->status_reg, ts->dd->update_data);
+		if (rc < 0)
+			dev_err(&ts->client->dev, "write failed, exiting\n");
+	}
+
+	process_rm31100_data(ts);
+schedule:
+	return rc;
+}
+
+static irqreturn_t rm31100_ts_irq(int irq, void *dev_id)
+{
+	struct rm31100_ts *ts = dev_id;
+	struct device *dev = &ts->client->dev;
+	int rc;
+
+	rc = rm31100_ts_xy_worker(ts);
+	if (rc < 0) {
+		dev_err(dev, "Handling message fails in IRQ, %d.\n",
+			rc);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int rm_ts_power_on(struct rm31100_ts *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->resout_gpio))
+		return 0;
+
+	gpiod_set_value_cansleep(ts->resout_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->dvdd);
+	if (error) {
+		dev_err(&ts->client->dev,
+			"failed to enable dvdd regulator: %d\n",
+			error);
+		regulator_disable(ts->dvdd);
+		goto release_reset_gpio;
+	}
+
+release_reset_gpio:
+	gpiod_set_value_cansleep(ts->resout_gpio, 0);
+	if (error)
+		return error;
+
+	msleep(20);
+
+	return 0;
+}
+
+static void rm_ts_power_off(void *_data)
+{
+	struct rm31100_ts *data = _data;
+
+	if (!IS_ERR_OR_NULL(data->resout_gpio)) {
+		/*
+		 * Activate reset gpio to prevent leakage through the
+		 * pin once we shut off power to the controller.
+		 */
+		gpiod_set_value_cansleep(data->resout_gpio, 1);
+		regulator_disable(data->avdd);
+		regulator_disable(data->dvdd);
+	}
+}
+
+static int rm31100_ts_init_ts(struct i2c_client *client, struct rm31100_ts *ts)
+{
+	/*struct input_dev *input_device;*/
+	/*int rc = 0;*/
+
+	ts->dd = &devices[ts->device_id];
+
+	if (!ts->pdata->nfingers) {
+		dev_err(&client->dev, "Touches information not specified\n");
+		return -EINVAL;
+	}
+
+	if (ts->device_id == rm3110x) {
+		if (ts->pdata->nfingers > 2) {
+			dev_err(&client->dev, "Touches >=1 & <= 2\n");
+			return -EINVAL;
+		}
+		ts->dd->data_size = ts->dd->touch_bytes;
+		ts->dd->touch_index = 0x0;
+	} else if (ts->device_id == rm31100) {
+		if (ts->pdata->nfingers > 10) {
+			dev_err(&client->dev, "Touches >=1 & <= 10\n");
+			return -EINVAL;
+		}
+		ts->dd->data_size = ts->pdata->nfingers * ts->dd->touch_bytes +
+						ts->dd->touch_meta_data;
+		ts->dd->touch_index = 0x0;
+	}
+	/* w001 */
+	else {
+		ts->dd->data_size = ts->pdata->nfingers * ts->dd->touch_bytes +
+		ts->dd->touch_meta_data;
+		ts->dd->touch_index = 0x0;
+	}
+
+	ts->touch_data = kzalloc(ts->dd->data_size, GFP_KERNEL);
+	if (!ts->touch_data) {
+		pr_err("%s: Unable to allocate memory\n", __func__);
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+static void __maybe_unused rm_enter_sleep(struct rm31100_ts *data)
+{
+	const u8 sleep_cmd[] = { 0x5A, 0xff, 0x00, 0x0f };
+	int rc;
+
+	rc = rm31100_ts_write(data->client, (u8 *)sleep_cmd,
+		ARRAY_SIZE(sleep_cmd));
+	if (rc)
+		dev_err(&data->client->dev,
+			"Send sleep failed: %d\n", rc);
+}
+
+static int __maybe_unused rm31100_ts_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct rm31100_ts *ts = i2c_get_clientdata(client);
+
+	disable_irq(ts->pen_irq);
+
+	if (device_may_wakeup(dev)) {
+		rm_enter_sleep(ts);
+		/* mark suspend flag */
+		ts->is_suspended = (enable_irq_wake(ts->pen_irq) == 0);
+	} else
+		rm_ts_power_off(ts);
+
+	return 0;
+}
+
+static int __maybe_unused rm31100_ts_resume(struct device *dev)
+{
+	struct rm31100_ts *ts = dev_get_drvdata(dev);
+
+	int rc = 0;
+
+	if (device_may_wakeup(dev)) {
+		disable_irq_wake(ts->pen_irq);
+
+		ts->is_suspended = false;
+	} else {
+		rc = rm_ts_power_on(ts);
+		if (rc) {
+			dev_err(dev, "unable to resume\n");
+			return rc;
+		}
+
+		enable_irq(ts->pen_irq);
+
+		/* Clear the status register of the TS controller */
+		rc = rm31100_ts_write_reg_u8(ts->client,
+			ts->dd->status_reg, ts->dd->update_data);
+		if (rc < 0) {
+			dev_err(&ts->client->dev,
+				"write failed, try once more\n");
+
+			rc = rm31100_ts_write_reg_u8(ts->client,
+				ts->dd->status_reg,
+				ts->dd->update_data);
+			if (rc < 0)
+				dev_err(&ts->client->dev,
+					"write failed, exiting\n");
+		}
+	}
+
+	return 0;
+}
+
+static void rm_ts_remove_sysfs_group(void *_data)
+{
+	struct rm31100_ts *ts = _data;
+
+	sysfs_remove_group(&ts->client->dev.kobj, &rm_ts_attr_group);
+}
+
+static SIMPLE_DEV_PM_OPS(rm31100_ts_pm_ops,
+			 rm31100_ts_suspend, rm31100_ts_resume);
+
+static int rm_input_dev_create(struct rm31100_ts *ts)
+{
+	struct input_dev *input_device;
+	int rc = 0;
+
+	input_device = input_allocate_device();
+	if (!input_device) {
+		rc = -ENOMEM;
+		goto error_alloc_dev;
+	}
+	ts->input = input_device;
+
+	input_device->name = "raydium_ts";
+	input_device->id.bustype = BUS_I2C;
+	input_device->dev.parent = &ts->client->dev;
+	input_set_drvdata(input_device, ts);
+
+	__set_bit(BTN_TOUCH, input_device->keybit);
+	__set_bit(EV_ABS, input_device->evbit);
+	__set_bit(EV_KEY, input_device->evbit);
+
+	/* For single touch */
+	input_set_abs_params(input_device, ABS_X,
+			     ts->pdata->dis_min_x, ts->pdata->dis_max_x, 0, 0);
+	input_set_abs_params(input_device, ABS_Y,
+			     ts->pdata->dis_min_x, ts->pdata->dis_max_y, 0, 0);
+	input_set_abs_params(input_device, ABS_PRESSURE,
+			     0, 255, 0, 0);
+	input_abs_set_res(input_device, ABS_X, ts->pdata->res_x);
+	input_abs_set_res(input_device, ABS_Y, ts->pdata->res_y);
+
+	/* Multitouch input params setup */
+	rc = input_mt_init_slots(input_device,
+		MAX_REPORT_TOUCHED_POINTS,
+		INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+	if (rc)
+		goto error_unreg_device;
+
+	input_set_abs_params(input_device, ABS_MT_POSITION_X,
+		ts->pdata->dis_min_x, ts->pdata->dis_max_x, 0, 0);
+	input_set_abs_params(input_device, ABS_MT_POSITION_Y,
+		ts->pdata->dis_min_y, ts->pdata->dis_max_y, 0, 0);
+	input_set_abs_params(input_device, ABS_MT_PRESSURE,
+		0, 0xFF, 0, 0);
+	input_set_abs_params(input_device, ABS_MT_TOUCH_MAJOR,
+		0, 0xFF, 0, 0);
+	input_abs_set_res(input_device, ABS_MT_POSITION_X, ts->pdata->res_x);
+	input_abs_set_res(input_device, ABS_MT_POSITION_Y, ts->pdata->res_y);
+
+	rc = input_register_device(input_device);
+	if (rc)
+		goto error_unreg_device;
+
+	return 0;
+
+error_unreg_device:
+	input_free_device(input_device);
+error_alloc_dev:
+	ts->input = NULL;
+	return rc;
+}
+
+static int rm31100_initialize(struct i2c_client *client)
+{
+	struct rm31100_ts *ts = i2c_get_clientdata(client);
+	int rc = 0, temp_reg;
+
+	/* read one byte to make sure i2c device exists */
+	if (ts->device_id == rm3110x)
+		temp_reg = 0x01;
+	else if (ts->device_id == rm31100)
+		temp_reg = 0x00;
+	else
+		temp_reg = 0x05;
+
+	rc = rm31100_ts_read_reg_u8(client, temp_reg);
+	if (rc < 0) {
+		dev_err(&client->dev, "i2c sanity check failed\n");
+		return rc;
+	}
+
+	rc = rm31100_ts_init_ts(client, ts);
+	if (rc < 0) {
+		dev_err(&client->dev, "rm31100_ts init failed\n");
+		return rc;
+	}
+
+	return 0;
+}
+
+static int rm31100_ts_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct rm31100_ts *ts;
+	struct rm3110x_ts_platform_data *pdata = client->dev.platform_data;
+	int rc;
+	union i2c_smbus_data dummy;
+
+	ts = devm_kzalloc(&client->dev, sizeof(struct rm31100_ts), GFP_KERNEL);
+	if (!ts) {
+		dev_err(&client->dev, "Failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	if (!i2c_check_functionality(client->adapter,
+		I2C_FUNC_SMBUS_READ_WORD_DATA)) {
+		dev_err(&client->dev, "I2C functionality not supported\n");
+		rc = -EIO;
+		goto error_touch_data_alloc;
+	}
+
+	ts->client = client;
+	ts->pdata = pdata;
+	i2c_set_clientdata(client, ts);
+	ts->device_id = id->driver_data;
+
+	ts->is_suspended = false;
+
+	mutex_init(&ts->access_lock);
+
+	ts->avdd = devm_regulator_get(&client->dev, "avdd");
+	if (IS_ERR(ts->avdd)) {
+		rc = PTR_ERR(ts->avdd);
+		if (rc != -EPROBE_DEFER)
+			dev_err(&client->dev,
+				"Failed to get 'avdd' regulator: %d\n",
+				rc);
+		return rc;
+	}
+
+	ts->dvdd = devm_regulator_get(&client->dev, "dvdd");
+	if (IS_ERR(ts->dvdd)) {
+		rc = PTR_ERR(ts->dvdd);
+		if (rc != -EPROBE_DEFER)
+			dev_err(&client->dev,
+				"Failed to get 'dvdd' regulator: %d\n",
+				rc);
+		return rc;
+	}
+#if 1
+	ts->resout_gpio = devm_gpiod_get(&client->dev, "rm31100_resout_gpio");
+	if (IS_ERR(ts->resout_gpio)) {
+		rc = PTR_ERR(ts->resout_gpio);
+
+		/*
+		 * On Chromebooks vendors like to source touch panels from
+		 * different vendors, but they are connected to the same
+		 * regulators/GPIO pin. The drivers also use asynchronous
+		 * probing, which means that more than one driver will
+		 * attempt to claim the reset line. If we find it busy,
+		 * let's try again later.
+		 */
+		if (rc == -EBUSY) {
+			dev_info(&client->dev,
+				 "reset gpio is busy, deferring probe\n");
+			return -EPROBE_DEFER;
+		}
+
+		if (rc == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+
+		if (rc != -ENOENT && rc != -ENOSYS) {
+			dev_err(&client->dev,
+				"failed to get reset gpio: %d\n",
+				rc);
+			return rc;
+		}
+
+	} else {
+		rc = gpiod_direction_output(ts->resout_gpio, 0);
+		if (rc) {
+			dev_err(&client->dev,
+				"failed to configure reset gpio as output: %d\n",
+				rc);
+			return rc;
+		}
+	}
+#else
+	ts->resout_gpio = devm_gpiod_get_optional(&client->dev,
+		"ts_reset", GPIOF_OUT_LOW);
+	if (IS_ERR(ts->resout_gpio)) {
+		rc = PTR_ERR(ts->resout_gpio);
+		/*
+		 * On Chromebooks vendors like to source touch panels from
+		 * different vendors, but they are connected to the same
+		 * regulators/GPIO pin. The drivers also use asynchronous
+		 * probing, which means that more than one driver will
+		 * attempt to claim the reset line. If we find it busy,
+		 * let's try again later.
+		 */
+		if (rc == -EBUSY) {
+			dev_info(&client->dev,
+				 "reset gpio is busy, deferring probe\n");
+			return -EPROBE_DEFER;
+		}
+
+		if (rc == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+
+		if (rc != -ENOENT && rc != -ENOSYS) {
+			dev_err(&client->dev,
+				"failed to get reset gpio: %d\n",
+				rc);
+			return rc;
+		}
+	}
+#endif
+	rc = rm_ts_power_on(ts);
+	if (rc)
+		return rc;
+
+	rc = devm_add_action(&client->dev, rm_ts_power_off, ts);
+	if (rc) {
+		dev_err(&client->dev,
+			"failed to install power off action: %d\n", rc);
+		rm_ts_power_off(ts);
+		return rc;
+	}
+
+	/* 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)
+		return -ENODEV;
+
+	rc = rm31100_initialize(client);
+	if (rc < 0) {
+		dev_err(&client->dev, "probe failed! unbind device.\n");
+		return rc;
+	}
+
+	rc = rm_input_dev_create(ts);
+	if (rc) {
+		dev_err(&client->dev, "%s crated failed, %d\n", __func__, rc);
+		return rc;
+	}
+
+	rc = devm_request_threaded_irq(&client->dev, ts->pen_irq,
+					NULL, rm31100_ts_irq,
+					IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+					client->name, ts);
+	if (rc) {
+		dev_err(&client->dev, "Failed to register interrupt\n");
+		return rc;
+	}
+
+	rc = sysfs_create_group(&client->dev.kobj, &rm_ts_attr_group);
+	if (rc) {
+		dev_err(&client->dev, "failed to create sysfs attributes: %d\n",
+			rc);
+		return rc;
+	}
+
+	rc = devm_add_action(&client->dev,
+				rm_ts_remove_sysfs_group, ts);
+	if (rc) {
+		rm_ts_remove_sysfs_group(ts);
+		dev_err(&client->dev,
+			"Failed to add sysfs cleanup action: %d\n",
+			rc);
+		return rc;
+	}
+	return 0;
+
+error_touch_data_alloc:
+	return rc;
+}
+
+static int rm31100_ts_remove(struct i2c_client *client)
+{
+	struct rm31100_ts *ts = i2c_get_clientdata(client);
+
+	device_init_wakeup(&client->dev, 0);
+
+	devm_free_irq(&client->dev, ts->pen_irq, ts);
+
+	input_unregister_device(ts->input);
+
+	mutex_destroy(&ts->access_lock);
+
+	rm_ts_power_off(ts);
+
+	kfree(ts->touch_data);
+	kfree(ts);
+
+	return 0;
+}
+
+static const struct i2c_device_id rm31100_ts_id[] = {
+	{"RM31100", 0},
+	{"RM3110x", 1},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, rm31100_ts_id);
+
+
+static struct i2c_driver rm31100_ts_driver = {
+	.probe = rm31100_ts_probe,
+	.remove = rm31100_ts_remove,
+	.id_table = rm31100_ts_id,
+	.driver = {
+		.name = "raydium_ts",
+	},
+};
+
+static int __init rm31100_ts_init(void)
+{
+	int rc;
+
+	rc = i2c_add_driver(&rm31100_ts_driver);
+
+	return rc;
+}
+/* Making this as late init to avoid power fluctuations
+ * during LCD initialization.
+ */
+module_init(rm31100_ts_init);
+
+static void __exit rm31100_ts_exit(void)
+{
+	i2c_del_driver(&rm31100_ts_driver);
+}
+module_exit(rm31100_ts_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("rm31100-rm3110x touchscreen controller driver");
+MODULE_AUTHOR("Raydium");
+MODULE_ALIAS("platform:rm31100_ts");
-- 
2.1.2

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

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2016-01-13  7:49 Jeffrey Lin
  2016-01-13  8:31 ` Dmitry Torokhov
@ 2016-01-13 19:14 ` Dmitry Torokhov
  1 sibling, 0 replies; 58+ messages in thread
From: Dmitry Torokhov @ 2016-01-13 19:14 UTC (permalink / raw)
  To: Jeffrey Lin
  Cc: rydberg, dianders, bleung, scott.liu, jeffrey.lin, roger.yang,
	KP.li, linux-kernel, linux-input

Hi Jeffrey,

On Wed, Jan 13, 2016 at 03:49:12PM +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".

Doing a quick pass over the driver to give you some more feedback for
the next version. 

> 
> Signed-off-by: jeffrey.lin<jeffrey.lin@rad-ic.com>
> ---
>  drivers/input/touchscreen/Kconfig      |  12 +
>  drivers/input/touchscreen/Makefile     |   1 +
>  drivers/input/touchscreen/rm31100_ts.c | 772 +++++++++++++++++++++++++++++++++
>  3 files changed, 785 insertions(+)
>  create mode 100644 drivers/input/touchscreen/rm31100_ts.c
> 
> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> index 0f13e52..4790e36 100644
> --- a/drivers/input/touchscreen/Kconfig
> +++ b/drivers/input/touchscreen/Kconfig
> @@ -955,4 +955,16 @@ config TOUCHSCREEN_ZFORCE
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called zforce_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 rm31100_ts.
> +
>  endif
> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
> index 687d5a7..3220f66 100644
> --- a/drivers/input/touchscreen/Makefile
> +++ b/drivers/input/touchscreen/Makefile
> @@ -78,3 +78,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE)	+= zylonite-wm97xx.o
>  obj-$(CONFIG_TOUCHSCREEN_W90X900)	+= w90p910_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_TPS6507X)	+= tps6507x-ts.o
>  obj-$(CONFIG_TOUCHSCREEN_ZFORCE)	+= zforce_ts.o
> +obj-$(CONFIG_TOUCHSCREEN_RM_TS)		+= rm31100_ts.o
> diff --git a/drivers/input/touchscreen/rm31100_ts.c b/drivers/input/touchscreen/rm31100_ts.c
> new file mode 100644
> index 0000000..e024bbd
> --- /dev/null
> +++ b/drivers/input/touchscreen/rm31100_ts.c
> @@ -0,0 +1,772 @@
> +/*
> + * Raydium RM31100_ts touchscreen 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/async.h>
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/i2c.h>
> +#include <linux/input.h>
> +#include <linux/slab.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/mutex.h>
> +#include <linux/delay.h>
> +#include <linux/pm.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/uaccess.h>
> +#include <asm/unaligned.h>
> +#include <linux/input/mt.h>
> +
> +#define rm31100	0x0
> +#define rm3110x	0x1
> +
> +#define INVALID_DATA	0xff
> +#define MAX_REPORT_TOUCHED_POINTS	10
> +
> +#define I2C_CLIENT_ADDR         0x39
> +#define I2C_DMA_CLIENT_ADDR     0x5A
> +
> +struct rm31100_ts_data {
> +	u8 x_index;
> +	u8 y_index;
> +	u8 z_index;
> +	u8 id_index;
> +	u8 touch_index;
> +	u8 data_reg;
> +	u8 status_reg;
> +	u8 data_size;
> +	u8 touch_bytes;
> +	u8 update_data;
> +	u8 touch_meta_data;
> +	u8 finger_size;
> +};
> +
> +struct rm3110x_ts_platform_data {
> +	u32 dis_min_x; /* display resoltion ABS min*/
> +	u32 dis_max_x;/* display resoltion ABS max*/
> +	u32 dis_min_y;
> +	u32 dis_max_y;
> +	u32 res_x; /* TS resolution unit*/
> +	u32 res_y;
> +	u32 swap_xy;
> +	u8 nfingers;
> +	bool wakeup;
> +};
> +
> +static struct rm31100_ts_data devices[] = {
> +	[0] = {
> +		.x_index = 2,
> +		.y_index = 4,
> +		.z_index = 6,
> +		.id_index = 1,
> +		.data_reg = 0x1,
> +		.status_reg = 0,
> +		.update_data = 0x0,
> +		.touch_bytes = 6,
> +		.touch_meta_data = 1,
> +		.finger_size = 70,
> +	},
> +	[1] = {
> +		.x_index = 2,
> +		.y_index = 4,
> +		.z_index = 6,
> +		.id_index = 1,
> +		.data_reg = 0x0,
> +		.status_reg = 0,
> +		.update_data = 0x0,
> +		.touch_bytes = 6,
> +		.touch_meta_data = 1,
> +		.finger_size = 70,
> +	},
> +};
> +
> +struct rm31100_ts {
> +	struct i2c_client *client;
> +	struct input_dev *input;
> +	struct regulator *dvdd;
> +	struct regulator *avdd;
> +	struct gpio_desc *resout_gpio;
> +	struct rm3110x_ts_platform_data *pdata;
> +	struct rm31100_ts_data *dd;
> +	u8 *touch_data;
> +	u8 device_id;
> +	u8 prev_touches;
> +	bool is_suspended;
> +	bool int_pending;
> +	struct mutex access_lock;
> +	u32 pen_irq;
> +	u8 fw_version;
> +	u8 u8_sub_version;
> +};
> +
> +/*
> +static inline u16 join_bytes(u8 a, u8 b)
> +{
> +	u16 ab = 0;
> +	ab = ab | a;
> +	ab = ab << 8 | b;
> +	return ab;

I do not see this being used in the driver (and if you needed something
like that you probably would need get_unaligned_le16() or
get_unaligned-be16()).

> +}
> +*/
> +static s32 rm31100_ts_write_reg_u8(struct i2c_client *client, u8 reg, u8 val)

Why s32? Just return "int".

> +{
> +	s32 data;
> +
> +	data = i2c_smbus_write_byte_data(client, reg, val);
> +	if (data < 0)
> +		dev_err(&client->dev, "error %d in writing reg 0x%x\n",
> +						 data, reg);
> +
> +	return data;
> +}
> +
> +static s32 rm31100_ts_read_reg_u8(struct i2c_client *client, u8 reg)

Same here.

> +{
> +	s32 data;
> +
> +	data = i2c_smbus_read_byte_data(client, reg);
> +	if (data < 0)
> +		dev_err(&client->dev, "error %d in reading reg 0x%x\n",
> +						 data, reg);
> +
> +	return data;
> +}
> +
> +static int rm31100_ts_read(struct i2c_client *client, u8 reg, u8 *buf, int num)
> +{
> +	struct i2c_msg xfer_msg[2];
> +
> +	xfer_msg[0].addr = client->addr;
> +	xfer_msg[0].len = 1;
> +	xfer_msg[0].flags = 0;
> +	xfer_msg[0].buf = &reg;
> +
> +	xfer_msg[1].addr = client->addr;
> +	xfer_msg[1].len = num;
> +	xfer_msg[1].flags = I2C_M_RD;
> +	xfer_msg[1].buf = buf;
> +
> +	return i2c_transfer(client->adapter, xfer_msg, 2);

ARRAY_SIZE(xfer_msg).

> +}
> +
> +static int rm31100_ts_write(struct i2c_client *client, u8 *buf, int num)
> +{
> +	struct i2c_msg xfer_msg[1];
> +
> +	xfer_msg[0].addr = client->addr;
> +	xfer_msg[0].len = num;
> +	xfer_msg[0].flags = 0;
> +	xfer_msg[0].buf = buf;
> +
> +	return i2c_transfer(client->adapter, xfer_msg, 1);
> +}
> +
> +static ssize_t rm_fw_version_show(struct device *dev,
> +				      struct device_attribute *attr,
> +				      char *buf)
> +{
> +	struct rm31100_ts *info = dev_get_drvdata(dev);
> +	return sprintf(buf, "Release V 0x%02X, Test V 0x%02X\n",
> +		info.u8_version,
> +		info.u8_sub_version);
> +}

We should have 1 value per sysfs attribute, so please split it on 2:
firmware version and test or subversion and output simply number. This
way it is much easier to process them in scripts.

> +
> +static DEVICE_ATTR(fw_version, S_IRUGO, rm_fw_version_show, NULL);
> +
> +static struct attribute *rm_ts_attributes[] = {
> +	&dev_attr_fw_version.attr,
> +	NULL
> +};
> +
> +static const struct attribute_group rm_ts_attr_group = {
> +	.attrs = rm_ts_attributes,
> +};
> +
> +static void report_data(struct rm31100_ts *dev, u16 x, u16 y,
> +	u8 pressure, u8 id)
> +{
> +	struct input_dev *input_dev = dev->input;
> +	if (dev->pdata->swap_xy)
> +		swap(x, y);
> +
> +	input_mt_slot(input_dev, id);
> +	input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, true);
> +	input_report_abs(input_dev, ABS_MT_POSITION_X, x);
> +	input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
> +	input_report_abs(input_dev, ABS_MT_PRESSURE, pressure);
> +	input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, dev->dd->finger_size);
> +}
> +
> +static void process_rm31100_data(struct rm31100_ts *ts)
> +{
> +	u8 id, pressure, touches, i;
> +	u16 x, y;
> +
> +	touches = ts->touch_data[ts->dd->touch_index];
> +
> +	if (touches > 0) {
> +		for (i = 0; i < touches; i++) {
> +			id = ts->touch_data[i * ts->dd->touch_bytes +
> +				ts->dd->id_index];
> +			pressure = ts->touch_data[i * ts->dd->touch_bytes +
> +				ts->dd->z_index];
> +			x = get_unaligned_le16(&(ts->touch_data[i *
> +				ts->dd->touch_bytes + ts->dd->x_index]));
> +			y = get_unaligned_le16(&(ts->touch_data[i *
> +				ts->dd->touch_bytes + ts->dd->y_index]));
> +			report_data(ts, x, y, pressure, id);
> +		}
> +	} else
> +		input_mt_sync(ts->input);
> +
> +	ts->prev_touches = touches;

Why do you need prev_touches? I do not see you reporting touch lifting
the surface, do you need to also pass INPUT_MT_DROP_UNUSED flag to
input_mt_init_slots()? 

> +
> +	input_mt_report_pointer_emulation(ts->input, true);
> +	input_sync(ts->input);
> +}
> +
> +static void rm31100_ts_xy_worker(struct rm31100_ts *work)
> +{
> +	int rc;
> +	struct rm31100_ts *ts = work;
> +
> +	mutex_lock(&ts->access_lock);

Why do we need to take the lock here? What are we trying to protect? The
interrupt thread is not reentrable.

> +	/* read data from DATA_REG */
> +	rc = rm31100_ts_read(ts->client, ts->dd->data_reg, ts->touch_data,
> +	ts->dd->data_size);
> +
> +	if (rc < 0) {
> +		dev_err(&ts->client->dev, "read failed\n");
> +		goto schedule;
> +	}
> +
> +	if (ts->touch_data[ts->dd->touch_index] == INVALID_DATA)
> +		goto schedule;
> +
> +	/* write to STATUS_REG to release lock */
> +	rc = rm31100_ts_write_reg_u8(ts->client,
> +		ts->dd->status_reg, ts->dd->update_data);
> +	if (rc < 0) {
> +		dev_err(&ts->client->dev, "write failed, try once more\n");
> +
> +		rc = rm31100_ts_write_reg_u8(ts->client,
> +			ts->dd->status_reg, ts->dd->update_data);
> +		if (rc < 0)
> +			dev_err(&ts->client->dev, "write failed, exiting\n");
> +	}
> +
> +	process_rm31100_data(ts);
> +schedule:
> +	mutex_unlock(&ts->access_lock);
> +}
> +
> +static irqreturn_t rm31100_ts_irq(int irq, void *dev_id)
> +{
> +	struct rm31100_ts *ts = dev_id;
> +
> +	rm31100_ts_xy_worker(ts);
> +	return IRQ_HANDLED;


I'd probably merge rm31100_ts_xy_worker() into rm31100_ts_irq().

> +}
> +
> +static int rm_ts_power_on(struct rm31100_ts *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->resout_gpio))
> +		return 0;
> +
> +	gpiod_set_value_cansleep(ts->resout_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->dvdd);
> +	if (error) {
> +		dev_err(&ts->client->dev,
> +			"failed to enable dvdd regulator: %d\n",
> +			error);
> +		regulator_disable(ts->dvdd);
> +		goto release_reset_gpio;
> +	}
> +
> +release_reset_gpio:
> +	gpiod_set_value_cansleep(ts->resout_gpio, 0);
> +	if (error)
> +		return error;
> +
> +	msleep(20);
> +
> +	return 0;
> +}
> +
> +static void rm_ts_power_off(void *_data)
> +{
> +	struct rm31100_ts *data = _data;
> +
> +	if (!IS_ERR_OR_NULL(data->resout_gpio)) {
> +		/*
> +		 * Activate reset gpio to prevent leakage through the
> +		 * pin once we shut off power to the controller.
> +		 */
> +		gpiod_set_value_cansleep(data->resout_gpio, 1);
> +		regulator_disable(data->avdd);
> +		regulator_disable(data->dvdd);
> +	}
> +}
> +
> +static int rm31100_ts_init_ts(struct i2c_client *client, struct rm31100_ts *ts)
> +{
> +	/*struct input_dev *input_device;*/
> +	/*int rc = 0;*/
> +
> +	ts->dd = &devices[ts->device_id];
> +
> +	if (!ts->pdata->nfingers) {
> +		dev_err(&client->dev, "Touches information not specified\n");
> +		return -EINVAL;
> +	}
> +
> +	if (ts->device_id == rm3110x) {
> +		if (ts->pdata->nfingers > 2) {
> +			dev_err(&client->dev, "Touches >=1 & <= 2\n");
> +			return -EINVAL;
> +		}
> +		ts->dd->data_size = ts->dd->touch_bytes;
> +		ts->dd->touch_index = 0x0;
> +	} else if (ts->device_id == rm31100) {
> +		if (ts->pdata->nfingers > 10) {
> +			dev_err(&client->dev, "Touches >=1 & <= 10\n");
> +			return -EINVAL;
> +		}
> +		ts->dd->data_size = ts->pdata->nfingers * ts->dd->touch_bytes +
> +						ts->dd->touch_meta_data;
> +		ts->dd->touch_index = 0x0;
> +	}
> +	/* w001 */
> +	else {
> +		ts->dd->data_size = ts->pdata->nfingers * ts->dd->touch_bytes +
> +		ts->dd->touch_meta_data;
> +		ts->dd->touch_index = 0x0;
> +	}
> +
> +	ts->touch_data = kzalloc(ts->dd->data_size, GFP_KERNEL);
> +	if (!ts->touch_data) {
> +		pr_err("%s: Unable to allocate memory\n", __func__);
> +		return -ENOMEM;
> +	}
> +	return 0;
> +}
> +
> +static int __maybe_unused rm31100_ts_suspend(struct device *dev)
> +{
> +	struct rm31100_ts *ts = dev_get_drvdata(dev);
> +	int rc = 0;
> +
> +	if (device_may_wakeup(dev)) {
> +		/* mark suspend flag */
> +		ts->is_suspended = true;
> +		enable_irq_wake(ts->pen_irq);
> +	}
> +
> +	disable_irq(ts->pen_irq);
> +
> +	gpiod_set_value_cansleep(ts->resout_gpio, 0);

Why do we set it as "inactive" only to set it to "active" in
rm_ts_power_off()?

> +
> +	rm_ts_power_off(ts);
> +
> +	return 0;
> +}
> +
> +static int __maybe_unused rm31100_ts_resume(struct device *dev)
> +{
> +	struct rm31100_ts *ts = dev_get_drvdata(dev);
> +
> +	int rc = 0;
> +
> +	if (device_may_wakeup(dev)) {
> +		disable_irq_wake(ts->pen_irq);
> +
> +		ts->is_suspended = false;
> +
> +		if (ts->int_pending == true)
> +			ts->int_pending = false;

Why do you need this?

> +	} else {
> +		rc = rm_ts_power_on(ts);
> +		if (rc) {
> +			dev_err(dev, "unable to resume\n");
> +			return rc;
> +		}
> +
> +		enable_irq(ts->pen_irq);
> +
> +		/* Clear the status register of the TS controller */
> +		rc = rm31100_ts_write_reg_u8(ts->client,
> +			ts->dd->status_reg, ts->dd->update_data);
> +		if (rc < 0) {
> +			dev_err(&ts->client->dev,
> +				"write failed, try once more\n");
> +
> +			rc = rm31100_ts_write_reg_u8(ts->client,
> +				ts->dd->status_reg,
> +				ts->dd->update_data);
> +			if (rc < 0)
> +				dev_err(&ts->client->dev,
> +					"write failed, exiting\n");
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static void rm_ts_remove_sysfs_group(void *_data)
> +{
> +	struct rm31100_ts *ts = _data;
> +
> +	sysfs_remove_group(&ts->client->dev.kobj, &rm_ts_attr_group);
> +}
> +
> +static SIMPLE_DEV_PM_OPS(rm31100_ts_pm_ops,
> +			 rm31100_ts_suspend, rm31100_ts_resume);
> +
> +static int rm_input_dev_create(struct rm31100_ts *ts)
> +{
> +	struct input_dev *input_device;
> +	int rc = 0;
> +	ts->prev_touches = 0;
> +
> +	input_device = input_allocate_device();
> +	if (!input_device) {
> +		rc = -ENOMEM;
> +		goto error_alloc_dev;
> +	}
> +	ts->input = input_device;
> +
> +	input_device->name = "raydium_ts";
> +	input_device->id.bustype = BUS_I2C;
> +	input_device->dev.parent = &ts->client->dev;
> +	input_set_drvdata(input_device, ts);
> +
> +	__set_bit(BTN_TOUCH, input_device->keybit);
> +	__set_bit(EV_ABS, input_device->evbit);
> +	__set_bit(EV_KEY, input_device->evbit);
> +
> +	/* For single touch */
> +	input_set_abs_params(input_device, ABS_X,
> +			     ts->pdata->dis_min_x, ts->pdata->dis_max_x, 0, 0);
> +	input_set_abs_params(input_device, ABS_Y,
> +			     ts->pdata->dis_min_x, ts->pdata->dis_max_y, 0, 0);
> +	input_set_abs_params(input_device, ABS_PRESSURE,
> +			     0, 255, 0, 0);
> +	input_abs_set_res(input_device, ABS_X, ts->pdata->res_x);
> +	input_abs_set_res(input_device, ABS_Y, ts->pdata->res_y);
> +
> +	/* Multitouch input params setup */
> +	rc = input_mt_init_slots(input_device,
> +		MAX_REPORT_TOUCHED_POINTS, INPUT_MT_DIRECT);
> +	if (rc)
> +		goto error_unreg_device;
> +
> +	input_set_abs_params(input_device, ABS_MT_POSITION_X,
> +		ts->pdata->dis_min_x, ts->pdata->dis_max_x, 0, 0);
> +	input_set_abs_params(input_device, ABS_MT_POSITION_Y,
> +		ts->pdata->dis_min_y, ts->pdata->dis_max_y, 0, 0);
> +	input_set_abs_params(input_device, ABS_MT_PRESSURE,
> +		0, 0xFF, 0, 0);
> +	input_set_abs_params(input_device, ABS_MT_TOUCH_MAJOR,
> +		0, 0xFF, 0, 0);
> +	input_abs_set_res(input_device, ABS_MT_POSITION_X, ts->pdata->res_x);
> +	input_abs_set_res(input_device, ABS_MT_POSITION_Y, ts->pdata->res_y);
> +
> +	rc = input_register_device(input_device);
> +	if (rc)
> +		goto error_unreg_device;
> +
> +	return 0;
> +
> +error_unreg_device:
> +	input_free_device(input_device);
> +error_alloc_dev:
> +	ts->input = NULL;
> +	return rc;
> +}
> +
> +static int rm31100_initialize(struct i2c_client *client)
> +{
> +	struct rm31100_ts *ts = i2c_get_clientdata(client);
> +	int rc = 0, temp_reg;
> +
> +	/* read one byte to make sure i2c device exists */
> +	if (ts->device_id == rm3110x)
> +		temp_reg = 0x01;
> +	else if (ts->device_id == rm31100)
> +		temp_reg = 0x00;
> +	else
> +		temp_reg = 0x05;
> +
> +	rc = rm31100_ts_read_reg_u8(client, temp_reg);
> +	if (rc < 0) {
> +		dev_err(&client->dev, "i2c sanity check failed\n");
> +		return rc;
> +	}
> +
> +	rc = rm31100_ts_init_ts(client, ts);
> +	if (rc < 0) {
> +		dev_err(&client->dev, "rm31100_ts init failed\n");
> +		return rc;
> +	}
> +
> +	return 0;
> +}
> +
> +static int rm31100_ts_probe(struct i2c_client *client,
> +			const struct i2c_device_id *id)
> +{
> +	struct rm31100_ts *ts;
> +	struct rm3110x_ts_platform_data *pdata = client->dev.platform_data;
> +	int rc;
> +	union i2c_smbus_data dummy;
> +
> +	ts = devm_kzalloc(&client->dev, sizeof(struct rm31100_ts), GFP_KERNEL);
> +	if (!ts) {
> +		dev_err(&client->dev, "Failed to allocate memory\n");
> +		return -ENOMEM;
> +	}
> +
> +	if (!i2c_check_functionality(client->adapter,
> +		I2C_FUNC_SMBUS_READ_WORD_DATA)) {
> +		dev_err(&client->dev, "I2C functionality not supported\n");
> +		rc = -EIO;
> +		goto error_touch_data_alloc;
> +	}
> +
> +	ts->client = client;
> +	ts->pdata = pdata;
> +	i2c_set_clientdata(client, ts);
> +	ts->device_id = id->driver_data;
> +
> +	ts->is_suspended = false;
> +	ts->int_pending = false;
> +
> +	mutex_init(&ts->access_lock);
> +
> +	ts->avdd = devm_regulator_get(&client->dev, "avdd");
> +	if (IS_ERR(ts->avdd)) {
> +		rc = PTR_ERR(ts->avdd);
> +		if (rc != -EPROBE_DEFER)
> +			dev_err(&client->dev,
> +				"Failed to get 'avdd' regulator: %d\n",
> +				rc);
> +		return rc;
> +	}
> +
> +	ts->dvdd = devm_regulator_get(&client->dev, "dvdd");
> +	if (IS_ERR(ts->dvdd)) {
> +		rc = PTR_ERR(ts->dvdd);
> +		if (rc != -EPROBE_DEFER)
> +			dev_err(&client->dev,
> +				"Failed to get 'dvdd' regulator: %d\n",
> +				rc);
> +		return rc;
> +	}
> +
> +	ts->resout_gpio = devm_gpiod_get(&client->dev, "rm31100_resout_gpio");
> +	if (IS_ERR(ts->resout_gpio)) {
> +		rc = PTR_ERR(ts->resout_gpio);
> +
> +		/*
> +		 * On Chromebooks vendors like to source touch panels from
> +		 * different vendors, but they are connected to the same
> +		 * regulators/GPIO pin. The drivers also use asynchronous
> +		 * probing, which means that more than one driver will
> +		 * attempt to claim the reset line. If we find it busy,
> +		 * let's try again later.
> +		 */
> +		if (rc == -EBUSY) {
> +			dev_info(&client->dev,
> +				 "reset gpio is busy, deferring probe\n");
> +			return -EPROBE_DEFER;
> +		}

This is Chrome OS specific and is not needed for mainline. Simply do:


	ts->resout_gpio = devm_gpiod_get_optional(&client->dev,
						  "resout", GPIOF_OUT_LOW);
	if (IS_ERR(ts->resout_gpio)) {
		error = PTR_ERR(ts->resout_gpio);
		if (error != -EPROBE_DEFER)
			dev_err(...);
		return error;
	}

> +
> +		if (rc == -EPROBE_DEFER)
> +			return -EPROBE_DEFER;
> +
> +		if (rc != -ENOENT && rc != -ENOSYS) {
> +			dev_err(&client->dev,
> +				"failed to get reset gpio: %d\n",
> +				rc);
> +			return rc;
> +		}
> +
> +	} else {
> +		rc = gpiod_direction_output(ts->resout_gpio, 0);
> +		if (rc) {
> +			dev_err(&client->dev,
> +				"failed to configure reset gpio as output: %d\n",
> +				rc);
> +			return rc;
> +		}
> +	}
> +
> +	rc = rm_ts_power_on(ts);
> +	if (rc)
> +		return rc;
> +
> +	rc = devm_add_action(&client->dev, rm_ts_power_off, ts);
> +	if (rc) {
> +		dev_err(&client->dev,
> +			"failed to install power off action: %d\n", rc);
> +		rm_ts_power_off(ts);
> +		return rc;
> +	}
> +
> +	/* 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)
> +		return -ENODEV;
> +
> +	rc = rm31100_initialize(client);
> +	if (rc < 0) {
> +		dev_err(&client->dev, "probe failed! unbind device.\n");
> +		return rc;
> +	}
> +
> +	rc = rm_input_dev_create(ts);
> +	if (rc) {
> +		dev_err(&client->dev, "%s crated failed, %d\n", __func__, err);
> +		return rc;
> +	}
> +
> +	rc = devm_request_threaded_irq(&client->dev, ts->pen_irq,
> +					NULL, rm31100_ts_irq,
> +					IRQF_TRIGGER_RISING | IRQF_ONESHOT,
> +					client->name, ts);
> +	if (rc) {
> +		dev_err(&client->dev, "Failed to register interrupt\n");
> +		return rc;
> +	}
> +
> +	device_set_wakeup_enable(&client->dev, false);

Why? Let platform code decide, drop this line.

> +
> +	rc = sysfs_create_group(&client->dev.kobj, &rm_ts_attr_group);
> +	if (rc) {
> +		dev_err(&client->dev, "failed to create sysfs attributes: %d\n",
> +			rc);
> +		return rc;
> +	}
> +
> +	rc = devm_add_action(&client->dev,
> +				rm_ts_remove_sysfs_group, ts);
> +	if (rc) {
> +		rm_ts_remove_sysfs_group(ts);
> +		dev_err(&client->dev,
> +			"Failed to add sysfs cleanup action: %d\n",
> +			rc);
> +		return rc;
> +	}
> +	return 0;
> +
> +error_touch_data_alloc:
> +	kfree(ts);
> +	return rc;

You can't kfree() data allocated with devm_kzalloc(), nor shoudl you -
it will deallocate automatically.

> +}
> +
> +static int rm31100_ts_remove(struct i2c_client *client)
> +{
> +	struct rm31100_ts *ts = i2c_get_clientdata(client);
> +
> +	device_init_wakeup(&client->dev, 0);
> +	free_irq(ts->pen_irq, ts);

No, it was requested with devm_*

> +
> +	if (ts->resout_gpio >= 0)
> +		gpiod_set_value_cansleep(ts->resout_gpio, 0);

Why do we do this here?

> +
> +	input_unregister_device(ts->input);

Manually-managed and devm-managed resources do not play well with each
other, you better switch all of them to devm, including input device.

> +
> +	/*mutex_destroy(&ts->sus_lock); JL remove*/

Why i it still here if it is marked for removal?

> +	mutex_destroy(&ts->access_lock);
> +
> +	rm_ts_power_off(ts);
> +
> +	kfree(ts->touch_data);
> +	kfree(ts);
> +
> +	return 0;
> +}
> +
> +static const struct i2c_device_id rm31100_ts_id[] = {
> +	{"RM31100", rm31100},
> +	{"RM3110x", rm3110x},

Instead of using a constant and then doing if/else if/else to reference
your devices[] array why don't you attach instance of rm31100_ts_data
directly?

> +	{}
> +};
> +MODULE_DEVICE_TABLE(i2c, rm31100_ts_id);
> +
> +
> +static struct i2c_driver rm31100_ts_driver = {
> +	.driver = {
> +		.name = "raydium_ts",
> +		.owner = THIS_MODULE,

No need to set owner explicitly.

> +#ifdef CONFIG_PM
> +		.pm = &rm31100_ts_pm_ops,
> +#endif

No need for #ifdef here.

> +	},
> +	.probe = rm31100_ts_probe,
> +	.remove = rm31100_ts_remove,
> +	.id_table = rm31100_ts_id,
> +};
> +
> +static int __init rm31100_ts_init(void)
> +{
> +	int rc;
> +	int rc2;
> +
> +	rc = i2c_add_driver(&rm31100_ts_driver);
> +
> +	rc2 = driver_create_file(&rm31100_ts_driver.driver,
> +		&driver_attr_myAttr);

What is this for? I suspect you do not need this, so init/exit can be
folded into module_i2c_driver() macro.

> +
> +	return rc;
> +}
> +/* Making this as late init to avoid power fluctuations
> + * during LCD initialization.
> + */
> +module_init(rm31100_ts_init);
> +
> +static void __exit rm31100_ts_exit(void)
> +{
> +	i2c_del_driver(&rm31100_ts_driver);
> +}
> +module_exit(rm31100_ts_exit);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("rm31100-rm3110x touchscreen controller driver");
> +MODULE_AUTHOR("Raydium");
> +MODULE_ALIAS("platform:rm31100_ts");
> -- 
> 2.1.2
> 

Thanks.

-- 
Dmitry

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

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2016-01-13  7:49 Jeffrey Lin
@ 2016-01-13  8:31 ` Dmitry Torokhov
  2016-01-13 19:14 ` Dmitry Torokhov
  1 sibling, 0 replies; 58+ messages in thread
From: Dmitry Torokhov @ 2016-01-13  8:31 UTC (permalink / raw)
  To: Jeffrey Lin
  Cc: Henrik Rydberg, Doug Anderson, Benson Leung, Scott Liu,
	jeffrey.lin, roger.yang, KP.li, lkml, linux-input

Jeffrey,

On Tue, Jan 12, 2016 at 11:49 PM, Jeffrey Lin <yajohn@gmail.com> wrote:
> 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/rm31100_ts.c | 772 +++++++++++++++++++++++++++++++++
>  3 files changed, 785 insertions(+)
>  create mode 100644 drivers/input/touchscreen/rm31100_ts.c

Thank you for sending the full patch. Unfortunately it does not even compile:

dtor@dtor-ws:~/kernel/work$ make drivers/input/touchscreen/rm31100_ts.o
  CHK     include/config/kernel.release
  CHK     include/generated/uapi/linux/version.h
  CHK     include/generated/utsrelease.h
  CHK     include/generated/bounds.h
  CHK     include/generated/timeconst.h
  CHK     include/generated/asm-offsets.h
  CALL    scripts/checksyscalls.sh
  CC [M]  drivers/input/touchscreen/rm31100_ts.o
drivers/input/touchscreen/rm31100_ts.c: In function ‘rm_fw_version_show’:
drivers/input/touchscreen/rm31100_ts.c:190:7: error: request for
member ‘u8_version’ in something not a structure or union
   info.u8_version,
       ^
drivers/input/touchscreen/rm31100_ts.c:191:7: error: request for
member ‘u8_sub_version’ in something not a structure or union
   info.u8_sub_version);
       ^
drivers/input/touchscreen/rm31100_ts.c: In function ‘rm_ts_power_on’:
drivers/input/touchscreen/rm31100_ts.c:304:2: error: implicit
declaration of function ‘regulator_enable’
[-Werror=implicit-function-declaration]
  error = regulator_enable(ts->avdd);
  ^
drivers/input/touchscreen/rm31100_ts.c:317:3: error: implicit
declaration of function ‘regulator_disable’
[-Werror=implicit-function-declaration]
   regulator_disable(ts->dvdd);
   ^
drivers/input/touchscreen/rm31100_ts.c: In function ‘rm31100_ts_suspend’:
drivers/input/touchscreen/rm31100_ts.c:392:6: warning: unused variable
‘rc’ [-Wunused-variable]
  int rc = 0;
      ^
drivers/input/touchscreen/rm31100_ts.c: In function ‘rm31100_ts_probe’:
drivers/input/touchscreen/rm31100_ts.c:581:2: error: implicit
declaration of function ‘devm_regulator_get’
[-Werror=implicit-function-declaration]
  ts->avdd = devm_regulator_get(&client->dev, "avdd");
  ^
drivers/input/touchscreen/rm31100_ts.c:581:11: warning: assignment
makes pointer from integer without a cast [enabled by default]
  ts->avdd = devm_regulator_get(&client->dev, "avdd");
           ^
drivers/input/touchscreen/rm31100_ts.c:591:11: warning: assignment
makes pointer from integer without a cast [enabled by default]
  ts->dvdd = devm_regulator_get(&client->dev, "dvdd");
           ^
drivers/input/touchscreen/rm31100_ts.c:601:2: error: too few arguments
to function ‘devm_gpiod_get’
  ts->resout_gpio = devm_gpiod_get(&client->dev, "rm31100_resout_gpio");
  ^
In file included from drivers/input/touchscreen/rm31100_ts.c:31:0:
include/linux/gpio/consumer.h:73:32: note: declared here
 struct gpio_desc *__must_check devm_gpiod_get(struct device *dev,
                                ^
drivers/input/touchscreen/rm31100_ts.c:664:61: error: ‘err’ undeclared
(first use in this function)
   dev_err(&client->dev, "%s crated failed, %d\n", __func__, err);
                                                             ^
drivers/input/touchscreen/rm31100_ts.c:664:61: note: each undeclared
identifier is reported only once for each function it appears in
drivers/input/touchscreen/rm31100_ts.c: In function ‘rm31100_ts_init’:
drivers/input/touchscreen/rm31100_ts.c:754:4: error:
‘driver_attr_myAttr’ undeclared (first use in this function)
   &driver_attr_myAttr);
    ^
drivers/input/touchscreen/rm31100_ts.c: At top level:
drivers/input/touchscreen/rm31100_ts.c:172:12: warning:
‘rm31100_ts_write’ defined but not used [-Wunused-function]
 static int rm31100_ts_write(struct i2c_client *client, u8 *buf, int num)
            ^
drivers/input/touchscreen/rm31100_ts.c: In function ‘rm_fw_version_show’:
drivers/input/touchscreen/rm31100_ts.c:192:1: warning: control reaches
end of non-void function [-Wreturn-type]
 }
 ^
cc1: some warnings being treated as errors
make[1]: *** [drivers/input/touchscreen/rm31100_ts.o] Error 1
make: *** [drivers/input/touchscreen/rm31100_ts.o] Error 2
dtor@dtor-ws:~/kernel/work$

Please next time submit version that you actually tested.

Thank you.

-- 
Dmitry

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

* [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
@ 2016-01-13  7:49 Jeffrey Lin
  2016-01-13  8:31 ` Dmitry Torokhov
  2016-01-13 19:14 ` Dmitry Torokhov
  0 siblings, 2 replies; 58+ messages in thread
From: Jeffrey Lin @ 2016-01-13  7:49 UTC (permalink / raw)
  To: dmitry.torokhov, rydberg, dianders, bleung, scott.liu
  Cc: jeffrey.lin, roger.yang, KP.li, linux-kernel, linux-input

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/rm31100_ts.c | 772 +++++++++++++++++++++++++++++++++
 3 files changed, 785 insertions(+)
 create mode 100644 drivers/input/touchscreen/rm31100_ts.c

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 0f13e52..4790e36 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -955,4 +955,16 @@ config TOUCHSCREEN_ZFORCE
 	  To compile this driver as a module, choose M here: the
 	  module will be called zforce_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 rm31100_ts.
+
 endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 687d5a7..3220f66 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -78,3 +78,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE)	+= zylonite-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_W90X900)	+= w90p910_ts.o
 obj-$(CONFIG_TOUCHSCREEN_TPS6507X)	+= tps6507x-ts.o
 obj-$(CONFIG_TOUCHSCREEN_ZFORCE)	+= zforce_ts.o
+obj-$(CONFIG_TOUCHSCREEN_RM_TS)		+= rm31100_ts.o
diff --git a/drivers/input/touchscreen/rm31100_ts.c b/drivers/input/touchscreen/rm31100_ts.c
new file mode 100644
index 0000000..e024bbd
--- /dev/null
+++ b/drivers/input/touchscreen/rm31100_ts.c
@@ -0,0 +1,772 @@
+/*
+ * Raydium RM31100_ts touchscreen 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/async.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio/consumer.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/uaccess.h>
+#include <asm/unaligned.h>
+#include <linux/input/mt.h>
+
+#define rm31100	0x0
+#define rm3110x	0x1
+
+#define INVALID_DATA	0xff
+#define MAX_REPORT_TOUCHED_POINTS	10
+
+#define I2C_CLIENT_ADDR         0x39
+#define I2C_DMA_CLIENT_ADDR     0x5A
+
+struct rm31100_ts_data {
+	u8 x_index;
+	u8 y_index;
+	u8 z_index;
+	u8 id_index;
+	u8 touch_index;
+	u8 data_reg;
+	u8 status_reg;
+	u8 data_size;
+	u8 touch_bytes;
+	u8 update_data;
+	u8 touch_meta_data;
+	u8 finger_size;
+};
+
+struct rm3110x_ts_platform_data {
+	u32 dis_min_x; /* display resoltion ABS min*/
+	u32 dis_max_x;/* display resoltion ABS max*/
+	u32 dis_min_y;
+	u32 dis_max_y;
+	u32 res_x; /* TS resolution unit*/
+	u32 res_y;
+	u32 swap_xy;
+	u8 nfingers;
+	bool wakeup;
+};
+
+static struct rm31100_ts_data devices[] = {
+	[0] = {
+		.x_index = 2,
+		.y_index = 4,
+		.z_index = 6,
+		.id_index = 1,
+		.data_reg = 0x1,
+		.status_reg = 0,
+		.update_data = 0x0,
+		.touch_bytes = 6,
+		.touch_meta_data = 1,
+		.finger_size = 70,
+	},
+	[1] = {
+		.x_index = 2,
+		.y_index = 4,
+		.z_index = 6,
+		.id_index = 1,
+		.data_reg = 0x0,
+		.status_reg = 0,
+		.update_data = 0x0,
+		.touch_bytes = 6,
+		.touch_meta_data = 1,
+		.finger_size = 70,
+	},
+};
+
+struct rm31100_ts {
+	struct i2c_client *client;
+	struct input_dev *input;
+	struct regulator *dvdd;
+	struct regulator *avdd;
+	struct gpio_desc *resout_gpio;
+	struct rm3110x_ts_platform_data *pdata;
+	struct rm31100_ts_data *dd;
+	u8 *touch_data;
+	u8 device_id;
+	u8 prev_touches;
+	bool is_suspended;
+	bool int_pending;
+	struct mutex access_lock;
+	u32 pen_irq;
+	u8 fw_version;
+	u8 u8_sub_version;
+};
+
+/*
+static inline u16 join_bytes(u8 a, u8 b)
+{
+	u16 ab = 0;
+	ab = ab | a;
+	ab = ab << 8 | b;
+	return ab;
+}
+*/
+static s32 rm31100_ts_write_reg_u8(struct i2c_client *client, u8 reg, u8 val)
+{
+	s32 data;
+
+	data = i2c_smbus_write_byte_data(client, reg, val);
+	if (data < 0)
+		dev_err(&client->dev, "error %d in writing reg 0x%x\n",
+						 data, reg);
+
+	return data;
+}
+
+static s32 rm31100_ts_read_reg_u8(struct i2c_client *client, u8 reg)
+{
+	s32 data;
+
+	data = i2c_smbus_read_byte_data(client, reg);
+	if (data < 0)
+		dev_err(&client->dev, "error %d in reading reg 0x%x\n",
+						 data, reg);
+
+	return data;
+}
+
+static int rm31100_ts_read(struct i2c_client *client, u8 reg, u8 *buf, int num)
+{
+	struct i2c_msg xfer_msg[2];
+
+	xfer_msg[0].addr = client->addr;
+	xfer_msg[0].len = 1;
+	xfer_msg[0].flags = 0;
+	xfer_msg[0].buf = &reg;
+
+	xfer_msg[1].addr = client->addr;
+	xfer_msg[1].len = num;
+	xfer_msg[1].flags = I2C_M_RD;
+	xfer_msg[1].buf = buf;
+
+	return i2c_transfer(client->adapter, xfer_msg, 2);
+}
+
+static int rm31100_ts_write(struct i2c_client *client, u8 *buf, int num)
+{
+	struct i2c_msg xfer_msg[1];
+
+	xfer_msg[0].addr = client->addr;
+	xfer_msg[0].len = num;
+	xfer_msg[0].flags = 0;
+	xfer_msg[0].buf = buf;
+
+	return i2c_transfer(client->adapter, xfer_msg, 1);
+}
+
+static ssize_t rm_fw_version_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf)
+{
+	struct rm31100_ts *info = dev_get_drvdata(dev);
+	return sprintf(buf, "Release V 0x%02X, Test V 0x%02X\n",
+		info.u8_version,
+		info.u8_sub_version);
+}
+
+static DEVICE_ATTR(fw_version, S_IRUGO, rm_fw_version_show, NULL);
+
+static struct attribute *rm_ts_attributes[] = {
+	&dev_attr_fw_version.attr,
+	NULL
+};
+
+static const struct attribute_group rm_ts_attr_group = {
+	.attrs = rm_ts_attributes,
+};
+
+static void report_data(struct rm31100_ts *dev, u16 x, u16 y,
+	u8 pressure, u8 id)
+{
+	struct input_dev *input_dev = dev->input;
+	if (dev->pdata->swap_xy)
+		swap(x, y);
+
+	input_mt_slot(input_dev, id);
+	input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, true);
+	input_report_abs(input_dev, ABS_MT_POSITION_X, x);
+	input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
+	input_report_abs(input_dev, ABS_MT_PRESSURE, pressure);
+	input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, dev->dd->finger_size);
+}
+
+static void process_rm31100_data(struct rm31100_ts *ts)
+{
+	u8 id, pressure, touches, i;
+	u16 x, y;
+
+	touches = ts->touch_data[ts->dd->touch_index];
+
+	if (touches > 0) {
+		for (i = 0; i < touches; i++) {
+			id = ts->touch_data[i * ts->dd->touch_bytes +
+				ts->dd->id_index];
+			pressure = ts->touch_data[i * ts->dd->touch_bytes +
+				ts->dd->z_index];
+			x = get_unaligned_le16(&(ts->touch_data[i *
+				ts->dd->touch_bytes + ts->dd->x_index]));
+			y = get_unaligned_le16(&(ts->touch_data[i *
+				ts->dd->touch_bytes + ts->dd->y_index]));
+			report_data(ts, x, y, pressure, id);
+		}
+	} else
+		input_mt_sync(ts->input);
+
+	ts->prev_touches = touches;
+
+	input_mt_report_pointer_emulation(ts->input, true);
+	input_sync(ts->input);
+}
+
+static void rm31100_ts_xy_worker(struct rm31100_ts *work)
+{
+	int rc;
+	struct rm31100_ts *ts = work;
+
+	mutex_lock(&ts->access_lock);
+	/* read data from DATA_REG */
+	rc = rm31100_ts_read(ts->client, ts->dd->data_reg, ts->touch_data,
+	ts->dd->data_size);
+
+	if (rc < 0) {
+		dev_err(&ts->client->dev, "read failed\n");
+		goto schedule;
+	}
+
+	if (ts->touch_data[ts->dd->touch_index] == INVALID_DATA)
+		goto schedule;
+
+	/* write to STATUS_REG to release lock */
+	rc = rm31100_ts_write_reg_u8(ts->client,
+		ts->dd->status_reg, ts->dd->update_data);
+	if (rc < 0) {
+		dev_err(&ts->client->dev, "write failed, try once more\n");
+
+		rc = rm31100_ts_write_reg_u8(ts->client,
+			ts->dd->status_reg, ts->dd->update_data);
+		if (rc < 0)
+			dev_err(&ts->client->dev, "write failed, exiting\n");
+	}
+
+	process_rm31100_data(ts);
+schedule:
+	mutex_unlock(&ts->access_lock);
+}
+
+static irqreturn_t rm31100_ts_irq(int irq, void *dev_id)
+{
+	struct rm31100_ts *ts = dev_id;
+
+	rm31100_ts_xy_worker(ts);
+	return IRQ_HANDLED;
+}
+
+static int rm_ts_power_on(struct rm31100_ts *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->resout_gpio))
+		return 0;
+
+	gpiod_set_value_cansleep(ts->resout_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->dvdd);
+	if (error) {
+		dev_err(&ts->client->dev,
+			"failed to enable dvdd regulator: %d\n",
+			error);
+		regulator_disable(ts->dvdd);
+		goto release_reset_gpio;
+	}
+
+release_reset_gpio:
+	gpiod_set_value_cansleep(ts->resout_gpio, 0);
+	if (error)
+		return error;
+
+	msleep(20);
+
+	return 0;
+}
+
+static void rm_ts_power_off(void *_data)
+{
+	struct rm31100_ts *data = _data;
+
+	if (!IS_ERR_OR_NULL(data->resout_gpio)) {
+		/*
+		 * Activate reset gpio to prevent leakage through the
+		 * pin once we shut off power to the controller.
+		 */
+		gpiod_set_value_cansleep(data->resout_gpio, 1);
+		regulator_disable(data->avdd);
+		regulator_disable(data->dvdd);
+	}
+}
+
+static int rm31100_ts_init_ts(struct i2c_client *client, struct rm31100_ts *ts)
+{
+	/*struct input_dev *input_device;*/
+	/*int rc = 0;*/
+
+	ts->dd = &devices[ts->device_id];
+
+	if (!ts->pdata->nfingers) {
+		dev_err(&client->dev, "Touches information not specified\n");
+		return -EINVAL;
+	}
+
+	if (ts->device_id == rm3110x) {
+		if (ts->pdata->nfingers > 2) {
+			dev_err(&client->dev, "Touches >=1 & <= 2\n");
+			return -EINVAL;
+		}
+		ts->dd->data_size = ts->dd->touch_bytes;
+		ts->dd->touch_index = 0x0;
+	} else if (ts->device_id == rm31100) {
+		if (ts->pdata->nfingers > 10) {
+			dev_err(&client->dev, "Touches >=1 & <= 10\n");
+			return -EINVAL;
+		}
+		ts->dd->data_size = ts->pdata->nfingers * ts->dd->touch_bytes +
+						ts->dd->touch_meta_data;
+		ts->dd->touch_index = 0x0;
+	}
+	/* w001 */
+	else {
+		ts->dd->data_size = ts->pdata->nfingers * ts->dd->touch_bytes +
+		ts->dd->touch_meta_data;
+		ts->dd->touch_index = 0x0;
+	}
+
+	ts->touch_data = kzalloc(ts->dd->data_size, GFP_KERNEL);
+	if (!ts->touch_data) {
+		pr_err("%s: Unable to allocate memory\n", __func__);
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+static int __maybe_unused rm31100_ts_suspend(struct device *dev)
+{
+	struct rm31100_ts *ts = dev_get_drvdata(dev);
+	int rc = 0;
+
+	if (device_may_wakeup(dev)) {
+		/* mark suspend flag */
+		ts->is_suspended = true;
+		enable_irq_wake(ts->pen_irq);
+	}
+
+	disable_irq(ts->pen_irq);
+
+	gpiod_set_value_cansleep(ts->resout_gpio, 0);
+
+	rm_ts_power_off(ts);
+
+	return 0;
+}
+
+static int __maybe_unused rm31100_ts_resume(struct device *dev)
+{
+	struct rm31100_ts *ts = dev_get_drvdata(dev);
+
+	int rc = 0;
+
+	if (device_may_wakeup(dev)) {
+		disable_irq_wake(ts->pen_irq);
+
+		ts->is_suspended = false;
+
+		if (ts->int_pending == true)
+			ts->int_pending = false;
+	} else {
+		rc = rm_ts_power_on(ts);
+		if (rc) {
+			dev_err(dev, "unable to resume\n");
+			return rc;
+		}
+
+		enable_irq(ts->pen_irq);
+
+		/* Clear the status register of the TS controller */
+		rc = rm31100_ts_write_reg_u8(ts->client,
+			ts->dd->status_reg, ts->dd->update_data);
+		if (rc < 0) {
+			dev_err(&ts->client->dev,
+				"write failed, try once more\n");
+
+			rc = rm31100_ts_write_reg_u8(ts->client,
+				ts->dd->status_reg,
+				ts->dd->update_data);
+			if (rc < 0)
+				dev_err(&ts->client->dev,
+					"write failed, exiting\n");
+		}
+	}
+
+	return 0;
+}
+
+static void rm_ts_remove_sysfs_group(void *_data)
+{
+	struct rm31100_ts *ts = _data;
+
+	sysfs_remove_group(&ts->client->dev.kobj, &rm_ts_attr_group);
+}
+
+static SIMPLE_DEV_PM_OPS(rm31100_ts_pm_ops,
+			 rm31100_ts_suspend, rm31100_ts_resume);
+
+static int rm_input_dev_create(struct rm31100_ts *ts)
+{
+	struct input_dev *input_device;
+	int rc = 0;
+	ts->prev_touches = 0;
+
+	input_device = input_allocate_device();
+	if (!input_device) {
+		rc = -ENOMEM;
+		goto error_alloc_dev;
+	}
+	ts->input = input_device;
+
+	input_device->name = "raydium_ts";
+	input_device->id.bustype = BUS_I2C;
+	input_device->dev.parent = &ts->client->dev;
+	input_set_drvdata(input_device, ts);
+
+	__set_bit(BTN_TOUCH, input_device->keybit);
+	__set_bit(EV_ABS, input_device->evbit);
+	__set_bit(EV_KEY, input_device->evbit);
+
+	/* For single touch */
+	input_set_abs_params(input_device, ABS_X,
+			     ts->pdata->dis_min_x, ts->pdata->dis_max_x, 0, 0);
+	input_set_abs_params(input_device, ABS_Y,
+			     ts->pdata->dis_min_x, ts->pdata->dis_max_y, 0, 0);
+	input_set_abs_params(input_device, ABS_PRESSURE,
+			     0, 255, 0, 0);
+	input_abs_set_res(input_device, ABS_X, ts->pdata->res_x);
+	input_abs_set_res(input_device, ABS_Y, ts->pdata->res_y);
+
+	/* Multitouch input params setup */
+	rc = input_mt_init_slots(input_device,
+		MAX_REPORT_TOUCHED_POINTS, INPUT_MT_DIRECT);
+	if (rc)
+		goto error_unreg_device;
+
+	input_set_abs_params(input_device, ABS_MT_POSITION_X,
+		ts->pdata->dis_min_x, ts->pdata->dis_max_x, 0, 0);
+	input_set_abs_params(input_device, ABS_MT_POSITION_Y,
+		ts->pdata->dis_min_y, ts->pdata->dis_max_y, 0, 0);
+	input_set_abs_params(input_device, ABS_MT_PRESSURE,
+		0, 0xFF, 0, 0);
+	input_set_abs_params(input_device, ABS_MT_TOUCH_MAJOR,
+		0, 0xFF, 0, 0);
+	input_abs_set_res(input_device, ABS_MT_POSITION_X, ts->pdata->res_x);
+	input_abs_set_res(input_device, ABS_MT_POSITION_Y, ts->pdata->res_y);
+
+	rc = input_register_device(input_device);
+	if (rc)
+		goto error_unreg_device;
+
+	return 0;
+
+error_unreg_device:
+	input_free_device(input_device);
+error_alloc_dev:
+	ts->input = NULL;
+	return rc;
+}
+
+static int rm31100_initialize(struct i2c_client *client)
+{
+	struct rm31100_ts *ts = i2c_get_clientdata(client);
+	int rc = 0, temp_reg;
+
+	/* read one byte to make sure i2c device exists */
+	if (ts->device_id == rm3110x)
+		temp_reg = 0x01;
+	else if (ts->device_id == rm31100)
+		temp_reg = 0x00;
+	else
+		temp_reg = 0x05;
+
+	rc = rm31100_ts_read_reg_u8(client, temp_reg);
+	if (rc < 0) {
+		dev_err(&client->dev, "i2c sanity check failed\n");
+		return rc;
+	}
+
+	rc = rm31100_ts_init_ts(client, ts);
+	if (rc < 0) {
+		dev_err(&client->dev, "rm31100_ts init failed\n");
+		return rc;
+	}
+
+	return 0;
+}
+
+static int rm31100_ts_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct rm31100_ts *ts;
+	struct rm3110x_ts_platform_data *pdata = client->dev.platform_data;
+	int rc;
+	union i2c_smbus_data dummy;
+
+	ts = devm_kzalloc(&client->dev, sizeof(struct rm31100_ts), GFP_KERNEL);
+	if (!ts) {
+		dev_err(&client->dev, "Failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	if (!i2c_check_functionality(client->adapter,
+		I2C_FUNC_SMBUS_READ_WORD_DATA)) {
+		dev_err(&client->dev, "I2C functionality not supported\n");
+		rc = -EIO;
+		goto error_touch_data_alloc;
+	}
+
+	ts->client = client;
+	ts->pdata = pdata;
+	i2c_set_clientdata(client, ts);
+	ts->device_id = id->driver_data;
+
+	ts->is_suspended = false;
+	ts->int_pending = false;
+
+	mutex_init(&ts->access_lock);
+
+	ts->avdd = devm_regulator_get(&client->dev, "avdd");
+	if (IS_ERR(ts->avdd)) {
+		rc = PTR_ERR(ts->avdd);
+		if (rc != -EPROBE_DEFER)
+			dev_err(&client->dev,
+				"Failed to get 'avdd' regulator: %d\n",
+				rc);
+		return rc;
+	}
+
+	ts->dvdd = devm_regulator_get(&client->dev, "dvdd");
+	if (IS_ERR(ts->dvdd)) {
+		rc = PTR_ERR(ts->dvdd);
+		if (rc != -EPROBE_DEFER)
+			dev_err(&client->dev,
+				"Failed to get 'dvdd' regulator: %d\n",
+				rc);
+		return rc;
+	}
+
+	ts->resout_gpio = devm_gpiod_get(&client->dev, "rm31100_resout_gpio");
+	if (IS_ERR(ts->resout_gpio)) {
+		rc = PTR_ERR(ts->resout_gpio);
+
+		/*
+		 * On Chromebooks vendors like to source touch panels from
+		 * different vendors, but they are connected to the same
+		 * regulators/GPIO pin. The drivers also use asynchronous
+		 * probing, which means that more than one driver will
+		 * attempt to claim the reset line. If we find it busy,
+		 * let's try again later.
+		 */
+		if (rc == -EBUSY) {
+			dev_info(&client->dev,
+				 "reset gpio is busy, deferring probe\n");
+			return -EPROBE_DEFER;
+		}
+
+		if (rc == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+
+		if (rc != -ENOENT && rc != -ENOSYS) {
+			dev_err(&client->dev,
+				"failed to get reset gpio: %d\n",
+				rc);
+			return rc;
+		}
+
+	} else {
+		rc = gpiod_direction_output(ts->resout_gpio, 0);
+		if (rc) {
+			dev_err(&client->dev,
+				"failed to configure reset gpio as output: %d\n",
+				rc);
+			return rc;
+		}
+	}
+
+	rc = rm_ts_power_on(ts);
+	if (rc)
+		return rc;
+
+	rc = devm_add_action(&client->dev, rm_ts_power_off, ts);
+	if (rc) {
+		dev_err(&client->dev,
+			"failed to install power off action: %d\n", rc);
+		rm_ts_power_off(ts);
+		return rc;
+	}
+
+	/* 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)
+		return -ENODEV;
+
+	rc = rm31100_initialize(client);
+	if (rc < 0) {
+		dev_err(&client->dev, "probe failed! unbind device.\n");
+		return rc;
+	}
+
+	rc = rm_input_dev_create(ts);
+	if (rc) {
+		dev_err(&client->dev, "%s crated failed, %d\n", __func__, err);
+		return rc;
+	}
+
+	rc = devm_request_threaded_irq(&client->dev, ts->pen_irq,
+					NULL, rm31100_ts_irq,
+					IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+					client->name, ts);
+	if (rc) {
+		dev_err(&client->dev, "Failed to register interrupt\n");
+		return rc;
+	}
+
+	device_set_wakeup_enable(&client->dev, false);
+
+	rc = sysfs_create_group(&client->dev.kobj, &rm_ts_attr_group);
+	if (rc) {
+		dev_err(&client->dev, "failed to create sysfs attributes: %d\n",
+			rc);
+		return rc;
+	}
+
+	rc = devm_add_action(&client->dev,
+				rm_ts_remove_sysfs_group, ts);
+	if (rc) {
+		rm_ts_remove_sysfs_group(ts);
+		dev_err(&client->dev,
+			"Failed to add sysfs cleanup action: %d\n",
+			rc);
+		return rc;
+	}
+	return 0;
+
+error_touch_data_alloc:
+	kfree(ts);
+	return rc;
+}
+
+static int rm31100_ts_remove(struct i2c_client *client)
+{
+	struct rm31100_ts *ts = i2c_get_clientdata(client);
+
+	device_init_wakeup(&client->dev, 0);
+	free_irq(ts->pen_irq, ts);
+
+	if (ts->resout_gpio >= 0)
+		gpiod_set_value_cansleep(ts->resout_gpio, 0);
+
+	input_unregister_device(ts->input);
+
+	/*mutex_destroy(&ts->sus_lock); JL remove*/
+	mutex_destroy(&ts->access_lock);
+
+	rm_ts_power_off(ts);
+
+	kfree(ts->touch_data);
+	kfree(ts);
+
+	return 0;
+}
+
+static const struct i2c_device_id rm31100_ts_id[] = {
+	{"RM31100", rm31100},
+	{"RM3110x", rm3110x},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, rm31100_ts_id);
+
+
+static struct i2c_driver rm31100_ts_driver = {
+	.driver = {
+		.name = "raydium_ts",
+		.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+		.pm = &rm31100_ts_pm_ops,
+#endif
+	},
+	.probe = rm31100_ts_probe,
+	.remove = rm31100_ts_remove,
+	.id_table = rm31100_ts_id,
+};
+
+static int __init rm31100_ts_init(void)
+{
+	int rc;
+	int rc2;
+
+	rc = i2c_add_driver(&rm31100_ts_driver);
+
+	rc2 = driver_create_file(&rm31100_ts_driver.driver,
+		&driver_attr_myAttr);
+
+	return rc;
+}
+/* Making this as late init to avoid power fluctuations
+ * during LCD initialization.
+ */
+module_init(rm31100_ts_init);
+
+static void __exit rm31100_ts_exit(void)
+{
+	i2c_del_driver(&rm31100_ts_driver);
+}
+module_exit(rm31100_ts_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("rm31100-rm3110x touchscreen controller driver");
+MODULE_AUTHOR("Raydium");
+MODULE_ALIAS("platform:rm31100_ts");
-- 
2.1.2

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

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2016-01-13  6:23 Jeffrey Lin
@ 2016-01-13  6:44 ` Dmitry Torokhov
  0 siblings, 0 replies; 58+ messages in thread
From: Dmitry Torokhov @ 2016-01-13  6:44 UTC (permalink / raw)
  To: Jeffrey Lin
  Cc: Henrik Rydberg, jeffrey.lin, roger.yang, KP.li, lkml, linux-input

Hi Jeffrey,

On Tue, Jan 12, 2016 at 10:23 PM, Jeffrey Lin <yajohn@gmail.com> wrote:
> 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/rm31100_ts.c | 571 +++++++++++++++------------------
>  1 file changed, 250 insertions(+), 321 deletions(-)
>
> diff --git a/drivers/input/touchscreen/rm31100_ts.c b/drivers/input/touchscreen/rm31100_ts.c
> index 941fa31..e024bbd 100644
> --- a/drivers/input/touchscreen/rm31100_ts.c
> +++ b/drivers/input/touchscreen/rm31100_ts.c
> @@ -28,14 +28,11 @@
>  #include <linux/slab.h>
>  #include <linux/interrupt.h>
>  #include <linux/irq.h>
> -#include <linux/gpio.h>
> +#include <linux/gpio/consumer.h>

You seem to have sent incremental patch against the version you sent
earlier. Until the driver gets accepted into mainline kernel please
continue posting the full patch.

Thanks.

-- 
Dmitry

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

* [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
@ 2016-01-13  6:23 Jeffrey Lin
  2016-01-13  6:44 ` Dmitry Torokhov
  0 siblings, 1 reply; 58+ messages in thread
From: Jeffrey Lin @ 2016-01-13  6:23 UTC (permalink / raw)
  To: dmitry.torokhov, rydberg
  Cc: jeffrey.lin, roger.yang, KP.li, linux-kernel, linux-input

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/rm31100_ts.c | 571 +++++++++++++++------------------
 1 file changed, 250 insertions(+), 321 deletions(-)

diff --git a/drivers/input/touchscreen/rm31100_ts.c b/drivers/input/touchscreen/rm31100_ts.c
index 941fa31..e024bbd 100644
--- a/drivers/input/touchscreen/rm31100_ts.c
+++ b/drivers/input/touchscreen/rm31100_ts.c
@@ -28,14 +28,11 @@
 #include <linux/slab.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
 #include <linux/mutex.h>
 #include <linux/delay.h>
 #include <linux/pm.h>
 #include <linux/pm_runtime.h>
-#ifdef CONFIG_MISC_DEV
-#include <linux/miscdevice.h>
-#endif
 #include <linux/uaccess.h>
 #include <asm/unaligned.h>
 #include <linux/input/mt.h>
@@ -65,27 +62,15 @@ struct rm31100_ts_data {
 };
 
 struct rm3110x_ts_platform_data {
-	int (*power_on)(int on);
-	int (*dev_setup)(bool on);
-	const char *ts_name;
 	u32 dis_min_x; /* display resoltion ABS min*/
 	u32 dis_max_x;/* display resoltion ABS max*/
 	u32 dis_min_y;
 	u32 dis_max_y;
-	u32 min_touch; /* no.of touches supported */
-	u32 max_touch;
-	u32 min_tid; /* track id */
-	u32 max_tid;
-	u32 min_width;/* size of the finger */
-	u32 max_width;
 	u32 res_x; /* TS resolution unit*/
 	u32 res_y;
 	u32 swap_xy;
 	u8 nfingers;
-	u32 irq_gpio;
-	int resout_gpio;
 	bool wakeup;
-	u32 irq_cfg;
 };
 
 static struct rm31100_ts_data devices[] = {
@@ -101,11 +86,26 @@ static struct rm31100_ts_data devices[] = {
 		.touch_meta_data = 1,
 		.finger_size = 70,
 	},
+	[1] = {
+		.x_index = 2,
+		.y_index = 4,
+		.z_index = 6,
+		.id_index = 1,
+		.data_reg = 0x0,
+		.status_reg = 0,
+		.update_data = 0x0,
+		.touch_bytes = 6,
+		.touch_meta_data = 1,
+		.finger_size = 70,
+	},
 };
 
 struct rm31100_ts {
 	struct i2c_client *client;
 	struct input_dev *input;
+	struct regulator *dvdd;
+	struct regulator *avdd;
+	struct gpio_desc *resout_gpio;
 	struct rm3110x_ts_platform_data *pdata;
 	struct rm31100_ts_data *dd;
 	u8 *touch_data;
@@ -115,9 +115,10 @@ struct rm31100_ts {
 	bool int_pending;
 	struct mutex access_lock;
 	u32 pen_irq;
+	u8 fw_version;
+	u8 u8_sub_version;
 };
 
-struct rm31100_ts *pts;
 /*
 static inline u16 join_bytes(u8 a, u8 b)
 {
@@ -168,19 +169,6 @@ static int rm31100_ts_read(struct i2c_client *client, u8 reg, u8 *buf, int num)
 	return i2c_transfer(client->adapter, xfer_msg, 2);
 }
 
-static int
-rm31100_ts_write_client_dma(struct i2c_client *client, u8 *buf, int num)
-{
-	struct i2c_msg xfer_msg[1];
-
-	xfer_msg[0].addr = I2C_DMA_CLIENT_ADDR;
-	xfer_msg[0].len = num;
-	xfer_msg[0].flags = 0;
-	xfer_msg[0].buf = buf;
-
-	return i2c_transfer(client->adapter, xfer_msg, 1);
-}
-
 static int rm31100_ts_write(struct i2c_client *client, u8 *buf, int num)
 {
 	struct i2c_msg xfer_msg[1];
@@ -192,119 +180,27 @@ static int rm31100_ts_write(struct i2c_client *client, u8 *buf, int num)
 
 	return i2c_transfer(client->adapter, xfer_msg, 1);
 }
-#ifdef CONFIG_MISC_DEV
-static int dev_open(struct inode *inode, struct file *filp)
-{
-	mutex_lock(&pts->access_lock);
-	return 0;
-}
 
-static int dev_release(struct inode *inode, struct file *filp)
+static ssize_t rm_fw_version_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf)
 {
-	mutex_unlock(&pts->access_lock);
-	return 0;
-}
-static ssize_t
-dev_read(struct file *filp, char __user *buf, size_t count, loff_t *pos)
-{
-	u8 *kbuf;
-	struct i2c_msg xfer_msg;
-	/*static char out[] = "1234567890";*/
-	/*static int idx;*//*= 0; remove by checkpatch*/
-	int i;
-
-	kbuf = kmalloc(count, GFP_KERNEL);
-	if (kbuf == NULL)
-		return -ENOMEM;
-
-	/*xfer_msg.addr = pts->client->addr;*/
-	xfer_msg.addr = I2C_CLIENT_ADDR;
-	xfer_msg.len = count;
-	xfer_msg.flags = I2C_M_RD;
-	xfer_msg.buf = kbuf;
-
-	i2c_transfer(pts->client->adapter, &xfer_msg, 1);
-
-	if (copy_to_user(buf, kbuf, count) == 0)
-		return count;
-	else
-		return -EFAULT;
+	struct rm31100_ts *info = dev_get_drvdata(dev);
+	return sprintf(buf, "Release V 0x%02X, Test V 0x%02X\n",
+		info.u8_version,
+		info.u8_sub_version);
 }
 
-static ssize_t
-dev_write(struct file *filp, const char __user *buf, size_t count, loff_t *pos)
-{
-	u8 *kbuf;
-	ssize_t status = 0;
-	int i;
-
-	kbuf = kmalloc(count, GFP_KERNEL);
-	if (kbuf == NULL) {
-		dev_err("kmalloc() fail\n");
-		return -ENOMEM;
-	}
-
-	if (copy_from_user(kbuf, buf, count) == 0) {
-		pts->client->addr = I2C_CLIENT_ADDR;
-		if (rm31100_ts_write(pts->client, kbuf, count) < 0)
-			status = -EFAULT;
-		else
-			status = count;
-	} else {
-		dev_err("copy_from_user() fail\n");
-		status = -EFAULT;
-	}
-
-	kfree(kbuf);
-	return status;
-}
+static DEVICE_ATTR(fw_version, S_IRUGO, rm_fw_version_show, NULL);
 
-static const struct file_operations dev_fops = {
-	.owner = THIS_MODULE,
-	.open = dev_open,
-	.release = dev_release,
-	.read = dev_read,
-	.write = dev_write,
-	/*.unlocked_ioctl = dev_ioctl,*/
+static struct attribute *rm_ts_attributes[] = {
+	&dev_attr_fw_version.attr,
+	NULL
 };
 
-static struct miscdevice raydium_ts_miscdev = {
-	.minor = MISC_DYNAMIC_MINOR,
-	.name = "raydium_ts",
-	.fops = &dev_fops,
+static const struct attribute_group rm_ts_attr_group = {
+	.attrs = rm_ts_attributes,
 };
-#endif
-
-
-ssize_t show(struct device_driver *drv, char *buff)
-{
-	struct i2c_msg xfer_msg;
-	int num = 10;
-	char buf[100];
-	/*int i;*/
-
-	xfer_msg.addr = pts->client->addr;
-	xfer_msg.len = num;
-	xfer_msg.flags = I2C_M_RD;
-	xfer_msg.buf = buf;
-	pts->client->addr = I2C_CLIENT_ADDR;
-	i2c_transfer(pts->client->adapter, &xfer_msg, 1);
-
-	return 0;
-}
-
-ssize_t store(struct device_driver *drv, const char *buf, size_t count)
-{
-	/*unsigned char pkt[] = { 0xF2, 5, 1, 1 };*/
-	unsigned char pkt[] = { 0xF1, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
-
-	pts->client->addr = I2C_CLIENT_ADDR;
-	rm31100_ts_write(pts->client, pkt, sizeof(pkt));
-
-	return sizeof(pkt);
-}
-
-DRIVER_ATTR(myAttr, 0x777, show, store);
 
 static void report_data(struct rm31100_ts *dev, u16 x, u16 y,
 	u8 pressure, u8 id)
@@ -319,10 +215,6 @@ static void report_data(struct rm31100_ts *dev, u16 x, u16 y,
 	input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
 	input_report_abs(input_dev, ABS_MT_PRESSURE, pressure);
 	input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, dev->dd->finger_size);
-/*
-	dev_dbg("%s(): id =%2hhd, x =%4hd, y =%4hd, pressure = %hhd\n",
-		__func__, id, x, y, pressure);
-*/
 }
 
 static void process_rm31100_data(struct rm31100_ts *ts)
@@ -348,32 +240,18 @@ static void process_rm31100_data(struct rm31100_ts *ts)
 		input_mt_sync(ts->input);
 
 	ts->prev_touches = touches;
-	/*input_report_key(ts->input, BTN_TOUCH, 1);*/
+
 	input_mt_report_pointer_emulation(ts->input, true);
 	input_sync(ts->input);
 }
 
-/*static void rm31100_ts_xy_worker(struct work_struct *work) JL remove*/
 static void rm31100_ts_xy_worker(struct rm31100_ts *work)
 {
 	int rc;
-	u8 client_dma_package[4] = {0x0f, 0x00, 0x20, 0x81};
 	struct rm31100_ts *ts = work;
 
-	if (ts->is_suspended == true) {
-		dev_dbg(&ts->client->dev, "TS is supended\n");
-		ts->int_pending = true;
-		return;
-	}
-
 	mutex_lock(&ts->access_lock);
 	/* read data from DATA_REG */
-	/*RM31100 DMA Mode*/
-	rc = rm31100_ts_write_client_dma(ts->client, client_dma_package, 0x04);
-	if (rc < 0) {
-		dev_err(&ts->client->dev, "write client dma failed\n");
-		goto schedule;
-	}
 	rc = rm31100_ts_read(ts->client, ts->dd->data_reg, ts->touch_data,
 	ts->dd->data_size);
 
@@ -410,6 +288,61 @@ static irqreturn_t rm31100_ts_irq(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
+static int rm_ts_power_on(struct rm31100_ts *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->resout_gpio))
+		return 0;
+
+	gpiod_set_value_cansleep(ts->resout_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->dvdd);
+	if (error) {
+		dev_err(&ts->client->dev,
+			"failed to enable dvdd regulator: %d\n",
+			error);
+		regulator_disable(ts->dvdd);
+		goto release_reset_gpio;
+	}
+
+release_reset_gpio:
+	gpiod_set_value_cansleep(ts->resout_gpio, 0);
+	if (error)
+		return error;
+
+	msleep(20);
+
+	return 0;
+}
+
+static void rm_ts_power_off(void *_data)
+{
+	struct rm31100_ts *data = _data;
+
+	if (!IS_ERR_OR_NULL(data->resout_gpio)) {
+		/*
+		 * Activate reset gpio to prevent leakage through the
+		 * pin once we shut off power to the controller.
+		 */
+		gpiod_set_value_cansleep(data->resout_gpio, 1);
+		regulator_disable(data->avdd);
+		regulator_disable(data->dvdd);
+	}
+}
+
 static int rm31100_ts_init_ts(struct i2c_client *client, struct rm31100_ts *ts)
 {
 	/*struct input_dev *input_device;*/
@@ -453,8 +386,7 @@ static int rm31100_ts_init_ts(struct i2c_client *client, struct rm31100_ts *ts)
 	return 0;
 }
 
-#ifdef CONFIG_PM
-static int rm31100_ts_suspend(struct device *dev)
+static int __maybe_unused rm31100_ts_suspend(struct device *dev)
 {
 	struct rm31100_ts *ts = dev_get_drvdata(dev);
 	int rc = 0;
@@ -467,19 +399,14 @@ static int rm31100_ts_suspend(struct device *dev)
 
 	disable_irq(ts->pen_irq);
 
-	gpio_free(ts->pdata->irq_gpio);
+	gpiod_set_value_cansleep(ts->resout_gpio, 0);
+
+	rm_ts_power_off(ts);
 
-	if (ts->pdata->power_on) {
-		rc = ts->pdata->power_on(0);
-		if (rc) {
-			dev_err(dev, "unable to goto suspend\n");
-			return rc;
-		}
-	}
 	return 0;
 }
 
-static int rm31100_ts_resume(struct device *dev)
+static int __maybe_unused rm31100_ts_resume(struct device *dev)
 {
 	struct rm31100_ts *ts = dev_get_drvdata(dev);
 
@@ -493,12 +420,10 @@ static int rm31100_ts_resume(struct device *dev)
 		if (ts->int_pending == true)
 			ts->int_pending = false;
 	} else {
-		if (ts->pdata->power_on) {
-			rc = ts->pdata->power_on(1);
-			if (rc) {
-				dev_err(dev, "unable to resume\n");
-				return rc;
-			}
+		rc = rm_ts_power_on(ts);
+		if (rc) {
+			dev_err(dev, "unable to resume\n");
+			return rc;
 		}
 
 		enable_irq(ts->pen_irq);
@@ -522,11 +447,15 @@ static int rm31100_ts_resume(struct device *dev)
 	return 0;
 }
 
-static const struct dev_pm_ops rm31100_ts_pm_ops = {
-	.suspend = rm31100_ts_suspend,
-	.resume = rm31100_ts_resume,
-};
-#endif
+static void rm_ts_remove_sysfs_group(void *_data)
+{
+	struct rm31100_ts *ts = _data;
+
+	sysfs_remove_group(&ts->client->dev.kobj, &rm_ts_attr_group);
+}
+
+static SIMPLE_DEV_PM_OPS(rm31100_ts_pm_ops,
+			 rm31100_ts_suspend, rm31100_ts_resume);
 
 static int rm_input_dev_create(struct rm31100_ts *ts)
 {
@@ -546,17 +475,26 @@ static int rm_input_dev_create(struct rm31100_ts *ts)
 	input_device->dev.parent = &ts->client->dev;
 	input_set_drvdata(input_device, ts);
 
-	__set_bit(EV_ABS, input_device->evbit);
-	__set_bit(INPUT_PROP_DIRECT, input_device->propbit);
 	__set_bit(BTN_TOUCH, input_device->keybit);
+	__set_bit(EV_ABS, input_device->evbit);
+	__set_bit(EV_KEY, input_device->evbit);
+
+	/* For single touch */
+	input_set_abs_params(input_device, ABS_X,
+			     ts->pdata->dis_min_x, ts->pdata->dis_max_x, 0, 0);
+	input_set_abs_params(input_device, ABS_Y,
+			     ts->pdata->dis_min_x, ts->pdata->dis_max_y, 0, 0);
+	input_set_abs_params(input_device, ABS_PRESSURE,
+			     0, 255, 0, 0);
+	input_abs_set_res(input_device, ABS_X, ts->pdata->res_x);
+	input_abs_set_res(input_device, ABS_Y, ts->pdata->res_y);
+
+	/* Multitouch input params setup */
+	rc = input_mt_init_slots(input_device,
+		MAX_REPORT_TOUCHED_POINTS, INPUT_MT_DIRECT);
+	if (rc)
+		goto error_unreg_device;
 
-
-	if (ts->device_id == rm31100) {
-		/* set up virtual key */
-		__set_bit(EV_KEY, input_device->evbit);
-	}
-	input_mt_init_slots(input_device,
-		MAX_REPORT_TOUCHED_POINTS, 0);
 	input_set_abs_params(input_device, ABS_MT_POSITION_X,
 		ts->pdata->dis_min_x, ts->pdata->dis_max_x, 0, 0);
 	input_set_abs_params(input_device, ABS_MT_POSITION_Y,
@@ -565,6 +503,9 @@ static int rm_input_dev_create(struct rm31100_ts *ts)
 		0, 0xFF, 0, 0);
 	input_set_abs_params(input_device, ABS_MT_TOUCH_MAJOR,
 		0, 0xFF, 0, 0);
+	input_abs_set_res(input_device, ABS_MT_POSITION_X, ts->pdata->res_x);
+	input_abs_set_res(input_device, ABS_MT_POSITION_Y, ts->pdata->res_y);
+
 	rc = input_register_device(input_device);
 	if (rc)
 		goto error_unreg_device;
@@ -581,15 +522,7 @@ error_alloc_dev:
 static int rm31100_initialize(struct i2c_client *client)
 {
 	struct rm31100_ts *ts = i2c_get_clientdata(client);
-	int rc = 0, /*retry_cnt = 0,*/ temp_reg;
-	/* power on the device */
-	if (ts->pdata->power_on) {
-		rc = ts->pdata->power_on(1);
-		if (rc) {
-			pr_err("%s: Unable to power on the device\n", __func__);
-			goto error_dev_setup;
-		}
-	}
+	int rc = 0, temp_reg;
 
 	/* read one byte to make sure i2c device exists */
 	if (ts->device_id == rm3110x)
@@ -602,190 +535,186 @@ static int rm31100_initialize(struct i2c_client *client)
 	rc = rm31100_ts_read_reg_u8(client, temp_reg);
 	if (rc < 0) {
 		dev_err(&client->dev, "i2c sanity check failed\n");
-		goto error_power_on;
+		return rc;
 	}
 
 	rc = rm31100_ts_init_ts(client, ts);
 	if (rc < 0) {
 		dev_err(&client->dev, "rm31100_ts init failed\n");
-		goto error_mutex_destroy;
-	}
-
-	/* configure touchscreen reset out gpio */
-	rc = gpio_request(ts->pdata->resout_gpio, "rm31100_resout_gpio");
-	if (rc) {
-		pr_err("%s: unable to request gpio %d\n",
-			__func__, ts->pdata->resout_gpio);
-		goto error_uninit_ts;
-	}
-
-	rc = gpio_direction_output(ts->pdata->resout_gpio, 0);
-	if (rc) {
-		pr_err("%s: unable to set direction for gpio %d\n",
-			__func__, ts->pdata->resout_gpio);
-		goto error_resout_gpio_dir;
+		return rc;
 	}
-	/* reset gpio stabilization time */
-	msleep(20);
 
 	return 0;
-error_resout_gpio_dir:
-	if (ts->pdata->resout_gpio >= 0)
-		gpio_free(ts->pdata->resout_gpio);
-error_uninit_ts:
-	input_unregister_device(ts->input);
-	kfree(ts->touch_data);
-error_mutex_destroy:
-	mutex_destroy(&ts->access_lock);
-error_power_on:
-	if (ts->pdata->power_on)
-		ts->pdata->power_on(0);
-error_dev_setup:
-	if (ts->pdata->dev_setup)
-		ts->pdata->dev_setup(0);
-	return rc;
 }
 
-static void rm_initialize_async(void *data, async_cookie_t cookie)
+static int rm31100_ts_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
 {
-	struct rm31100_ts *ts = data;
-	struct i2c_client *client = ts->client;
-	unsigned long irqflags;
-	int err = 0;
-
-	mutex_lock(&ts->access_lock);
+	struct rm31100_ts *ts;
+	struct rm3110x_ts_platform_data *pdata = client->dev.platform_data;
+	int rc;
+	union i2c_smbus_data dummy;
 
-	err = rm31100_initialize(client);
-	if (err < 0) {
-		dev_err(&client->dev, "probe failed! unbind device.\n");
-		goto error_free_mem;
+	ts = devm_kzalloc(&client->dev, sizeof(struct rm31100_ts), GFP_KERNEL);
+	if (!ts) {
+		dev_err(&client->dev, "Failed to allocate memory\n");
+		return -ENOMEM;
 	}
 
-	err = rm_input_dev_create(ts);
-	if (err) {
-		dev_err(&client->dev, "%s crated failed, %d\n", __func__, err);
-		goto error_goio_release;
+	if (!i2c_check_functionality(client->adapter,
+		I2C_FUNC_SMBUS_READ_WORD_DATA)) {
+		dev_err(&client->dev, "I2C functionality not supported\n");
+		rc = -EIO;
+		goto error_touch_data_alloc;
 	}
 
-	irqflags = client->dev.of_node ? 0 : IRQF_TRIGGER_FALLING;
+	ts->client = client;
+	ts->pdata = pdata;
+	i2c_set_clientdata(client, ts);
+	ts->device_id = id->driver_data;
 
-	err = request_threaded_irq(ts->pen_irq, NULL,
-				   rm31100_ts_irq,
-				   irqflags | IRQF_ONESHOT,
-				   ts->client->dev.driver->name, ts);
-	if (err) {
-		dev_err(&client->dev, "Failed to register interrupt\n");
-		goto error_dev_release;
-	}
+	ts->is_suspended = false;
+	ts->int_pending = false;
 
-	mutex_unlock(&ts->access_lock);
+	mutex_init(&ts->access_lock);
 
-	return;
+	ts->avdd = devm_regulator_get(&client->dev, "avdd");
+	if (IS_ERR(ts->avdd)) {
+		rc = PTR_ERR(ts->avdd);
+		if (rc != -EPROBE_DEFER)
+			dev_err(&client->dev,
+				"Failed to get 'avdd' regulator: %d\n",
+				rc);
+		return rc;
+	}
 
-error_dev_release:
-	input_free_device(ts->input);
-error_goio_release:
-	if (ts->pdata->resout_gpio >= 0)
-		gpio_free(ts->pdata->resout_gpio);
-error_free_mem:
-	mutex_unlock(&ts->access_lock);
-	kfree(ts);
-	return;
-}
+	ts->dvdd = devm_regulator_get(&client->dev, "dvdd");
+	if (IS_ERR(ts->dvdd)) {
+		rc = PTR_ERR(ts->dvdd);
+		if (rc != -EPROBE_DEFER)
+			dev_err(&client->dev,
+				"Failed to get 'dvdd' regulator: %d\n",
+				rc);
+		return rc;
+	}
+
+	ts->resout_gpio = devm_gpiod_get(&client->dev, "rm31100_resout_gpio");
+	if (IS_ERR(ts->resout_gpio)) {
+		rc = PTR_ERR(ts->resout_gpio);
+
+		/*
+		 * On Chromebooks vendors like to source touch panels from
+		 * different vendors, but they are connected to the same
+		 * regulators/GPIO pin. The drivers also use asynchronous
+		 * probing, which means that more than one driver will
+		 * attempt to claim the reset line. If we find it busy,
+		 * let's try again later.
+		 */
+		if (rc == -EBUSY) {
+			dev_info(&client->dev,
+				 "reset gpio is busy, deferring probe\n");
+			return -EPROBE_DEFER;
+		}
 
+		if (rc == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
 
-/*static int __devinit rm31100_ts_probe(struct i2c_client *client,
-			const struct i2c_device_id *id)*/
-static int rm31100_ts_probe(struct i2c_client *client,
-			const struct i2c_device_id *id)
-{
-	struct rm31100_ts *ts;
-	struct rm3110x_ts_platform_data *pdata = client->dev.platform_data;
-	int rc/*, temp_reg*/;
-	union i2c_smbus_data dummy;
+		if (rc != -ENOENT && rc != -ENOSYS) {
+			dev_err(&client->dev,
+				"failed to get reset gpio: %d\n",
+				rc);
+			return rc;
+		}
 
-	if (!pdata) {
-		dev_err(&client->dev, "platform data is required!\n");
-		return -EINVAL;
+	} else {
+		rc = gpiod_direction_output(ts->resout_gpio, 0);
+		if (rc) {
+			dev_err(&client->dev,
+				"failed to configure reset gpio as output: %d\n",
+				rc);
+			return rc;
+		}
 	}
 
-	if (!i2c_check_functionality(client->adapter,
-		I2C_FUNC_SMBUS_READ_WORD_DATA)) {
-		dev_err(&client->dev, "I2C functionality not supported\n");
-		return -EIO;
+	rc = rm_ts_power_on(ts);
+	if (rc)
+		return rc;
+
+	rc = devm_add_action(&client->dev, rm_ts_power_off, ts);
+	if (rc) {
+		dev_err(&client->dev,
+			"failed to install power off action: %d\n", rc);
+		rm_ts_power_off(ts);
+		return rc;
 	}
+
 	/* 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)
 		return -ENODEV;
 
-	ts = kzalloc(sizeof(*ts), GFP_KERNEL);
-	if (!ts)
-		return -ENOMEM;
-	pts = ts;
-
-	/* Enable runtime PM ops, start in ACTIVE mode */
-	rc = pm_runtime_set_active(&client->dev);
-	if (rc < 0)
-		dev_warn(&client->dev, "unable to set runtime pm state\n");
-	pm_runtime_enable(&client->dev);
-
-	ts->client = client;
-	ts->pdata = pdata;
-	i2c_set_clientdata(client, ts);
-	ts->device_id = id->driver_data;
+	rc = rm31100_initialize(client);
+	if (rc < 0) {
+		dev_err(&client->dev, "probe failed! unbind device.\n");
+		return rc;
+	}
 
-	if (ts->pdata->dev_setup) {
-		rc = ts->pdata->dev_setup(1);
-		if (rc < 0) {
-			dev_err(&client->dev, "dev setup failed\n");
-			goto error_touch_data_alloc;
-		}
+	rc = rm_input_dev_create(ts);
+	if (rc) {
+		dev_err(&client->dev, "%s crated failed, %d\n", __func__, err);
+		return rc;
 	}
 
+	rc = devm_request_threaded_irq(&client->dev, ts->pen_irq,
+					NULL, rm31100_ts_irq,
+					IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+					client->name, ts);
+	if (rc) {
+		dev_err(&client->dev, "Failed to register interrupt\n");
+		return rc;
+	}
 
-	ts->is_suspended = false;
-	ts->int_pending = false;
-	/*mutex_init(&ts->sus_lock); JL remove*/
-	mutex_init(&ts->access_lock);
+	device_set_wakeup_enable(&client->dev, false);
 
-	async_schedule(rm_initialize_async, ts);
+	rc = sysfs_create_group(&client->dev.kobj, &rm_ts_attr_group);
+	if (rc) {
+		dev_err(&client->dev, "failed to create sysfs attributes: %d\n",
+			rc);
+		return rc;
+	}
 
-	device_init_wakeup(&client->dev, ts->pdata->wakeup);
+	rc = devm_add_action(&client->dev,
+				rm_ts_remove_sysfs_group, ts);
+	if (rc) {
+		rm_ts_remove_sysfs_group(ts);
+		dev_err(&client->dev,
+			"Failed to add sysfs cleanup action: %d\n",
+			rc);
+		return rc;
+	}
 	return 0;
 
 error_touch_data_alloc:
-	pm_runtime_set_suspended(&client->dev);
-	pm_runtime_disable(&client->dev);
 	kfree(ts);
 	return rc;
 }
 
-/*static int __devexit RM31100_ts_remove(struct i2c_client *client)*/
 static int rm31100_ts_remove(struct i2c_client *client)
 {
 	struct rm31100_ts *ts = i2c_get_clientdata(client);
 
-	pm_runtime_set_suspended(&client->dev);
-	pm_runtime_disable(&client->dev);
-
 	device_init_wakeup(&client->dev, 0);
 	free_irq(ts->pen_irq, ts);
 
-	gpio_free(ts->pdata->irq_gpio);
+	if (ts->resout_gpio >= 0)
+		gpiod_set_value_cansleep(ts->resout_gpio, 0);
 
-	if (ts->pdata->resout_gpio >= 0)
-		gpio_free(ts->pdata->resout_gpio);
 	input_unregister_device(ts->input);
 
 	/*mutex_destroy(&ts->sus_lock); JL remove*/
 	mutex_destroy(&ts->access_lock);
 
-	if (ts->pdata->power_on)
-		ts->pdata->power_on(0);
-
-	if (ts->pdata->dev_setup)
-		ts->pdata->dev_setup(0);
+	rm_ts_power_off(ts);
 
 	kfree(ts->touch_data);
 	kfree(ts);
-- 
2.1.2

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

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2016-01-08 15:17 Jeffrey Lin
@ 2016-01-09 19:20 ` Dmitry Torokhov
  0 siblings, 0 replies; 58+ messages in thread
From: Dmitry Torokhov @ 2016-01-09 19:20 UTC (permalink / raw)
  To: Jeffrey Lin
  Cc: rydberg, dianders, bleung, scott.liu, jeffrey.lin, roger.yang,
	KP.li, linux-kernel, linux-input

Hi Jeffrey,

On Fri, Jan 08, 2016 at 11:17:36PM +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".
> 
> Signed-off-by: jeffrey lin <jeffrey.lin@rad-ic.com>
> ---
>  drivers/input/touchscreen/Kconfig      |  12 +
>  drivers/input/touchscreen/Makefile     |   1 +
>  drivers/input/touchscreen/rm31100_ts.c | 843 +++++++++++++++++++++++++++++++++
>  3 files changed, 856 insertions(+)
>  create mode 100644 drivers/input/touchscreen/rm31100_ts.c
> 
> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> index 0f13e52..2a85353 100644
> --- a/drivers/input/touchscreen/Kconfig
> +++ b/drivers/input/touchscreen/Kconfig
> @@ -329,6 +329,18 @@ config TOUCHSCREEN_ELAN
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called elants_i2c.
>  
> +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 rm31100_ts.
> +
>  config TOUCHSCREEN_ELO
>  	tristate "Elo serial touchscreens"
>  	select SERIO
> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
> index 687d5a7..aae4af2 100644
> --- a/drivers/input/touchscreen/Makefile
> +++ b/drivers/input/touchscreen/Makefile
> @@ -78,3 +78,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE)	+= zylonite-wm97xx.o
>  obj-$(CONFIG_TOUCHSCREEN_W90X900)	+= w90p910_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_TPS6507X)	+= tps6507x-ts.o
>  obj-$(CONFIG_TOUCHSCREEN_ZFORCE)	+= zforce_ts.o
> +obj-$(CONFIG_TOUCHSCREEN_RM_TS)	+= rm31100_ts.o
> diff --git a/drivers/input/touchscreen/rm31100_ts.c b/drivers/input/touchscreen/rm31100_ts.c
> new file mode 100644
> index 0000000..941fa31
> --- /dev/null
> +++ b/drivers/input/touchscreen/rm31100_ts.c
> @@ -0,0 +1,843 @@
> +/*
> + * Raydium RM31100_ts touchscreen 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/async.h>
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/i2c.h>
> +#include <linux/input.h>
> +#include <linux/slab.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/gpio.h>
> +#include <linux/mutex.h>
> +#include <linux/delay.h>
> +#include <linux/pm.h>
> +#include <linux/pm_runtime.h>
> +#ifdef CONFIG_MISC_DEV
> +#include <linux/miscdevice.h>
> +#endif
> +#include <linux/uaccess.h>
> +#include <asm/unaligned.h>
> +#include <linux/input/mt.h>
> +
> +#define rm31100	0x0
> +#define rm3110x	0x1
> +
> +#define INVALID_DATA	0xff
> +#define MAX_REPORT_TOUCHED_POINTS	10
> +
> +#define I2C_CLIENT_ADDR         0x39
> +#define I2C_DMA_CLIENT_ADDR     0x5A
> +
> +struct rm31100_ts_data {
> +	u8 x_index;
> +	u8 y_index;
> +	u8 z_index;
> +	u8 id_index;
> +	u8 touch_index;
> +	u8 data_reg;
> +	u8 status_reg;
> +	u8 data_size;
> +	u8 touch_bytes;
> +	u8 update_data;
> +	u8 touch_meta_data;
> +	u8 finger_size;
> +};
> +
> +struct rm3110x_ts_platform_data {
> +	int (*power_on)(int on);
> +	int (*dev_setup)(bool on);
> +	const char *ts_name;
> +	u32 dis_min_x; /* display resoltion ABS min*/
> +	u32 dis_max_x;/* display resoltion ABS max*/
> +	u32 dis_min_y;
> +	u32 dis_max_y;
> +	u32 min_touch; /* no.of touches supported */
> +	u32 max_touch;
> +	u32 min_tid; /* track id */

Not used?

> +	u32 max_tid;

Not used?

> +	u32 min_width;/* size of the finger */
> +	u32 max_width;

No used?

> +	u32 res_x; /* TS resolution unit*/
> +	u32 res_y;

Not used?

> +	u32 swap_xy;
> +	u8 nfingers;

Why is this part of platform data? Isn't the maximum number of touches
property of the hardware.

> +	u32 irq_gpio;
> +	int resout_gpio;
> +	bool wakeup;
> +	u32 irq_cfg;
> +};
> +
> +static struct rm31100_ts_data devices[] = {
> +	[0] = {
> +		.x_index = 2,
> +		.y_index = 4,
> +		.z_index = 6,
> +		.id_index = 1,
> +		.data_reg = 0x1,
> +		.status_reg = 0,
> +		.update_data = 0x0,
> +		.touch_bytes = 6,
> +		.touch_meta_data = 1,
> +		.finger_size = 70,
> +	},
> +};
> +
> +struct rm31100_ts {
> +	struct i2c_client *client;
> +	struct input_dev *input;
> +	struct rm3110x_ts_platform_data *pdata;
> +	struct rm31100_ts_data *dd;
> +	u8 *touch_data;
> +	u8 device_id;
> +	u8 prev_touches;
> +	bool is_suspended;
> +	bool int_pending;
> +	struct mutex access_lock;
> +	u32 pen_irq;
> +};
> +
> +struct rm31100_ts *pts;
> +/*
> +static inline u16 join_bytes(u8 a, u8 b)
> +{
> +	u16 ab = 0;
> +	ab = ab | a;
> +	ab = ab << 8 | b;
> +	return ab;
> +}
> +*/
> +static s32 rm31100_ts_write_reg_u8(struct i2c_client *client, u8 reg, u8 val)
> +{
> +	s32 data;
> +
> +	data = i2c_smbus_write_byte_data(client, reg, val);
> +	if (data < 0)
> +		dev_err(&client->dev, "error %d in writing reg 0x%x\n",
> +						 data, reg);
> +
> +	return data;
> +}
> +
> +static s32 rm31100_ts_read_reg_u8(struct i2c_client *client, u8 reg)
> +{
> +	s32 data;
> +
> +	data = i2c_smbus_read_byte_data(client, reg);
> +	if (data < 0)
> +		dev_err(&client->dev, "error %d in reading reg 0x%x\n",
> +						 data, reg);
> +
> +	return data;
> +}
> +
> +static int rm31100_ts_read(struct i2c_client *client, u8 reg, u8 *buf, int num)
> +{
> +	struct i2c_msg xfer_msg[2];
> +
> +	xfer_msg[0].addr = client->addr;
> +	xfer_msg[0].len = 1;
> +	xfer_msg[0].flags = 0;
> +	xfer_msg[0].buf = &reg;
> +
> +	xfer_msg[1].addr = client->addr;
> +	xfer_msg[1].len = num;
> +	xfer_msg[1].flags = I2C_M_RD;
> +	xfer_msg[1].buf = buf;
> +
> +	return i2c_transfer(client->adapter, xfer_msg, 2);
> +}
> +
> +static int
> +rm31100_ts_write_client_dma(struct i2c_client *client, u8 *buf, int num)
> +{
> +	struct i2c_msg xfer_msg[1];
> +
> +	xfer_msg[0].addr = I2C_DMA_CLIENT_ADDR;
> +	xfer_msg[0].len = num;
> +	xfer_msg[0].flags = 0;
> +	xfer_msg[0].buf = buf;

What does DMA here stand for? Not "direct memory access" for sure...

> +
> +	return i2c_transfer(client->adapter, xfer_msg, 1);
> +}
> +
> +static int rm31100_ts_write(struct i2c_client *client, u8 *buf, int num)
> +{
> +	struct i2c_msg xfer_msg[1];
> +
> +	xfer_msg[0].addr = client->addr;
> +	xfer_msg[0].len = num;
> +	xfer_msg[0].flags = 0;
> +	xfer_msg[0].buf = buf;
> +
> +	return i2c_transfer(client->adapter, xfer_msg, 1);
> +}
> +#ifdef CONFIG_MISC_DEV
> +static int dev_open(struct inode *inode, struct file *filp)
> +{
> +	mutex_lock(&pts->access_lock);
> +	return 0;
> +}
> +
> +static int dev_release(struct inode *inode, struct file *filp)
> +{
> +	mutex_unlock(&pts->access_lock);
> +	return 0;
> +}
> +static ssize_t
> +dev_read(struct file *filp, char __user *buf, size_t count, loff_t *pos)
> +{
> +	u8 *kbuf;
> +	struct i2c_msg xfer_msg;
> +	/*static char out[] = "1234567890";*/
> +	/*static int idx;*//*= 0; remove by checkpatch*/
> +	int i;
> +
> +	kbuf = kmalloc(count, GFP_KERNEL);
> +	if (kbuf == NULL)
> +		return -ENOMEM;
> +
> +	/*xfer_msg.addr = pts->client->addr;*/
> +	xfer_msg.addr = I2C_CLIENT_ADDR;
> +	xfer_msg.len = count;
> +	xfer_msg.flags = I2C_M_RD;
> +	xfer_msg.buf = kbuf;
> +
> +	i2c_transfer(pts->client->adapter, &xfer_msg, 1);
> +
> +	if (copy_to_user(buf, kbuf, count) == 0)
> +		return count;
> +	else
> +		return -EFAULT;
> +}
> +
> +static ssize_t
> +dev_write(struct file *filp, const char __user *buf, size_t count, loff_t *pos)
> +{
> +	u8 *kbuf;
> +	ssize_t status = 0;
> +	int i;
> +
> +	kbuf = kmalloc(count, GFP_KERNEL);
> +	if (kbuf == NULL) {
> +		dev_err("kmalloc() fail\n");
> +		return -ENOMEM;
> +	}
> +
> +	if (copy_from_user(kbuf, buf, count) == 0) {
> +		pts->client->addr = I2C_CLIENT_ADDR;
> +		if (rm31100_ts_write(pts->client, kbuf, count) < 0)
> +			status = -EFAULT;
> +		else
> +			status = count;
> +	} else {
> +		dev_err("copy_from_user() fail\n");
> +		status = -EFAULT;
> +	}
> +
> +	kfree(kbuf);
> +	return status;
> +}
> +
> +static const struct file_operations dev_fops = {
> +	.owner = THIS_MODULE,
> +	.open = dev_open,
> +	.release = dev_release,
> +	.read = dev_read,
> +	.write = dev_write,
> +	/*.unlocked_ioctl = dev_ioctl,*/
> +};
> +
> +static struct miscdevice raydium_ts_miscdev = {
> +	.minor = MISC_DYNAMIC_MINOR,
> +	.name = "raydium_ts",
> +	.fops = &dev_fops,
> +};

A custom character device is not the appropriate interface for an input
controller. Please explain what you are trying to do here?

> +#endif
> +
> +
> +ssize_t show(struct device_driver *drv, char *buff)
> +{
> +	struct i2c_msg xfer_msg;
> +	int num = 10;
> +	char buf[100];
> +	/*int i;*/
> +
> +	xfer_msg.addr = pts->client->addr;
> +	xfer_msg.len = num;
> +	xfer_msg.flags = I2C_M_RD;
> +	xfer_msg.buf = buf;
> +	pts->client->addr = I2C_CLIENT_ADDR;
> +	i2c_transfer(pts->client->adapter, &xfer_msg, 1);
> +
> +	return 0;
> +}
> +
> +ssize_t store(struct device_driver *drv, const char *buf, size_t count)
> +{
> +	/*unsigned char pkt[] = { 0xF2, 5, 1, 1 };*/
> +	unsigned char pkt[] = { 0xF1, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
> +
> +	pts->client->addr = I2C_CLIENT_ADDR;
> +	rm31100_ts_write(pts->client, pkt, sizeof(pkt));
> +
> +	return sizeof(pkt);
> +}
> +
> +DRIVER_ATTR(myAttr, 0x777, show, store);

What is this supposed to do?

> +
> +static void report_data(struct rm31100_ts *dev, u16 x, u16 y,
> +	u8 pressure, u8 id)
> +{
> +	struct input_dev *input_dev = dev->input;
> +	if (dev->pdata->swap_xy)
> +		swap(x, y);
> +
> +	input_mt_slot(input_dev, id);
> +	input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, true);
> +	input_report_abs(input_dev, ABS_MT_POSITION_X, x);
> +	input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
> +	input_report_abs(input_dev, ABS_MT_PRESSURE, pressure);
> +	input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, dev->dd->finger_size);
> +/*
> +	dev_dbg("%s(): id =%2hhd, x =%4hd, y =%4hd, pressure = %hhd\n",
> +		__func__, id, x, y, pressure);
> +*/
> +}
> +
> +static void process_rm31100_data(struct rm31100_ts *ts)
> +{
> +	u8 id, pressure, touches, i;
> +	u16 x, y;
> +
> +	touches = ts->touch_data[ts->dd->touch_index];
> +
> +	if (touches > 0) {
> +		for (i = 0; i < touches; i++) {
> +			id = ts->touch_data[i * ts->dd->touch_bytes +
> +				ts->dd->id_index];
> +			pressure = ts->touch_data[i * ts->dd->touch_bytes +
> +				ts->dd->z_index];
> +			x = get_unaligned_le16(&(ts->touch_data[i *
> +				ts->dd->touch_bytes + ts->dd->x_index]));
> +			y = get_unaligned_le16(&(ts->touch_data[i *
> +				ts->dd->touch_bytes + ts->dd->y_index]));
> +			report_data(ts, x, y, pressure, id);
> +		}
> +	} else
> +		input_mt_sync(ts->input);
> +
> +	ts->prev_touches = touches;
> +	/*input_report_key(ts->input, BTN_TOUCH, 1);*/
> +	input_mt_report_pointer_emulation(ts->input, true);
> +	input_sync(ts->input);
> +}
> +
> +/*static void rm31100_ts_xy_worker(struct work_struct *work) JL remove*/
> +static void rm31100_ts_xy_worker(struct rm31100_ts *work)
> +{
> +	int rc;
> +	u8 client_dma_package[4] = {0x0f, 0x00, 0x20, 0x81};
> +	struct rm31100_ts *ts = work;
> +
> +	if (ts->is_suspended == true) {
> +		dev_dbg(&ts->client->dev, "TS is supended\n");
> +		ts->int_pending = true;

You disable the interrupt in suspend path so I do not see how this
condition can ever be true, please remove.

> +		return;
> +	}
> +
> +	mutex_lock(&ts->access_lock);
> +	/* read data from DATA_REG */
> +	/*RM31100 DMA Mode*/
> +	rc = rm31100_ts_write_client_dma(ts->client, client_dma_package, 0x04);
> +	if (rc < 0) {
> +		dev_err(&ts->client->dev, "write client dma failed\n");
> +		goto schedule;
> +	}
> +	rc = rm31100_ts_read(ts->client, ts->dd->data_reg, ts->touch_data,
> +	ts->dd->data_size);
> +
> +	if (rc < 0) {
> +		dev_err(&ts->client->dev, "read failed\n");
> +		goto schedule;
> +	}
> +
> +	if (ts->touch_data[ts->dd->touch_index] == INVALID_DATA)
> +		goto schedule;
> +
> +	/* write to STATUS_REG to release lock */
> +	rc = rm31100_ts_write_reg_u8(ts->client,
> +		ts->dd->status_reg, ts->dd->update_data);
> +	if (rc < 0) {
> +		dev_err(&ts->client->dev, "write failed, try once more\n");
> +
> +		rc = rm31100_ts_write_reg_u8(ts->client,
> +			ts->dd->status_reg, ts->dd->update_data);
> +		if (rc < 0)
> +			dev_err(&ts->client->dev, "write failed, exiting\n");
> +	}
> +
> +	process_rm31100_data(ts);
> +schedule:
> +	mutex_unlock(&ts->access_lock);
> +}
> +
> +static irqreturn_t rm31100_ts_irq(int irq, void *dev_id)
> +{
> +	struct rm31100_ts *ts = dev_id;
> +
> +	rm31100_ts_xy_worker(ts);
> +	return IRQ_HANDLED;
> +}
> +
> +static int rm31100_ts_init_ts(struct i2c_client *client, struct rm31100_ts *ts)
> +{
> +	/*struct input_dev *input_device;*/
> +	/*int rc = 0;*/
> +
> +	ts->dd = &devices[ts->device_id];
> +
> +	if (!ts->pdata->nfingers) {
> +		dev_err(&client->dev, "Touches information not specified\n");
> +		return -EINVAL;
> +	}
> +
> +	if (ts->device_id == rm3110x) {
> +		if (ts->pdata->nfingers > 2) {
> +			dev_err(&client->dev, "Touches >=1 & <= 2\n");
> +			return -EINVAL;
> +		}
> +		ts->dd->data_size = ts->dd->touch_bytes;
> +		ts->dd->touch_index = 0x0;

Umm, you only have 1 entry in devices array, this is bound to blow up.

> +	} else if (ts->device_id == rm31100) {
> +		if (ts->pdata->nfingers > 10) {
> +			dev_err(&client->dev, "Touches >=1 & <= 10\n");
> +			return -EINVAL;
> +		}
> +		ts->dd->data_size = ts->pdata->nfingers * ts->dd->touch_bytes +
> +						ts->dd->touch_meta_data;
> +		ts->dd->touch_index = 0x0;
> +	}
> +	/* w001 */
> +	else {
> +		ts->dd->data_size = ts->pdata->nfingers * ts->dd->touch_bytes +
> +		ts->dd->touch_meta_data;
> +		ts->dd->touch_index = 0x0;
> +	}
> +
> +	ts->touch_data = kzalloc(ts->dd->data_size, GFP_KERNEL);
> +	if (!ts->touch_data) {
> +		pr_err("%s: Unable to allocate memory\n", __func__);
> +		return -ENOMEM;
> +	}
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static int rm31100_ts_suspend(struct device *dev)

Instead of guarding with #ifdef please mark as __maybe_unused

> +{
> +	struct rm31100_ts *ts = dev_get_drvdata(dev);
> +	int rc = 0;
> +
> +	if (device_may_wakeup(dev)) {
> +		/* mark suspend flag */
> +		ts->is_suspended = true;
> +		enable_irq_wake(ts->pen_irq);
> +	}
> +
> +	disable_irq(ts->pen_irq);
> +
> +	gpio_free(ts->pdata->irq_gpio);
> +
> +	if (ts->pdata->power_on) {
> +		rc = ts->pdata->power_on(0);
> +		if (rc) {
> +			dev_err(dev, "unable to goto suspend\n");
> +			return rc;
> +		}
> +	}
> +	return 0;
> +}
> +
> +static int rm31100_ts_resume(struct device *dev)

Mark with __maybe_unused

> +{
> +	struct rm31100_ts *ts = dev_get_drvdata(dev);
> +
> +	int rc = 0;
> +
> +	if (device_may_wakeup(dev)) {
> +		disable_irq_wake(ts->pen_irq);
> +
> +		ts->is_suspended = false;
> +
> +		if (ts->int_pending == true)
> +			ts->int_pending = false;
> +	} else {
> +		if (ts->pdata->power_on) {
> +			rc = ts->pdata->power_on(1);
> +			if (rc) {
> +				dev_err(dev, "unable to resume\n");
> +				return rc;
> +			}
> +		}
> +
> +		enable_irq(ts->pen_irq);
> +
> +		/* Clear the status register of the TS controller */
> +		rc = rm31100_ts_write_reg_u8(ts->client,
> +			ts->dd->status_reg, ts->dd->update_data);
> +		if (rc < 0) {
> +			dev_err(&ts->client->dev,
> +				"write failed, try once more\n");
> +
> +			rc = rm31100_ts_write_reg_u8(ts->client,
> +				ts->dd->status_reg,
> +				ts->dd->update_data);
> +			if (rc < 0)
> +				dev_err(&ts->client->dev,
> +					"write failed, exiting\n");
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct dev_pm_ops rm31100_ts_pm_ops = {
> +	.suspend = rm31100_ts_suspend,
> +	.resume = rm31100_ts_resume,
> +};
> +#endif

Use SIMPLE_DEV_PM_OPS().


> +
> +static int rm_input_dev_create(struct rm31100_ts *ts)
> +{
> +	struct input_dev *input_device;
> +	int rc = 0;
> +	ts->prev_touches = 0;
> +
> +	input_device = input_allocate_device();
> +	if (!input_device) {
> +		rc = -ENOMEM;
> +		goto error_alloc_dev;
> +	}
> +	ts->input = input_device;
> +
> +	input_device->name = "raydium_ts";
> +	input_device->id.bustype = BUS_I2C;
> +	input_device->dev.parent = &ts->client->dev;
> +	input_set_drvdata(input_device, ts);
> +
> +	__set_bit(EV_ABS, input_device->evbit);
> +	__set_bit(INPUT_PROP_DIRECT, input_device->propbit);
> +	__set_bit(BTN_TOUCH, input_device->keybit);

No need to do this, simply pass INPUT_MT_DIRECT as the 3rd argument to
input_mt_init_slots().

> +
> +
> +	if (ts->device_id == rm31100) {
> +		/* set up virtual key */
> +		__set_bit(EV_KEY, input_device->evbit);
> +	}

What virtual key?

> +	input_mt_init_slots(input_device,
> +		MAX_REPORT_TOUCHED_POINTS, 0);
> +	input_set_abs_params(input_device, ABS_MT_POSITION_X,
> +		ts->pdata->dis_min_x, ts->pdata->dis_max_x, 0, 0);
> +	input_set_abs_params(input_device, ABS_MT_POSITION_Y,
> +		ts->pdata->dis_min_y, ts->pdata->dis_max_y, 0, 0);
> +	input_set_abs_params(input_device, ABS_MT_PRESSURE,
> +		0, 0xFF, 0, 0);
> +	input_set_abs_params(input_device, ABS_MT_TOUCH_MAJOR,
> +		0, 0xFF, 0, 0);
> +	rc = input_register_device(input_device);
> +	if (rc)
> +		goto error_unreg_device;
> +
> +	return 0;
> +
> +error_unreg_device:
> +	input_free_device(input_device);
> +error_alloc_dev:
> +	ts->input = NULL;
> +	return rc;
> +}
> +
> +static int rm31100_initialize(struct i2c_client *client)
> +{
> +	struct rm31100_ts *ts = i2c_get_clientdata(client);
> +	int rc = 0, /*retry_cnt = 0,*/ temp_reg;
> +	/* power on the device */
> +	if (ts->pdata->power_on) {
> +		rc = ts->pdata->power_on(1);
> +		if (rc) {
> +			pr_err("%s: Unable to power on the device\n", __func__);
> +			goto error_dev_setup;
> +		}
> +	}
> +
> +	/* read one byte to make sure i2c device exists */
> +	if (ts->device_id == rm3110x)
> +		temp_reg = 0x01;
> +	else if (ts->device_id == rm31100)
> +		temp_reg = 0x00;
> +	else
> +		temp_reg = 0x05;
> +
> +	rc = rm31100_ts_read_reg_u8(client, temp_reg);
> +	if (rc < 0) {
> +		dev_err(&client->dev, "i2c sanity check failed\n");
> +		goto error_power_on;
> +	}
> +
> +	rc = rm31100_ts_init_ts(client, ts);
> +	if (rc < 0) {
> +		dev_err(&client->dev, "rm31100_ts init failed\n");
> +		goto error_mutex_destroy;
> +	}
> +
> +	/* configure touchscreen reset out gpio */
> +	rc = gpio_request(ts->pdata->resout_gpio, "rm31100_resout_gpio");

Please use gpiod_* or devm_gpiod_* interface.

> +	if (rc) {
> +		pr_err("%s: unable to request gpio %d\n",
> +			__func__, ts->pdata->resout_gpio);
> +		goto error_uninit_ts;
> +	}
> +
> +	rc = gpio_direction_output(ts->pdata->resout_gpio, 0);
> +	if (rc) {
> +		pr_err("%s: unable to set direction for gpio %d\n",
> +			__func__, ts->pdata->resout_gpio);
> +		goto error_resout_gpio_dir;
> +	}
> +	/* reset gpio stabilization time */
> +	msleep(20);
> +
> +	return 0;
> +error_resout_gpio_dir:
> +	if (ts->pdata->resout_gpio >= 0)
> +		gpio_free(ts->pdata->resout_gpio);
> +error_uninit_ts:
> +	input_unregister_device(ts->input);
> +	kfree(ts->touch_data);
> +error_mutex_destroy:
> +	mutex_destroy(&ts->access_lock);
> +error_power_on:
> +	if (ts->pdata->power_on)
> +		ts->pdata->power_on(0);
> +error_dev_setup:
> +	if (ts->pdata->dev_setup)
> +		ts->pdata->dev_setup(0);
> +	return rc;
> +}
> +
> +static void rm_initialize_async(void *data, async_cookie_t cookie)
> +{
> +	struct rm31100_ts *ts = data;
> +	struct i2c_client *client = ts->client;
> +	unsigned long irqflags;
> +	int err = 0;
> +
> +	mutex_lock(&ts->access_lock);
> +
> +	err = rm31100_initialize(client);
> +	if (err < 0) {
> +		dev_err(&client->dev, "probe failed! unbind device.\n");
> +		goto error_free_mem;
> +	}
> +
> +	err = rm_input_dev_create(ts);
> +	if (err) {
> +		dev_err(&client->dev, "%s crated failed, %d\n", __func__, err);
> +		goto error_goio_release;
> +	}
> +
> +	irqflags = client->dev.of_node ? 0 : IRQF_TRIGGER_FALLING;

This is new driver in mainline, let's rely on platform to properly set
up irq for the part. Please remove this line.

> +
> +	err = request_threaded_irq(ts->pen_irq, NULL,
> +				   rm31100_ts_irq,
> +				   irqflags | IRQF_ONESHOT,
> +				   ts->client->dev.driver->name, ts);
> +	if (err) {
> +		dev_err(&client->dev, "Failed to register interrupt\n");
> +		goto error_dev_release;
> +	}
> +
> +	mutex_unlock(&ts->access_lock);
> +
> +	return;
> +
> +error_dev_release:
> +	input_free_device(ts->input);
> +error_goio_release:
> +	if (ts->pdata->resout_gpio >= 0)
> +		gpio_free(ts->pdata->resout_gpio);
> +error_free_mem:
> +	mutex_unlock(&ts->access_lock);
> +	kfree(ts);
> +	return;
> +}
> +
> +
> +/*static int __devinit rm31100_ts_probe(struct i2c_client *client,
> +			const struct i2c_device_id *id)*/

Please drop old commented out code (such as above), it is not needed in
mainline.

> +static int rm31100_ts_probe(struct i2c_client *client,
> +			const struct i2c_device_id *id)
> +{
> +	struct rm31100_ts *ts;
> +	struct rm3110x_ts_platform_data *pdata = client->dev.platform_data;
> +	int rc/*, temp_reg*/;
> +	union i2c_smbus_data dummy;
> +
> +	if (!pdata) {
> +		dev_err(&client->dev, "platform data is required!\n");
> +		return -EINVAL;
> +	}
> +
> +	if (!i2c_check_functionality(client->adapter,
> +		I2C_FUNC_SMBUS_READ_WORD_DATA)) {
> +		dev_err(&client->dev, "I2C functionality not supported\n");
> +		return -EIO;
> +	}
> +	/* 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)
> +		return -ENODEV;

You need to do this after powering up the device.

> +
> +	ts = kzalloc(sizeof(*ts), GFP_KERNEL);
> +	if (!ts)

Consider switching to devm_* interfaces to simplify error handling.

> +		return -ENOMEM;
> +	pts = ts;
> +
> +	/* Enable runtime PM ops, start in ACTIVE mode */
> +	rc = pm_runtime_set_active(&client->dev);
> +	if (rc < 0)
> +		dev_warn(&client->dev, "unable to set runtime pm state\n");
> +	pm_runtime_enable(&client->dev);

You do not have any runtime PM methods implemented in the driver, why do
you do this?

> +
> +	ts->client = client;
> +	ts->pdata = pdata;
> +	i2c_set_clientdata(client, ts);
> +	ts->device_id = id->driver_data;
> +
> +	if (ts->pdata->dev_setup) {
> +		rc = ts->pdata->dev_setup(1);
> +		if (rc < 0) {
> +			dev_err(&client->dev, "dev setup failed\n");
> +			goto error_touch_data_alloc;
> +		}
> +	}

Having device setup functions in platform data is an obsolete style that
is not compatible with devicetree or newer ACPI systems. Try establish
common power up/power down procedures expressed as sequence of
operations on regulators and gpios.

> +
> +
> +	ts->is_suspended = false;
> +	ts->int_pending = false;
> +	/*mutex_init(&ts->sus_lock); JL remove*/
> +	mutex_init(&ts->access_lock);
> +
> +	async_schedule(rm_initialize_async, ts);

This is racy (try doing modprobe <your module>; rmmod <your module> and
observe kernel crashing). Please mark the driver as async probe capable
(probe_type = PROBE_PREFER_ASYNCHRONOUS) and device core will handle
asynchronous probing for you.

> +
> +	device_init_wakeup(&client->dev, ts->pdata->wakeup);

I2C core will mark device as wakeup-capable as needed for us.

> +	return 0;
> +
> +error_touch_data_alloc:
> +	pm_runtime_set_suspended(&client->dev);
> +	pm_runtime_disable(&client->dev);
> +	kfree(ts);
> +	return rc;
> +}
> +
> +/*static int __devexit RM31100_ts_remove(struct i2c_client *client)*/
> +static int rm31100_ts_remove(struct i2c_client *client)
> +{
> +	struct rm31100_ts *ts = i2c_get_clientdata(client);
> +
> +	pm_runtime_set_suspended(&client->dev);
> +	pm_runtime_disable(&client->dev);
> +
> +	device_init_wakeup(&client->dev, 0);
> +	free_irq(ts->pen_irq, ts);
> +
> +	gpio_free(ts->pdata->irq_gpio);
> +
> +	if (ts->pdata->resout_gpio >= 0)
> +		gpio_free(ts->pdata->resout_gpio);
> +	input_unregister_device(ts->input);
> +
> +	/*mutex_destroy(&ts->sus_lock); JL remove*/
> +	mutex_destroy(&ts->access_lock);
> +
> +	if (ts->pdata->power_on)
> +		ts->pdata->power_on(0);
> +
> +	if (ts->pdata->dev_setup)
> +		ts->pdata->dev_setup(0);
> +
> +	kfree(ts->touch_data);
> +	kfree(ts);
> +
> +	return 0;
> +}
> +
> +static const struct i2c_device_id rm31100_ts_id[] = {
> +	{"RM31100", rm31100},
> +	{"RM3110x", rm3110x},
> +	{}
> +};
> +MODULE_DEVICE_TABLE(i2c, rm31100_ts_id);
> +
> +
> +static struct i2c_driver rm31100_ts_driver = {
> +	.driver = {
> +		.name = "raydium_ts",
> +		.owner = THIS_MODULE,
> +#ifdef CONFIG_PM
> +		.pm = &rm31100_ts_pm_ops,
> +#endif
> +	},
> +	.probe = rm31100_ts_probe,
> +	.remove = rm31100_ts_remove,
> +	.id_table = rm31100_ts_id,
> +};
> +
> +static int __init rm31100_ts_init(void)
> +{
> +	int rc;
> +	int rc2;
> +
> +	rc = i2c_add_driver(&rm31100_ts_driver);
> +
> +	rc2 = driver_create_file(&rm31100_ts_driver.driver,
> +		&driver_attr_myAttr);
> +
> +	return rc;
> +}
> +/* Making this as late init to avoid power fluctuations
> + * during LCD initialization.
> + */
> +module_init(rm31100_ts_init);
> +
> +static void __exit rm31100_ts_exit(void)
> +{
> +	i2c_del_driver(&rm31100_ts_driver);
> +}
> +module_exit(rm31100_ts_exit);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("rm31100-rm3110x touchscreen controller driver");
> +MODULE_AUTHOR("Raydium");
> +MODULE_ALIAS("platform:rm31100_ts");
> -- 
> 2.1.2
> 

Thanks.

-- 
Dmitry

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

* [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
@ 2016-01-08 15:17 Jeffrey Lin
  2016-01-09 19:20 ` Dmitry Torokhov
  0 siblings, 1 reply; 58+ messages in thread
From: Jeffrey Lin @ 2016-01-08 15:17 UTC (permalink / raw)
  To: dmitry.torokhov, rydberg, dianders, bleung, scott.liu
  Cc: jeffrey.lin, roger.yang, KP.li, linux-kernel, linux-input

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/rm31100_ts.c | 843 +++++++++++++++++++++++++++++++++
 3 files changed, 856 insertions(+)
 create mode 100644 drivers/input/touchscreen/rm31100_ts.c

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 0f13e52..2a85353 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -329,6 +329,18 @@ config TOUCHSCREEN_ELAN
 	  To compile this driver as a module, choose M here: the
 	  module will be called elants_i2c.
 
+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 rm31100_ts.
+
 config TOUCHSCREEN_ELO
 	tristate "Elo serial touchscreens"
 	select SERIO
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 687d5a7..aae4af2 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -78,3 +78,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE)	+= zylonite-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_W90X900)	+= w90p910_ts.o
 obj-$(CONFIG_TOUCHSCREEN_TPS6507X)	+= tps6507x-ts.o
 obj-$(CONFIG_TOUCHSCREEN_ZFORCE)	+= zforce_ts.o
+obj-$(CONFIG_TOUCHSCREEN_RM_TS)	+= rm31100_ts.o
diff --git a/drivers/input/touchscreen/rm31100_ts.c b/drivers/input/touchscreen/rm31100_ts.c
new file mode 100644
index 0000000..941fa31
--- /dev/null
+++ b/drivers/input/touchscreen/rm31100_ts.c
@@ -0,0 +1,843 @@
+/*
+ * Raydium RM31100_ts touchscreen 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/async.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#ifdef CONFIG_MISC_DEV
+#include <linux/miscdevice.h>
+#endif
+#include <linux/uaccess.h>
+#include <asm/unaligned.h>
+#include <linux/input/mt.h>
+
+#define rm31100	0x0
+#define rm3110x	0x1
+
+#define INVALID_DATA	0xff
+#define MAX_REPORT_TOUCHED_POINTS	10
+
+#define I2C_CLIENT_ADDR         0x39
+#define I2C_DMA_CLIENT_ADDR     0x5A
+
+struct rm31100_ts_data {
+	u8 x_index;
+	u8 y_index;
+	u8 z_index;
+	u8 id_index;
+	u8 touch_index;
+	u8 data_reg;
+	u8 status_reg;
+	u8 data_size;
+	u8 touch_bytes;
+	u8 update_data;
+	u8 touch_meta_data;
+	u8 finger_size;
+};
+
+struct rm3110x_ts_platform_data {
+	int (*power_on)(int on);
+	int (*dev_setup)(bool on);
+	const char *ts_name;
+	u32 dis_min_x; /* display resoltion ABS min*/
+	u32 dis_max_x;/* display resoltion ABS max*/
+	u32 dis_min_y;
+	u32 dis_max_y;
+	u32 min_touch; /* no.of touches supported */
+	u32 max_touch;
+	u32 min_tid; /* track id */
+	u32 max_tid;
+	u32 min_width;/* size of the finger */
+	u32 max_width;
+	u32 res_x; /* TS resolution unit*/
+	u32 res_y;
+	u32 swap_xy;
+	u8 nfingers;
+	u32 irq_gpio;
+	int resout_gpio;
+	bool wakeup;
+	u32 irq_cfg;
+};
+
+static struct rm31100_ts_data devices[] = {
+	[0] = {
+		.x_index = 2,
+		.y_index = 4,
+		.z_index = 6,
+		.id_index = 1,
+		.data_reg = 0x1,
+		.status_reg = 0,
+		.update_data = 0x0,
+		.touch_bytes = 6,
+		.touch_meta_data = 1,
+		.finger_size = 70,
+	},
+};
+
+struct rm31100_ts {
+	struct i2c_client *client;
+	struct input_dev *input;
+	struct rm3110x_ts_platform_data *pdata;
+	struct rm31100_ts_data *dd;
+	u8 *touch_data;
+	u8 device_id;
+	u8 prev_touches;
+	bool is_suspended;
+	bool int_pending;
+	struct mutex access_lock;
+	u32 pen_irq;
+};
+
+struct rm31100_ts *pts;
+/*
+static inline u16 join_bytes(u8 a, u8 b)
+{
+	u16 ab = 0;
+	ab = ab | a;
+	ab = ab << 8 | b;
+	return ab;
+}
+*/
+static s32 rm31100_ts_write_reg_u8(struct i2c_client *client, u8 reg, u8 val)
+{
+	s32 data;
+
+	data = i2c_smbus_write_byte_data(client, reg, val);
+	if (data < 0)
+		dev_err(&client->dev, "error %d in writing reg 0x%x\n",
+						 data, reg);
+
+	return data;
+}
+
+static s32 rm31100_ts_read_reg_u8(struct i2c_client *client, u8 reg)
+{
+	s32 data;
+
+	data = i2c_smbus_read_byte_data(client, reg);
+	if (data < 0)
+		dev_err(&client->dev, "error %d in reading reg 0x%x\n",
+						 data, reg);
+
+	return data;
+}
+
+static int rm31100_ts_read(struct i2c_client *client, u8 reg, u8 *buf, int num)
+{
+	struct i2c_msg xfer_msg[2];
+
+	xfer_msg[0].addr = client->addr;
+	xfer_msg[0].len = 1;
+	xfer_msg[0].flags = 0;
+	xfer_msg[0].buf = &reg;
+
+	xfer_msg[1].addr = client->addr;
+	xfer_msg[1].len = num;
+	xfer_msg[1].flags = I2C_M_RD;
+	xfer_msg[1].buf = buf;
+
+	return i2c_transfer(client->adapter, xfer_msg, 2);
+}
+
+static int
+rm31100_ts_write_client_dma(struct i2c_client *client, u8 *buf, int num)
+{
+	struct i2c_msg xfer_msg[1];
+
+	xfer_msg[0].addr = I2C_DMA_CLIENT_ADDR;
+	xfer_msg[0].len = num;
+	xfer_msg[0].flags = 0;
+	xfer_msg[0].buf = buf;
+
+	return i2c_transfer(client->adapter, xfer_msg, 1);
+}
+
+static int rm31100_ts_write(struct i2c_client *client, u8 *buf, int num)
+{
+	struct i2c_msg xfer_msg[1];
+
+	xfer_msg[0].addr = client->addr;
+	xfer_msg[0].len = num;
+	xfer_msg[0].flags = 0;
+	xfer_msg[0].buf = buf;
+
+	return i2c_transfer(client->adapter, xfer_msg, 1);
+}
+#ifdef CONFIG_MISC_DEV
+static int dev_open(struct inode *inode, struct file *filp)
+{
+	mutex_lock(&pts->access_lock);
+	return 0;
+}
+
+static int dev_release(struct inode *inode, struct file *filp)
+{
+	mutex_unlock(&pts->access_lock);
+	return 0;
+}
+static ssize_t
+dev_read(struct file *filp, char __user *buf, size_t count, loff_t *pos)
+{
+	u8 *kbuf;
+	struct i2c_msg xfer_msg;
+	/*static char out[] = "1234567890";*/
+	/*static int idx;*//*= 0; remove by checkpatch*/
+	int i;
+
+	kbuf = kmalloc(count, GFP_KERNEL);
+	if (kbuf == NULL)
+		return -ENOMEM;
+
+	/*xfer_msg.addr = pts->client->addr;*/
+	xfer_msg.addr = I2C_CLIENT_ADDR;
+	xfer_msg.len = count;
+	xfer_msg.flags = I2C_M_RD;
+	xfer_msg.buf = kbuf;
+
+	i2c_transfer(pts->client->adapter, &xfer_msg, 1);
+
+	if (copy_to_user(buf, kbuf, count) == 0)
+		return count;
+	else
+		return -EFAULT;
+}
+
+static ssize_t
+dev_write(struct file *filp, const char __user *buf, size_t count, loff_t *pos)
+{
+	u8 *kbuf;
+	ssize_t status = 0;
+	int i;
+
+	kbuf = kmalloc(count, GFP_KERNEL);
+	if (kbuf == NULL) {
+		dev_err("kmalloc() fail\n");
+		return -ENOMEM;
+	}
+
+	if (copy_from_user(kbuf, buf, count) == 0) {
+		pts->client->addr = I2C_CLIENT_ADDR;
+		if (rm31100_ts_write(pts->client, kbuf, count) < 0)
+			status = -EFAULT;
+		else
+			status = count;
+	} else {
+		dev_err("copy_from_user() fail\n");
+		status = -EFAULT;
+	}
+
+	kfree(kbuf);
+	return status;
+}
+
+static const struct file_operations dev_fops = {
+	.owner = THIS_MODULE,
+	.open = dev_open,
+	.release = dev_release,
+	.read = dev_read,
+	.write = dev_write,
+	/*.unlocked_ioctl = dev_ioctl,*/
+};
+
+static struct miscdevice raydium_ts_miscdev = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "raydium_ts",
+	.fops = &dev_fops,
+};
+#endif
+
+
+ssize_t show(struct device_driver *drv, char *buff)
+{
+	struct i2c_msg xfer_msg;
+	int num = 10;
+	char buf[100];
+	/*int i;*/
+
+	xfer_msg.addr = pts->client->addr;
+	xfer_msg.len = num;
+	xfer_msg.flags = I2C_M_RD;
+	xfer_msg.buf = buf;
+	pts->client->addr = I2C_CLIENT_ADDR;
+	i2c_transfer(pts->client->adapter, &xfer_msg, 1);
+
+	return 0;
+}
+
+ssize_t store(struct device_driver *drv, const char *buf, size_t count)
+{
+	/*unsigned char pkt[] = { 0xF2, 5, 1, 1 };*/
+	unsigned char pkt[] = { 0xF1, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
+
+	pts->client->addr = I2C_CLIENT_ADDR;
+	rm31100_ts_write(pts->client, pkt, sizeof(pkt));
+
+	return sizeof(pkt);
+}
+
+DRIVER_ATTR(myAttr, 0x777, show, store);
+
+static void report_data(struct rm31100_ts *dev, u16 x, u16 y,
+	u8 pressure, u8 id)
+{
+	struct input_dev *input_dev = dev->input;
+	if (dev->pdata->swap_xy)
+		swap(x, y);
+
+	input_mt_slot(input_dev, id);
+	input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, true);
+	input_report_abs(input_dev, ABS_MT_POSITION_X, x);
+	input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
+	input_report_abs(input_dev, ABS_MT_PRESSURE, pressure);
+	input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, dev->dd->finger_size);
+/*
+	dev_dbg("%s(): id =%2hhd, x =%4hd, y =%4hd, pressure = %hhd\n",
+		__func__, id, x, y, pressure);
+*/
+}
+
+static void process_rm31100_data(struct rm31100_ts *ts)
+{
+	u8 id, pressure, touches, i;
+	u16 x, y;
+
+	touches = ts->touch_data[ts->dd->touch_index];
+
+	if (touches > 0) {
+		for (i = 0; i < touches; i++) {
+			id = ts->touch_data[i * ts->dd->touch_bytes +
+				ts->dd->id_index];
+			pressure = ts->touch_data[i * ts->dd->touch_bytes +
+				ts->dd->z_index];
+			x = get_unaligned_le16(&(ts->touch_data[i *
+				ts->dd->touch_bytes + ts->dd->x_index]));
+			y = get_unaligned_le16(&(ts->touch_data[i *
+				ts->dd->touch_bytes + ts->dd->y_index]));
+			report_data(ts, x, y, pressure, id);
+		}
+	} else
+		input_mt_sync(ts->input);
+
+	ts->prev_touches = touches;
+	/*input_report_key(ts->input, BTN_TOUCH, 1);*/
+	input_mt_report_pointer_emulation(ts->input, true);
+	input_sync(ts->input);
+}
+
+/*static void rm31100_ts_xy_worker(struct work_struct *work) JL remove*/
+static void rm31100_ts_xy_worker(struct rm31100_ts *work)
+{
+	int rc;
+	u8 client_dma_package[4] = {0x0f, 0x00, 0x20, 0x81};
+	struct rm31100_ts *ts = work;
+
+	if (ts->is_suspended == true) {
+		dev_dbg(&ts->client->dev, "TS is supended\n");
+		ts->int_pending = true;
+		return;
+	}
+
+	mutex_lock(&ts->access_lock);
+	/* read data from DATA_REG */
+	/*RM31100 DMA Mode*/
+	rc = rm31100_ts_write_client_dma(ts->client, client_dma_package, 0x04);
+	if (rc < 0) {
+		dev_err(&ts->client->dev, "write client dma failed\n");
+		goto schedule;
+	}
+	rc = rm31100_ts_read(ts->client, ts->dd->data_reg, ts->touch_data,
+	ts->dd->data_size);
+
+	if (rc < 0) {
+		dev_err(&ts->client->dev, "read failed\n");
+		goto schedule;
+	}
+
+	if (ts->touch_data[ts->dd->touch_index] == INVALID_DATA)
+		goto schedule;
+
+	/* write to STATUS_REG to release lock */
+	rc = rm31100_ts_write_reg_u8(ts->client,
+		ts->dd->status_reg, ts->dd->update_data);
+	if (rc < 0) {
+		dev_err(&ts->client->dev, "write failed, try once more\n");
+
+		rc = rm31100_ts_write_reg_u8(ts->client,
+			ts->dd->status_reg, ts->dd->update_data);
+		if (rc < 0)
+			dev_err(&ts->client->dev, "write failed, exiting\n");
+	}
+
+	process_rm31100_data(ts);
+schedule:
+	mutex_unlock(&ts->access_lock);
+}
+
+static irqreturn_t rm31100_ts_irq(int irq, void *dev_id)
+{
+	struct rm31100_ts *ts = dev_id;
+
+	rm31100_ts_xy_worker(ts);
+	return IRQ_HANDLED;
+}
+
+static int rm31100_ts_init_ts(struct i2c_client *client, struct rm31100_ts *ts)
+{
+	/*struct input_dev *input_device;*/
+	/*int rc = 0;*/
+
+	ts->dd = &devices[ts->device_id];
+
+	if (!ts->pdata->nfingers) {
+		dev_err(&client->dev, "Touches information not specified\n");
+		return -EINVAL;
+	}
+
+	if (ts->device_id == rm3110x) {
+		if (ts->pdata->nfingers > 2) {
+			dev_err(&client->dev, "Touches >=1 & <= 2\n");
+			return -EINVAL;
+		}
+		ts->dd->data_size = ts->dd->touch_bytes;
+		ts->dd->touch_index = 0x0;
+	} else if (ts->device_id == rm31100) {
+		if (ts->pdata->nfingers > 10) {
+			dev_err(&client->dev, "Touches >=1 & <= 10\n");
+			return -EINVAL;
+		}
+		ts->dd->data_size = ts->pdata->nfingers * ts->dd->touch_bytes +
+						ts->dd->touch_meta_data;
+		ts->dd->touch_index = 0x0;
+	}
+	/* w001 */
+	else {
+		ts->dd->data_size = ts->pdata->nfingers * ts->dd->touch_bytes +
+		ts->dd->touch_meta_data;
+		ts->dd->touch_index = 0x0;
+	}
+
+	ts->touch_data = kzalloc(ts->dd->data_size, GFP_KERNEL);
+	if (!ts->touch_data) {
+		pr_err("%s: Unable to allocate memory\n", __func__);
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int rm31100_ts_suspend(struct device *dev)
+{
+	struct rm31100_ts *ts = dev_get_drvdata(dev);
+	int rc = 0;
+
+	if (device_may_wakeup(dev)) {
+		/* mark suspend flag */
+		ts->is_suspended = true;
+		enable_irq_wake(ts->pen_irq);
+	}
+
+	disable_irq(ts->pen_irq);
+
+	gpio_free(ts->pdata->irq_gpio);
+
+	if (ts->pdata->power_on) {
+		rc = ts->pdata->power_on(0);
+		if (rc) {
+			dev_err(dev, "unable to goto suspend\n");
+			return rc;
+		}
+	}
+	return 0;
+}
+
+static int rm31100_ts_resume(struct device *dev)
+{
+	struct rm31100_ts *ts = dev_get_drvdata(dev);
+
+	int rc = 0;
+
+	if (device_may_wakeup(dev)) {
+		disable_irq_wake(ts->pen_irq);
+
+		ts->is_suspended = false;
+
+		if (ts->int_pending == true)
+			ts->int_pending = false;
+	} else {
+		if (ts->pdata->power_on) {
+			rc = ts->pdata->power_on(1);
+			if (rc) {
+				dev_err(dev, "unable to resume\n");
+				return rc;
+			}
+		}
+
+		enable_irq(ts->pen_irq);
+
+		/* Clear the status register of the TS controller */
+		rc = rm31100_ts_write_reg_u8(ts->client,
+			ts->dd->status_reg, ts->dd->update_data);
+		if (rc < 0) {
+			dev_err(&ts->client->dev,
+				"write failed, try once more\n");
+
+			rc = rm31100_ts_write_reg_u8(ts->client,
+				ts->dd->status_reg,
+				ts->dd->update_data);
+			if (rc < 0)
+				dev_err(&ts->client->dev,
+					"write failed, exiting\n");
+		}
+	}
+
+	return 0;
+}
+
+static const struct dev_pm_ops rm31100_ts_pm_ops = {
+	.suspend = rm31100_ts_suspend,
+	.resume = rm31100_ts_resume,
+};
+#endif
+
+static int rm_input_dev_create(struct rm31100_ts *ts)
+{
+	struct input_dev *input_device;
+	int rc = 0;
+	ts->prev_touches = 0;
+
+	input_device = input_allocate_device();
+	if (!input_device) {
+		rc = -ENOMEM;
+		goto error_alloc_dev;
+	}
+	ts->input = input_device;
+
+	input_device->name = "raydium_ts";
+	input_device->id.bustype = BUS_I2C;
+	input_device->dev.parent = &ts->client->dev;
+	input_set_drvdata(input_device, ts);
+
+	__set_bit(EV_ABS, input_device->evbit);
+	__set_bit(INPUT_PROP_DIRECT, input_device->propbit);
+	__set_bit(BTN_TOUCH, input_device->keybit);
+
+
+	if (ts->device_id == rm31100) {
+		/* set up virtual key */
+		__set_bit(EV_KEY, input_device->evbit);
+	}
+	input_mt_init_slots(input_device,
+		MAX_REPORT_TOUCHED_POINTS, 0);
+	input_set_abs_params(input_device, ABS_MT_POSITION_X,
+		ts->pdata->dis_min_x, ts->pdata->dis_max_x, 0, 0);
+	input_set_abs_params(input_device, ABS_MT_POSITION_Y,
+		ts->pdata->dis_min_y, ts->pdata->dis_max_y, 0, 0);
+	input_set_abs_params(input_device, ABS_MT_PRESSURE,
+		0, 0xFF, 0, 0);
+	input_set_abs_params(input_device, ABS_MT_TOUCH_MAJOR,
+		0, 0xFF, 0, 0);
+	rc = input_register_device(input_device);
+	if (rc)
+		goto error_unreg_device;
+
+	return 0;
+
+error_unreg_device:
+	input_free_device(input_device);
+error_alloc_dev:
+	ts->input = NULL;
+	return rc;
+}
+
+static int rm31100_initialize(struct i2c_client *client)
+{
+	struct rm31100_ts *ts = i2c_get_clientdata(client);
+	int rc = 0, /*retry_cnt = 0,*/ temp_reg;
+	/* power on the device */
+	if (ts->pdata->power_on) {
+		rc = ts->pdata->power_on(1);
+		if (rc) {
+			pr_err("%s: Unable to power on the device\n", __func__);
+			goto error_dev_setup;
+		}
+	}
+
+	/* read one byte to make sure i2c device exists */
+	if (ts->device_id == rm3110x)
+		temp_reg = 0x01;
+	else if (ts->device_id == rm31100)
+		temp_reg = 0x00;
+	else
+		temp_reg = 0x05;
+
+	rc = rm31100_ts_read_reg_u8(client, temp_reg);
+	if (rc < 0) {
+		dev_err(&client->dev, "i2c sanity check failed\n");
+		goto error_power_on;
+	}
+
+	rc = rm31100_ts_init_ts(client, ts);
+	if (rc < 0) {
+		dev_err(&client->dev, "rm31100_ts init failed\n");
+		goto error_mutex_destroy;
+	}
+
+	/* configure touchscreen reset out gpio */
+	rc = gpio_request(ts->pdata->resout_gpio, "rm31100_resout_gpio");
+	if (rc) {
+		pr_err("%s: unable to request gpio %d\n",
+			__func__, ts->pdata->resout_gpio);
+		goto error_uninit_ts;
+	}
+
+	rc = gpio_direction_output(ts->pdata->resout_gpio, 0);
+	if (rc) {
+		pr_err("%s: unable to set direction for gpio %d\n",
+			__func__, ts->pdata->resout_gpio);
+		goto error_resout_gpio_dir;
+	}
+	/* reset gpio stabilization time */
+	msleep(20);
+
+	return 0;
+error_resout_gpio_dir:
+	if (ts->pdata->resout_gpio >= 0)
+		gpio_free(ts->pdata->resout_gpio);
+error_uninit_ts:
+	input_unregister_device(ts->input);
+	kfree(ts->touch_data);
+error_mutex_destroy:
+	mutex_destroy(&ts->access_lock);
+error_power_on:
+	if (ts->pdata->power_on)
+		ts->pdata->power_on(0);
+error_dev_setup:
+	if (ts->pdata->dev_setup)
+		ts->pdata->dev_setup(0);
+	return rc;
+}
+
+static void rm_initialize_async(void *data, async_cookie_t cookie)
+{
+	struct rm31100_ts *ts = data;
+	struct i2c_client *client = ts->client;
+	unsigned long irqflags;
+	int err = 0;
+
+	mutex_lock(&ts->access_lock);
+
+	err = rm31100_initialize(client);
+	if (err < 0) {
+		dev_err(&client->dev, "probe failed! unbind device.\n");
+		goto error_free_mem;
+	}
+
+	err = rm_input_dev_create(ts);
+	if (err) {
+		dev_err(&client->dev, "%s crated failed, %d\n", __func__, err);
+		goto error_goio_release;
+	}
+
+	irqflags = client->dev.of_node ? 0 : IRQF_TRIGGER_FALLING;
+
+	err = request_threaded_irq(ts->pen_irq, NULL,
+				   rm31100_ts_irq,
+				   irqflags | IRQF_ONESHOT,
+				   ts->client->dev.driver->name, ts);
+	if (err) {
+		dev_err(&client->dev, "Failed to register interrupt\n");
+		goto error_dev_release;
+	}
+
+	mutex_unlock(&ts->access_lock);
+
+	return;
+
+error_dev_release:
+	input_free_device(ts->input);
+error_goio_release:
+	if (ts->pdata->resout_gpio >= 0)
+		gpio_free(ts->pdata->resout_gpio);
+error_free_mem:
+	mutex_unlock(&ts->access_lock);
+	kfree(ts);
+	return;
+}
+
+
+/*static int __devinit rm31100_ts_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)*/
+static int rm31100_ts_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct rm31100_ts *ts;
+	struct rm3110x_ts_platform_data *pdata = client->dev.platform_data;
+	int rc/*, temp_reg*/;
+	union i2c_smbus_data dummy;
+
+	if (!pdata) {
+		dev_err(&client->dev, "platform data is required!\n");
+		return -EINVAL;
+	}
+
+	if (!i2c_check_functionality(client->adapter,
+		I2C_FUNC_SMBUS_READ_WORD_DATA)) {
+		dev_err(&client->dev, "I2C functionality not supported\n");
+		return -EIO;
+	}
+	/* 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)
+		return -ENODEV;
+
+	ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+	pts = ts;
+
+	/* Enable runtime PM ops, start in ACTIVE mode */
+	rc = pm_runtime_set_active(&client->dev);
+	if (rc < 0)
+		dev_warn(&client->dev, "unable to set runtime pm state\n");
+	pm_runtime_enable(&client->dev);
+
+	ts->client = client;
+	ts->pdata = pdata;
+	i2c_set_clientdata(client, ts);
+	ts->device_id = id->driver_data;
+
+	if (ts->pdata->dev_setup) {
+		rc = ts->pdata->dev_setup(1);
+		if (rc < 0) {
+			dev_err(&client->dev, "dev setup failed\n");
+			goto error_touch_data_alloc;
+		}
+	}
+
+
+	ts->is_suspended = false;
+	ts->int_pending = false;
+	/*mutex_init(&ts->sus_lock); JL remove*/
+	mutex_init(&ts->access_lock);
+
+	async_schedule(rm_initialize_async, ts);
+
+	device_init_wakeup(&client->dev, ts->pdata->wakeup);
+	return 0;
+
+error_touch_data_alloc:
+	pm_runtime_set_suspended(&client->dev);
+	pm_runtime_disable(&client->dev);
+	kfree(ts);
+	return rc;
+}
+
+/*static int __devexit RM31100_ts_remove(struct i2c_client *client)*/
+static int rm31100_ts_remove(struct i2c_client *client)
+{
+	struct rm31100_ts *ts = i2c_get_clientdata(client);
+
+	pm_runtime_set_suspended(&client->dev);
+	pm_runtime_disable(&client->dev);
+
+	device_init_wakeup(&client->dev, 0);
+	free_irq(ts->pen_irq, ts);
+
+	gpio_free(ts->pdata->irq_gpio);
+
+	if (ts->pdata->resout_gpio >= 0)
+		gpio_free(ts->pdata->resout_gpio);
+	input_unregister_device(ts->input);
+
+	/*mutex_destroy(&ts->sus_lock); JL remove*/
+	mutex_destroy(&ts->access_lock);
+
+	if (ts->pdata->power_on)
+		ts->pdata->power_on(0);
+
+	if (ts->pdata->dev_setup)
+		ts->pdata->dev_setup(0);
+
+	kfree(ts->touch_data);
+	kfree(ts);
+
+	return 0;
+}
+
+static const struct i2c_device_id rm31100_ts_id[] = {
+	{"RM31100", rm31100},
+	{"RM3110x", rm3110x},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, rm31100_ts_id);
+
+
+static struct i2c_driver rm31100_ts_driver = {
+	.driver = {
+		.name = "raydium_ts",
+		.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+		.pm = &rm31100_ts_pm_ops,
+#endif
+	},
+	.probe = rm31100_ts_probe,
+	.remove = rm31100_ts_remove,
+	.id_table = rm31100_ts_id,
+};
+
+static int __init rm31100_ts_init(void)
+{
+	int rc;
+	int rc2;
+
+	rc = i2c_add_driver(&rm31100_ts_driver);
+
+	rc2 = driver_create_file(&rm31100_ts_driver.driver,
+		&driver_attr_myAttr);
+
+	return rc;
+}
+/* Making this as late init to avoid power fluctuations
+ * during LCD initialization.
+ */
+module_init(rm31100_ts_init);
+
+static void __exit rm31100_ts_exit(void)
+{
+	i2c_del_driver(&rm31100_ts_driver);
+}
+module_exit(rm31100_ts_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("rm31100-rm3110x touchscreen controller driver");
+MODULE_AUTHOR("Raydium");
+MODULE_ALIAS("platform:rm31100_ts");
-- 
2.1.2

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

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2015-01-06 19:43 ` Jeremiah Mahler
@ 2015-01-07  0:40   ` Dmitry Torokhov
  0 siblings, 0 replies; 58+ messages in thread
From: Dmitry Torokhov @ 2015-01-07  0:40 UTC (permalink / raw)
  To: Jeremiah Mahler, jeffrey.lin, rydberg, shc_work, charliemooney,
	bleung, lee.jones, jeffrey.lin, roger.yang, KP.li, linux-kernel,
	linux-input

On Tue, Jan 06, 2015 at 11:43:54AM -0800, Jeremiah Mahler wrote:
> On Tue, Jan 06, 2015 at 05:25:50PM +0800, jeffrey.lin wrote:
> > From: "jeffrey.lin" <jeffrey.lin@rad-ic.com>
> > 
> > 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@rad-ic.com
> 
> You need your full legal name in your Signed-off-by:

Also, there was feedback provided earlier and I still see it not being
addresses (the presence of custom char dev interface for example). It
would also be helpful if refreshed mentioned what was change from the
previos version.

Thanks.

-- 
Dmitry

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

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2015-01-06  9:25 jeffrey.lin
@ 2015-01-06 19:43 ` Jeremiah Mahler
  2015-01-07  0:40   ` Dmitry Torokhov
  0 siblings, 1 reply; 58+ messages in thread
From: Jeremiah Mahler @ 2015-01-06 19:43 UTC (permalink / raw)
  To: jeffrey.lin
  Cc: dmitry.torokhov, rydberg, shc_work, charliemooney, bleung,
	lee.jones, jeffrey.lin, roger.yang, KP.li, linux-kernel,
	linux-input

On Tue, Jan 06, 2015 at 05:25:50PM +0800, jeffrey.lin wrote:
> From: "jeffrey.lin" <jeffrey.lin@rad-ic.com>
> 
> 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@rad-ic.com

You need your full legal name in your Signed-off-by:

-- 
- Jeremiah Mahler

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

* [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
@ 2015-01-06  9:25 jeffrey.lin
  2015-01-06 19:43 ` Jeremiah Mahler
  0 siblings, 1 reply; 58+ messages in thread
From: jeffrey.lin @ 2015-01-06  9:25 UTC (permalink / raw)
  To: dmitry.torokhov, rydberg, shc_work, charliemooney, bleung, lee.jones
  Cc: jeffrey.lin, roger.yang, KP.li, linux-kernel, linux-input

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

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@rad-ic.com
---
 drivers/input/touchscreen/Kconfig      |  12 +
 drivers/input/touchscreen/Makefile     |   1 +
 drivers/input/touchscreen/rm31100_ts.c | 846 +++++++++++++++++++++++++++++++++
 3 files changed, 859 insertions(+)
 create mode 100644 drivers/input/touchscreen/rm31100_ts.c

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 3ce9181..d0324d2 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -955,4 +955,16 @@ config TOUCHSCREEN_ZFORCE
 	  To compile this driver as a module, choose M here: the
 	  module will be called zforce_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 rm31100_ts.
+
 endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 687d5a7..3220f66 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -78,3 +78,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE)	+= zylonite-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_W90X900)	+= w90p910_ts.o
 obj-$(CONFIG_TOUCHSCREEN_TPS6507X)	+= tps6507x-ts.o
 obj-$(CONFIG_TOUCHSCREEN_ZFORCE)	+= zforce_ts.o
+obj-$(CONFIG_TOUCHSCREEN_RM_TS)		+= rm31100_ts.o
diff --git a/drivers/input/touchscreen/rm31100_ts.c b/drivers/input/touchscreen/rm31100_ts.c
new file mode 100644
index 0000000..997dc0c
--- /dev/null
+++ b/drivers/input/touchscreen/rm31100_ts.c
@@ -0,0 +1,846 @@
+/*
+ * Raydium RM31100_ts touchscreen 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
+ *
+ * History:
+ *			(C) 2012 Raydium - Update for GPL distribution
+ *			(C) 2009 Enea - Original prototype
+ *
+ */
+#include <linux/async.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#ifdef CONFIG_MISC_DEV
+#include <linux/miscdevice.h>
+#endif
+#include <linux/uaccess.h>
+#include <asm/unaligned.h>
+#include <linux/input/mt.h>
+
+#define rm31100	0x0
+#define rm3110x	0x1
+
+#define INVALID_DATA	0xff
+#define MAX_REPORT_TOUCHED_POINTS	10
+
+#define I2C_CLIENT_ADDR         0x39
+#define I2C_DMA_CLIENT_ADDR     0x5A
+
+struct rm31100_ts_data {
+	u8 x_index;
+	u8 y_index;
+	u8 z_index;
+	u8 id_index;
+	u8 touch_index;
+	u8 data_reg;
+	u8 status_reg;
+	u8 data_size;
+	u8 touch_bytes;
+	u8 update_data;
+	u8 touch_meta_data;
+	u8 finger_size;
+};
+
+struct rm3110x_ts_platform_data {
+	int (*power_on)(int on);
+	int (*dev_setup)(bool on);
+	const char *ts_name;
+	u32 dis_min_x; /* display resoltion ABS min*/
+	u32 dis_max_x;/* display resoltion ABS max*/
+	u32 dis_min_y;
+	u32 dis_max_y;
+	u32 min_touch; /* no.of touches supported */
+	u32 max_touch;
+	u32 min_tid; /* track id */
+	u32 max_tid;
+	u32 min_width;/* size of the finger */
+	u32 max_width;
+	u32 res_x; /* TS resolution unit*/
+	u32 res_y;
+	u32 swap_xy;
+	u8 nfingers;
+	u32 irq_gpio;
+	int resout_gpio;
+	bool wakeup;
+	u32 irq_cfg;
+};
+
+static struct rm31100_ts_data devices[] = {
+	[0] = {
+		.x_index = 2,
+		.y_index = 4,
+		.z_index = 6,
+		.id_index = 1,
+		.data_reg = 0x1,
+		.status_reg = 0,
+		.update_data = 0x0,
+		.touch_bytes = 6,
+		.touch_meta_data = 1,
+		.finger_size = 70,
+	},
+};
+
+struct rm31100_ts {
+	struct i2c_client *client;
+	struct input_dev *input;
+	struct rm3110x_ts_platform_data *pdata;
+	struct rm31100_ts_data *dd;
+	u8 *touch_data;
+	u8 device_id;
+	u8 prev_touches;
+	bool is_suspended;
+	bool int_pending;
+	struct mutex access_lock;
+	u32 pen_irq;
+};
+
+struct rm31100_ts *pts;
+/*
+static inline u16 join_bytes(u8 a, u8 b)
+{
+	u16 ab = 0;
+	ab = ab | a;
+	ab = ab << 8 | b;
+	return ab;
+}
+*/
+static s32 rm31100_ts_write_reg_u8(struct i2c_client *client, u8 reg, u8 val)
+{
+	s32 data;
+
+	data = i2c_smbus_write_byte_data(client, reg, val);
+	if (data < 0)
+		dev_err(&client->dev, "error %d in writing reg 0x%x\n",
+						 data, reg);
+
+	return data;
+}
+
+static s32 rm31100_ts_read_reg_u8(struct i2c_client *client, u8 reg)
+{
+	s32 data;
+
+	data = i2c_smbus_read_byte_data(client, reg);
+	if (data < 0)
+		dev_err(&client->dev, "error %d in reading reg 0x%x\n",
+						 data, reg);
+
+	return data;
+}
+
+static int rm31100_ts_read(struct i2c_client *client, u8 reg, u8 *buf, int num)
+{
+	struct i2c_msg xfer_msg[2];
+
+	xfer_msg[0].addr = client->addr;
+	xfer_msg[0].len = 1;
+	xfer_msg[0].flags = 0;
+	xfer_msg[0].buf = &reg;
+
+	xfer_msg[1].addr = client->addr;
+	xfer_msg[1].len = num;
+	xfer_msg[1].flags = I2C_M_RD;
+	xfer_msg[1].buf = buf;
+
+	return i2c_transfer(client->adapter, xfer_msg, 2);
+}
+
+static int rm31100_ts_write_client_dma(struct i2c_client *client, u8 *buf, int num)
+{
+	struct i2c_msg xfer_msg[1];
+
+	xfer_msg[0].addr = I2C_DMA_CLIENT_ADDR;
+	xfer_msg[0].len = num;
+	xfer_msg[0].flags = 0;
+	xfer_msg[0].buf = buf;
+
+	return i2c_transfer(client->adapter, xfer_msg, 1);
+}
+
+static int rm31100_ts_write(struct i2c_client *client, u8 *buf, int num)
+{
+	struct i2c_msg xfer_msg[1];
+
+	xfer_msg[0].addr = client->addr;
+	xfer_msg[0].len = num;
+	xfer_msg[0].flags = 0;
+	xfer_msg[0].buf = buf;
+
+	return i2c_transfer(client->adapter, xfer_msg, 1);
+}
+#ifdef CONFIG_MISC_DEV
+static int dev_open(struct inode *inode, struct file *filp)
+{
+	mutex_lock(&pts->access_lock);
+	return 0;
+}
+
+static int dev_release(struct inode *inode, struct file *filp)
+{
+	mutex_unlock(&pts->access_lock);
+	return 0;
+}
+static ssize_t
+dev_read(struct file *filp, char __user *buf, size_t count, loff_t *pos)
+{
+	u8 *kbuf;
+	struct i2c_msg xfer_msg;
+	/*static char out[] = "1234567890";*/
+	/*static int idx;*//*= 0; remove by checkpatch*/
+	int i;
+
+	kbuf = kmalloc(count, GFP_KERNEL);
+	if (kbuf == NULL)
+		return -ENOMEM;
+
+	/*xfer_msg.addr = pts->client->addr;*/
+	xfer_msg.addr = I2C_CLIENT_ADDR;
+	xfer_msg.len = count;
+	xfer_msg.flags = I2C_M_RD;
+	xfer_msg.buf = kbuf;
+
+	i2c_transfer(pts->client->adapter, &xfer_msg, 1);
+
+	if (copy_to_user(buf, kbuf, count) == 0)
+		return count;
+	else
+		return -EFAULT;
+}
+
+static ssize_t
+dev_write(struct file *filp, const char __user *buf, size_t count, loff_t *pos)
+{
+	u8 *kbuf;
+	ssize_t status = 0;
+	int i;
+
+	kbuf = kmalloc(count, GFP_KERNEL);
+	if (kbuf == NULL) {
+		dev_err("kmalloc() fail\n");
+		return -ENOMEM;
+	}
+
+	if (copy_from_user(kbuf, buf, count) == 0) {
+		pts->client->addr = I2C_CLIENT_ADDR;
+		if (rm31100_ts_write(pts->client, kbuf, count) < 0)
+			status = -EFAULT;
+		else
+			status = count;
+	} else {
+		dev_err("copy_from_user() fail\n");
+		status = -EFAULT;
+	}
+
+	kfree(kbuf);
+	return status;
+}
+
+static struct file_operations dev_fops = {
+	.owner = THIS_MODULE,
+	.open = dev_open,
+	.release = dev_release,
+	.read = dev_read,
+	.write = dev_write,
+	/*.unlocked_ioctl = dev_ioctl,*/
+};
+
+static struct miscdevice raydium_ts_miscdev = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "raydium_ts",
+	.fops = &dev_fops,
+};
+#endif
+
+
+ssize_t show(struct device_driver *drv, char *buff)
+{
+	struct i2c_msg xfer_msg;
+	int num = 10;
+	char buf[100];
+	/*int i;*/
+
+	xfer_msg.addr = pts->client->addr;
+	xfer_msg.len = num;
+	xfer_msg.flags = I2C_M_RD;
+	xfer_msg.buf = buf;
+	pts->client->addr = I2C_CLIENT_ADDR;
+	i2c_transfer(pts->client->adapter, &xfer_msg, 1);
+
+	return 0;
+}
+
+ssize_t store(struct device_driver *drv, const char *buf, size_t count)
+{
+	/*unsigned char pkt[] = { 0xF2, 5, 1, 1 };*/
+	unsigned char pkt[] = { 0xF1, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
+
+	pts->client->addr = I2C_CLIENT_ADDR;
+	rm31100_ts_write(pts->client, pkt, sizeof(pkt));
+
+	return sizeof(pkt);
+}
+
+DRIVER_ATTR(myAttr, 0x777, show, store);
+
+static void report_data(struct rm31100_ts *dev, u16 x, u16 y,
+	u8 pressure, u8 id)
+{
+	struct input_dev *input_dev = dev->input;
+	if (dev->pdata->swap_xy)
+		swap(x, y);
+
+	input_mt_slot(input_dev, id);
+	input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, true);
+	input_report_abs(input_dev, ABS_MT_POSITION_X, x);
+	input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
+	input_report_abs(input_dev, ABS_MT_PRESSURE, pressure);
+	input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, dev->dd->finger_size);
+/*
+	dev_dbg("%s(): id =%2hhd, x =%4hd, y =%4hd, pressure = %hhd\n",
+		__func__, id, x, y, pressure);
+*/
+}
+
+static void process_rm31100_data(struct rm31100_ts *ts)
+{
+	u8 id, pressure, touches, i;
+	u16 x, y;
+
+	touches = ts->touch_data[ts->dd->touch_index];
+
+	if (touches > 0) {
+		for (i = 0; i < touches; i++) {
+			id = ts->touch_data[i * ts->dd->touch_bytes +
+				ts->dd->id_index];
+			pressure = ts->touch_data[i * ts->dd->touch_bytes +
+				ts->dd->z_index];
+			x = get_unaligned_le16(&(ts->touch_data[i *
+				ts->dd->touch_bytes + ts->dd->x_index]));
+			y = get_unaligned_le16(&(ts->touch_data[i *
+				ts->dd->touch_bytes + ts->dd->y_index]));
+			report_data(ts, x, y, pressure, id);
+		}
+	} else
+		input_mt_sync(ts->input);
+
+	ts->prev_touches = touches;
+	/*input_report_key(ts->input, BTN_TOUCH, 1);*/
+	input_mt_report_pointer_emulation(ts->input, true);
+	input_sync(ts->input);
+}
+
+/*static void rm31100_ts_xy_worker(struct work_struct *work) JL remove*/
+static void rm31100_ts_xy_worker(struct rm31100_ts *work)
+{
+	int rc;
+	u8 client_dma_package[4] = {0x0f, 0x00, 0x20, 0x81};
+	struct rm31100_ts *ts = work;
+
+	if (ts->is_suspended == true) {
+		dev_dbg(&ts->client->dev, "TS is supended\n");
+		ts->int_pending = true;
+		return;
+	}
+
+	mutex_lock(&ts->access_lock);
+	/* read data from DATA_REG */
+	/*RM31100 DMA Mode*/
+	rc = rm31100_ts_write_client_dma(ts->client, client_dma_package, 0x04);
+	if (rc < 0) {
+		dev_err(&ts->client->dev, "write client dma failed\n");
+		goto schedule;
+	}
+	rc = rm31100_ts_read(ts->client, ts->dd->data_reg, ts->touch_data,
+	ts->dd->data_size);
+
+	if (rc < 0) {
+		dev_err(&ts->client->dev, "read failed\n");
+		goto schedule;
+	}
+
+	if (ts->touch_data[ts->dd->touch_index] == INVALID_DATA)
+		goto schedule;
+
+	/* write to STATUS_REG to release lock */
+	rc = rm31100_ts_write_reg_u8(ts->client,
+		ts->dd->status_reg, ts->dd->update_data);
+	if (rc < 0) {
+		dev_err(&ts->client->dev, "write failed, try once more\n");
+
+		rc = rm31100_ts_write_reg_u8(ts->client,
+			ts->dd->status_reg, ts->dd->update_data);
+		if (rc < 0)
+			dev_err(&ts->client->dev, "write failed, exiting\n");
+	}
+
+	process_rm31100_data(ts);
+schedule:
+	mutex_unlock(&ts->access_lock);
+}
+
+static irqreturn_t rm31100_ts_irq(int irq, void *dev_id)
+{
+	struct rm31100_ts *ts = dev_id;
+
+	rm31100_ts_xy_worker(ts);
+	return IRQ_HANDLED;
+}
+
+static int rm31100_ts_init_ts(struct i2c_client *client, struct rm31100_ts *ts)
+{
+	/*struct input_dev *input_device;*/
+	/*int rc = 0;*/
+
+	ts->dd = &devices[ts->device_id];
+
+	if (!ts->pdata->nfingers) {
+		dev_err(&client->dev, "Touches information not specified\n");
+		return -EINVAL;
+	}
+
+	if (ts->device_id == rm3110x) {
+		if (ts->pdata->nfingers > 2) {
+			dev_err(&client->dev, "Touches >=1 & <= 2\n");
+			return -EINVAL;
+		}
+		ts->dd->data_size = ts->dd->touch_bytes;
+		ts->dd->touch_index = 0x0;
+	} else if (ts->device_id == rm31100) {
+		if (ts->pdata->nfingers > 10) {
+			dev_err(&client->dev, "Touches >=1 & <= 10\n");
+			return -EINVAL;
+		}
+		ts->dd->data_size = ts->pdata->nfingers * ts->dd->touch_bytes +
+						ts->dd->touch_meta_data;
+		ts->dd->touch_index = 0x0;
+	}
+	/* w001 */
+	else {
+		ts->dd->data_size = ts->pdata->nfingers * ts->dd->touch_bytes +
+		ts->dd->touch_meta_data;
+		ts->dd->touch_index = 0x0;
+	}
+
+	ts->touch_data = kzalloc(ts->dd->data_size, GFP_KERNEL);
+	if (!ts->touch_data) {
+		pr_err("%s: Unable to allocate memory\n", __func__);
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int rm31100_ts_suspend(struct device *dev)
+{
+	struct rm31100_ts *ts = dev_get_drvdata(dev);
+	int rc = 0;
+
+	if (device_may_wakeup(dev)) {
+		/* mark suspend flag */
+		ts->is_suspended = true;
+		enable_irq_wake(ts->pen_irq);
+	}
+
+	disable_irq(ts->pen_irq);
+
+	gpio_free(ts->pdata->irq_gpio);
+
+	if (ts->pdata->power_on) {
+		rc = ts->pdata->power_on(0);
+		if (rc) {
+			dev_err(dev, "unable to goto suspend\n");
+			return rc;
+		}
+	}
+	return 0;
+}
+
+static int rm31100_ts_resume(struct device *dev)
+{
+	struct rm31100_ts *ts = dev_get_drvdata(dev);
+
+	int rc = 0;
+
+	if (device_may_wakeup(dev)) {
+		disable_irq_wake(ts->pen_irq);
+
+		ts->is_suspended = false;
+
+		if (ts->int_pending == true)
+			ts->int_pending = false;
+	} else {
+		if (ts->pdata->power_on) {
+			rc = ts->pdata->power_on(1);
+			if (rc) {
+				dev_err(dev, "unable to resume\n");
+				return rc;
+			}
+		}
+
+		enable_irq(ts->pen_irq);
+
+		/* Clear the status register of the TS controller */
+		rc = rm31100_ts_write_reg_u8(ts->client,
+			ts->dd->status_reg, ts->dd->update_data);
+		if (rc < 0) {
+			dev_err(&ts->client->dev,
+				"write failed, try once more\n");
+
+			rc = rm31100_ts_write_reg_u8(ts->client,
+				ts->dd->status_reg,
+				ts->dd->update_data);
+			if (rc < 0)
+				dev_err(&ts->client->dev,
+					"write failed, exiting\n");
+		}
+	}
+
+	return 0;
+}
+
+static struct dev_pm_ops rm31100_ts_pm_ops = {
+	.suspend = rm31100_ts_suspend,
+	.resume = rm31100_ts_resume,
+};
+#endif
+
+static int rm_input_dev_create(struct rm31100_ts *ts)
+{
+	struct input_dev *input_device;
+	int rc = 0;
+	ts->prev_touches = 0;
+
+	input_device = input_allocate_device();
+	if (!input_device) {
+		rc = -ENOMEM;
+		goto error_alloc_dev;
+	}
+	ts->input = input_device;
+
+	input_device->name = "raydium_ts";
+	input_device->id.bustype = BUS_I2C;
+	input_device->dev.parent = &ts->client->dev;
+	input_set_drvdata(input_device, ts);
+
+	__set_bit(EV_ABS, input_device->evbit);
+	__set_bit(INPUT_PROP_DIRECT, input_device->propbit);
+	__set_bit(BTN_TOUCH, input_device->keybit);
+
+
+	if (ts->device_id == rm31100) {
+		/* set up virtual key */
+		__set_bit(EV_KEY, input_device->evbit);
+	}
+	input_mt_init_slots(input_device,
+		MAX_REPORT_TOUCHED_POINTS, 0);
+	input_set_abs_params(input_device, ABS_MT_POSITION_X,
+		ts->pdata->dis_min_x, ts->pdata->dis_max_x, 0, 0);
+	input_set_abs_params(input_device, ABS_MT_POSITION_Y,
+		ts->pdata->dis_min_y, ts->pdata->dis_max_y, 0, 0);
+	input_set_abs_params(input_device, ABS_MT_PRESSURE,
+		0, 0xFF, 0, 0);
+	input_set_abs_params(input_device, ABS_MT_TOUCH_MAJOR,
+		0, 0xFF, 0, 0);
+	rc = input_register_device(input_device);
+	if (rc)
+		goto error_unreg_device;
+
+	return 0;
+
+error_unreg_device:
+	input_free_device(input_device);
+error_alloc_dev:
+	ts->input = NULL;
+	return rc;
+}
+
+static int rm31100_initialize(struct i2c_client *client)
+{
+	struct rm31100_ts *ts = i2c_get_clientdata(client);
+	int rc = 0, /*retry_cnt = 0,*/ temp_reg;
+	/* power on the device */
+	if (ts->pdata->power_on) {
+		rc = ts->pdata->power_on(1);
+		if (rc) {
+			pr_err("%s: Unable to power on the device\n", __func__);
+			goto error_dev_setup;
+		}
+	}
+
+	/* read one byte to make sure i2c device exists */
+	if (ts->device_id == rm3110x)
+		temp_reg = 0x01;
+	else if (ts->device_id == rm31100)
+		temp_reg = 0x00;
+	else
+		temp_reg = 0x05;
+
+	rc = rm31100_ts_read_reg_u8(client, temp_reg);
+	if (rc < 0) {
+		dev_err(&client->dev, "i2c sanity check failed\n");
+		goto error_power_on;
+	}
+
+	rc = rm31100_ts_init_ts(client, ts);
+	if (rc < 0) {
+		dev_err(&client->dev, "rm31100_ts init failed\n");
+		goto error_mutex_destroy;
+	}
+
+	/* configure touchscreen reset out gpio */
+	rc = gpio_request(ts->pdata->resout_gpio, "rm31100_resout_gpio");
+	if (rc) {
+		pr_err("%s: unable to request gpio %d\n",
+			__func__, ts->pdata->resout_gpio);
+		goto error_uninit_ts;
+	}
+
+	rc = gpio_direction_output(ts->pdata->resout_gpio, 0);
+	if (rc) {
+		pr_err("%s: unable to set direction for gpio %d\n",
+			__func__, ts->pdata->resout_gpio);
+		goto error_resout_gpio_dir;
+	}
+	/* reset gpio stabilization time */
+	msleep(20);
+
+	return 0;
+error_resout_gpio_dir:
+	if (ts->pdata->resout_gpio >= 0)
+		gpio_free(ts->pdata->resout_gpio);
+error_uninit_ts:
+	input_unregister_device(ts->input);
+	kfree(ts->touch_data);
+error_mutex_destroy:
+	mutex_destroy(&ts->access_lock);
+error_power_on:
+	if (ts->pdata->power_on)
+		ts->pdata->power_on(0);
+error_dev_setup:
+	if (ts->pdata->dev_setup)
+		ts->pdata->dev_setup(0);
+	return rc;
+}
+
+static void rm_initialize_async(void *data, async_cookie_t cookie)
+{
+	struct rm31100_ts *ts = data;
+	struct i2c_client *client = ts->client;
+	unsigned long irqflags;
+	int err = 0;
+
+	mutex_lock(&ts->access_lock);
+
+	err = rm31100_initialize(client);
+	if (err < 0) {
+		dev_err(&client->dev, "probe failed! unbind device.\n");
+		goto error_free_mem;
+	}
+
+	err = rm_input_dev_create(ts);
+	if (err) {
+		dev_err(&client->dev, "%s crated failed, %d\n", __func__, err);
+		goto error_goio_release;
+	}
+
+	irqflags = client->dev.of_node ? 0 : IRQF_TRIGGER_FALLING;
+
+	err = request_threaded_irq(ts->pen_irq, NULL,
+				   rm31100_ts_irq,
+				   irqflags | IRQF_ONESHOT,
+				   ts->client->dev.driver->name, ts);
+	if (err) {
+		dev_err(&client->dev, "Failed to register interrupt\n");
+		goto error_dev_release;
+	}
+
+	mutex_unlock(&ts->access_lock);
+
+	return;
+
+error_dev_release:
+	input_free_device(ts->input);
+error_goio_release:
+	if (ts->pdata->resout_gpio >= 0)
+		gpio_free(ts->pdata->resout_gpio);
+error_free_mem:
+	mutex_unlock(&ts->access_lock);
+	kfree(ts);
+	return;
+}
+
+
+/*static int __devinit rm31100_ts_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)*/
+static int rm31100_ts_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct rm31100_ts *ts;
+	struct rm3110x_ts_platform_data *pdata = client->dev.platform_data;
+	int rc/*, temp_reg*/;
+	union i2c_smbus_data dummy;
+
+	if (!pdata) {
+		dev_err(&client->dev, "platform data is required!\n");
+		return -EINVAL;
+	}
+
+	if (!i2c_check_functionality(client->adapter,
+		I2C_FUNC_SMBUS_READ_WORD_DATA)) {
+		dev_err(&client->dev, "I2C functionality not supported\n");
+		return -EIO;
+	}
+	/* 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)
+		return -ENODEV;
+
+	ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+	pts = ts;
+
+	/* Enable runtime PM ops, start in ACTIVE mode */
+	rc = pm_runtime_set_active(&client->dev);
+	if (rc < 0)
+		dev_warn(&client->dev, "unable to set runtime pm state\n");
+	pm_runtime_enable(&client->dev);
+
+	ts->client = client;
+	ts->pdata = pdata;
+	i2c_set_clientdata(client, ts);
+	ts->device_id = id->driver_data;
+
+	if (ts->pdata->dev_setup) {
+		rc = ts->pdata->dev_setup(1);
+		if (rc < 0) {
+			dev_err(&client->dev, "dev setup failed\n");
+			goto error_touch_data_alloc;
+		}
+	}
+
+
+	ts->is_suspended = false;
+	ts->int_pending = false;
+	/*mutex_init(&ts->sus_lock); JL remove*/
+	mutex_init(&ts->access_lock);
+
+	async_schedule(rm_initialize_async, ts);
+
+	device_init_wakeup(&client->dev, ts->pdata->wakeup);
+	return 0;
+
+error_touch_data_alloc:
+	pm_runtime_set_suspended(&client->dev);
+	pm_runtime_disable(&client->dev);
+	kfree(ts);
+	return rc;
+}
+
+/*static int __devexit RM31100_ts_remove(struct i2c_client *client)*/
+static int rm31100_ts_remove(struct i2c_client *client)
+{
+	struct rm31100_ts *ts = i2c_get_clientdata(client);
+
+	pm_runtime_set_suspended(&client->dev);
+	pm_runtime_disable(&client->dev);
+
+	device_init_wakeup(&client->dev, 0);
+	free_irq(ts->pen_irq, ts);
+
+	gpio_free(ts->pdata->irq_gpio);
+
+	if (ts->pdata->resout_gpio >= 0)
+		gpio_free(ts->pdata->resout_gpio);
+	input_unregister_device(ts->input);
+
+	/*mutex_destroy(&ts->sus_lock); JL remove*/
+	mutex_destroy(&ts->access_lock);
+
+	if (ts->pdata->power_on)
+		ts->pdata->power_on(0);
+
+	if (ts->pdata->dev_setup)
+		ts->pdata->dev_setup(0);
+
+	kfree(ts->touch_data);
+	kfree(ts);
+
+	return 0;
+}
+
+static const struct i2c_device_id rm31100_ts_id[] = {
+	{"RM31100", rm31100},
+	{"RM3110x", rm3110x},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, rm31100_ts_id);
+
+
+static struct i2c_driver rm31100_ts_driver = {
+	.driver = {
+		.name = "raydium_ts",
+		.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+		.pm = &rm31100_ts_pm_ops,
+#endif
+	},
+	.probe = rm31100_ts_probe,
+	.remove = rm31100_ts_remove,
+	.id_table = rm31100_ts_id,
+};
+
+static int __init rm31100_ts_init(void)
+{
+	int rc;
+	int rc2;
+
+	rc = i2c_add_driver(&rm31100_ts_driver);
+
+	rc2 = driver_create_file(&rm31100_ts_driver.driver,
+		&driver_attr_myAttr);
+
+	return rc;
+}
+/* Making this as late init to avoid power fluctuations
+ * during LCD initialization.
+ */
+module_init(rm31100_ts_init);
+
+static void __exit rm31100_ts_exit(void)
+{
+	i2c_del_driver(&rm31100_ts_driver);
+}
+module_exit(rm31100_ts_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("rm31100-rm3110x touchscreen controller driver");
+MODULE_AUTHOR("Raydium");
+MODULE_ALIAS("platform:rm31100_ts");
-- 
2.1.2


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

* [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
@ 2014-12-08  3:42 jeffrey.lin
  0 siblings, 0 replies; 58+ messages in thread
From: jeffrey.lin @ 2014-12-08  3:42 UTC (permalink / raw)
  To: dmitry.torokhov, rydberg, shc_work, bleung, lee.jones, charliemooney
  Cc: jeffrey.lin, roger.yang, KP.li, linux-kernel, linux-input

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

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@rad-ic.com
---
 drivers/input/touchscreen/Kconfig      |  12 +
 drivers/input/touchscreen/Makefile     |   1 +
 drivers/input/touchscreen/rm31100_ts.c | 846 +++++++++++++++++++++++++++++++++
 3 files changed, 859 insertions(+)
 create mode 100644 drivers/input/touchscreen/rm31100_ts.c

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 3ce9181..d0324d2 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -955,4 +955,16 @@ config TOUCHSCREEN_ZFORCE
 	  To compile this driver as a module, choose M here: the
 	  module will be called zforce_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 rm31100_ts.
+
 endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 687d5a7..3220f66 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -78,3 +78,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE)	+= zylonite-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_W90X900)	+= w90p910_ts.o
 obj-$(CONFIG_TOUCHSCREEN_TPS6507X)	+= tps6507x-ts.o
 obj-$(CONFIG_TOUCHSCREEN_ZFORCE)	+= zforce_ts.o
+obj-$(CONFIG_TOUCHSCREEN_RM_TS)		+= rm31100_ts.o
diff --git a/drivers/input/touchscreen/rm31100_ts.c b/drivers/input/touchscreen/rm31100_ts.c
new file mode 100644
index 0000000..997dc0c
--- /dev/null
+++ b/drivers/input/touchscreen/rm31100_ts.c
@@ -0,0 +1,846 @@
+/*
+ * Raydium RM31100_ts touchscreen 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
+ *
+ * History:
+ *			(C) 2012 Raydium - Update for GPL distribution
+ *			(C) 2009 Enea - Original prototype
+ *
+ */
+#include <linux/async.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#ifdef CONFIG_MISC_DEV
+#include <linux/miscdevice.h>
+#endif
+#include <linux/uaccess.h>
+#include <asm/unaligned.h>
+#include <linux/input/mt.h>
+
+#define rm31100	0x0
+#define rm3110x	0x1
+
+#define INVALID_DATA	0xff
+#define MAX_REPORT_TOUCHED_POINTS	10
+
+#define I2C_CLIENT_ADDR         0x39
+#define I2C_DMA_CLIENT_ADDR     0x5A
+
+struct rm31100_ts_data {
+	u8 x_index;
+	u8 y_index;
+	u8 z_index;
+	u8 id_index;
+	u8 touch_index;
+	u8 data_reg;
+	u8 status_reg;
+	u8 data_size;
+	u8 touch_bytes;
+	u8 update_data;
+	u8 touch_meta_data;
+	u8 finger_size;
+};
+
+struct rm3110x_ts_platform_data {
+	int (*power_on)(int on);
+	int (*dev_setup)(bool on);
+	const char *ts_name;
+	u32 dis_min_x; /* display resoltion ABS min*/
+	u32 dis_max_x;/* display resoltion ABS max*/
+	u32 dis_min_y;
+	u32 dis_max_y;
+	u32 min_touch; /* no.of touches supported */
+	u32 max_touch;
+	u32 min_tid; /* track id */
+	u32 max_tid;
+	u32 min_width;/* size of the finger */
+	u32 max_width;
+	u32 res_x; /* TS resolution unit*/
+	u32 res_y;
+	u32 swap_xy;
+	u8 nfingers;
+	u32 irq_gpio;
+	int resout_gpio;
+	bool wakeup;
+	u32 irq_cfg;
+};
+
+static struct rm31100_ts_data devices[] = {
+	[0] = {
+		.x_index = 2,
+		.y_index = 4,
+		.z_index = 6,
+		.id_index = 1,
+		.data_reg = 0x1,
+		.status_reg = 0,
+		.update_data = 0x0,
+		.touch_bytes = 6,
+		.touch_meta_data = 1,
+		.finger_size = 70,
+	},
+};
+
+struct rm31100_ts {
+	struct i2c_client *client;
+	struct input_dev *input;
+	struct rm3110x_ts_platform_data *pdata;
+	struct rm31100_ts_data *dd;
+	u8 *touch_data;
+	u8 device_id;
+	u8 prev_touches;
+	bool is_suspended;
+	bool int_pending;
+	struct mutex access_lock;
+	u32 pen_irq;
+};
+
+struct rm31100_ts *pts;
+/*
+static inline u16 join_bytes(u8 a, u8 b)
+{
+	u16 ab = 0;
+	ab = ab | a;
+	ab = ab << 8 | b;
+	return ab;
+}
+*/
+static s32 rm31100_ts_write_reg_u8(struct i2c_client *client, u8 reg, u8 val)
+{
+	s32 data;
+
+	data = i2c_smbus_write_byte_data(client, reg, val);
+	if (data < 0)
+		dev_err(&client->dev, "error %d in writing reg 0x%x\n",
+						 data, reg);
+
+	return data;
+}
+
+static s32 rm31100_ts_read_reg_u8(struct i2c_client *client, u8 reg)
+{
+	s32 data;
+
+	data = i2c_smbus_read_byte_data(client, reg);
+	if (data < 0)
+		dev_err(&client->dev, "error %d in reading reg 0x%x\n",
+						 data, reg);
+
+	return data;
+}
+
+static int rm31100_ts_read(struct i2c_client *client, u8 reg, u8 *buf, int num)
+{
+	struct i2c_msg xfer_msg[2];
+
+	xfer_msg[0].addr = client->addr;
+	xfer_msg[0].len = 1;
+	xfer_msg[0].flags = 0;
+	xfer_msg[0].buf = &reg;
+
+	xfer_msg[1].addr = client->addr;
+	xfer_msg[1].len = num;
+	xfer_msg[1].flags = I2C_M_RD;
+	xfer_msg[1].buf = buf;
+
+	return i2c_transfer(client->adapter, xfer_msg, 2);
+}
+
+static int rm31100_ts_write_client_dma(struct i2c_client *client, u8 *buf, int num)
+{
+	struct i2c_msg xfer_msg[1];
+
+	xfer_msg[0].addr = I2C_DMA_CLIENT_ADDR;
+	xfer_msg[0].len = num;
+	xfer_msg[0].flags = 0;
+	xfer_msg[0].buf = buf;
+
+	return i2c_transfer(client->adapter, xfer_msg, 1);
+}
+
+static int rm31100_ts_write(struct i2c_client *client, u8 *buf, int num)
+{
+	struct i2c_msg xfer_msg[1];
+
+	xfer_msg[0].addr = client->addr;
+	xfer_msg[0].len = num;
+	xfer_msg[0].flags = 0;
+	xfer_msg[0].buf = buf;
+
+	return i2c_transfer(client->adapter, xfer_msg, 1);
+}
+#ifdef CONFIG_MISC_DEV
+static int dev_open(struct inode *inode, struct file *filp)
+{
+	mutex_lock(&pts->access_lock);
+	return 0;
+}
+
+static int dev_release(struct inode *inode, struct file *filp)
+{
+	mutex_unlock(&pts->access_lock);
+	return 0;
+}
+static ssize_t
+dev_read(struct file *filp, char __user *buf, size_t count, loff_t *pos)
+{
+	u8 *kbuf;
+	struct i2c_msg xfer_msg;
+	/*static char out[] = "1234567890";*/
+	/*static int idx;*//*= 0; remove by checkpatch*/
+	int i;
+
+	kbuf = kmalloc(count, GFP_KERNEL);
+	if (kbuf == NULL)
+		return -ENOMEM;
+
+	/*xfer_msg.addr = pts->client->addr;*/
+	xfer_msg.addr = I2C_CLIENT_ADDR;
+	xfer_msg.len = count;
+	xfer_msg.flags = I2C_M_RD;
+	xfer_msg.buf = kbuf;
+
+	i2c_transfer(pts->client->adapter, &xfer_msg, 1);
+
+	if (copy_to_user(buf, kbuf, count) == 0)
+		return count;
+	else
+		return -EFAULT;
+}
+
+static ssize_t
+dev_write(struct file *filp, const char __user *buf, size_t count, loff_t *pos)
+{
+	u8 *kbuf;
+	ssize_t status = 0;
+	int i;
+
+	kbuf = kmalloc(count, GFP_KERNEL);
+	if (kbuf == NULL) {
+		dev_err("kmalloc() fail\n");
+		return -ENOMEM;
+	}
+
+	if (copy_from_user(kbuf, buf, count) == 0) {
+		pts->client->addr = I2C_CLIENT_ADDR;
+		if (rm31100_ts_write(pts->client, kbuf, count) < 0)
+			status = -EFAULT;
+		else
+			status = count;
+	} else {
+		dev_err("copy_from_user() fail\n");
+		status = -EFAULT;
+	}
+
+	kfree(kbuf);
+	return status;
+}
+
+static struct file_operations dev_fops = {
+	.owner = THIS_MODULE,
+	.open = dev_open,
+	.release = dev_release,
+	.read = dev_read,
+	.write = dev_write,
+	/*.unlocked_ioctl = dev_ioctl,*/
+};
+
+static struct miscdevice raydium_ts_miscdev = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "raydium_ts",
+	.fops = &dev_fops,
+};
+#endif
+
+
+ssize_t show(struct device_driver *drv, char *buff)
+{
+	struct i2c_msg xfer_msg;
+	int num = 10;
+	char buf[100];
+	/*int i;*/
+
+	xfer_msg.addr = pts->client->addr;
+	xfer_msg.len = num;
+	xfer_msg.flags = I2C_M_RD;
+	xfer_msg.buf = buf;
+	pts->client->addr = I2C_CLIENT_ADDR;
+	i2c_transfer(pts->client->adapter, &xfer_msg, 1);
+
+	return 0;
+}
+
+ssize_t store(struct device_driver *drv, const char *buf, size_t count)
+{
+	/*unsigned char pkt[] = { 0xF2, 5, 1, 1 };*/
+	unsigned char pkt[] = { 0xF1, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
+
+	pts->client->addr = I2C_CLIENT_ADDR;
+	rm31100_ts_write(pts->client, pkt, sizeof(pkt));
+
+	return sizeof(pkt);
+}
+
+DRIVER_ATTR(myAttr, 0x777, show, store);
+
+static void report_data(struct rm31100_ts *dev, u16 x, u16 y,
+	u8 pressure, u8 id)
+{
+	struct input_dev *input_dev = dev->input;
+	if (dev->pdata->swap_xy)
+		swap(x, y);
+
+	input_mt_slot(input_dev, id);
+	input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, true);
+	input_report_abs(input_dev, ABS_MT_POSITION_X, x);
+	input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
+	input_report_abs(input_dev, ABS_MT_PRESSURE, pressure);
+	input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, dev->dd->finger_size);
+/*
+	dev_dbg("%s(): id =%2hhd, x =%4hd, y =%4hd, pressure = %hhd\n",
+		__func__, id, x, y, pressure);
+*/
+}
+
+static void process_rm31100_data(struct rm31100_ts *ts)
+{
+	u8 id, pressure, touches, i;
+	u16 x, y;
+
+	touches = ts->touch_data[ts->dd->touch_index];
+
+	if (touches > 0) {
+		for (i = 0; i < touches; i++) {
+			id = ts->touch_data[i * ts->dd->touch_bytes +
+				ts->dd->id_index];
+			pressure = ts->touch_data[i * ts->dd->touch_bytes +
+				ts->dd->z_index];
+			x = get_unaligned_le16(&(ts->touch_data[i *
+				ts->dd->touch_bytes + ts->dd->x_index]));
+			y = get_unaligned_le16(&(ts->touch_data[i *
+				ts->dd->touch_bytes + ts->dd->y_index]));
+			report_data(ts, x, y, pressure, id);
+		}
+	} else
+		input_mt_sync(ts->input);
+
+	ts->prev_touches = touches;
+	/*input_report_key(ts->input, BTN_TOUCH, 1);*/
+	input_mt_report_pointer_emulation(ts->input, true);
+	input_sync(ts->input);
+}
+
+/*static void rm31100_ts_xy_worker(struct work_struct *work) JL remove*/
+static void rm31100_ts_xy_worker(struct rm31100_ts *work)
+{
+	int rc;
+	u8 client_dma_package[4] = {0x0f, 0x00, 0x20, 0x81};
+	struct rm31100_ts *ts = work;
+
+	if (ts->is_suspended == true) {
+		dev_dbg(&ts->client->dev, "TS is supended\n");
+		ts->int_pending = true;
+		return;
+	}
+
+	mutex_lock(&ts->access_lock);
+	/* read data from DATA_REG */
+	/*RM31100 DMA Mode*/
+	rc = rm31100_ts_write_client_dma(ts->client, client_dma_package, 0x04);
+	if (rc < 0) {
+		dev_err(&ts->client->dev, "write client dma failed\n");
+		goto schedule;
+	}
+	rc = rm31100_ts_read(ts->client, ts->dd->data_reg, ts->touch_data,
+	ts->dd->data_size);
+
+	if (rc < 0) {
+		dev_err(&ts->client->dev, "read failed\n");
+		goto schedule;
+	}
+
+	if (ts->touch_data[ts->dd->touch_index] == INVALID_DATA)
+		goto schedule;
+
+	/* write to STATUS_REG to release lock */
+	rc = rm31100_ts_write_reg_u8(ts->client,
+		ts->dd->status_reg, ts->dd->update_data);
+	if (rc < 0) {
+		dev_err(&ts->client->dev, "write failed, try once more\n");
+
+		rc = rm31100_ts_write_reg_u8(ts->client,
+			ts->dd->status_reg, ts->dd->update_data);
+		if (rc < 0)
+			dev_err(&ts->client->dev, "write failed, exiting\n");
+	}
+
+	process_rm31100_data(ts);
+schedule:
+	mutex_unlock(&ts->access_lock);
+}
+
+static irqreturn_t rm31100_ts_irq(int irq, void *dev_id)
+{
+	struct rm31100_ts *ts = dev_id;
+
+	rm31100_ts_xy_worker(ts);
+	return IRQ_HANDLED;
+}
+
+static int rm31100_ts_init_ts(struct i2c_client *client, struct rm31100_ts *ts)
+{
+	/*struct input_dev *input_device;*/
+	/*int rc = 0;*/
+
+	ts->dd = &devices[ts->device_id];
+
+	if (!ts->pdata->nfingers) {
+		dev_err(&client->dev, "Touches information not specified\n");
+		return -EINVAL;
+	}
+
+	if (ts->device_id == rm3110x) {
+		if (ts->pdata->nfingers > 2) {
+			dev_err(&client->dev, "Touches >=1 & <= 2\n");
+			return -EINVAL;
+		}
+		ts->dd->data_size = ts->dd->touch_bytes;
+		ts->dd->touch_index = 0x0;
+	} else if (ts->device_id == rm31100) {
+		if (ts->pdata->nfingers > 10) {
+			dev_err(&client->dev, "Touches >=1 & <= 10\n");
+			return -EINVAL;
+		}
+		ts->dd->data_size = ts->pdata->nfingers * ts->dd->touch_bytes +
+						ts->dd->touch_meta_data;
+		ts->dd->touch_index = 0x0;
+	}
+	/* w001 */
+	else {
+		ts->dd->data_size = ts->pdata->nfingers * ts->dd->touch_bytes +
+		ts->dd->touch_meta_data;
+		ts->dd->touch_index = 0x0;
+	}
+
+	ts->touch_data = kzalloc(ts->dd->data_size, GFP_KERNEL);
+	if (!ts->touch_data) {
+		pr_err("%s: Unable to allocate memory\n", __func__);
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int rm31100_ts_suspend(struct device *dev)
+{
+	struct rm31100_ts *ts = dev_get_drvdata(dev);
+	int rc = 0;
+
+	if (device_may_wakeup(dev)) {
+		/* mark suspend flag */
+		ts->is_suspended = true;
+		enable_irq_wake(ts->pen_irq);
+	}
+
+	disable_irq(ts->pen_irq);
+
+	gpio_free(ts->pdata->irq_gpio);
+
+	if (ts->pdata->power_on) {
+		rc = ts->pdata->power_on(0);
+		if (rc) {
+			dev_err(dev, "unable to goto suspend\n");
+			return rc;
+		}
+	}
+	return 0;
+}
+
+static int rm31100_ts_resume(struct device *dev)
+{
+	struct rm31100_ts *ts = dev_get_drvdata(dev);
+
+	int rc = 0;
+
+	if (device_may_wakeup(dev)) {
+		disable_irq_wake(ts->pen_irq);
+
+		ts->is_suspended = false;
+
+		if (ts->int_pending == true)
+			ts->int_pending = false;
+	} else {
+		if (ts->pdata->power_on) {
+			rc = ts->pdata->power_on(1);
+			if (rc) {
+				dev_err(dev, "unable to resume\n");
+				return rc;
+			}
+		}
+
+		enable_irq(ts->pen_irq);
+
+		/* Clear the status register of the TS controller */
+		rc = rm31100_ts_write_reg_u8(ts->client,
+			ts->dd->status_reg, ts->dd->update_data);
+		if (rc < 0) {
+			dev_err(&ts->client->dev,
+				"write failed, try once more\n");
+
+			rc = rm31100_ts_write_reg_u8(ts->client,
+				ts->dd->status_reg,
+				ts->dd->update_data);
+			if (rc < 0)
+				dev_err(&ts->client->dev,
+					"write failed, exiting\n");
+		}
+	}
+
+	return 0;
+}
+
+static struct dev_pm_ops rm31100_ts_pm_ops = {
+	.suspend = rm31100_ts_suspend,
+	.resume = rm31100_ts_resume,
+};
+#endif
+
+static int rm_input_dev_create(struct rm31100_ts *ts)
+{
+	struct input_dev *input_device;
+	int rc = 0;
+	ts->prev_touches = 0;
+
+	input_device = input_allocate_device();
+	if (!input_device) {
+		rc = -ENOMEM;
+		goto error_alloc_dev;
+	}
+	ts->input = input_device;
+
+	input_device->name = "raydium_ts";
+	input_device->id.bustype = BUS_I2C;
+	input_device->dev.parent = &ts->client->dev;
+	input_set_drvdata(input_device, ts);
+
+	__set_bit(EV_ABS, input_device->evbit);
+	__set_bit(INPUT_PROP_DIRECT, input_device->propbit);
+	__set_bit(BTN_TOUCH, input_device->keybit);
+
+
+	if (ts->device_id == rm31100) {
+		/* set up virtual key */
+		__set_bit(EV_KEY, input_device->evbit);
+	}
+	input_mt_init_slots(input_device,
+		MAX_REPORT_TOUCHED_POINTS, 0);
+	input_set_abs_params(input_device, ABS_MT_POSITION_X,
+		ts->pdata->dis_min_x, ts->pdata->dis_max_x, 0, 0);
+	input_set_abs_params(input_device, ABS_MT_POSITION_Y,
+		ts->pdata->dis_min_y, ts->pdata->dis_max_y, 0, 0);
+	input_set_abs_params(input_device, ABS_MT_PRESSURE,
+		0, 0xFF, 0, 0);
+	input_set_abs_params(input_device, ABS_MT_TOUCH_MAJOR,
+		0, 0xFF, 0, 0);
+	rc = input_register_device(input_device);
+	if (rc)
+		goto error_unreg_device;
+
+	return 0;
+
+error_unreg_device:
+	input_free_device(input_device);
+error_alloc_dev:
+	ts->input = NULL;
+	return rc;
+}
+
+static int rm31100_initialize(struct i2c_client *client)
+{
+	struct rm31100_ts *ts = i2c_get_clientdata(client);
+	int rc = 0, /*retry_cnt = 0,*/ temp_reg;
+	/* power on the device */
+	if (ts->pdata->power_on) {
+		rc = ts->pdata->power_on(1);
+		if (rc) {
+			pr_err("%s: Unable to power on the device\n", __func__);
+			goto error_dev_setup;
+		}
+	}
+
+	/* read one byte to make sure i2c device exists */
+	if (ts->device_id == rm3110x)
+		temp_reg = 0x01;
+	else if (ts->device_id == rm31100)
+		temp_reg = 0x00;
+	else
+		temp_reg = 0x05;
+
+	rc = rm31100_ts_read_reg_u8(client, temp_reg);
+	if (rc < 0) {
+		dev_err(&client->dev, "i2c sanity check failed\n");
+		goto error_power_on;
+	}
+
+	rc = rm31100_ts_init_ts(client, ts);
+	if (rc < 0) {
+		dev_err(&client->dev, "rm31100_ts init failed\n");
+		goto error_mutex_destroy;
+	}
+
+	/* configure touchscreen reset out gpio */
+	rc = gpio_request(ts->pdata->resout_gpio, "rm31100_resout_gpio");
+	if (rc) {
+		pr_err("%s: unable to request gpio %d\n",
+			__func__, ts->pdata->resout_gpio);
+		goto error_uninit_ts;
+	}
+
+	rc = gpio_direction_output(ts->pdata->resout_gpio, 0);
+	if (rc) {
+		pr_err("%s: unable to set direction for gpio %d\n",
+			__func__, ts->pdata->resout_gpio);
+		goto error_resout_gpio_dir;
+	}
+	/* reset gpio stabilization time */
+	msleep(20);
+
+	return 0;
+error_resout_gpio_dir:
+	if (ts->pdata->resout_gpio >= 0)
+		gpio_free(ts->pdata->resout_gpio);
+error_uninit_ts:
+	input_unregister_device(ts->input);
+	kfree(ts->touch_data);
+error_mutex_destroy:
+	mutex_destroy(&ts->access_lock);
+error_power_on:
+	if (ts->pdata->power_on)
+		ts->pdata->power_on(0);
+error_dev_setup:
+	if (ts->pdata->dev_setup)
+		ts->pdata->dev_setup(0);
+	return rc;
+}
+
+static void rm_initialize_async(void *data, async_cookie_t cookie)
+{
+	struct rm31100_ts *ts = data;
+	struct i2c_client *client = ts->client;
+	unsigned long irqflags;
+	int err = 0;
+
+	mutex_lock(&ts->access_lock);
+
+	err = rm31100_initialize(client);
+	if (err < 0) {
+		dev_err(&client->dev, "probe failed! unbind device.\n");
+		goto error_free_mem;
+	}
+
+	err = rm_input_dev_create(ts);
+	if (err) {
+		dev_err(&client->dev, "%s crated failed, %d\n", __func__, err);
+		goto error_goio_release;
+	}
+
+	irqflags = client->dev.of_node ? 0 : IRQF_TRIGGER_FALLING;
+
+	err = request_threaded_irq(ts->pen_irq, NULL,
+				   rm31100_ts_irq,
+				   irqflags | IRQF_ONESHOT,
+				   ts->client->dev.driver->name, ts);
+	if (err) {
+		dev_err(&client->dev, "Failed to register interrupt\n");
+		goto error_dev_release;
+	}
+
+	mutex_unlock(&ts->access_lock);
+
+	return;
+
+error_dev_release:
+	input_free_device(ts->input);
+error_goio_release:
+	if (ts->pdata->resout_gpio >= 0)
+		gpio_free(ts->pdata->resout_gpio);
+error_free_mem:
+	mutex_unlock(&ts->access_lock);
+	kfree(ts);
+	return;
+}
+
+
+/*static int __devinit rm31100_ts_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)*/
+static int rm31100_ts_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct rm31100_ts *ts;
+	struct rm3110x_ts_platform_data *pdata = client->dev.platform_data;
+	int rc/*, temp_reg*/;
+	union i2c_smbus_data dummy;
+
+	if (!pdata) {
+		dev_err(&client->dev, "platform data is required!\n");
+		return -EINVAL;
+	}
+
+	if (!i2c_check_functionality(client->adapter,
+		I2C_FUNC_SMBUS_READ_WORD_DATA)) {
+		dev_err(&client->dev, "I2C functionality not supported\n");
+		return -EIO;
+	}
+	/* 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)
+		return -ENODEV;
+
+	ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+	pts = ts;
+
+	/* Enable runtime PM ops, start in ACTIVE mode */
+	rc = pm_runtime_set_active(&client->dev);
+	if (rc < 0)
+		dev_warn(&client->dev, "unable to set runtime pm state\n");
+	pm_runtime_enable(&client->dev);
+
+	ts->client = client;
+	ts->pdata = pdata;
+	i2c_set_clientdata(client, ts);
+	ts->device_id = id->driver_data;
+
+	if (ts->pdata->dev_setup) {
+		rc = ts->pdata->dev_setup(1);
+		if (rc < 0) {
+			dev_err(&client->dev, "dev setup failed\n");
+			goto error_touch_data_alloc;
+		}
+	}
+
+
+	ts->is_suspended = false;
+	ts->int_pending = false;
+	/*mutex_init(&ts->sus_lock); JL remove*/
+	mutex_init(&ts->access_lock);
+
+	async_schedule(rm_initialize_async, ts);
+
+	device_init_wakeup(&client->dev, ts->pdata->wakeup);
+	return 0;
+
+error_touch_data_alloc:
+	pm_runtime_set_suspended(&client->dev);
+	pm_runtime_disable(&client->dev);
+	kfree(ts);
+	return rc;
+}
+
+/*static int __devexit RM31100_ts_remove(struct i2c_client *client)*/
+static int rm31100_ts_remove(struct i2c_client *client)
+{
+	struct rm31100_ts *ts = i2c_get_clientdata(client);
+
+	pm_runtime_set_suspended(&client->dev);
+	pm_runtime_disable(&client->dev);
+
+	device_init_wakeup(&client->dev, 0);
+	free_irq(ts->pen_irq, ts);
+
+	gpio_free(ts->pdata->irq_gpio);
+
+	if (ts->pdata->resout_gpio >= 0)
+		gpio_free(ts->pdata->resout_gpio);
+	input_unregister_device(ts->input);
+
+	/*mutex_destroy(&ts->sus_lock); JL remove*/
+	mutex_destroy(&ts->access_lock);
+
+	if (ts->pdata->power_on)
+		ts->pdata->power_on(0);
+
+	if (ts->pdata->dev_setup)
+		ts->pdata->dev_setup(0);
+
+	kfree(ts->touch_data);
+	kfree(ts);
+
+	return 0;
+}
+
+static const struct i2c_device_id rm31100_ts_id[] = {
+	{"RM31100", rm31100},
+	{"RM3110x", rm3110x},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, rm31100_ts_id);
+
+
+static struct i2c_driver rm31100_ts_driver = {
+	.driver = {
+		.name = "raydium_ts",
+		.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+		.pm = &rm31100_ts_pm_ops,
+#endif
+	},
+	.probe = rm31100_ts_probe,
+	.remove = rm31100_ts_remove,
+	.id_table = rm31100_ts_id,
+};
+
+static int __init rm31100_ts_init(void)
+{
+	int rc;
+	int rc2;
+
+	rc = i2c_add_driver(&rm31100_ts_driver);
+
+	rc2 = driver_create_file(&rm31100_ts_driver.driver,
+		&driver_attr_myAttr);
+
+	return rc;
+}
+/* Making this as late init to avoid power fluctuations
+ * during LCD initialization.
+ */
+module_init(rm31100_ts_init);
+
+static void __exit rm31100_ts_exit(void)
+{
+	i2c_del_driver(&rm31100_ts_driver);
+}
+module_exit(rm31100_ts_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("rm31100-rm3110x touchscreen controller driver");
+MODULE_AUTHOR("Raydium");
+MODULE_ALIAS("platform:rm31100_ts");
-- 
2.1.2


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

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2014-12-05  8:15 jeffrey.lin
@ 2014-12-05 15:04 ` Dmitry Torokhov
  0 siblings, 0 replies; 58+ messages in thread
From: Dmitry Torokhov @ 2014-12-05 15:04 UTC (permalink / raw)
  To: jeffrey.lin, shc_work, bleung, lee.jones, charliemooney
  Cc: KP.li, linux-kernel, linux-input, jeffrey.lin

Hi Jeffrey,

On December 5, 2014 12:15:01 AM PST, "jeffrey.lin" <yajohn@gmail.com> wrote:
>From: "jeffrey.lin" <jeffrey.lin@rad-ic.com>
>
>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@rad-ic.com
>---
> drivers/input/touchscreen/Kconfig  | 12 ++++++++++++
> drivers/input/touchscreen/Makefile |  1 +
> 2 files changed, 13 insertions(+)
>
>diff --git a/drivers/input/touchscreen/Kconfig
>b/drivers/input/touchscreen/Kconfig
>index 3ce9181..d0324d2 100644
>--- a/drivers/input/touchscreen/Kconfig
>+++ b/drivers/input/touchscreen/Kconfig
>@@ -955,4 +955,16 @@ config TOUCHSCREEN_ZFORCE
> 	  To compile this driver as a module, choose M here: the
> 	  module will be called zforce_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 rm31100_ts.
>+
> endif
>diff --git a/drivers/input/touchscreen/Makefile
>b/drivers/input/touchscreen/Makefile
>index 687d5a7..3220f66 100644
>--- a/drivers/input/touchscreen/Makefile
>+++ b/drivers/input/touchscreen/Makefile
>@@ -78,3 +78,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE)	+=
>zylonite-wm97xx.o
> obj-$(CONFIG_TOUCHSCREEN_W90X900)	+= w90p910_ts.o
> obj-$(CONFIG_TOUCHSCREEN_TPS6507X)	+= tps6507x-ts.o
> obj-$(CONFIG_TOUCHSCREEN_ZFORCE)	+= zforce_ts.o
>+obj-$(CONFIG_TOUCHSCREEN_RM_TS)		+= rm31100_ts.o

The source to the driver itself is missing, you only sent make file and kconfig changes.


Thanks.

-- 
Dmitry

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

* [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
@ 2014-12-05  8:15 jeffrey.lin
  2014-12-05 15:04 ` Dmitry Torokhov
  0 siblings, 1 reply; 58+ messages in thread
From: jeffrey.lin @ 2014-12-05  8:15 UTC (permalink / raw)
  To: dmitry.torokhov, shc_work, bleung, lee.jones, charliemooney
  Cc: KP.li, linux-kernel, linux-input, jeffrey.lin

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

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@rad-ic.com
---
 drivers/input/touchscreen/Kconfig  | 12 ++++++++++++
 drivers/input/touchscreen/Makefile |  1 +
 2 files changed, 13 insertions(+)

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 3ce9181..d0324d2 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -955,4 +955,16 @@ config TOUCHSCREEN_ZFORCE
 	  To compile this driver as a module, choose M here: the
 	  module will be called zforce_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 rm31100_ts.
+
 endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 687d5a7..3220f66 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -78,3 +78,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE)	+= zylonite-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_W90X900)	+= w90p910_ts.o
 obj-$(CONFIG_TOUCHSCREEN_TPS6507X)	+= tps6507x-ts.o
 obj-$(CONFIG_TOUCHSCREEN_ZFORCE)	+= zforce_ts.o
+obj-$(CONFIG_TOUCHSCREEN_RM_TS)		+= rm31100_ts.o
-- 
2.1.2


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

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2014-11-14  7:19 jeffrey.lin
  2014-11-14 17:43 ` Benson Leung
@ 2014-11-14 18:00 ` Dmitry Torokhov
  1 sibling, 0 replies; 58+ messages in thread
From: Dmitry Torokhov @ 2014-11-14 18:00 UTC (permalink / raw)
  To: jeffrey.lin
  Cc: rydberg, olof, dianders, benzh, davidriley, bleung, joe.konno,
	jeffrey.lin, roger.yang, KP.li, linux-kernel, linux-input

Hi Jeffrey,

On Fri, Nov 14, 2014 at 03:19:22PM +0800, jeffrey.lin wrote:
> From: "jeffrey.lin" <jeffrey.lin@rad-ic.com>
> 
> this patch is porting Raydium I2C touch driver.

Thank you for your submission.

>  Developer can enable
>  Raydium touch driver by modifying define "CONFIG_TOUCHSCREEN_RM31100"
>  in config/base.config

This does not make sense in context of mainline kernel, it is Chromeos
(or Android?) construct.

Also, for mainline submission, please get rid of EARLYSUSPEND bits,
convert the driver to threaded interrupt handler instead of private
workqueue, remove driver specific device interface, #if 0., DEBUG_PRINT,
convert to MT-B protocol.

Thank you.

-- 
Dmitry

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

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
  2014-11-14  7:19 jeffrey.lin
@ 2014-11-14 17:43 ` Benson Leung
  2014-11-14 18:00 ` Dmitry Torokhov
  1 sibling, 0 replies; 58+ messages in thread
From: Benson Leung @ 2014-11-14 17:43 UTC (permalink / raw)
  To: jeffrey.lin
  Cc: Dmitry Torokhov, Henrik Rydberg, Olof Johansson,
	Douglas Anderson, benzh, davidriley, Joe Konno, jeffrey.lin,
	roger.yang, KP.li, linux-kernel, linux-input

Hi Jeffrey,


On Thu, Nov 13, 2014 at 11:19 PM, jeffrey.lin <yajohn@gmail.com> wrote:
> this patch is porting Raydium I2C touch driver. Developer can enable
>  Raydium touch driver by modifying define "CONFIG_TOUCHSCREEN_RM31100"
>  in config/base.config
>
> Change-Id: Idae54cc4bca17f321a1d0895a8b59680bf9af859

This looks like it came from the chromiumos kernel. When you send a
patch to the linux-input list, make sure to strip out this stuff in
the commit message.

>
> Signed-off-by: jeffrey.lin@rad-ic.com
>
> chromeos: config: add Raydium touch default config
>
> Add to default table and set as not to use
>
> Signed-off-by: jeffrey.lin@rad-ic.com
> ---
>  chromeos/config/base.config         |    1 +

This file doesn't exist in upstream linux (only exists in
chromeos-kernels) so don't include this in the patch.

Basically, make sure whatever you send to the list actually applies to
mainline linux or linux-next.

>  drivers/input/touchscreen/RM31100.c | 1037 +++++++++++++++++++++++++++++++++++
>  include/linux/input/RM31100.h       |   60 ++
>  3 files changed, 1098 insertions(+)
>  create mode 100644 drivers/input/touchscreen/RM31100.c
>  create mode 100644 include/linux/input/RM31100.h
>
> diff --git a/chromeos/config/base.config b/chromeos/config/base.config
> index 59fa689..45b1023 100644
> --- a/chromeos/config/base.config
> +++ b/chromeos/config/base.config
> @@ -1727,6 +1727,7 @@ CONFIG_TOUCHSCREEN_ATMEL_MXT=y
>  # CONFIG_TOUCHSCREEN_TSC2007 is not set
>  # CONFIG_TOUCHSCREEN_TSC_SERIO is not set
>  CONFIG_TOUCHSCREEN_USB_COMPOSITE=m
> +# CONFIG_TOUCHSCREEN_RM_TS is not set
>  # CONFIG_TOUCHSCREEN_WACOM_I2C is not set
>  # CONFIG_TOUCHSCREEN_WACOM_W8001 is not set
>  # CONFIG_TPS6105X is not set


Thanks

-- 
Benson Leung
Software Engineer, Chrom* OS
bleung@chromium.org

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

* [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
@ 2014-11-14  7:19 jeffrey.lin
  2014-11-14 17:43 ` Benson Leung
  2014-11-14 18:00 ` Dmitry Torokhov
  0 siblings, 2 replies; 58+ messages in thread
From: jeffrey.lin @ 2014-11-14  7:19 UTC (permalink / raw)
  To: dmitry.torokhov, rydberg, olof, dianders, benzh, davidriley,
	bleung, joe.konno
  Cc: jeffrey.lin, roger.yang, KP.li, linux-kernel, linux-input

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

this patch is porting Raydium I2C touch driver. Developer can enable
 Raydium touch driver by modifying define "CONFIG_TOUCHSCREEN_RM31100"
 in config/base.config

Change-Id: Idae54cc4bca17f321a1d0895a8b59680bf9af859

Signed-off-by: jeffrey.lin@rad-ic.com

chromeos: config: add Raydium touch default config

Add to default table and set as not to use

Signed-off-by: jeffrey.lin@rad-ic.com
---
 chromeos/config/base.config         |    1 +
 drivers/input/touchscreen/RM31100.c | 1037 +++++++++++++++++++++++++++++++++++
 include/linux/input/RM31100.h       |   60 ++
 3 files changed, 1098 insertions(+)
 create mode 100644 drivers/input/touchscreen/RM31100.c
 create mode 100644 include/linux/input/RM31100.h

diff --git a/chromeos/config/base.config b/chromeos/config/base.config
index 59fa689..45b1023 100644
--- a/chromeos/config/base.config
+++ b/chromeos/config/base.config
@@ -1727,6 +1727,7 @@ CONFIG_TOUCHSCREEN_ATMEL_MXT=y
 # CONFIG_TOUCHSCREEN_TSC2007 is not set
 # CONFIG_TOUCHSCREEN_TSC_SERIO is not set
 CONFIG_TOUCHSCREEN_USB_COMPOSITE=m
+# CONFIG_TOUCHSCREEN_RM_TS is not set
 # CONFIG_TOUCHSCREEN_WACOM_I2C is not set
 # CONFIG_TOUCHSCREEN_WACOM_W8001 is not set
 # CONFIG_TPS6105X is not set
diff --git a/drivers/input/touchscreen/RM31100.c b/drivers/input/touchscreen/RM31100.c
new file mode 100644
index 0000000..58eb850
--- /dev/null
+++ b/drivers/input/touchscreen/RM31100.c
@@ -0,0 +1,1037 @@
+/* Source for:
+ * Raydium RM31100 Prototype touchscreen driver.
+ * drivers/input/touchscreen/RM31100.c
+ *
+ * Copyright (C) 2012,
+ *
+ * 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
+ *
+ * History:
+ *			(C) 2012 Raydium - Update for GPL distribution
+ *			(C) 2009 Enea - Original prototype
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/input/RM31100.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+/*#include <plat/gpio-cfg.h>*/
+#include <linux/miscdevice.h>
+/*#include <asm/uaccess.h> copy_to_user() */
+#include <linux/uaccess.h>
+
+
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+#include <linux/earlysuspend.h>
+
+/* Early-suspend level */
+#define RM31100_TS_SUSPEND_LEVEL 1
+#endif
+
+#define RM31100	    0x0
+#define RM3110x	    0x1
+
+#define INVALID_DATA	0xff
+
+#define TOUCHSCREEN_TIMEOUT	(msecs_to_jiffies(10))
+#define INITIAL_DELAY		(msecs_to_jiffies(25000))
+
+#define EVAL_REPORT_RATE    1
+#define ENABLE_DEBUG_MSG    0
+
+#if ENABLE_DEBUG_MSG
+#define DEBUG_PRINT     printk
+#else
+#define DEBUG_PRINT(arg...)
+#endif
+
+#define I2C_CLIENT_ADDR         0x39
+#define I2C_DMA_CLIENT_ADDR     0x5A
+#undef CONFIG_PM
+struct RM31100_ts_data {
+	u8 x_index;
+	u8 y_index;
+	u8 z_index;
+	u8 id_index;
+	u8 touch_index;
+	u8 data_reg;
+	u8 status_reg;
+	u8 data_size;
+	u8 touch_bytes;
+	u8 update_data;
+	u8 touch_meta_data;
+	u8 finger_size;
+};
+
+static struct RM31100_ts_data devices[] = {
+	[0] = {
+		.x_index = 2,
+		.y_index = 4,
+		.z_index = 6,
+		.id_index = 1,
+		.data_reg = 0x1,
+		.status_reg = 0,
+		.update_data = 0x0,/*0x4*/
+		.touch_bytes = 6,
+		.touch_meta_data = 1,
+		.finger_size = 70,
+	},
+};
+
+struct RM31100_ts {
+	struct i2c_client *client;
+	struct input_dev *input;
+	struct delayed_work work;
+	struct workqueue_struct *wq;
+	struct RM3110x_ts_platform_data *pdata;
+	struct RM31100_ts_data *dd;
+	u8 *touch_data;
+	u8 device_id;
+	u8 prev_touches;
+	bool is_suspended;
+	bool int_pending;
+	struct mutex sus_lock;
+	struct mutex access_lock;
+	u32 pen_irq;
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+	struct early_suspend early_suspend;
+#endif
+};
+
+struct RM31100_ts *pts;
+
+static inline u16 join_bytes(u8 a, u8 b)
+{
+	u16 ab = 0;
+	ab = ab | a;
+	ab = ab << 8 | b;
+	return ab;
+}
+
+static s32 RM31100_ts_write_reg_u8(struct i2c_client *client, u8 reg, u8 val)
+{
+	s32 data;
+
+	data = i2c_smbus_write_byte_data(client, reg, val);
+	if (data < 0)
+		dev_err(&client->dev, "error %d in writing reg 0x%x\n",
+						 data, reg);
+
+	return data;
+}
+
+static s32 RM31100_ts_read_reg_u8(struct i2c_client *client, u8 reg)
+{
+	s32 data;
+
+	data = i2c_smbus_read_byte_data(client, reg);
+	if (data < 0)
+		dev_err(&client->dev, "error %d in reading reg 0x%x\n",
+						 data, reg);
+
+	return data;
+}
+
+static int RM31100_ts_read(struct i2c_client *client, u8 reg, u8 *buf, int num)
+{
+	struct i2c_msg xfer_msg[2];
+
+	xfer_msg[0].addr = client->addr;
+	xfer_msg[0].len = 1;
+	xfer_msg[0].flags = 0;
+	xfer_msg[0].buf = &reg;
+
+	xfer_msg[1].addr = client->addr;
+	xfer_msg[1].len = num;
+	xfer_msg[1].flags = I2C_M_RD;
+	xfer_msg[1].buf = buf;
+
+	return i2c_transfer(client->adapter, xfer_msg, 2);
+}
+
+static int RM31100_ts_write(struct i2c_client *client, u8 *buf, int num)
+{
+	struct i2c_msg xfer_msg[1];
+
+	xfer_msg[0].addr = client->addr;
+	xfer_msg[0].len = num;
+	xfer_msg[0].flags = 0;
+	xfer_msg[0].buf = buf;
+
+	return i2c_transfer(client->adapter, xfer_msg, 1);
+}
+
+static int dev_open(struct inode *inode, struct file *filp)
+{
+	/*printk("***%s***", __func__);*/
+	mutex_lock(&pts->access_lock);
+	return 0;
+}
+
+static int dev_release(struct inode *inode, struct file *filp)
+{
+	/*printk("***%s***", __func__);*/
+	mutex_unlock(&pts->access_lock);
+	return 0;
+}
+
+static ssize_t
+dev_read(struct file *filp, char __user *buf, size_t count, loff_t *pos)
+{
+	u8 *kbuf;
+	struct i2c_msg xfer_msg;
+	/*static char out[] = "1234567890";*/
+	/*static int idx;*//*= 0; remove by checkpatch*/
+	int i;
+
+#if 0
+	DEBUG_PRINT("===%s===", __func__);
+	copy_to_user(buf, &out[idx], count);
+	idx = (idx+3);
+	if (idx >= sizeof(out)) {
+		idx = 0;
+		return 0;
+	}
+	return 3;
+#else
+	kbuf = kmalloc(count, GFP_KERNEL);
+	if (kbuf == NULL)
+		return -ENOMEM;
+
+	/*xfer_msg.addr = pts->client->addr;*/
+	xfer_msg.addr = I2C_CLIENT_ADDR;
+	xfer_msg.len = count;
+	xfer_msg.flags = I2C_M_RD;
+	xfer_msg.buf = kbuf;
+
+	i2c_transfer(pts->client->adapter, &xfer_msg, 1);
+
+	DEBUG_PRINT("dev_read(): count=%zu, data = ", count);
+	for (i = 0; i < count; i++)
+		DEBUG_PRINT("%hhx ", kbuf[i]);
+
+	DEBUG_PRINT("\n");
+
+	if (copy_to_user(buf, kbuf, count) == 0)
+		return count;
+	else
+		return -EFAULT;
+#endif
+}
+
+static ssize_t
+dev_write(struct file *filp, const char __user *buf, size_t count, loff_t *pos)
+{
+	u8 *kbuf;
+	ssize_t status = 0;
+	int i;
+
+	kbuf = kmalloc(count, GFP_KERNEL);
+	if (kbuf == NULL) {
+		DEBUG_PRINT("kmalloc() fail\n");
+		return -ENOMEM;
+	}
+
+	if (copy_from_user(kbuf, buf, count) == 0) {
+		DEBUG_PRINT("dev_write(): count=%zu, data = ", count);
+		for (i = 0; i < count; i++)
+			DEBUG_PRINT("%hhx ", kbuf[i]);
+
+		DEBUG_PRINT("\n");
+		pts->client->addr = I2C_CLIENT_ADDR;
+		if (RM31100_ts_write(pts->client, kbuf, count) < 0)
+			status = -EFAULT;
+		else
+			status = count;
+	} else {
+		DEBUG_PRINT("copy_from_user() fail\n");
+		status = -EFAULT;
+	}
+
+	kfree(kbuf);
+	return status;
+}
+
+static struct file_operations dev_fops = {
+	.owner = THIS_MODULE,
+	.open = dev_open,
+	.release = dev_release,
+	.read = dev_read,
+	.write = dev_write,
+	/*.unlocked_ioctl = dev_ioctl,*/
+};
+
+static struct miscdevice raydium_ts_miscdev = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "raydium_ts",
+	.fops = &dev_fops,
+};
+
+
+
+ssize_t show(struct device_driver *drv, char *buff)
+{
+#if 0
+	unsigned char tmp[100];
+
+	/*i2c_master_recv(pts->client, tmp, 1);*/
+	if (RM31100_ts_read(pts->client, 50, tmp, 61) < 0)
+		DEBUG_PRINT("RM31100_ts_read fail\n");
+
+	return snprintf(buf, PAGE_SIZE, "%hhu\n", tmp[0]);
+#else
+	struct i2c_msg xfer_msg;
+	int num = 10;
+	char buf[100];
+	/*int i;*/
+
+	xfer_msg.addr = pts->client->addr;
+	xfer_msg.len = num;
+	xfer_msg.flags = I2C_M_RD;
+	xfer_msg.buf = buf;
+	pts->client->addr = I2C_CLIENT_ADDR;
+	i2c_transfer(pts->client->adapter, &xfer_msg, 1);
+	/*
+	printk("\n");
+	for (i = 0; i < num; i++)
+	{
+	printk("%hhu ", buf[i]);
+	}
+	printk("\n");
+	*/
+	return 0;
+#endif
+}
+
+ssize_t store(struct device_driver *drv, const char *buf, size_t count)
+{
+	/*unsigned char pkt[] = { 0xF2, 5, 1, 1 };*/
+	unsigned char pkt[] = { 0xF1, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
+#if 0
+	unsigned char val, ret;
+
+	ret = sscanf(buf, "%hhx", &val);
+/*
+	if (RM31100_ts_write(pts->client, &val, 1) < 0)
+		printk("RM31100_ts_write fail\n");
+*/
+
+	return count;
+#else
+	pts->client->addr = I2C_CLIENT_ADDR;
+	RM31100_ts_write(pts->client, pkt, sizeof(pkt));
+	/*RM31100_ts_write_reg_u8(pts->client, 12, 17);*/
+#endif
+	/*printk("***%s***", __func__);*/
+	return sizeof(pkt);
+}
+
+DRIVER_ATTR(myAttr, 0x777, show, store);
+
+static void report_data(struct RM31100_ts *ts, u16 x, u16 y, u8 pressure, u8 id)
+{
+	if (ts->pdata->swap_xy)
+		swap(x, y);
+
+	/* handle inverting coordinates */
+	if (ts->pdata->invert_x)
+		x = ts->pdata->res_x - x;
+	if (ts->pdata->invert_y)
+		y = ts->pdata->res_y - y;
+
+	input_report_abs(ts->input, ABS_MT_TRACKING_ID, id);
+	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_TOUCH_MAJOR, pressure);
+	input_report_abs(ts->input, ABS_MT_WIDTH_MAJOR, ts->dd->finger_size);
+	input_mt_sync(ts->input);
+
+	DEBUG_PRINT("%s(): id =%2hhd, x =%4hd, y =%4hd, pressure = %hhd\n",
+		__func__, id, x, y, pressure);
+}
+
+static void process_RM31100_data(struct RM31100_ts *ts)
+{
+	u8 id, pressure, touches, i;
+	u16 x, y;
+
+	touches = ts->touch_data[ts->dd->touch_index];
+
+	if (touches > 0) {
+		for (i = 0; i < touches; i++) {
+			id = ts->touch_data[i * ts->dd->touch_bytes +
+				ts->dd->id_index];
+			pressure = ts->touch_data[i * ts->dd->touch_bytes +
+				ts->dd->z_index];
+			x = join_bytes(ts->touch_data[i * ts->dd->touch_bytes +
+				ts->dd->x_index + 1],
+			ts->touch_data[i * ts->dd->touch_bytes +
+				ts->dd->x_index]);
+			y = join_bytes(ts->touch_data[i * ts->dd->touch_bytes +
+				ts->dd->y_index + 1],
+			ts->touch_data[i * ts->dd->touch_bytes +
+				ts->dd->y_index]);
+			report_data(ts, x, y, pressure, id);
+		}
+	} else
+		input_mt_sync(ts->input);
+
+#if 0
+	for (i = 0; i < ts->prev_touches - (char)touches; i++) {
+		input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, 0);
+		input_report_abs(ts->input, ABS_MT_WIDTH_MAJOR, 0);
+		input_mt_sync(ts->input);
+	}
+#endif
+
+	ts->prev_touches = touches;
+	input_report_key(ts->input, BTN_TOUCH, 1);
+	input_sync(ts->input);
+}
+
+static void RM31100_ts_xy_worker(struct work_struct *work)
+{
+	int rc;
+	u8 DMAAddress[4];
+	struct RM31100_ts *ts;
+#if EVAL_REPORT_RATE
+	static struct timeval tv_start;
+	struct timeval tv_now;
+	static int cnt = -1;
+	int us;
+#endif /* EVAL_REPORT_RATE*/
+
+
+	ts = container_of(work, struct RM31100_ts,
+		work.work);
+	DEBUG_PRINT("****RM31100_ts_xy_worker******\n");
+	mutex_lock(&ts->sus_lock);
+	if (ts->is_suspended == true) {
+		dev_dbg(&ts->client->dev, "TS is supended\n");
+		ts->int_pending = true;
+		mutex_unlock(&ts->sus_lock);
+		return;
+	}
+	mutex_unlock(&ts->sus_lock);
+
+	mutex_lock(&ts->access_lock);
+	/* read data from DATA_REG */
+	/*RM31100 DMA Mode*/
+#if 1 /*T010 OR w001+T012*/
+	DMAAddress[0] = 0x0F;
+	DMAAddress[1] = 0x00;
+	DMAAddress[2] = 0x20;
+	DMAAddress[3] = 0x81;/* Turn on DMA Mode*/
+	ts->client->addr = I2C_DMA_CLIENT_ADDR;
+	rc = RM31100_ts_write(ts->client, DMAAddress, 0x04);
+	if (rc < 0) {
+		dev_err(&ts->client->dev, "write failed\n");
+		goto schedule;
+	}
+	ts->client->addr = I2C_CLIENT_ADDR;
+	rc = RM31100_ts_read(ts->client, ts->dd->data_reg, ts->touch_data,
+	ts->dd->data_size);
+#endif
+
+	if (rc < 0) {
+		dev_err(&ts->client->dev, "read failed\n");
+		goto schedule;
+	}
+
+	if (ts->touch_data[ts->dd->touch_index] == INVALID_DATA)
+		goto schedule;
+
+	/* write to STATUS_REG to release lock */
+	rc = RM31100_ts_write_reg_u8(ts->client,
+		ts->dd->status_reg, ts->dd->update_data);
+	if (rc < 0) {
+		dev_err(&ts->client->dev, "write failed, try once more\n");
+
+		rc = RM31100_ts_write_reg_u8(ts->client,
+			ts->dd->status_reg, ts->dd->update_data);
+		if (rc < 0)
+			dev_err(&ts->client->dev, "write failed, exiting\n");
+	}
+
+	process_RM31100_data(ts);
+
+#if EVAL_REPORT_RATE
+	cnt++;
+
+	if (cnt == 0)/* first time this function is executed.*/
+		do_gettimeofday(&tv_start);
+	else if (cnt == 100) {
+		do_gettimeofday(&tv_now);
+		us = 1000000 * (tv_now.tv_sec - tv_start.tv_sec)
+			+ tv_now.tv_usec - tv_start.tv_usec;
+		tv_start.tv_sec = tv_now.tv_sec;
+		tv_start.tv_usec = tv_now.tv_usec;
+/*printk("Report Rate = %d(100 frames / %d us\n",100000000 / us, us);*/
+		cnt = 0;
+	}
+#endif /* EVAL_REPORT_RATE*/
+schedule:
+
+	mutex_unlock(&ts->access_lock);
+	DEBUG_PRINT("****Leave RM31100_ts_xy_worker******\n");
+	enable_irq(ts->pen_irq);
+}
+
+static irqreturn_t RM31100_ts_irq(int irq, void *dev_id)
+{
+	struct RM31100_ts *ts = dev_id;
+
+	disable_irq_nosync(irq);
+
+	queue_delayed_work(ts->wq, &ts->work, 0);
+
+	return IRQ_HANDLED;
+}
+
+static int RM31100_ts_init_ts(struct i2c_client *client, struct RM31100_ts *ts)
+{
+	struct input_dev *input_device;
+	int rc = 0;
+	/*printk("RM31100_ts_init_ts!!\n");*/
+	ts->dd = &devices[ts->device_id];
+
+	if (!ts->pdata->nfingers) {
+		dev_err(&client->dev, "Touches information not specified\n");
+		return -EINVAL;
+	}
+
+	if (ts->device_id == RM3110x) {
+		if (ts->pdata->nfingers > 2) {
+			dev_err(&client->dev, "Touches >=1 & <= 2\n");
+			return -EINVAL;
+		}
+		ts->dd->data_size = ts->dd->touch_bytes;
+		ts->dd->touch_index = 0x0;
+	} else if (ts->device_id == RM31100) {
+		if (ts->pdata->nfingers > 10) {
+			dev_err(&client->dev, "Touches >=1 & <= 10\n");
+			return -EINVAL;
+		}
+		ts->dd->data_size = ts->pdata->nfingers * ts->dd->touch_bytes +
+						ts->dd->touch_meta_data;
+		ts->dd->touch_index = 0x0;
+	}
+#if 1 /* w001 */
+	else {
+		ts->dd->data_size = ts->pdata->nfingers * ts->dd->touch_bytes +
+		ts->dd->touch_meta_data;
+		ts->dd->touch_index = 0x0;
+	}
+#endif
+	ts->touch_data = kzalloc(ts->dd->data_size, GFP_KERNEL);
+	if (!ts->touch_data) {
+		pr_err("%s: Unable to allocate memory\n", __func__);
+		return -ENOMEM;
+	}
+
+	ts->prev_touches = 0;
+
+	input_device = input_allocate_device();
+	if (!input_device) {
+		rc = -ENOMEM;
+		goto error_alloc_dev;
+	}
+
+	ts->input = input_device;
+	input_device->name = ts->pdata->ts_name;
+	input_device->id.bustype = BUS_I2C;
+	input_device->dev.parent = &client->dev;
+	input_set_drvdata(input_device, ts);
+
+	__set_bit(EV_ABS, input_device->evbit);
+	__set_bit(INPUT_PROP_DIRECT, input_device->propbit);
+	/*__set_bit(EV_SYN, input_device->evbit);*/
+	/*__set_bit(BTN_TOUCH, input_device->keybit);*/
+
+
+	if (ts->device_id == RM31100) {
+		/* set up virtual key */
+		__set_bit(EV_KEY, input_device->evbit);
+		/* set dummy key to make driver work with virtual keys */
+		input_set_capability(input_device, EV_KEY, KEY_PROG1);
+	}
+
+	input_set_abs_params(input_device, ABS_MT_POSITION_X,
+			ts->pdata->dis_min_x, ts->pdata->dis_max_x, 0, 0);
+	input_set_abs_params(input_device, ABS_MT_POSITION_Y,
+			ts->pdata->dis_min_y, ts->pdata->dis_max_y, 0, 0);
+#if 0
+	input_set_abs_params(input_device, ABS_MT_TOUCH_MAJOR,
+			ts->pdata->min_touch, ts->pdata->max_touch, 0, 0);
+	input_set_abs_params(input_device, ABS_MT_WIDTH_MAJOR,
+			ts->pdata->min_width, ts->pdata->max_width, 0, 0);
+#endif
+	input_set_abs_params(input_device, ABS_MT_TRACKING_ID,
+			ts->pdata->min_tid, ts->pdata->max_tid, 0, 0);
+
+	ts->wq = create_singlethread_workqueue("kworkqueue_ts");
+	if (!ts->wq) {
+		dev_err(&client->dev, "Could not create workqueue\n");
+		goto error_wq_create;
+	}
+
+	INIT_DELAYED_WORK(&ts->work, RM31100_ts_xy_worker);
+
+	rc = input_register_device(input_device);
+	if (rc)
+		goto error_unreg_device;
+
+	/*printk("RM31100_init_OKK\n"); */
+	return 0;
+
+error_unreg_device:
+	destroy_workqueue(ts->wq);
+error_wq_create:
+	input_free_device(input_device);
+error_alloc_dev:
+	kfree(ts->touch_data);
+	return rc;
+}
+
+#ifdef CONFIG_PM
+static int RM31100_ts_suspend(struct device *dev)
+{
+	struct RM31100_ts *ts = dev_get_drvdata(dev);
+	int rc = 0;
+	/*printk("****RM31100_ts_suspend******\n"); */
+	if (device_may_wakeup(dev)) {
+		/* mark suspend flag */
+		mutex_lock(&ts->sus_lock);
+		ts->is_suspended = true;
+		mutex_unlock(&ts->sus_lock);
+
+		enable_irq_wake(ts->pen_irq);
+	} else {
+		disable_irq_nosync(ts->pen_irq);
+
+		rc = cancel_delayed_work_sync(&ts->work);
+
+		if (rc) {
+			/* missed the worker, write to STATUS_REG to
+			   acknowledge interrupt */
+			rc = RM31100_ts_write_reg_u8(ts->client,
+				ts->dd->status_reg, ts->dd->update_data);
+			if (rc < 0) {
+				dev_err(&ts->client->dev,
+					"write failed, try once more\n");
+
+				rc = RM31100_ts_write_reg_u8(ts->client,
+					ts->dd->status_reg,
+					ts->dd->update_data);
+				if (rc < 0)
+					dev_err(&ts->client->dev,
+						"write failed, exiting\n");
+			}
+
+			enable_irq(ts->pen_irq);
+		}
+
+		gpio_free(ts->pdata->irq_gpio);
+
+		if (ts->pdata->power_on) {
+			rc = ts->pdata->power_on(0);
+			if (rc) {
+				dev_err(dev, "unable to goto suspend\n");
+				return rc;
+			}
+		}
+	}
+	/*printk("****Leave RM31100_ts_suspend******\n"); */
+	return 0;
+}
+
+static int RM31100_ts_resume(struct device *dev)
+{
+	struct RM31100_ts *ts = dev_get_drvdata(dev);
+
+	int rc = 0;
+	/*printk("**** RM31100_ts_resume******\n"); */
+	if (device_may_wakeup(dev)) {
+		disable_irq_wake(ts->pen_irq);
+
+		mutex_lock(&ts->sus_lock);
+		ts->is_suspended = false;
+
+		if (ts->int_pending == true) {
+			ts->int_pending = false;
+
+			/* start a delayed work */
+			queue_delayed_work(ts->wq, &ts->work, 0);
+		}
+		mutex_unlock(&ts->sus_lock);
+
+	} else {
+		if (ts->pdata->power_on) {
+			rc = ts->pdata->power_on(1);
+			if (rc) {
+				dev_err(dev, "unable to resume\n");
+				return rc;
+			}
+		}
+
+		/* configure touchscreen interrupt gpio */
+		rc = gpio_request(ts->pdata->irq_gpio, "RM31100_irq_gpio");
+		if (rc) {
+			pr_err("%s: unable to request gpio %d\n",
+				__func__, ts->pdata->irq_gpio);
+			goto err_power_off;
+		}
+		if (ts->pdata->irq_cfg) {
+			s3c_gpio_cfgpin(ts->pdata->irq_gpio,
+				ts->pdata->irq_cfg);
+			s3c_gpio_setpull(ts->pdata->irq_gpio,
+				S3C_GPIO_PULL_NONE);
+		}
+
+		rc = gpio_direction_input(ts->pdata->irq_gpio);
+		if (rc) {
+			pr_err("%s: unable to set direction for gpio %d\n",
+				__func__, ts->pdata->irq_gpio);
+			goto err_gpio_free;
+		}
+
+		enable_irq(ts->pen_irq);
+
+		/* Clear the status register of the TS controller */
+		rc = RM31100_ts_write_reg_u8(ts->client,
+			ts->dd->status_reg, ts->dd->update_data);
+		if (rc < 0) {
+			dev_err(&ts->client->dev,
+				"write failed, try once more\n");
+
+			rc = RM31100_ts_write_reg_u8(ts->client,
+				ts->dd->status_reg,
+				ts->dd->update_data);
+			if (rc < 0)
+				dev_err(&ts->client->dev,
+					"write failed, exiting\n");
+		}
+	}
+	/*printk("**** Leave RM31100_ts_resume******\n");*/
+	return 0;
+err_gpio_free:
+	gpio_free(ts->pdata->irq_gpio);
+err_power_off:
+	if (ts->pdata->power_on)
+		rc = ts->pdata->power_on(0);
+	return rc;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void RM31100_ts_early_suspend(struct early_suspend *h)
+{
+	struct RM31100_ts *ts =
+		container_of(h, struct RM31100_ts, early_suspend);
+
+	RM31100_ts_suspend(&ts->client->dev);
+}
+
+static void RM31100_ts_late_resume(struct early_suspend *h)
+{
+	struct RM31100_ts *ts = container_of(h,
+		struct RM31100_ts, early_suspend);
+
+	RM31100_ts_resume(&ts->client->dev);
+}
+#endif
+
+static struct dev_pm_ops RM31100_ts_pm_ops = {
+#ifndef CONFIG_HAS_EARLYSUSPEND
+	.suspend = RM31100_ts_suspend,
+	.resume = RM31100_ts_resume,
+#endif
+};
+#endif
+
+/*static int __devinit RM31100_ts_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)*/
+static int __init RM31100_ts_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct RM31100_ts *ts;
+	struct RM3110x_ts_platform_data *pdata = client->dev.platform_data;
+	int rc, temp_reg;
+
+/*printk("RM31100_ts_probe\n"); */
+	if (!pdata) {
+		dev_err(&client->dev, "platform data is required!\n");
+		return -EINVAL;
+	}
+	if (!i2c_check_functionality(client->adapter,
+				     I2C_FUNC_SMBUS_READ_WORD_DATA)) {
+		dev_err(&client->dev, "I2C functionality not supported\n");
+		return -EIO;
+	}
+
+	ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+	pts = ts;
+
+	/* Enable runtime PM ops, start in ACTIVE mode */
+	rc = pm_runtime_set_active(&client->dev);
+	if (rc < 0)
+		dev_dbg(&client->dev, "unable to set runtime pm state\n");
+	pm_runtime_enable(&client->dev);
+
+	ts->client = client;
+	ts->pdata = pdata;
+	i2c_set_clientdata(client, ts);
+	ts->device_id = id->driver_data;
+
+	if (ts->pdata->dev_setup) {
+		rc = ts->pdata->dev_setup(1);
+		if (rc < 0) {
+			dev_err(&client->dev, "dev setup failed\n");
+			goto error_touch_data_alloc;
+		}
+	}
+
+	/* power on the device */
+	if (ts->pdata->power_on) {
+		rc = ts->pdata->power_on(1);
+		if (rc) {
+			pr_err("%s: Unable to power on the device\n", __func__);
+			goto error_dev_setup;
+		}
+	}
+
+	/* read one byte to make sure i2c device exists */
+	if (id->driver_data == RM3110x)
+		temp_reg = 0x01;
+	else if (id->driver_data == RM31100)
+		temp_reg = 0x00;
+	else
+		temp_reg = 0x05;
+
+/*printk("RM31100 read one byte to make sure i2c device exists\n");*/
+
+	rc = RM31100_ts_read_reg_u8(client, temp_reg);
+	if (rc < 0) {
+		dev_err(&client->dev, "i2c sanity check failed\n");
+		goto error_power_on;
+	}
+
+	ts->is_suspended = false;
+	ts->int_pending = false;
+	mutex_init(&ts->sus_lock);
+	mutex_init(&ts->access_lock);
+
+	rc = RM31100_ts_init_ts(client, ts);
+	if (rc < 0) {
+		dev_err(&client->dev, "RM31100 init failed\n");
+		goto error_mutex_destroy;
+	}
+
+	if (ts->pdata->resout_gpio < 0)
+		goto config_irq_gpio;
+
+	/* configure touchscreen reset out gpio */
+	rc = gpio_request(ts->pdata->resout_gpio, "RM31100_resout_gpio");
+	if (rc) {
+		pr_err("%s: unable to request gpio %d\n",
+			__func__, ts->pdata->resout_gpio);
+		goto error_uninit_ts;
+	}
+
+	rc = gpio_direction_output(ts->pdata->resout_gpio, 0);
+	if (rc) {
+		pr_err("%s: unable to set direction for gpio %d\n",
+			__func__, ts->pdata->resout_gpio);
+		goto error_resout_gpio_dir;
+	}
+	/* reset gpio stabilization time */
+	msleep(20);
+
+config_irq_gpio:
+	/* configure touchscreen interrupt gpio */
+	rc = gpio_request(ts->pdata->irq_gpio, "RM31100_irq_gpio");
+	if (rc) {
+		pr_err("%s: unable to request gpio %d\n",
+			__func__, ts->pdata->irq_gpio);
+		goto error_irq_gpio_req;
+	}
+
+	rc = gpio_direction_input(ts->pdata->irq_gpio);
+	if (rc) {
+		pr_err("%s: unable to set direction for gpio %d\n",
+			__func__, ts->pdata->irq_gpio);
+		goto error_irq_gpio_dir;
+	}
+
+	ts->pen_irq = gpio_to_irq(ts->pdata->irq_gpio);
+	rc = request_irq(ts->pen_irq, RM31100_ts_irq,
+				IRQF_TRIGGER_FALLING,
+				ts->client->dev.driver->name, ts);
+	if (rc) {
+		dev_err(&ts->client->dev, "could not request irq\n");
+		goto error_req_irq_fail;
+	}
+
+	device_init_wakeup(&client->dev, ts->pdata->wakeup);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN +
+						RM31100_TS_SUSPEND_LEVEL;
+	ts->early_suspend.suspend = RM31100_ts_early_suspend;
+	ts->early_suspend.resume = RM31100_ts_late_resume;
+	register_early_suspend(&ts->early_suspend);
+#endif
+
+	rc = misc_register(&raydium_ts_miscdev);
+	if (rc) {
+		dev_err(&ts->client->dev,
+		"Raydium TS: cannot register miscdev:%d\n", rc);
+		goto error_reg_misc_dev;
+	}
+
+
+	return 0;
+error_reg_misc_dev:
+error_req_irq_fail:
+error_irq_gpio_dir:
+	gpio_free(ts->pdata->irq_gpio);
+error_irq_gpio_req:
+error_resout_gpio_dir:
+	if (ts->pdata->resout_gpio >= 0)
+		gpio_free(ts->pdata->resout_gpio);
+error_uninit_ts:
+	destroy_workqueue(ts->wq);
+	input_unregister_device(ts->input);
+	kfree(ts->touch_data);
+error_mutex_destroy:
+	mutex_destroy(&ts->sus_lock);
+	mutex_destroy(&ts->access_lock);
+error_power_on:
+/*	if (ts->pdata->power_on)
+		ts->pdata->power_on(0);*/
+error_dev_setup:
+	if (ts->pdata->dev_setup)
+		ts->pdata->dev_setup(0);
+error_touch_data_alloc:
+	pm_runtime_set_suspended(&client->dev);
+	pm_runtime_disable(&client->dev);
+	kfree(ts);
+	return rc;
+}
+
+/*static int __devexit RM31100_ts_remove(struct i2c_client *client)*/
+static int __exit RM31100_ts_remove(struct i2c_client *client)
+{
+	struct RM31100_ts *ts = i2c_get_clientdata(client);
+
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+	unregister_early_suspend(&ts->early_suspend);
+#endif
+	pm_runtime_set_suspended(&client->dev);
+	pm_runtime_disable(&client->dev);
+
+	device_init_wakeup(&client->dev, 0);
+
+	cancel_delayed_work_sync(&ts->work);
+
+	free_irq(ts->pen_irq, ts);
+
+	gpio_free(ts->pdata->irq_gpio);
+
+	if (ts->pdata->resout_gpio >= 0)
+		gpio_free(ts->pdata->resout_gpio);
+
+	destroy_workqueue(ts->wq);
+
+	input_unregister_device(ts->input);
+
+	mutex_destroy(&ts->sus_lock);
+	mutex_destroy(&ts->access_lock);
+
+	if (ts->pdata->power_on)
+		ts->pdata->power_on(0);
+
+	if (ts->pdata->dev_setup)
+		ts->pdata->dev_setup(0);
+
+	kfree(ts->touch_data);
+	kfree(ts);
+
+	return 0;
+}
+
+static const struct i2c_device_id RM31100_ts_id[] = {
+	{"RM31100", RM31100},
+	{"RM3110x", RM3110x},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, RM31100_ts_id);
+
+
+static struct i2c_driver RM31100_ts_driver = {
+	.driver = {
+		.name = "RM31100_ts",
+		.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+		.pm = &RM31100_ts_pm_ops,
+#endif
+	},
+	.probe = RM31100_ts_probe,
+	/*.remove		= __devexit_p(RM31100_ts_remove),*/
+	.remove = __exit_p(RM31100_ts_remove),
+	.id_table = RM31100_ts_id,
+};
+
+static int __init RM31100_ts_init(void)
+{
+	int rc;
+	int rc2;
+	/*printk("Raydium I2C Driver Initial!!!!!!!\n");*/
+	rc = i2c_add_driver(&RM31100_ts_driver);
+
+	rc2 = driver_create_file(&RM31100_ts_driver.driver,
+		&driver_attr_myAttr);
+
+	return rc;
+}
+/* Making this as late init to avoid power fluctuations
+ * during LCD initialization.
+ */
+late_initcall(RM31100_ts_init);
+
+static void __exit RM31100_ts_exit(void)
+{
+	return i2c_del_driver(&RM31100_ts_driver);
+}
+module_exit(RM31100_ts_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("RM31100-RM3110x touchscreen controller driver");
+MODULE_AUTHOR("Raydium");
+MODULE_ALIAS("platform:RM31100_ts");
diff --git a/include/linux/input/RM31100.h b/include/linux/input/RM31100.h
new file mode 100644
index 0000000..714a14b
--- /dev/null
+++ b/include/linux/input/RM31100.h
@@ -0,0 +1,60 @@
+/* Header file for:
+ * Raydium RM31100 Prototype touchscreen driver.
+ *
+ * Copyright (C) 2012, Raydium Semiconductor, Inc.
+ *
+ * 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 at www.rad-ic.com
+ *
+ * History:
+ *			(C) 2012 Raydium - Update for GPL distribution
+ *			(C) 2009 Enea - Original prototype
+ *
+ */
+#ifndef __RM3110xTS_H__
+#define __RM3110xTS_H__
+
+
+/* RM3110x platform data
+ */
+struct RM3110x_ts_platform_data {
+	int (*power_on)(int on);
+	int (*dev_setup)(bool on);
+	const char *ts_name;
+	u32 dis_min_x; /* display resoltion */
+	u32 dis_max_x;
+	u32 dis_min_y;
+	u32 dis_max_y;
+	u32 min_touch; /* no.of touches supported */
+	u32 max_touch;
+	u32 min_tid; /* track id */
+	u32 max_tid;
+	u32 min_width;/* size of the finger */
+	u32 max_width;
+	u32 res_x; /* TS resolution */
+	u32 res_y;
+	u32 swap_xy;
+	u32 flags;
+	u16 invert_x;
+	u16 invert_y;
+	u8 nfingers;
+	u32 irq_gpio;
+	int resout_gpio;
+	bool wakeup;
+	u32 irq_cfg;
+};
+
+#endif
-- 
2.1.2


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

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

Thread overview: 58+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-03-03  6:42 [PATCH] driver: input :touchscreen : add Raydium I2C touch driver jeffrey.lin
2016-03-10 18:47 ` Dmitry Torokhov
2016-03-11  2:10   ` Jeffrey Lin (林義章)
  -- 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-29  9:45 jeffrey.lin
2016-05-04 22:58 ` Dmitry Torokhov
2016-05-06  8:24 ` jeffrey.lin
2016-05-11 16:04 ` jeffrey.lin
2016-05-13  4:18 ` Dmitry Torokhov
2016-05-13 17:08   ` jeffrey.lin
2016-05-16 15:46   ` jeffrey.lin
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
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
2016-05-23 16:27     ` Dmitry Torokhov
2016-05-24  9:31   ` jeffrey.lin
2016-05-27 16:34     ` Dmitry Torokhov
2016-04-22 10:01 dan.huang
2016-04-22 22:50 ` Dmitry Torokhov
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  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
2016-01-21  4:02 Jeffrey Lin
2016-01-21  9:37 ` Dmitry Torokhov
2016-01-15  3:30 Jeffrey Lin
2016-01-13  7:49 Jeffrey Lin
2016-01-13  8:31 ` Dmitry Torokhov
2016-01-13 19:14 ` Dmitry Torokhov
2016-01-13  6:23 Jeffrey Lin
2016-01-13  6:44 ` Dmitry Torokhov
2016-01-08 15:17 Jeffrey Lin
2016-01-09 19:20 ` Dmitry Torokhov
2015-01-06  9:25 jeffrey.lin
2015-01-06 19:43 ` Jeremiah Mahler
2015-01-07  0:40   ` Dmitry Torokhov
2014-12-08  3:42 jeffrey.lin
2014-12-05  8:15 jeffrey.lin
2014-12-05 15:04 ` Dmitry Torokhov
2014-11-14  7:19 jeffrey.lin
2014-11-14 17:43 ` Benson Leung
2014-11-14 18:00 ` Dmitry Torokhov

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).