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 = ®
> +
> + 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
next prev parent reply other threads:[~2016-01-13 19:14 UTC|newest]
Thread overview: 69+ 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 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-05 10:23 ` Jeffrey Lin (林義章)
2016-05-05 10:25 ` Jeffrey Lin (林義章)
2016-05-06 8:24 ` jeffrey.lin
2016-05-11 16:04 ` jeffrey.lin
[not found] ` <1461923113-426-1-git-send-email-jeffrey.lin-s3Ivl27awEzQT0dZR+AlfA@public.gmane.org>
2016-05-13 4:18 ` Dmitry Torokhov
2016-05-13 4:18 ` Dmitry Torokhov
2016-05-13 17:08 ` jeffrey.lin
2016-05-16 15:46 ` jeffrey.lin
2016-05-16 15:46 ` jeffrey.lin
[not found] ` <1463413611-367-1-git-send-email-jeffrey.lin-s3Ivl27awEzQT0dZR+AlfA@public.gmane.org>
2016-05-16 16:41 ` Dmitry Torokhov
2016-05-16 16:41 ` Dmitry Torokhov
2016-05-16 16:43 ` Dmitry Torokhov
2016-05-16 15:57 ` jeffrey.lin
2016-05-17 16:07 ` jeffrey.lin
[not found] ` <1463501223-20723-1-git-send-email-jeffrey.lin-s3Ivl27awEzQT0dZR+AlfA@public.gmane.org>
2016-05-21 18:18 ` Dmitry Torokhov
2016-05-21 18:18 ` Dmitry Torokhov
2016-05-22 9:32 ` jeffrey.lin
2016-05-22 22:37 ` Dmitry Torokhov
2016-05-22 9:35 ` jeffrey.lin
2016-05-23 14:43 ` jeffrey.lin
[not found] ` <1464014633-20829-1-git-send-email-jeffrey.lin-s3Ivl27awEzQT0dZR+AlfA@public.gmane.org>
2016-05-23 16:27 ` Dmitry Torokhov
2016-05-23 16:27 ` Dmitry Torokhov
2016-05-24 9:31 ` jeffrey.lin
[not found] ` <1464082301-11539-1-git-send-email-jeffrey.lin-s3Ivl27awEzQT0dZR+AlfA@public.gmane.org>
2016-05-27 16:34 ` Dmitry Torokhov
2016-05-27 16:34 ` Dmitry Torokhov
2016-04-22 10:01 dan.huang
[not found] ` <1461319268-362-1-git-send-email-dan.huang-s3Ivl27awEzQT0dZR+AlfA@public.gmane.org>
2016-04-22 22:50 ` Dmitry Torokhov
2016-04-22 22:50 ` Dmitry Torokhov
2016-04-25 2:48 ` yajohn lin
2016-03-25 5:21 jeffrey.lin
2016-04-11 8:24 ` Dmitry Torokhov
2016-04-11 9:57 ` Jeffrey Lin (林義章)
2016-04-14 9:28 ` Jeffrey Lin (林義章)
2016-03-22 6:23 jeffrey.lin
2016-03-15 8:44 jeffrey.lin
2016-03-18 21:04 ` Rob Herring
2016-03-03 6:42 jeffrey.lin
2016-03-10 18:47 ` Dmitry Torokhov
2016-03-11 2:10 ` Jeffrey Lin (林義章)
2016-03-03 2:29 jeffrey.lin
2016-03-03 2:44 ` Joe Perches
2016-03-03 3:14 ` Jeffrey Lin (林義章)
2016-03-03 3:55 ` Joe Perches
2016-02-23 8:11 jeffrey.lin
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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.