Re: [PATCH] Touchscreen driver for FT5x06 based EDT displays

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Simon,

On Thu, Apr 05, 2012 at 02:54:18PM +0200, Simon Budig wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
> 
> On 04/04/2012 09:10 PM, Dmitry Torokhov wrote:
> [suggestions for my patch]
> 
> Based on your input I have prepared two patches which might be easier to
> review than the complete diff.
> 
> The first one is IMHO pretty straightforward and implements most of the
> small bits.
> 
> The second one sits on top of the first one and is an attempt at more
> generic attribute handling. I must say that I am not too happy about it,
> but maybe I am thinking too convoluted here. It saves about 3 lines of
> code and exchanges a lot of easy and straightforward code with stuff
> that is harder to follow - especially the lookup of the matching
> attribute makes me unhappy. There are bits in it I like and will
> probably use. But the lookup...
> 
> any suggestions are welcome.

Sorry for the long delay. I agree that the new version of attribute
handling code is not any better than previous one, and this is not what
I had in mind.

Below is a patch on top of your original one plus the
first patch you send in the last email. It implements the attribute
handling the way I see it and also has some additional changes. Could
you please give it a spin and let me know if the device still works?

Thanks.

-- 
Dmitry


Input: miscellaneous edt fixes

- reworked attribute handling
- remove fake ABS_PRESSURE
- locking - take mutex on sysfs operations; disable interrupts
  when switching mode to factory. Do not take mutex in interrupt;
  rely on I2C code to provide necessary locking.
- do not carry around reset pin gpio.
- use gpio_request_one()
- use IRQ from client structure.

Signed-off-by: Dmitry Torokhov <dtor@xxxxxxx>
---

 drivers/input/touchscreen/edt-ft5x06.c |  889 +++++++++++++++++---------------
 1 files changed, 461 insertions(+), 428 deletions(-)


diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c
index edd7f9f..acd00ba 100644
--- a/drivers/input/touchscreen/edt-ft5x06.c
+++ b/drivers/input/touchscreen/edt-ft5x06.c
@@ -30,29 +30,30 @@
 #include <linux/uaccess.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
-
 #include <linux/gpio.h>
-
 #include <linux/input/edt-ft5x06.h>
 
 #define DRIVER_VERSION "v0.5"
 
-#define WORK_REGISTER_THRESHOLD   0x00
-#define WORK_REGISTER_REPORT_RATE 0x08
-#define WORK_REGISTER_GAIN        0x30
-#define WORK_REGISTER_OFFSET      0x31
-#define WORK_REGISTER_NUM_X       0x33
-#define WORK_REGISTER_NUM_Y       0x34
+#define WORK_REGISTER_THRESHOLD		0x00
+#define WORK_REGISTER_REPORT_RATE	0x08
+#define WORK_REGISTER_GAIN		0x30
+#define WORK_REGISTER_OFFSET		0x31
+#define WORK_REGISTER_NUM_X		0x33
+#define WORK_REGISTER_NUM_Y		0x34
 
-#define WORK_REGISTER_OPMODE      0x3c
-#define FACTORY_REGISTER_OPMODE   0x01
+#define WORK_REGISTER_OPMODE		0x3c
+#define FACTORY_REGISTER_OPMODE		0x01
+
+#define EDT_NAME_LEN			23
+#define EDT_SWITCH_MODE_RETRIES		10
+#define EDT_SWITCH_MODE_DELAY		5 /* msec */
+#define EDT_RAW_DATA_RETRIES		100
+#define EDT_RAW_DATA_DELAY		1 /* msec */
 
 struct edt_ft5x06_i2c_ts_data {
 	struct i2c_client *client;
 	struct input_dev *input;
-	int irq;
-	int irq_pin;
-	int reset_pin;
 	int num_x;
 	int num_y;
 
@@ -62,7 +63,8 @@ struct edt_ft5x06_i2c_ts_data {
 	int gain;
 	int offset;
 	int report_rate;
-	char name[24];
+
+	char name[EDT_NAME_LEN];
 };
 
 static int edt_ft5x06_ts_readwrite(struct i2c_client *client,
@@ -70,7 +72,8 @@ static int edt_ft5x06_ts_readwrite(struct i2c_client *client,
                                    u16 rd_len, u8 *rd_buf)
 {
 	struct i2c_msg wrmsg[2];
-	int i, ret;
+	int i = 0;
+	int ret;
 
 	i = 0;
 	if (wr_len) {
@@ -89,544 +92,594 @@ static int edt_ft5x06_ts_readwrite(struct i2c_client *client,
 	}
 
 	ret = i2c_transfer(client->adapter, wrmsg, i);
-	if (ret < 0) {
-		dev_err(&client->dev, "i2c_transfer failed: %d\n", ret);
+	if (ret < 0)
 		return ret;
-	}
+	if (ret != i)
+		return -EIO;
 
-	return ret;
+	return 0;
 }
 
-
 static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
 {
 	struct edt_ft5x06_i2c_ts_data *tsdata = dev_id;
-	unsigned char touching = 0;
-	unsigned char rdbuf[26], wrbuf[1];
-	int i, have_abs, type, x, y, id, ret;
+	u8 num_touching;
+	u8 cmd = 0xf9;
+	u8 rdbuf[26];
+	bool have_abs = false;
+	int i, type, x, y, id;
+	int error;
 
-	memset(wrbuf, 0, sizeof(wrbuf));
 	memset(rdbuf, 0, sizeof(rdbuf));
 
-	wrbuf[0] = 0xf9;
-
-	mutex_lock(&tsdata->mutex);
-	ret = edt_ft5x06_ts_readwrite(tsdata->client,
-	                              1, wrbuf,
-	                              sizeof(rdbuf), rdbuf);
-	mutex_unlock(&tsdata->mutex);
-	if (ret < 0) {
+	error = edt_ft5x06_ts_readwrite(tsdata->client,
+					sizeof(cmd), &cmd,
+					sizeof(rdbuf), rdbuf);
+	if (error) {
 		dev_err(&tsdata->client->dev,
-		        "Unable to write to i2c touchscreen!\n");
+			"Unable to write to fetch data, error: %d\n", error);
 		goto out;
 	}
 
 	if (rdbuf[0] != 0xaa || rdbuf[1] != 0xaa || rdbuf[2] != 26) {
 		dev_err(&tsdata->client->dev,
-		        "Unexpected header: %02x%02x%02x!\n",
-		        rdbuf[0], rdbuf[1], rdbuf[2]);
+			"Unexpected header: %02x%02x%02x!\n",
+			rdbuf[0], rdbuf[1], rdbuf[2]);
+		goto out;
 	}
 
-	have_abs = false;
-	touching = rdbuf[3];
-	for (i = 0; i < touching; i++) {
-		type = rdbuf[i*4+5] >> 6;
+	num_touching = rdbuf[3];
+	for (i = 0; i < num_touching; i++) {
+		u8 *buf = &rdbuf[i * 4];
+
+		type = buf[5] >> 6;
 		/* ignore Touch Down and Reserved events */
 		if (type == 0x01 || type == 0x03)
 			continue;
 
-		x = ((rdbuf[i*4+5] << 8) | rdbuf[i*4+6]) & 0x0fff;
-		y = ((rdbuf[i*4+7] << 8) | rdbuf[i*4+8]) & 0x0fff;
-		id = (rdbuf[i*4+7] >> 4) & 0x0f;
+		x = ((buf[5] << 8) | rdbuf[6]) & 0x0fff;
+		y = ((buf[7] << 8) | rdbuf[8]) & 0x0fff;
+		id = (buf[7] >> 4) & 0x0f;
 
 		if (!have_abs) {
-			input_report_key(tsdata->input, BTN_TOUCH,    1);
-			input_report_abs(tsdata->input, ABS_PRESSURE, 1);
-			input_report_abs(tsdata->input, ABS_X,        x);
-			input_report_abs(tsdata->input, ABS_Y,        y);
+			input_report_key(tsdata->input, BTN_TOUCH, 1);
+			input_report_abs(tsdata->input, ABS_X, x);
+			input_report_abs(tsdata->input, ABS_Y, y);
 			have_abs = true;
 		}
+
 		input_report_abs(tsdata->input, ABS_MT_POSITION_X, x);
 		input_report_abs(tsdata->input, ABS_MT_POSITION_Y, y);
 		input_report_abs(tsdata->input, ABS_MT_TRACKING_ID, id);
 		input_mt_sync(tsdata->input);
 	}
-	if (!have_abs) {
-		input_report_key(tsdata->input, BTN_TOUCH,    0);
-		input_report_abs(tsdata->input, ABS_PRESSURE, 0);
-	}
+
+	if (!have_abs)
+		input_report_key(tsdata->input, BTN_TOUCH, 0);
+
 	input_sync(tsdata->input);
 
 out:
 	return IRQ_HANDLED;
 }
 
-
 static int edt_ft5x06_i2c_register_write(struct edt_ft5x06_i2c_ts_data *tsdata,
-                                         u8 addr, u8 value)
+					 u8 addr, u8 value)
 {
 	u8 wrbuf[4];
-	int ret;
-
-	wrbuf[0]  = tsdata->factory_mode ? 0xf3 : 0xfc;
-	wrbuf[1]  = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f;
-	wrbuf[2]  = value;
-	wrbuf[3]  = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2];
-
-	disable_irq(tsdata->irq);
-
-	ret = edt_ft5x06_ts_readwrite(tsdata->client,
-	                              4, wrbuf,
-	                              0, NULL);
 
-	enable_irq(tsdata->irq);
+	wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc;
+	wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f;
+	wrbuf[2] = value;
+	wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2];
 
-	return ret;
+	return edt_ft5x06_ts_readwrite(tsdata->client, 4, wrbuf, 0, NULL);
 }
 
 static int edt_ft5x06_i2c_register_read(struct edt_ft5x06_i2c_ts_data *tsdata,
                                         u8 addr)
 {
 	u8 wrbuf[2], rdbuf[2];
-	int ret;
+	int error;
 
-	wrbuf[0]  = tsdata->factory_mode ? 0xf3 : 0xfc;
-	wrbuf[1]  = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f;
+	wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc;
+	wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f;
 	wrbuf[1] |= tsdata->factory_mode ? 0x80 : 0x40;
 
-	disable_irq(tsdata->irq);
-
-	ret = edt_ft5x06_ts_readwrite(tsdata->client,
-	                              2, wrbuf,
-	                              2, rdbuf);
-
-	enable_irq(tsdata->irq);
+	error = edt_ft5x06_ts_readwrite(tsdata->client, 2, wrbuf, 2, rdbuf);
+	if (error)
+		return error;
 
-	if ((wrbuf[0] ^ wrbuf[1] ^ rdbuf[0]) != rdbuf[1])
+	if ((wrbuf[0] ^ wrbuf[1] ^ rdbuf[0]) != rdbuf[1]) {
 		dev_err(&tsdata->client->dev,
-		        "crc error: 0x%02x expected, got 0x%02x\n",
-		        (wrbuf[0] ^ wrbuf[1] ^ rdbuf[0]), rdbuf[1]);
+			"crc error: 0x%02x expected, got 0x%02x\n",
+			wrbuf[0] ^ wrbuf[1] ^ rdbuf[0], rdbuf[1]);
+		return -EIO;
+	}
 
-	return ret < 0 ? ret : rdbuf[0];
+	return rdbuf[0];
 }
 
-static ssize_t edt_ft5x06_i2c_setting_show(struct device *dev,
-                                           struct device_attribute *attr,
-                                           char *buf)
-{
-	struct edt_ft5x06_i2c_ts_data *tsdata = dev_get_drvdata(dev);
-	struct i2c_client *client = tsdata->client;
-	int ret = 0;
-	int *value;
+struct edt_ft5x06_attribute {
+	struct device_attribute dattr;
+	size_t field_offset;
+	u8 limit_low;
+	u8 limit_high;
 	u8 addr;
+};
 
-	switch (attr->attr.name[0]) {
-	case 't':    /* threshold */
-		addr = WORK_REGISTER_THRESHOLD;
-		value = &tsdata->threshold;
-		break;
-	case 'g':    /* gain */
-		addr = WORK_REGISTER_GAIN;
-		value = &tsdata->gain;
-		break;
-	case 'o':    /* offset */
-		addr = WORK_REGISTER_OFFSET;
-		value = &tsdata->offset;
-		break;
-	case 'r':    /* report rate */
-		addr = WORK_REGISTER_REPORT_RATE;
-		value = &tsdata->report_rate;
-		break;
-	default:
-		dev_err(&client->dev,
-		        "unknown attribute for edt_ft5x06_i2c_setting_show: %s\n",
-		        attr->attr.name);
-		return -EINVAL;
+#define EDT_ATTR(_field, _mode, _addr, _limit_low, _limit_high)		\
+	struct edt_ft5x06_attribute edt_ft5x06_attr_##_field = {	\
+		.dattr = __ATTR(_field, _mode,				\
+				edt_ft5x06_i2c_setting_show,		\
+				edt_ft5x06_i2c_setting_store),		\
+		.field_offset =						\
+			offsetof(struct edt_ft5x06_i2c_ts_data, _field),\
+		.limit_low = _limit_low,				\
+		.limit_high = _limit_high,				\
+		.addr = _addr,						\
 	}
 
+static ssize_t edt_ft5x06_i2c_setting_show(struct device *dev,
+                                           struct device_attribute *dattr,
+                                           char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct edt_ft5x06_i2c_ts_data *tsdata = i2c_get_clientdata(client);
+	struct edt_ft5x06_attribute *attr =
+			container_of(dattr, struct edt_ft5x06_attribute, dattr);
+	u8 *field = (u8 *)((char *)tsdata + attr->field_offset);
+	int val;
+	size_t count;
+	int error = 0;
+
 	mutex_lock(&tsdata->mutex);
 
 	if (tsdata->factory_mode) {
-		dev_err(dev,
-		        "setting register not available in factory mode\n");
-		mutex_unlock(&tsdata->mutex);
-		return -EIO;
+		error = -EIO;
+		goto out;
 	}
 
-	ret = edt_ft5x06_i2c_register_read(tsdata, addr);
-	if (ret < 0) {
+	val = edt_ft5x06_i2c_register_read(tsdata, attr->addr);
+	if (val < 0) {
+		error = val;
 		dev_err(&tsdata->client->dev,
-		        "Unable to write to i2c touchscreen!\n");
-		mutex_unlock(&tsdata->mutex);
-		return ret;
+			"Failed to fetch attribute %s, error %d\n",
+			dattr->attr.name, error);
+		goto out;
 	}
-	mutex_unlock(&tsdata->mutex);
 
-	if (ret != *value) {
-		dev_info(&tsdata->client->dev,
-		         "i2c read (%d) and stored value (%d) differ. Huh?\n",
-		         ret, *value);
-		*value = ret;
+	if (val != *field) {
+		dev_warn(&tsdata->client->dev,
+			 "%s: read (%d) and stored value (%d) differ\n",
+			 dattr->attr.name, val, *field);
+		*field = val;
 	}
 
-	return sprintf(buf, "%d\n", ret);
+	count = scnprintf(buf, PAGE_SIZE, "%d\n", val);
+out:
+	mutex_unlock(&tsdata->mutex);
+	return error ?: count;
 }
 
 static ssize_t edt_ft5x06_i2c_setting_store(struct device *dev,
-                                            struct device_attribute *attr,
-                                            const char *buf, size_t count)
+					    struct device_attribute *dattr,
+					    const char *buf, size_t count)
 {
-	struct edt_ft5x06_i2c_ts_data *tsdata = dev_get_drvdata(dev);
-	struct i2c_client *client = tsdata->client;
-	int ret = 0;
-	u8 addr;
+	struct i2c_client *client = to_i2c_client(dev);
+	struct edt_ft5x06_i2c_ts_data *tsdata = i2c_get_clientdata(client);
+	struct edt_ft5x06_attribute *attr =
+			container_of(dattr, struct edt_ft5x06_attribute, dattr);
+	u8 *field = (u8 *)((char *)tsdata + attr->field_offset);
 	unsigned int val;
+	int error;
 
 	mutex_lock(&tsdata->mutex);
 
 	if (tsdata->factory_mode) {
-		dev_err(dev,
-		        "setting register not available in factory mode\n");
-		ret = -EIO;
+		error = -EIO;
 		goto out;
 	}
 
-	if (sscanf(buf, "%u", &val) != 1) {
-		dev_err(dev, "Invalid value for attribute %s\n",
-		        attr->attr.name);
-		ret = -EINVAL;
+	error = kstrtouint(buf, 0, &val);
+	if (error)
 		goto out;
-	}
 
-	switch (attr->attr.name[0]) {
-	case 't':    /* threshold */
-		addr = WORK_REGISTER_THRESHOLD;
-		val = val < 20 ? 20 : val > 80 ? 80 : val;
-		tsdata->threshold = val;
-		break;
-	case 'g':    /* gain */
-		addr = WORK_REGISTER_GAIN;
-		val = val < 0 ? 0 : val > 31 ? 31 : val;
-		tsdata->gain = val;
-		break;
-	case 'o':    /* offset */
-		addr = WORK_REGISTER_OFFSET;
-		val = val < 0 ? 0 : val > 31 ? 31 : val;
-		tsdata->offset = val;
-		break;
-	case 'r':    /* report rate */
-		addr = WORK_REGISTER_REPORT_RATE;
-		val = val < 3 ? 3 : val > 14 ? 14 : val;
-		tsdata->report_rate = val;
-		break;
-	default:
-		dev_err(&client->dev,
-		        "unknown attribute for edt_ft5x06_i2c_setting_show: %s\n",
-		        attr->attr.name);
-		ret = -EINVAL;
+	if (val < attr->limit_low || val > attr->limit_high) {
+		error = -ERANGE;
 		goto out;
 	}
 
-	ret = edt_ft5x06_i2c_register_write(tsdata, addr, val);
-
-	if (ret < 0) {
+	error = edt_ft5x06_i2c_register_write(tsdata, attr->addr, val);
+	if (error) {
 		dev_err(&tsdata->client->dev,
-		        "Unable to write to i2c touchscreen!\n");
+			"Failed to update attribute %s, error: %d\n",
+			dattr->attr.name, error);
 		goto out;
 	}
 
+	*field = val;
+
 out:
 	mutex_unlock(&tsdata->mutex);
-	return ret < 0 ? ret : count;
+	return error ?: count;
 }
 
-
 static ssize_t edt_ft5x06_i2c_mode_show(struct device *dev,
-                                        struct device_attribute *attr,
-                                        char *buf)
+					struct device_attribute *attr,
+					char *buf)
 {
-	struct edt_ft5x06_i2c_ts_data *tsdata = dev_get_drvdata(dev);
-	return sprintf(buf, "%d\n", tsdata->factory_mode ? 1 : 0);
+	struct i2c_client *client = to_i2c_client(dev);
+	struct edt_ft5x06_i2c_ts_data *tsdata = i2c_get_clientdata(client);
+
+	return sprintf(buf, "%d\n", tsdata->factory_mode);
+}
+
+static int edt_ft5x06_i2c_factory_mode(struct edt_ft5x06_i2c_ts_data *tsdata)
+{
+	int retries = EDT_SWITCH_MODE_RETRIES;
+	int ret;
+	int error;
+
+	disable_irq(tsdata->client->irq);
+
+	/* mode register is 0x3c when in the work mode */
+	error = edt_ft5x06_i2c_register_write(tsdata,
+					    WORK_REGISTER_OPMODE, 0x03);
+	if (error) {
+		dev_err(&tsdata->client->dev,
+			"failed to switch to factory mode, error %d\n",
+			error);
+	}
+
+	do {
+		mdelay(EDT_SWITCH_MODE_DELAY);
+		/* mode register is 0x01 when in factory mode */
+		ret = edt_ft5x06_i2c_register_read(tsdata,
+						   FACTORY_REGISTER_OPMODE);
+		if (ret == 0x03)
+			break;
+	} while (--retries > 0);
+
+	if (retries == 0) {
+		dev_err(&tsdata->client->dev,
+			"not in factory mode after %dms.\n",
+			EDT_SWITCH_MODE_RETRIES * EDT_SWITCH_MODE_DELAY);
+		error = -EIO;
+		goto err_out;
+	}
+
+	tsdata->factory_mode = true;
+	return 0;
+
+err_out:
+	enable_irq(tsdata->client->irq);
+	return error;
+}
+
+static int edt_ft5x06_i2c_work_mode(struct edt_ft5x06_i2c_ts_data *tsdata)
+{
+	int retries = EDT_SWITCH_MODE_RETRIES;
+	int ret;
+	int error;
+
+	/* mode register is 0x01 when in the factory mode */
+	error = edt_ft5x06_i2c_register_write(tsdata,
+					      FACTORY_REGISTER_OPMODE, 0x01);
+	if (error) {
+		dev_err(&tsdata->client->dev,
+			"failed to switch to work mode, error: %d\n",
+			error);
+		return error;
+	}
+
+	do {
+		mdelay(EDT_SWITCH_MODE_DELAY);
+		/* mode register is 0x01 when in factory mode */
+		ret = edt_ft5x06_i2c_register_read(tsdata,
+						   WORK_REGISTER_OPMODE);
+		if (ret == 0x01)
+			break;
+	} while (--retries > 0);
+
+	if (retries == 0) {
+		dev_err(&tsdata->client->dev,
+			"not in work mode after %dms.\n",
+			EDT_SWITCH_MODE_RETRIES * EDT_SWITCH_MODE_DELAY);
+		return -EIO;
+	}
+
+	/* restore parameters */
+	edt_ft5x06_i2c_register_write(tsdata,
+				      WORK_REGISTER_THRESHOLD,
+				      tsdata->threshold);
+	edt_ft5x06_i2c_register_write(tsdata,
+				      WORK_REGISTER_GAIN,
+				      tsdata->gain);
+	edt_ft5x06_i2c_register_write(tsdata,
+				      WORK_REGISTER_OFFSET,
+				      tsdata->offset);
+	edt_ft5x06_i2c_register_write(tsdata,
+				      WORK_REGISTER_REPORT_RATE,
+				      tsdata->report_rate);
+
+	tsdata->factory_mode = false;
+	enable_irq(tsdata->client->irq);
+
+	return 0;
 }
 
 static ssize_t edt_ft5x06_i2c_mode_store(struct device *dev,
                                          struct device_attribute *attr,
                                          const char *buf, size_t count)
 {
-	struct edt_ft5x06_i2c_ts_data *tsdata = dev_get_drvdata(dev);
-	int i, ret = 0;
+	struct i2c_client *client = to_i2c_client(dev);
+	struct edt_ft5x06_i2c_ts_data *tsdata = i2c_get_clientdata(client);
 	unsigned int mode;
+	int error;
 
-	if (sscanf(buf, "%u", &mode) != 1 || (mode | 1) != 1) {
-		dev_err(dev, "Invalid value for operation mode\n");
-		return -EINVAL;
-	}
+	error = kstrtouint(buf, 0, &mode);
+	if (error)
+		return error;
 
-	/* no change, return without doing anything */
-	if (mode == tsdata->factory_mode)
-		return count;
+	if (mode > 1)
+		return -ERANGE;
 
 	mutex_lock(&tsdata->mutex);
-	if (!tsdata->factory_mode) { /* switch to factory mode */
-		disable_irq(tsdata->irq);
-		/* mode register is 0x3c when in the work mode */
-		ret = edt_ft5x06_i2c_register_write(tsdata,
-		                                    WORK_REGISTER_OPMODE, 0x03);
-		if (ret < 0) {
-			dev_err(dev, "failed to switch to factory mode (%d)\n",
-			        ret);
-		} else {
-			tsdata->factory_mode = true;
-			for (i = 0; i < 10; i++) {
-				mdelay(5);
-				/* mode register is 0x01 when in factory mode */
-				ret = edt_ft5x06_i2c_register_read(tsdata, FACTORY_REGISTER_OPMODE);
-				if (ret == 0x03)
-					break;
-			}
-			if (i == 10)
-				dev_err(dev,
-				        "not in factory mode after %dms.\n",
-				        i*5);
-		}
-	} else {  /* switch to work mode */
-		/* mode register is 0x01 when in the factory mode */
-		ret = edt_ft5x06_i2c_register_write(tsdata,
-		                                    FACTORY_REGISTER_OPMODE,
-		                                    0x01);
-		if (ret < 0) {
-			dev_err(dev, "failed to switch to work mode (%d)\n",
-			        ret);
-		} else {
-			tsdata->factory_mode = false;
-			for (i = 0; i < 10; i++) {
-				mdelay(5);
-				/* mode register is 0x01 when in factory mode */
-				ret = edt_ft5x06_i2c_register_read(tsdata, WORK_REGISTER_OPMODE);
-				if (ret == 0x01)
-					break;
-			}
-			if (i == 10)
-				dev_err(dev, "not in work mode after %dms.\n",
-				        i*5);
-
-			/* restore parameters */
-			edt_ft5x06_i2c_register_write(tsdata,
-			                              WORK_REGISTER_THRESHOLD,
-			                              tsdata->threshold);
-			edt_ft5x06_i2c_register_write(tsdata,
-			                              WORK_REGISTER_GAIN,
-			                              tsdata->gain);
-			edt_ft5x06_i2c_register_write(tsdata,
-			                              WORK_REGISTER_OFFSET,
-			                              tsdata->offset);
-			edt_ft5x06_i2c_register_write(tsdata,
-			                              WORK_REGISTER_REPORT_RATE,
-			                              tsdata->report_rate);
-
-			enable_irq(tsdata->irq);
-		}
+
+	if (mode != tsdata->factory_mode) {
+		error = mode ? edt_ft5x06_i2c_factory_mode(tsdata) :
+			       edt_ft5x06_i2c_work_mode(tsdata);
 	}
 
 	mutex_unlock(&tsdata->mutex);
-	return count;
+	return error ?: count;
 }
 
-
 static ssize_t edt_ft5x06_i2c_raw_data_show(struct device *dev,
                                             struct device_attribute *attr,
                                             char *buf)
 {
-	struct edt_ft5x06_i2c_ts_data *tsdata = dev_get_drvdata(dev);
+	struct i2c_client *client = to_i2c_client(dev);
+	struct edt_ft5x06_i2c_ts_data *tsdata = i2c_get_clientdata(client);
+	int retries  = EDT_RAW_DATA_RETRIES;
+	size_t count = 0;
 	int i, ret;
-	char *ptr, wrbuf[3];
+	int error;
+	char wrbuf[3];
+
+	/* Make sure we have enough space */
+	if (tsdata->num_x * tsdata->num_y * 2 >= PAGE_SIZE)
+		return -ENOBUFS;
+
+	mutex_lock(&tsdata->mutex);
 
 	if (!tsdata->factory_mode) {
-		dev_err(dev, "raw data not available in work mode\n");
-		return -EIO;
+		error = -EIO;
+		goto out;
 	}
 
-	mutex_lock(&tsdata->mutex);
-	ret = edt_ft5x06_i2c_register_write(tsdata, 0x08, 0x01);
-	for (i = 0; i < 100; i++) {
+	error = edt_ft5x06_i2c_register_write(tsdata, 0x08, 0x01);
+	if (error) {
+		dev_dbg(dev, "failed to write 0x08 register, error %d\n",
+			error);
+		goto out;
+	}
+
+	do {
+		msleep(EDT_RAW_DATA_DELAY);
 		ret = edt_ft5x06_i2c_register_read(tsdata, 0x08);
 		if (ret < 1)
 			break;
-		udelay(1000);
+	} while (--retries > 0);
+
+	if (ret < 0) {
+		error = ret;
+		dev_dbg(dev, "failed to read 0x08 register, error %d\n", error);
+		goto out;
 	}
 
-	if (i == 100 || ret < 0) {
-		dev_err(dev, "waiting time exceeded or error: %d\n", ret);
-		mutex_unlock(&tsdata->mutex);
-		return ret < 0 ? ret : -ETIMEDOUT;
+	if (retries == 0) {
+		dev_dbg(dev, "timed out waiting for register to settle\n");
+		error = -ETIMEDOUT;
+		goto out;
 	}
 
-	ptr = buf;
 	wrbuf[0] = 0xf5;
 	wrbuf[1] = 0x0e;
-	for (i = 0; i <= tsdata->num_x; i++) {
+	for (i = 0; i < tsdata->num_x; i++) {
 		wrbuf[2] = i;
-		ret = edt_ft5x06_ts_readwrite(tsdata->client,
-		                               3, wrbuf,
-		                               tsdata->num_y * 2, ptr);
-		if (ret < 0) {
-			mutex_unlock(&tsdata->mutex);
-			return ret;
-		}
-
-		ptr += tsdata->num_y * 2;
+		error = edt_ft5x06_ts_readwrite(tsdata->client,
+						sizeof(wrbuf), wrbuf,
+						tsdata->num_y * 2,
+						&buf[count]);
+		if (error)
+			goto out;
+
+		count += tsdata->num_y * 2;
 	}
 
+out:
 	mutex_unlock(&tsdata->mutex);
-	return ptr - buf;
+	return error ?: count;
 }
 
+static EDT_ATTR(gain, S_IWUSR | S_IRUGO, WORK_REGISTER_GAIN, 0, 31);
+static EDT_ATTR(offset, S_IWUSR | S_IRUGO, WORK_REGISTER_OFFSET, 0, 31);
+static EDT_ATTR(threshold, S_IWUSR | S_IRUGO,
+		WORK_REGISTER_THRESHOLD, 20, 80);
+static EDT_ATTR(report_rate, S_IWUSR | S_IRUGO,
+		WORK_REGISTER_REPORT_RATE, 3, 14);
 
-static DEVICE_ATTR(gain,      0664,
-                   edt_ft5x06_i2c_setting_show, edt_ft5x06_i2c_setting_store);
-static DEVICE_ATTR(offset,    0664,
-                   edt_ft5x06_i2c_setting_show, edt_ft5x06_i2c_setting_store);
-static DEVICE_ATTR(threshold, 0664,
-                   edt_ft5x06_i2c_setting_show, edt_ft5x06_i2c_setting_store);
-static DEVICE_ATTR(report_rate, 0664,
-                   edt_ft5x06_i2c_setting_show, edt_ft5x06_i2c_setting_store);
-static DEVICE_ATTR(mode,      0664,
+static DEVICE_ATTR(mode, S_IWUSR | S_IRUGO,
                    edt_ft5x06_i2c_mode_show, edt_ft5x06_i2c_mode_store);
-static DEVICE_ATTR(raw_data,  0444,
-                   edt_ft5x06_i2c_raw_data_show, NULL);
+static DEVICE_ATTR(raw_data, S_IRUGO, edt_ft5x06_i2c_raw_data_show, NULL);
 
 static struct attribute *edt_ft5x06_i2c_attrs[] = {
-	&dev_attr_gain.attr,
-	&dev_attr_offset.attr,
-	&dev_attr_threshold.attr,
-	&dev_attr_report_rate.attr,
+	&edt_ft5x06_attr_gain.dattr.attr,
+	&edt_ft5x06_attr_offset.dattr.attr,
+	&edt_ft5x06_attr_threshold.dattr.attr,
+	&edt_ft5x06_attr_report_rate.dattr.attr,
 	&dev_attr_mode.attr,
 	&dev_attr_raw_data.attr,
 	NULL
 };
 
+static umode_t edt_ft5x06_i2c_attr_is_visible(struct kobject *kobj,
+					      struct attribute *attr, int n)
+{
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct i2c_client *client = to_i2c_client(dev);
+	struct edt_ft5x06_i2c_ts_data *tsdata = i2c_get_clientdata(client);
+	umode_t mode = attr->mode;
+
+	if (attr == &dev_attr_raw_data.attr) {
+		if (!tsdata->factory_mode)
+			mode = 0;
+
+	} else if (attr == &edt_ft5x06_attr_gain.dattr.attr ||
+		   attr == &edt_ft5x06_attr_offset.dattr.attr ||
+		   attr == &edt_ft5x06_attr_threshold.dattr.attr ||
+		   attr == &edt_ft5x06_attr_report_rate.dattr.attr) {
+
+		if (tsdata->factory_mode)
+			mode = 0;
+	}
+
+	return mode;
+}
+
 static const struct attribute_group edt_ft5x06_i2c_attr_group = {
 	.attrs = edt_ft5x06_i2c_attrs,
+	.is_visible = edt_ft5x06_i2c_attr_is_visible,
 };
 
-static int edt_ft5x06_i2c_ts_probe(struct i2c_client *client,
-                                   const struct i2c_device_id *id)
+static int __devinit edt_ft5x06_i2c_ts_reset(struct i2c_client *client,
+					     int reset_pin)
+{
+	int error;
+
+	if (gpio_is_valid(reset_pin)) {
+		/* this pulls reset down, enabling the low active reset */
+		error = gpio_request_one(reset_pin, GPIOF_OUT_INIT_LOW,
+					 "edt-ft5x06 reset");
+		if (error) {
+			dev_err(&client->dev,
+				"Failed to request GPIO %d as reset pin, error %d\n",
+				reset_pin, error);
+			return error;
+		}
+
+		mdelay(50);
+		gpio_set_value(reset_pin, 1);
+		mdelay(100);
+
+		gpio_free(reset_pin);
+	}
+
+	return 0;
+}
+
+static int __devinit edt_ft5x06_i2c_ts_identify(struct i2c_client *client,
+						char *model_name,
+						char *fw_version)
+{
+	u8 rdbuf[EDT_NAME_LEN];
+	char *p;
+	int error;
+
+	error = edt_ft5x06_ts_readwrite(client,
+					1, "\xbb", EDT_NAME_LEN - 1, rdbuf);
+	if (error)
+		return error;
+
+	/* remove last '$' end marker */
+	rdbuf[EDT_NAME_LEN - 1] = '\0';
+	if (rdbuf[EDT_NAME_LEN - 2] == '$')
+		rdbuf[EDT_NAME_LEN - 2] = '\0';
+
+	/* look for Model/Version separator */
+	p = strchr(rdbuf, '*');
+	if (p)
+		*p++ = '\0';
+
+	strlcpy(model_name, rdbuf + 1, EDT_NAME_LEN);
+	strlcpy(fw_version, p ? p : "", EDT_NAME_LEN);
+
+	return 0;
+}
+
+static int __devinit edt_ft5x06_i2c_ts_probe(struct i2c_client *client,
+					     const struct i2c_device_id *id)
 {
 
 	const struct edt_ft5x06_platform_data *pdata = client->dev.platform_data;
 	struct edt_ft5x06_i2c_ts_data *tsdata;
 	struct input_dev *input;
 	int error;
-	u8 rdbuf[23];
-	char *model_name, *fw_version;
+	char fw_version[EDT_NAME_LEN];
 
 	dev_dbg(&client->dev, "probing for EDT FT5x06 I2C\n");
 
 	if (!pdata) {
 		dev_err(&client->dev, "no platform data?\n");
-		return -ENODEV;
-	}
-
-	tsdata = kzalloc(sizeof(*tsdata), GFP_KERNEL);
-	if (!tsdata) {
-		dev_err(&client->dev, "failed to allocate driver data!\n");
-		dev_set_drvdata(&client->dev, NULL);
-		return -ENOMEM;
+		return -EINVAL;
 	}
 
-	dev_set_drvdata(&client->dev, tsdata);
-	tsdata->client = client;
-
-	tsdata->reset_pin = pdata->reset_pin;
-	mutex_init(&tsdata->mutex);
+	error = edt_ft5x06_i2c_ts_reset(client, pdata->reset_pin);
+	if (error)
+		return error;
 
-	if (tsdata->reset_pin >= 0) {
-		error = gpio_request(tsdata->reset_pin, "edt-ft5x06 reset");
-		if (error < 0) {
+	if (gpio_is_valid(pdata->irq_pin)) {
+		error = gpio_request_one(pdata->irq_pin,
+					 GPIOF_IN, "edt-ft5x06 irq");
+		if (error) {
 			dev_err(&client->dev,
-			        "Failed to request GPIO %d as reset pin, error %d\n",
-			         tsdata->reset_pin, error);
-			goto err_free_tsdata;
+				"Failed to request GPIO %d, error %d\n",
+				pdata->irq_pin, error);
+			return error;
 		}
-
-		/* this pulls reset down, enabling the low active reset */
-		if (gpio_direction_output(tsdata->reset_pin, 0) < 0) {
-			dev_info(&client->dev, "switching to output failed\n");
-			goto err_free_reset_pin;
-		}
-	}
-
-	/* request IRQ pin */
-	tsdata->irq_pin = pdata->irq_pin;
-	tsdata->irq = gpio_to_irq(tsdata->irq_pin);
-
-	error = gpio_request(tsdata->irq_pin, "edt-ft5x06 irq");
-	if (error < 0) {
-		dev_err(&client->dev,
-		        "Failed to request GPIO %d for IRQ %d, error %d\n",
-		        tsdata->irq_pin, tsdata->irq, error);
-		goto err_free_reset_pin;
 	}
-	gpio_direction_input(tsdata->irq_pin);
 
-	if (tsdata->reset_pin >= 0) {
-		/* release reset */
-		mdelay(50);
-		gpio_set_value(tsdata->reset_pin, 1);
-		mdelay(100);
+	tsdata = kzalloc(sizeof(*tsdata), GFP_KERNEL);
+	input = input_allocate_device();
+	if (!tsdata || !input) {
+		dev_err(&client->dev, "failed to allocate driver data.\n");
+		error = -ENOMEM;
+		goto err_free_mem;
 	}
 
-	mutex_lock(&tsdata->mutex);
-
+	mutex_init(&tsdata->mutex);
+	tsdata->client = client;
+	tsdata->input = input;
 	tsdata->factory_mode = false;
 
-	if (edt_ft5x06_ts_readwrite(client, 1, "\xbb", 22, rdbuf) < 0) {
-		dev_err(&client->dev, "probing failed\n");
-		error = -ENODEV;
-		goto err_free_irq_pin;
+	error = edt_ft5x06_i2c_ts_identify(client, tsdata->name, fw_version);
+	if (error) {
+		dev_err(&client->dev, "touchscreen probe failed\n");
+		goto err_free_mem;
 	}
 
 	tsdata->threshold = edt_ft5x06_i2c_register_read(tsdata,
-	                                                 WORK_REGISTER_THRESHOLD);
-	tsdata->gain      = edt_ft5x06_i2c_register_read(tsdata,
-	                                                 WORK_REGISTER_GAIN);
-	tsdata->offset    = edt_ft5x06_i2c_register_read(tsdata,
-	                                                 WORK_REGISTER_OFFSET);
+						WORK_REGISTER_THRESHOLD);
+	tsdata->gain = edt_ft5x06_i2c_register_read(tsdata,
+						WORK_REGISTER_GAIN);
+	tsdata->offset = edt_ft5x06_i2c_register_read(tsdata,
+						WORK_REGISTER_OFFSET);
 	tsdata->report_rate = edt_ft5x06_i2c_register_read(tsdata,
-	                                                 WORK_REGISTER_REPORT_RATE);
-	tsdata->num_x     = edt_ft5x06_i2c_register_read(tsdata,
-	                                                 WORK_REGISTER_NUM_X);
-	tsdata->num_y     = edt_ft5x06_i2c_register_read(tsdata,
-	                                                 WORK_REGISTER_NUM_Y);
+						WORK_REGISTER_REPORT_RATE);
+	tsdata->num_x = edt_ft5x06_i2c_register_read(tsdata,
+						WORK_REGISTER_NUM_X);
+	tsdata->num_y = edt_ft5x06_i2c_register_read(tsdata,
+						WORK_REGISTER_NUM_Y);
 
-	mutex_unlock(&tsdata->mutex);
+	dev_dbg(&client->dev,
+		"Model \"%s\", Rev. \"%s\", %dx%d sensors\n",
+		tsdata->name, fw_version, tsdata->num_x, tsdata->num_y);
 
-	/* remove last '$' end marker */
-	rdbuf[22] = '\0';
-	if (rdbuf[21] == '$')
-		rdbuf[21] = '\0';
-
-	model_name = rdbuf + 1;
-	/* look for Model/Version separator */
-	fw_version = strchr(rdbuf, '*');
-
-	if (fw_version) {
-		fw_version[0] = '\0';
-		fw_version++;
-		dev_info(&client->dev,
-		         "Model \"%s\", Rev. \"%s\", %dx%d sensors\n",
-		         model_name, fw_version, tsdata->num_x, tsdata->num_y);
-	} else {
-		dev_info(&client->dev, "Product ID \"%s\"\n", model_name);
-	}
-	strncpy (tsdata->name, model_name, sizeof (tsdata->name) - 1);
-
-	input = input_allocate_device();
-	if (!input) {
-		dev_err(&client->dev, "failed to allocate input device!\n");
-		error = -ENOMEM;
-		goto err_free_irq_pin;
-	}
+	input->name = tsdata->name;
+	input->id.bustype = BUS_I2C;
+	input->dev.parent = &client->dev;
 
 	__set_bit(EV_SYN, input->evbit);
 	__set_bit(EV_KEY, input->evbit);
@@ -634,31 +687,20 @@ static int edt_ft5x06_i2c_ts_probe(struct i2c_client *client,
 	__set_bit(BTN_TOUCH, input->keybit);
 	input_set_abs_params(input, ABS_X, 0, tsdata->num_x * 64 - 1, 0, 0);
 	input_set_abs_params(input, ABS_Y, 0, tsdata->num_y * 64 - 1, 0, 0);
-	input_set_abs_params(input, ABS_PRESSURE, 0, 1, 0, 0);
 	input_set_abs_params(input, ABS_MT_POSITION_X,
 	                     0, tsdata->num_x * 64 - 1, 0, 0);
 	input_set_abs_params(input, ABS_MT_POSITION_Y,
 	                     0, tsdata->num_y * 64 - 1, 0, 0);
 	input_set_abs_params(input, ABS_MT_TRACKING_ID, 0, 15, 0, 0);
 
-	input->name = tsdata->name;
-	input->id.bustype = BUS_I2C;
-	input->dev.parent = &client->dev;
-
 	input_set_drvdata(input, tsdata);
 
-	tsdata->input = input;
-
-	error = input_register_device(input);
-	if (error)
-		goto err_free_input_device;
-
-	if (request_threaded_irq(tsdata->irq, NULL, edt_ft5x06_ts_isr,
-	                         IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
-	                         client->name, tsdata)) {
+	error = request_threaded_irq(client->irq, NULL, edt_ft5x06_ts_isr,
+				     IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+				     client->name, tsdata);
+	if (error) {
 		dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
-		error = -ENOMEM;
-		goto err_unregister_device;
+		goto err_free_mem;
 	}
 
 	error = sysfs_create_group(&client->dev.kobj,
@@ -666,43 +708,44 @@ static int edt_ft5x06_i2c_ts_probe(struct i2c_client *client,
 	if (error)
 		goto err_free_irq;
 
+	error = input_register_device(input);
+	if (error)
+		goto err_remove_attrs;
+
+	i2c_set_clientdata(client, tsdata);
 	device_init_wakeup(&client->dev, 1);
 
 	dev_dbg(&tsdata->client->dev,
-	        "EDT FT5x06 initialized: IRQ pin %d, Reset pin %d.\n",
-	        tsdata->irq_pin, tsdata->reset_pin);
+		"EDT FT5x06 initialized: IRQ pin %d, Reset pin %d.\n",
+		pdata->irq_pin, pdata->reset_pin);
 
 	return 0;
 
+err_remove_attrs:
+	sysfs_remove_group(&client->dev.kobj, &edt_ft5x06_i2c_attr_group);
 err_free_irq:
-	free_irq(tsdata->irq, tsdata);
-err_unregister_device:
-	input_unregister_device(input);
-	input = NULL;
-err_free_input_device:
-	if (input)
-		input_free_device(input);
-err_free_irq_pin:
-	gpio_free(tsdata->irq_pin);
-err_free_reset_pin:
-	if (tsdata->reset_pin >= 0)
-		gpio_free(tsdata->reset_pin);
-err_free_tsdata:
+	free_irq(client->irq, tsdata);
+err_free_mem:
+	input_free_device(input);
 	kfree(tsdata);
+
+	if (gpio_is_valid(pdata->irq_pin))
+		gpio_free(pdata->irq_pin);
+
 	return error;
 }
 
-static int edt_ft5x06_i2c_ts_remove(struct i2c_client *client)
+static int __devexit edt_ft5x06_i2c_ts_remove(struct i2c_client *client)
 {
-	struct edt_ft5x06_i2c_ts_data *tsdata = dev_get_drvdata(&client->dev);
+	const struct edt_ft5x06_platform_data *pdata = client->dev.platform_data;
+	struct edt_ft5x06_i2c_ts_data *tsdata = i2c_get_clientdata(client);
 
 	sysfs_remove_group(&client->dev.kobj, &edt_ft5x06_i2c_attr_group);
 
-	free_irq(tsdata->irq, tsdata);
+	free_irq(client->irq, tsdata);
 	input_unregister_device(tsdata->input);
-	gpio_free(tsdata->irq_pin);
-	if (tsdata->reset_pin >= 0)
-		gpio_free(tsdata->reset_pin);
+	if (gpio_is_valid(pdata->irq_pin))
+		gpio_free(pdata->irq_pin);
 	kfree(tsdata);
 
 	return 0;
@@ -711,20 +754,20 @@ static int edt_ft5x06_i2c_ts_remove(struct i2c_client *client)
 #ifdef CONFIG_PM_SLEEP
 static int edt_ft5x06_i2c_ts_suspend(struct device *dev)
 {
-	struct edt_ft5x06_i2c_ts_data *tsdata = dev_get_drvdata(dev);
+	struct i2c_client *client = to_i2c_client(dev);
 
 	if (device_may_wakeup(dev))
-		enable_irq_wake(tsdata->irq);
+		enable_irq_wake(client->irq);
 
 	return 0;
 }
 
 static int edt_ft5x06_i2c_ts_resume(struct device *dev)
 {
-	struct edt_ft5x06_i2c_ts_data *tsdata = dev_get_drvdata(dev);
+	struct i2c_client *client = to_i2c_client(dev);
 
 	if (device_may_wakeup(dev))
-		disable_irq_wake(tsdata->irq);
+		disable_irq_wake(client->irq);
 
 	return 0;
 }
@@ -747,20 +790,10 @@ static struct i2c_driver edt_ft5x06_i2c_ts_driver = {
 	},
 	.id_table = edt_ft5x06_i2c_ts_id,
 	.probe    = edt_ft5x06_i2c_ts_probe,
-	.remove   = edt_ft5x06_i2c_ts_remove,
+	.remove   = __devexit_p(edt_ft5x06_i2c_ts_remove),
 };
 
-static int __init edt_ft5x06_i2c_ts_init(void)
-{
-	return i2c_add_driver(&edt_ft5x06_i2c_ts_driver);
-}
-module_init(edt_ft5x06_i2c_ts_init);
-
-static void __exit edt_ft5x06_i2c_ts_exit(void)
-{
-	i2c_del_driver(&edt_ft5x06_i2c_ts_driver);
-}
-module_exit(edt_ft5x06_i2c_ts_exit);
+module_i2c_driver(edt_ft5x06_i2c_ts_driver);
 
 MODULE_AUTHOR("Simon Budig <simon.budig@xxxxxxxxxxxxxxxxx>");
 MODULE_DESCRIPTION("EDT FT5x06 I2C Touchscreen Driver");
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux