diff options
author | Marek Vasut <marex@denx.de> | 2019-02-07 01:02:02 -0500 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2019-02-07 01:18:16 -0500 |
commit | 4958891764749304ac1511f6140ae3888c088e23 (patch) | |
tree | 5576aa02834b31131494599d12c94b00d20849a3 /drivers/input | |
parent | c5d0e4b5154ac81cffdf6472d5f5e1408faa8ccd (diff) |
Input: ili210x - add ILI251X support
Add support for ILI251x touch controller. This controller is similar
to the ILI210x, except for the following differences:
- Does not support I2C R-W transfer, Read must be followed by an
obscenely long delay, and then followed by Write
- Does support 10 simultaneous touch inputs.
- Touch data format is slightly different, pressure reporting does not
work although the touch data contain such information.
Signed-off-by: Marek Vasut <marex@denx.de>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Diffstat (limited to 'drivers/input')
-rw-r--r-- | drivers/input/touchscreen/ili210x.c | 118 |
1 files changed, 103 insertions, 15 deletions
diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c index 4e550fe0cb15..6cfe463ac118 100644 --- a/drivers/input/touchscreen/ili210x.c +++ b/drivers/input/touchscreen/ili210x.c | |||
@@ -7,9 +7,11 @@ | |||
7 | #include <linux/delay.h> | 7 | #include <linux/delay.h> |
8 | #include <linux/workqueue.h> | 8 | #include <linux/workqueue.h> |
9 | #include <linux/gpio/consumer.h> | 9 | #include <linux/gpio/consumer.h> |
10 | #include <linux/of_device.h> | ||
10 | #include <asm/unaligned.h> | 11 | #include <asm/unaligned.h> |
11 | 12 | ||
12 | #define MAX_TOUCHES 2 | 13 | #define ILI210X_TOUCHES 2 |
14 | #define ILI251X_TOUCHES 10 | ||
13 | #define DEFAULT_POLL_PERIOD 20 | 15 | #define DEFAULT_POLL_PERIOD 20 |
14 | 16 | ||
15 | /* Touchscreen commands */ | 17 | /* Touchscreen commands */ |
@@ -33,17 +35,25 @@ struct firmware_version { | |||
33 | u8 minor; | 35 | u8 minor; |
34 | } __packed; | 36 | } __packed; |
35 | 37 | ||
38 | enum ili2xxx_model { | ||
39 | MODEL_ILI210X, | ||
40 | MODEL_ILI251X, | ||
41 | }; | ||
42 | |||
36 | struct ili210x { | 43 | struct ili210x { |
37 | struct i2c_client *client; | 44 | struct i2c_client *client; |
38 | struct input_dev *input; | 45 | struct input_dev *input; |
39 | unsigned int poll_period; | 46 | unsigned int poll_period; |
40 | struct delayed_work dwork; | 47 | struct delayed_work dwork; |
41 | struct gpio_desc *reset_gpio; | 48 | struct gpio_desc *reset_gpio; |
49 | enum ili2xxx_model model; | ||
50 | unsigned int max_touches; | ||
42 | }; | 51 | }; |
43 | 52 | ||
44 | static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf, | 53 | static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf, |
45 | size_t len) | 54 | size_t len) |
46 | { | 55 | { |
56 | struct ili210x *priv = i2c_get_clientdata(client); | ||
47 | struct i2c_msg msg[2] = { | 57 | struct i2c_msg msg[2] = { |
48 | { | 58 | { |
49 | .addr = client->addr, | 59 | .addr = client->addr, |
@@ -59,7 +69,38 @@ static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf, | |||
59 | } | 69 | } |
60 | }; | 70 | }; |
61 | 71 | ||
62 | if (i2c_transfer(client->adapter, msg, 2) != 2) { | 72 | if (priv->model == MODEL_ILI251X) { |
73 | if (i2c_transfer(client->adapter, msg, 1) != 1) { | ||
74 | dev_err(&client->dev, "i2c transfer failed\n"); | ||
75 | return -EIO; | ||
76 | } | ||
77 | |||
78 | usleep_range(5000, 5500); | ||
79 | |||
80 | if (i2c_transfer(client->adapter, msg + 1, 1) != 1) { | ||
81 | dev_err(&client->dev, "i2c transfer failed\n"); | ||
82 | return -EIO; | ||
83 | } | ||
84 | } else { | ||
85 | if (i2c_transfer(client->adapter, msg, 2) != 2) { | ||
86 | dev_err(&client->dev, "i2c transfer failed\n"); | ||
87 | return -EIO; | ||
88 | } | ||
89 | } | ||
90 | |||
91 | return 0; | ||
92 | } | ||
93 | |||
94 | static int ili210x_read(struct i2c_client *client, void *buf, size_t len) | ||
95 | { | ||
96 | struct i2c_msg msg = { | ||
97 | .addr = client->addr, | ||
98 | .flags = I2C_M_RD, | ||
99 | .len = len, | ||
100 | .buf = buf, | ||
101 | }; | ||
102 | |||
103 | if (i2c_transfer(client->adapter, &msg, 1) != 1) { | ||
63 | dev_err(&client->dev, "i2c transfer failed\n"); | 104 | dev_err(&client->dev, "i2c transfer failed\n"); |
64 | return -EIO; | 105 | return -EIO; |
65 | } | 106 | } |
@@ -71,7 +112,7 @@ static bool ili210x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata, | |||
71 | unsigned int finger, | 112 | unsigned int finger, |
72 | unsigned int *x, unsigned int *y) | 113 | unsigned int *x, unsigned int *y) |
73 | { | 114 | { |
74 | if (finger >= MAX_TOUCHES) | 115 | if (finger >= ILI210X_TOUCHES) |
75 | return false; | 116 | return false; |
76 | 117 | ||
77 | if (touchdata[0] & BIT(finger)) | 118 | if (touchdata[0] & BIT(finger)) |
@@ -83,17 +124,43 @@ static bool ili210x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata, | |||
83 | return true; | 124 | return true; |
84 | } | 125 | } |
85 | 126 | ||
127 | static bool ili251x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata, | ||
128 | unsigned int finger, | ||
129 | unsigned int *x, unsigned int *y) | ||
130 | { | ||
131 | if (finger >= ILI251X_TOUCHES) | ||
132 | return false; | ||
133 | |||
134 | *x = get_unaligned_be16(touchdata + 1 + (finger * 5) + 0); | ||
135 | if (!(*x & BIT(15))) /* Touch indication */ | ||
136 | return false; | ||
137 | |||
138 | *x &= 0x3fff; | ||
139 | *y = get_unaligned_be16(touchdata + 1 + (finger * 5) + 2); | ||
140 | |||
141 | return true; | ||
142 | } | ||
143 | |||
86 | static bool ili210x_report_events(struct ili210x *priv, u8 *touchdata) | 144 | static bool ili210x_report_events(struct ili210x *priv, u8 *touchdata) |
87 | { | 145 | { |
88 | struct input_dev *input = priv->input; | 146 | struct input_dev *input = priv->input; |
89 | int i; | 147 | int i; |
90 | bool touch; | 148 | bool contact = false, touch = false; |
91 | unsigned int x, y; | 149 | unsigned int x = 0, y = 0; |
92 | 150 | ||
93 | for (i = 0; i < MAX_TOUCHES; i++) { | 151 | for (i = 0; i < priv->max_touches; i++) { |
94 | input_mt_slot(input, i); | 152 | input_mt_slot(input, i); |
95 | 153 | ||
96 | touch = ili210x_touchdata_to_coords(priv, touchdata, i, &x, &y); | 154 | if (priv->model == MODEL_ILI210X) { |
155 | touch = ili210x_touchdata_to_coords(priv, touchdata, | ||
156 | i, &x, &y); | ||
157 | } else if (priv->model == MODEL_ILI251X) { | ||
158 | touch = ili251x_touchdata_to_coords(priv, touchdata, | ||
159 | i, &x, &y); | ||
160 | if (touch) | ||
161 | contact = true; | ||
162 | } | ||
163 | |||
97 | input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); | 164 | input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); |
98 | if (touch) { | 165 | if (touch) { |
99 | input_report_abs(input, ABS_MT_POSITION_X, x); | 166 | input_report_abs(input, ABS_MT_POSITION_X, x); |
@@ -104,7 +171,10 @@ static bool ili210x_report_events(struct ili210x *priv, u8 *touchdata) | |||
104 | input_mt_report_pointer_emulation(input, false); | 171 | input_mt_report_pointer_emulation(input, false); |
105 | input_sync(input); | 172 | input_sync(input); |
106 | 173 | ||
107 | return touchdata[0] & 0xf3; | 174 | if (priv->model == MODEL_ILI210X) |
175 | contact = touchdata[0] & 0xf3; | ||
176 | |||
177 | return contact; | ||
108 | } | 178 | } |
109 | 179 | ||
110 | static void ili210x_work(struct work_struct *work) | 180 | static void ili210x_work(struct work_struct *work) |
@@ -112,12 +182,20 @@ static void ili210x_work(struct work_struct *work) | |||
112 | struct ili210x *priv = container_of(work, struct ili210x, | 182 | struct ili210x *priv = container_of(work, struct ili210x, |
113 | dwork.work); | 183 | dwork.work); |
114 | struct i2c_client *client = priv->client; | 184 | struct i2c_client *client = priv->client; |
115 | u8 touchdata[1 + 4 * MAX_TOUCHES]; | 185 | u8 touchdata[64] = { 0 }; |
116 | bool touch; | 186 | bool touch; |
117 | int error; | 187 | int error = -EINVAL; |
188 | |||
189 | if (priv->model == MODEL_ILI210X) { | ||
190 | error = ili210x_read_reg(client, REG_TOUCHDATA, | ||
191 | touchdata, sizeof(touchdata)); | ||
192 | } else if (priv->model == MODEL_ILI251X) { | ||
193 | error = ili210x_read_reg(client, REG_TOUCHDATA, | ||
194 | touchdata, 31); | ||
195 | if (!error && touchdata[0] == 2) | ||
196 | error = ili210x_read(client, &touchdata[31], 20); | ||
197 | } | ||
118 | 198 | ||
119 | error = ili210x_read_reg(client, REG_TOUCHDATA, | ||
120 | touchdata, sizeof(touchdata)); | ||
121 | if (error) { | 199 | if (error) { |
122 | dev_err(&client->dev, | 200 | dev_err(&client->dev, |
123 | "Unable to get touchdata, err = %d\n", error); | 201 | "Unable to get touchdata, err = %d\n", error); |
@@ -198,9 +276,12 @@ static int ili210x_i2c_probe(struct i2c_client *client, | |||
198 | struct input_dev *input; | 276 | struct input_dev *input; |
199 | struct panel_info panel; | 277 | struct panel_info panel; |
200 | struct firmware_version firmware; | 278 | struct firmware_version firmware; |
279 | enum ili2xxx_model model; | ||
201 | int xmax, ymax; | 280 | int xmax, ymax; |
202 | int error; | 281 | int error; |
203 | 282 | ||
283 | model = (enum ili2xxx_model)id->driver_data; | ||
284 | |||
204 | dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver"); | 285 | dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver"); |
205 | 286 | ||
206 | if (client->irq <= 0) { | 287 | if (client->irq <= 0) { |
@@ -236,6 +317,11 @@ static int ili210x_i2c_probe(struct i2c_client *client, | |||
236 | priv->poll_period = DEFAULT_POLL_PERIOD; | 317 | priv->poll_period = DEFAULT_POLL_PERIOD; |
237 | INIT_DELAYED_WORK(&priv->dwork, ili210x_work); | 318 | INIT_DELAYED_WORK(&priv->dwork, ili210x_work); |
238 | priv->reset_gpio = reset_gpio; | 319 | priv->reset_gpio = reset_gpio; |
320 | priv->model = model; | ||
321 | if (model == MODEL_ILI210X) | ||
322 | priv->max_touches = ILI210X_TOUCHES; | ||
323 | if (model == MODEL_ILI251X) | ||
324 | priv->max_touches = ILI251X_TOUCHES; | ||
239 | 325 | ||
240 | i2c_set_clientdata(client, priv); | 326 | i2c_set_clientdata(client, priv); |
241 | 327 | ||
@@ -274,7 +360,7 @@ static int ili210x_i2c_probe(struct i2c_client *client, | |||
274 | input_set_abs_params(input, ABS_Y, 0, ymax, 0, 0); | 360 | input_set_abs_params(input, ABS_Y, 0, ymax, 0, 0); |
275 | 361 | ||
276 | /* Multi touch */ | 362 | /* Multi touch */ |
277 | input_mt_init_slots(input, MAX_TOUCHES, 0); | 363 | input_mt_init_slots(input, priv->max_touches, 0); |
278 | input_set_abs_params(input, ABS_MT_POSITION_X, 0, xmax, 0, 0); | 364 | input_set_abs_params(input, ABS_MT_POSITION_X, 0, xmax, 0, 0); |
279 | input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ymax, 0, 0); | 365 | input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ymax, 0, 0); |
280 | 366 | ||
@@ -347,13 +433,15 @@ static SIMPLE_DEV_PM_OPS(ili210x_i2c_pm, | |||
347 | ili210x_i2c_suspend, ili210x_i2c_resume); | 433 | ili210x_i2c_suspend, ili210x_i2c_resume); |
348 | 434 | ||
349 | static const struct i2c_device_id ili210x_i2c_id[] = { | 435 | static const struct i2c_device_id ili210x_i2c_id[] = { |
350 | { "ili210x", 0 }, | 436 | { "ili210x", MODEL_ILI210X }, |
437 | { "ili251x", MODEL_ILI251X }, | ||
351 | { } | 438 | { } |
352 | }; | 439 | }; |
353 | MODULE_DEVICE_TABLE(i2c, ili210x_i2c_id); | 440 | MODULE_DEVICE_TABLE(i2c, ili210x_i2c_id); |
354 | 441 | ||
355 | static const struct of_device_id ili210x_dt_ids[] = { | 442 | static const struct of_device_id ili210x_dt_ids[] = { |
356 | { .compatible = "ilitek,ili210x", }, | 443 | { .compatible = "ilitek,ili210x", .data = (void *)MODEL_ILI210X }, |
444 | { .compatible = "ilitek,ili251x", .data = (void *)MODEL_ILI251X }, | ||
357 | { }, | 445 | { }, |
358 | }; | 446 | }; |
359 | MODULE_DEVICE_TABLE(of, ili210x_dt_ids); | 447 | MODULE_DEVICE_TABLE(of, ili210x_dt_ids); |