brcm2708: update to latest patches from RPi Foundation
[openwrt/staging/chunkeey.git] / target / linux / brcm2708 / patches-4.19 / 950-0364-Input-ili210x-fetch-touchscreen-geometry-from-DT.patch
diff --git a/target/linux/brcm2708/patches-4.19/950-0364-Input-ili210x-fetch-touchscreen-geometry-from-DT.patch b/target/linux/brcm2708/patches-4.19/950-0364-Input-ili210x-fetch-touchscreen-geometry-from-DT.patch
new file mode 100644 (file)
index 0000000..84c99ff
--- /dev/null
@@ -0,0 +1,490 @@
+From 9c823e2ee1ec1b815b8ec29c231b112c5e397202 Mon Sep 17 00:00:00 2001
+From: Samuel Hsu <hsu@distec.de>
+Date: Mon, 8 Apr 2019 16:42:17 +0200
+Subject: [PATCH] Input: ili210x - fetch touchscreen geometry from DT
+
+commit f67cc3e927d8414ad3872e046764534ea1f5db0d upstream
+
+Fetching the geometry from the ILI251x registers seems unreliable and
+sometimes returns all zeroes. Add support for fetching the geometry and
+axis inversion from DT instead.
+
+Signed-off-by: Marek Vasut <marex@denx.de>
+Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+---
+ drivers/input/touchscreen/ili210x.c | 321 +++++++++++++++++-----------
+ 1 file changed, 194 insertions(+), 127 deletions(-)
+
+--- a/drivers/input/touchscreen/ili210x.c
++++ b/drivers/input/touchscreen/ili210x.c
+@@ -4,11 +4,15 @@
+ #include <linux/slab.h>
+ #include <linux/input.h>
+ #include <linux/input/mt.h>
++#include <linux/input/touchscreen.h>
+ #include <linux/delay.h>
+ #include <linux/workqueue.h>
+-#include <linux/input/ili210x.h>
++#include <linux/gpio/consumer.h>
++#include <linux/of_device.h>
++#include <asm/unaligned.h>
+-#define MAX_TOUCHES           2
++#define ILI210X_TOUCHES               2
++#define ILI251X_TOUCHES               10
+ #define DEFAULT_POLL_PERIOD   20
+ /* Touchscreen commands */
+@@ -17,41 +21,32 @@
+ #define REG_FIRMWARE_VERSION  0x40
+ #define REG_CALIBRATE         0xcc
+-struct finger {
+-      u8 x_low;
+-      u8 x_high;
+-      u8 y_low;
+-      u8 y_high;
+-} __packed;
+-
+-struct touchdata {
+-      u8 status;
+-      struct finger finger[MAX_TOUCHES];
+-} __packed;
+-
+-struct panel_info {
+-      struct finger finger_max;
+-      u8 xchannel_num;
+-      u8 ychannel_num;
+-} __packed;
+-
+ struct firmware_version {
+       u8 id;
+       u8 major;
+       u8 minor;
+ } __packed;
++enum ili2xxx_model {
++      MODEL_ILI210X,
++      MODEL_ILI251X,
++};
++
+ struct ili210x {
+       struct i2c_client *client;
+       struct input_dev *input;
+-      bool (*get_pendown_state)(void);
+       unsigned int poll_period;
+       struct delayed_work dwork;
++      struct gpio_desc *reset_gpio;
++      struct touchscreen_properties prop;
++      enum ili2xxx_model model;
++      unsigned int max_touches;
+ };
+ static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf,
+                           size_t len)
+ {
++      struct ili210x *priv = i2c_get_clientdata(client);
+       struct i2c_msg msg[2] = {
+               {
+                       .addr   = client->addr,
+@@ -67,7 +62,38 @@ static int ili210x_read_reg(struct i2c_c
+               }
+       };
+-      if (i2c_transfer(client->adapter, msg, 2) != 2) {
++      if (priv->model == MODEL_ILI251X) {
++              if (i2c_transfer(client->adapter, msg, 1) != 1) {
++                      dev_err(&client->dev, "i2c transfer failed\n");
++                      return -EIO;
++              }
++
++              usleep_range(5000, 5500);
++
++              if (i2c_transfer(client->adapter, msg + 1, 1) != 1) {
++                      dev_err(&client->dev, "i2c transfer failed\n");
++                      return -EIO;
++              }
++      } else {
++              if (i2c_transfer(client->adapter, msg, 2) != 2) {
++                      dev_err(&client->dev, "i2c transfer failed\n");
++                      return -EIO;
++              }
++      }
++
++      return 0;
++}
++
++static int ili210x_read(struct i2c_client *client, void *buf, size_t len)
++{
++      struct i2c_msg msg = {
++              .addr   = client->addr,
++              .flags  = I2C_M_RD,
++              .len    = len,
++              .buf    = buf,
++      };
++
++      if (i2c_transfer(client->adapter, &msg, 1) != 1) {
+               dev_err(&client->dev, "i2c transfer failed\n");
+               return -EIO;
+       }
+@@ -75,42 +101,72 @@ static int ili210x_read_reg(struct i2c_c
+       return 0;
+ }
+-static void ili210x_report_events(struct input_dev *input,
+-                                const struct touchdata *touchdata)
++static bool ili210x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata,
++                                      unsigned int finger,
++                                      unsigned int *x, unsigned int *y)
+ {
+-      int i;
+-      bool touch;
+-      unsigned int x, y;
+-      const struct finger *finger;
++      if (finger >= ILI210X_TOUCHES)
++              return false;
+-      for (i = 0; i < MAX_TOUCHES; i++) {
+-              input_mt_slot(input, i);
++      if (touchdata[0] & BIT(finger))
++              return false;
+-              finger = &touchdata->finger[i];
++      *x = get_unaligned_be16(touchdata + 1 + (finger * 4) + 0);
++      *y = get_unaligned_be16(touchdata + 1 + (finger * 4) + 2);
+-              touch = touchdata->status & (1 << i);
+-              input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
+-              if (touch) {
+-                      x = finger->x_low | (finger->x_high << 8);
+-                      y = finger->y_low | (finger->y_high << 8);
++      return true;
++}
++
++static bool ili251x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata,
++                                      unsigned int finger,
++                                      unsigned int *x, unsigned int *y)
++{
++      if (finger >= ILI251X_TOUCHES)
++              return false;
++
++      *x = get_unaligned_be16(touchdata + 1 + (finger * 5) + 0);
++      if (!(*x & BIT(15)))    /* Touch indication */
++              return false;
++
++      *x &= 0x3fff;
++      *y = get_unaligned_be16(touchdata + 1 + (finger * 5) + 2);
++
++      return true;
++}
++
++static bool ili210x_report_events(struct ili210x *priv, u8 *touchdata)
++{
++      struct input_dev *input = priv->input;
++      int i;
++      bool contact = false, touch = false;
++      unsigned int x = 0, y = 0;
+-                      input_report_abs(input, ABS_MT_POSITION_X, x);
+-                      input_report_abs(input, ABS_MT_POSITION_Y, y);
++      for (i = 0; i < priv->max_touches; i++) {
++              if (priv->model == MODEL_ILI210X) {
++                      touch = ili210x_touchdata_to_coords(priv, touchdata,
++                                                          i, &x, &y);
++              } else if (priv->model == MODEL_ILI251X) {
++                      touch = ili251x_touchdata_to_coords(priv, touchdata,
++                                                          i, &x, &y);
++                      if (touch)
++                              contact = true;
+               }
++
++              input_mt_slot(input, i);
++              input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
++              if (!touch)
++                      continue;
++              touchscreen_report_pos(input, &priv->prop, x, y,
++                                     true);
+       }
+       input_mt_report_pointer_emulation(input, false);
+       input_sync(input);
+-}
+-static bool get_pendown_state(const struct ili210x *priv)
+-{
+-      bool state = false;
+-
+-      if (priv->get_pendown_state)
+-              state = priv->get_pendown_state();
++      if (priv->model == MODEL_ILI210X)
++              contact = touchdata[0] & 0xf3;
+-      return state;
++      return contact;
+ }
+ static void ili210x_work(struct work_struct *work)
+@@ -118,20 +174,29 @@ static void ili210x_work(struct work_str
+       struct ili210x *priv = container_of(work, struct ili210x,
+                                           dwork.work);
+       struct i2c_client *client = priv->client;
+-      struct touchdata touchdata;
+-      int error;
++      u8 touchdata[64] = { 0 };
++      bool touch;
++      int error = -EINVAL;
++
++      if (priv->model == MODEL_ILI210X) {
++              error = ili210x_read_reg(client, REG_TOUCHDATA,
++                                       touchdata, sizeof(touchdata));
++      } else if (priv->model == MODEL_ILI251X) {
++              error = ili210x_read_reg(client, REG_TOUCHDATA,
++                                       touchdata, 31);
++              if (!error && touchdata[0] == 2)
++                      error = ili210x_read(client, &touchdata[31], 20);
++      }
+-      error = ili210x_read_reg(client, REG_TOUCHDATA,
+-                               &touchdata, sizeof(touchdata));
+       if (error) {
+               dev_err(&client->dev,
+                       "Unable to get touchdata, err = %d\n", error);
+               return;
+       }
+-      ili210x_report_events(priv->input, &touchdata);
++      touch = ili210x_report_events(priv, touchdata);
+-      if ((touchdata.status & 0xf3) || get_pendown_state(priv))
++      if (touch)
+               schedule_delayed_work(&priv->dwork,
+                                     msecs_to_jiffies(priv->poll_period));
+ }
+@@ -180,30 +245,76 @@ static const struct attribute_group ili2
+       .attrs = ili210x_attributes,
+ };
++static void ili210x_power_down(void *data)
++{
++      struct gpio_desc *reset_gpio = data;
++
++      gpiod_set_value_cansleep(reset_gpio, 1);
++}
++
++static void ili210x_cancel_work(void *data)
++{
++      struct ili210x *priv = data;
++
++      cancel_delayed_work_sync(&priv->dwork);
++}
++
+ static int ili210x_i2c_probe(struct i2c_client *client,
+                                      const struct i2c_device_id *id)
+ {
+       struct device *dev = &client->dev;
+-      const struct ili210x_platform_data *pdata = dev_get_platdata(dev);
+       struct ili210x *priv;
++      struct gpio_desc *reset_gpio;
+       struct input_dev *input;
+-      struct panel_info panel;
+       struct firmware_version firmware;
+-      int xmax, ymax;
++      enum ili2xxx_model model;
+       int error;
+-      dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver");
++      model = (enum ili2xxx_model)id->driver_data;
+-      if (!pdata) {
+-              dev_err(dev, "No platform data!\n");
+-              return -EINVAL;
+-      }
++      dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver");
+       if (client->irq <= 0) {
+               dev_err(dev, "No IRQ!\n");
+               return -EINVAL;
+       }
++      reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
++      if (IS_ERR(reset_gpio))
++              return PTR_ERR(reset_gpio);
++
++      if (reset_gpio) {
++              error = devm_add_action_or_reset(dev, ili210x_power_down,
++                                               reset_gpio);
++              if (error)
++                      return error;
++
++              usleep_range(50, 100);
++              gpiod_set_value_cansleep(reset_gpio, 0);
++              msleep(100);
++      }
++
++      priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
++      if (!priv)
++              return -ENOMEM;
++
++      input = devm_input_allocate_device(dev);
++      if (!input)
++              return -ENOMEM;
++
++      priv->client = client;
++      priv->input = input;
++      priv->poll_period = DEFAULT_POLL_PERIOD;
++      INIT_DELAYED_WORK(&priv->dwork, ili210x_work);
++      priv->reset_gpio = reset_gpio;
++      priv->model = model;
++      if (model == MODEL_ILI210X)
++              priv->max_touches = ILI210X_TOUCHES;
++      if (model == MODEL_ILI251X)
++              priv->max_touches = ILI251X_TOUCHES;
++
++      i2c_set_clientdata(client, priv);
++
+       /* Get firmware version */
+       error = ili210x_read_reg(client, REG_FIRMWARE_VERSION,
+                                &firmware, sizeof(firmware));
+@@ -213,70 +324,40 @@ static int ili210x_i2c_probe(struct i2c_
+               return error;
+       }
+-      /* get panel info */
+-      error = ili210x_read_reg(client, REG_PANEL_INFO, &panel, sizeof(panel));
+-      if (error) {
+-              dev_err(dev, "Failed to get panel information, err: %d\n",
+-                      error);
+-              return error;
+-      }
+-
+-      xmax = panel.finger_max.x_low | (panel.finger_max.x_high << 8);
+-      ymax = panel.finger_max.y_low | (panel.finger_max.y_high << 8);
+-
+-      priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+-      input = input_allocate_device();
+-      if (!priv || !input) {
+-              error = -ENOMEM;
+-              goto err_free_mem;
+-      }
+-
+-      priv->client = client;
+-      priv->input = input;
+-      priv->get_pendown_state = pdata->get_pendown_state;
+-      priv->poll_period = pdata->poll_period ? : DEFAULT_POLL_PERIOD;
+-      INIT_DELAYED_WORK(&priv->dwork, ili210x_work);
+-
+       /* Setup input device */
+       input->name = "ILI210x Touchscreen";
+       input->id.bustype = BUS_I2C;
+       input->dev.parent = 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);
+-
+-      /* Single touch */
+-      input_set_abs_params(input, ABS_X, 0, xmax, 0, 0);
+-      input_set_abs_params(input, ABS_Y, 0, ymax, 0, 0);
+-
+       /* Multi touch */
+-      input_mt_init_slots(input, MAX_TOUCHES, 0);
+-      input_set_abs_params(input, ABS_MT_POSITION_X, 0, xmax, 0, 0);
+-      input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ymax, 0, 0);
++      input_set_abs_params(input, ABS_MT_POSITION_X, 0, 0xffff, 0, 0);
++      input_set_abs_params(input, ABS_MT_POSITION_Y, 0, 0xffff, 0, 0);
++      touchscreen_parse_properties(input, true, &priv->prop);
++      input_mt_init_slots(input, priv->max_touches, INPUT_MT_DIRECT);
+-      i2c_set_clientdata(client, priv);
++      error = devm_add_action(dev, ili210x_cancel_work, priv);
++      if (error)
++              return error;
+-      error = request_irq(client->irq, ili210x_irq, pdata->irq_flags,
+-                          client->name, priv);
++      error = devm_request_irq(dev, client->irq, ili210x_irq, 0,
++                               client->name, priv);
+       if (error) {
+               dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n",
+                       error);
+-              goto err_free_mem;
++              return error;
+       }
+-      error = sysfs_create_group(&dev->kobj, &ili210x_attr_group);
++      error = devm_device_add_group(dev, &ili210x_attr_group);
+       if (error) {
+               dev_err(dev, "Unable to create sysfs attributes, err: %d\n",
+                       error);
+-              goto err_free_irq;
++              return error;
+       }
+       error = input_register_device(priv->input);
+       if (error) {
+               dev_err(dev, "Cannot register input device, err: %d\n", error);
+-              goto err_remove_sysfs;
++              return error;
+       }
+       device_init_wakeup(dev, 1);
+@@ -286,28 +367,6 @@ static int ili210x_i2c_probe(struct i2c_
+               client->irq, firmware.id, firmware.major, firmware.minor);
+       return 0;
+-
+-err_remove_sysfs:
+-      sysfs_remove_group(&dev->kobj, &ili210x_attr_group);
+-err_free_irq:
+-      free_irq(client->irq, priv);
+-err_free_mem:
+-      input_free_device(input);
+-      kfree(priv);
+-      return error;
+-}
+-
+-static int ili210x_i2c_remove(struct i2c_client *client)
+-{
+-      struct ili210x *priv = i2c_get_clientdata(client);
+-
+-      sysfs_remove_group(&client->dev.kobj, &ili210x_attr_group);
+-      free_irq(priv->client->irq, priv);
+-      cancel_delayed_work_sync(&priv->dwork);
+-      input_unregister_device(priv->input);
+-      kfree(priv);
+-
+-      return 0;
+ }
+ static int __maybe_unused ili210x_i2c_suspend(struct device *dev)
+@@ -334,19 +393,27 @@ static SIMPLE_DEV_PM_OPS(ili210x_i2c_pm,
+                        ili210x_i2c_suspend, ili210x_i2c_resume);
+ static const struct i2c_device_id ili210x_i2c_id[] = {
+-      { "ili210x", 0 },
++      { "ili210x", MODEL_ILI210X },
++      { "ili251x", MODEL_ILI251X },
+       { }
+ };
+ MODULE_DEVICE_TABLE(i2c, ili210x_i2c_id);
++static const struct of_device_id ili210x_dt_ids[] = {
++      { .compatible = "ilitek,ili210x", .data = (void *)MODEL_ILI210X },
++      { .compatible = "ilitek,ili251x", .data = (void *)MODEL_ILI251X },
++      { },
++};
++MODULE_DEVICE_TABLE(of, ili210x_dt_ids);
++
+ static struct i2c_driver ili210x_ts_driver = {
+       .driver = {
+               .name = "ili210x_i2c",
+               .pm = &ili210x_i2c_pm,
++              .of_match_table = ili210x_dt_ids,
+       },
+       .id_table = ili210x_i2c_id,
+       .probe = ili210x_i2c_probe,
+-      .remove = ili210x_i2c_remove,
+ };
+ module_i2c_driver(ili210x_ts_driver);