linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
To: Jeffrey Lin <yajohn@gmail.com>
Cc: rydberg@euromail.se, dianders@chromium.org, bleung@chromium.org,
	scott.liu@emc.com.tw, jeffrey.lin@rad-ic.com,
	roger.yang@rad-ic.com, KP.li@rad-ic.com,
	linux-kernel@vger.kernel.org, linux-input@vger.kernel.org
Subject: Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
Date: Wed, 13 Jan 2016 11:14:38 -0800	[thread overview]
Message-ID: <20160113191438.GD39593@dtor-ws> (raw)
In-Reply-To: <1452671352-792-1-git-send-email-jeffrey.lin@rad-ic.com>

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

  parent reply	other threads:[~2016-01-13 19:14 UTC|newest]

Thread overview: 58+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-01-13  7:49 [PATCH] driver: input :touchscreen : add Raydium I2C touch driver Jeffrey Lin
2016-01-13  8:31 ` Dmitry Torokhov
2016-01-13 19:14 ` Dmitry Torokhov [this message]
  -- 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  6:42 jeffrey.lin
2016-03-10 18:47 ` Dmitry Torokhov
2016-03-11  2:10   ` Jeffrey Lin (林義章)
2016-03-03  2:29 jeffrey.lin
2016-03-03  2:44 ` Joe Perches
2016-03-03  3:14   ` Jeffrey Lin (林義章)
2016-03-03  3:55     ` Joe Perches
2016-02-23  8:11 jeffrey.lin
2016-01-21  4:02 Jeffrey Lin
2016-01-21  9:37 ` Dmitry Torokhov
2016-01-15  3:30 Jeffrey Lin
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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20160113191438.GD39593@dtor-ws \
    --to=dmitry.torokhov@gmail.com \
    --cc=KP.li@rad-ic.com \
    --cc=bleung@chromium.org \
    --cc=dianders@chromium.org \
    --cc=jeffrey.lin@rad-ic.com \
    --cc=linux-input@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=roger.yang@rad-ic.com \
    --cc=rydberg@euromail.se \
    --cc=scott.liu@emc.com.tw \
    --cc=yajohn@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).