Custom Search
|
|
Re: [PATCH v7] Touchscreen driver for FT5x06 based EDT displays | |
| [Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] | |
Hi Simon,
> This is a driver for the EDT "Polytouch" family of touch controllers
> based on the FocalTech FT5x06 line of chips.
>
> Signed-off-by: Simon Budig <simon.budig@xxxxxxxxxxxxxxxxx>
> ---
Thank you for the thorough set of changes. Some minor comments below.
> +#define MIN(a, b) (((a) < (b)) ? (a) : (b))
We have min() defined in kernel.h.
> +static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
> +{
> + struct edt_ft5x06_ts_data *tsdata = dev_id;
> + struct device *dev = &tsdata->client->dev;
> + u8 cmd = 0xf9;
> + u8 rdbuf[26];
> + u8 crc;
> + int i, type, x, y, id;
> + int error;
> +
> + memset(rdbuf, 0, sizeof(rdbuf));
> +
> + error = edt_ft5x06_ts_readwrite(tsdata->client,
> + sizeof(cmd), &cmd,
> + sizeof(rdbuf), rdbuf);
> + if (error) {
> + dev_err_ratelimited(dev, "Unable to fetch data, error: %d\n",
> + error);
> + goto out;
> + }
> +
> + if (rdbuf[0] != 0xaa || rdbuf[1] != 0xaa || rdbuf[2] != 26) {
> + dev_err_ratelimited(dev, "Unexpected header: %02x%02x%02x!\n",
> + rdbuf[0], rdbuf[1], rdbuf[2]);
> + goto out;
> + }
> +
> + crc = 0;
> + for (i = 0; i < 25; i++)
> + crc ^= rdbuf[i];
> + if (crc != rdbuf[25]) {
A separate function for the crc check would be nice.
> + dev_err_ratelimited(dev,
> + "crc error: 0x%02x expected, got 0x%02x\n",
> + crc, rdbuf[25]);
It is alright to let the string exceed 80 characters, even by checkpatch standards.
> + goto out;
> + }
> +
> + for (i = 0; i < MAX_SUPPORT_POINTS; i++) {
> + u8 *buf = &rdbuf[i * 4];
> + bool down;
> +
> + type = buf[5] >> 6;
> + /* ignore Reserved events */
> + if (type == TOUCH_EVENT_RESERVED)
> + continue;
> +
> + x = ((buf[5] << 8) | buf[6]) & 0x0fff;
> + y = ((buf[7] << 8) | buf[8]) & 0x0fff;
> + id = (buf[7] >> 4) & 0x0f;
> + down = (type != TOUCH_EVENT_UP);
> +
> + input_mt_slot(tsdata->input, id);
> + input_mt_report_slot_state(tsdata->input, MT_TOOL_FINGER, down);
> +
> + if (!down)
> + continue;
> +
> + input_report_abs(tsdata->input, ABS_MT_POSITION_X, x);
> + input_report_abs(tsdata->input, ABS_MT_POSITION_Y, y);
> + }
> +
> + input_mt_report_pointer_emulation(tsdata->input, true);
> + input_sync(tsdata->input);
> +
> +out:
> + return IRQ_HANDLED;
> +}
> +ssize_t edt_ft5x06_debugfs_raw_data_read(struct file *file,
> + char *buf,
> + size_t count,
> + loff_t *off)
> +{
> + struct edt_ft5x06_ts_data *tsdata = file->private_data;
> + int retries = EDT_RAW_DATA_RETRIES;
> + int ret = 0, error;
> + int colbytes;
> + int pos, endpos, start_off;
> + char wrbuf[3];
> + char *rdbuf;
> +
> + colbytes = tsdata->num_y * 2;
> +
> + if (*off < 0 || *off >= tsdata->num_x * colbytes)
> + return 0;
> +
> + rdbuf = kmalloc(colbytes, GFP_KERNEL);
> + if (!rdbuf)
> + return -ENOMEM;
> +
> + mutex_lock(&tsdata->mutex);
> +
> + if (!tsdata->factory_mode) {
> + error = -EIO;
> + goto out;
> + }
> +
> + error = edt_ft5x06_register_write(tsdata, 0x08, 0x01);
> + if (error) {
> + dev_dbg(&tsdata->client->dev,
> + "failed to write 0x08 register, error %d\n",
> + error);
> + goto out;
> + }
> +
> + do {
> + msleep(EDT_RAW_DATA_DELAY);
> + ret = edt_ft5x06_register_read(tsdata, 0x08);
> + if (ret < 1)
> + break;
> + } while (--retries > 0);
> +
> + if (ret < 0) {
> + error = ret;
> + dev_dbg(&tsdata->client->dev,
> + "failed to read 0x08 register, error %d\n",
> + error);
> + goto out;
> + }
> +
> + if (retries == 0) {
> + dev_dbg(&tsdata->client->dev,
> + "timed out waiting for register to settle\n");
> + error = -ETIMEDOUT;
> + goto out;
> + }
> +
> + endpos = MIN(*off + count, colbytes * tsdata->num_x);
> +
> + wrbuf[0] = 0xf5;
> + wrbuf[1] = 0x0e;
> + for (pos = *off; pos < endpos; pos += colbytes) {
> + wrbuf[2] = pos / colbytes; /* column index */
> + error = edt_ft5x06_ts_readwrite(tsdata->client,
> + sizeof(wrbuf), wrbuf,
> + colbytes, rdbuf);
> + if (error)
> + goto out;
> +
> + start_off = pos % colbytes;
> + error = copy_to_user(buf + pos - *off, rdbuf + start_off,
> + MIN(colbytes - start_off, endpos - pos));
Quite a few variables in play here... it looks correct, but a)
breaking out parts of this function would not hurt, and b) I bet the
column-by column copy-to-user algorithm could be slightly less
involved.
> + if (error)
> + goto out;
> +
> + pos -= start_off;
> + }
> +
> + ret = endpos - *off;
> + if (!error)
> + *off += ret;
> +out:
> + mutex_unlock(&tsdata->mutex);
> + kfree(rdbuf);
> + return error ?: ret;
> +};
> +static int __devinit edt_ft5x06_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_ts_data *tsdata;
> + struct input_dev *input;
> + int error;
> + 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 -EINVAL;
> + }
> +
> + error = edt_ft5x06_ts_reset(client, pdata->reset_pin);
> + if (error)
> + return error;
> +
> + 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, error %d\n",
> + pdata->irq_pin, error);
> + return error;
> + }
> + }
> +
> + 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_init(&tsdata->mutex);
> + tsdata->client = client;
> + tsdata->input = input;
> + tsdata->factory_mode = false;
> +
> + error = edt_ft5x06_ts_identify(client, tsdata->name, fw_version);
> + if (error) {
> + dev_err(&client->dev, "touchscreen probe failed\n");
> + goto err_free_mem;
> + }
> +
> + if (pdata->use_parameters) {
> + /* pick up defaults from the platform data */
> + if (pdata->threshold >= edt_ft5x06_attr_threshold.limit_low &&
> + pdata->threshold <= edt_ft5x06_attr_threshold.limit_high)
> + edt_ft5x06_register_write(tsdata,
> + WORK_REGISTER_THRESHOLD,
> + pdata->threshold);
> + if (pdata->gain >= edt_ft5x06_attr_gain.limit_low &&
> + pdata->gain <= edt_ft5x06_attr_gain.limit_high)
> + edt_ft5x06_register_write(tsdata,
> + WORK_REGISTER_GAIN,
> + pdata->gain);
> + if (pdata->offset >= edt_ft5x06_attr_offset.limit_low &&
> + pdata->offset <= edt_ft5x06_attr_offset.limit_high)
> + edt_ft5x06_register_write(tsdata,
> + WORK_REGISTER_OFFSET,
> + pdata->offset);
> + if (pdata->report_rate
> + >= edt_ft5x06_attr_report_rate.limit_low &&
> + pdata->report_rate
> + <= edt_ft5x06_attr_report_rate.limit_high)
> + edt_ft5x06_register_write(tsdata,
> + WORK_REGISTER_REPORT_RATE,
> + pdata->report_rate);
> + }
Putting this in a function, perhaps?
> +
> + tsdata->threshold = edt_ft5x06_register_read(tsdata,
> + WORK_REGISTER_THRESHOLD);
> + tsdata->gain = edt_ft5x06_register_read(tsdata, WORK_REGISTER_GAIN);
> + tsdata->offset = edt_ft5x06_register_read(tsdata, WORK_REGISTER_OFFSET);
> + tsdata->report_rate = edt_ft5x06_register_read(tsdata,
> + WORK_REGISTER_REPORT_RATE);
> + tsdata->num_x = edt_ft5x06_register_read(tsdata, WORK_REGISTER_NUM_X);
> + tsdata->num_y = edt_ft5x06_register_read(tsdata, WORK_REGISTER_NUM_Y);
And this?
> +
> + dev_dbg(&client->dev,
> + "Model \"%s\", Rev. \"%s\", %dx%d sensors\n",
> + tsdata->name, fw_version, tsdata->num_x, tsdata->num_y);
> +
> + 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);
> + __set_bit(EV_ABS, input->evbit);
> + __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_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);
> + error = input_mt_init_slots(input, MAX_SUPPORT_POINTS);
> + if (error) {
> + dev_err(&client->dev, "Unable to init MT slots.\n");
> + goto err_free_mem;
> + }
> +
> + input_set_drvdata(input, tsdata);
> + i2c_set_clientdata(client, 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");
> + goto err_free_mem;
> + }
> +
> + error = sysfs_create_group(&client->dev.kobj, &edt_ft5x06_attr_group);
> + if (error)
> + goto err_free_irq;
> +
> + error = input_register_device(input);
> + if (error)
> + goto err_remove_attrs;
> +
> +#if defined(CONFIG_DEBUG_FS)
> + tsdata->debug_dir = debugfs_create_dir(dev_driver_string(&client->dev),
> + NULL);
> +
> + if (tsdata->debug_dir) {
> + debugfs_create_u16("num_x", S_IRUSR, tsdata->debug_dir,
> + &tsdata->num_x);
> + debugfs_create_u16("num_y", S_IRUSR, tsdata->debug_dir,
> + &tsdata->num_y);
> + debugfs_create_file("mode", S_IRUSR | S_IWUSR,
> + tsdata->debug_dir, tsdata,
> + &debugfs_mode_fops);
> + debugfs_create_file("raw_data", S_IRUSR,
> + tsdata->debug_dir, tsdata,
> + &debugfs_raw_data_fops);
> + }
> +#endif
And this?
> +
> + device_init_wakeup(&client->dev, 1);
> +
> + dev_dbg(&tsdata->client->dev,
> + "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_attr_group);
> +err_free_irq:
> + 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;
> +}
Thanks,
Henrik
--
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
![]() |