Skip to content

Commit 0f5e5ed

Browse files
committed
Input: novatek-nvt-ts - support being panel follower
Sometimes Novatek touchscreen is paired together with Novatek panel, and they both need to be powered together in sync. Add support for nvt-ts driver to operate in panel follower mode, in which touchscreen isn't in full control of its lifecycle. This is managed using callbacks from panel, to get informed when the panel is powered on and off. From there we can match the nvt-ts device's power state to that of the panel. Without this probing touchscreen before panel sometimes randomly fails with I2C read errors. * split some code from probe function to separate initial_power_on function, which is called once from panel_prepared callback, or from probe function, depending on mode; * update Kconfig to depend on DRM. Signed-off-by: Alexey Minnekhanov <alexeymin@postmarketos.org>
1 parent 2e11264 commit 0f5e5ed

File tree

2 files changed

+111
-23
lines changed

2 files changed

+111
-23
lines changed

drivers/input/touchscreen/Kconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -660,7 +660,7 @@ config TOUCHSCREEN_MTOUCH
660660

661661
config TOUCHSCREEN_NOVATEK_NVT_TS
662662
tristate "Novatek NT11205 touchscreen support"
663-
depends on I2C
663+
depends on I2C && (DRM || !DRM)
664664
help
665665
Say Y here if you have a Novatek NT11205 touchscreen.
666666
If unsure, say N.

drivers/input/touchscreen/novatek-nvt-ts.c

Lines changed: 110 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* Copyright (c) 2023 Hans de Goede <hdegoede@redhat.com>
77
*/
88

9+
#include <drm/drm_panel.h>
910
#include <linux/delay.h>
1011
#include <linux/gpio/consumer.h>
1112
#include <linux/interrupt.h>
@@ -14,7 +15,6 @@
1415
#include <linux/input/mt.h>
1516
#include <linux/input/touchscreen.h>
1617
#include <linux/module.h>
17-
1818
#include <linux/unaligned.h>
1919

2020
#define NVT_TS_TOUCH_START 0x00
@@ -61,6 +61,12 @@ struct nvt_ts_data {
6161
struct touchscreen_properties prop;
6262
int max_touches;
6363
u8 buf[NVT_TS_TOUCH_SIZE * NVT_TS_MAX_TOUCHES];
64+
/*
65+
* Sometimes Novatek touchscreen is paired together with Novatek panel,
66+
* and they need to be powered together in sync.
67+
*/
68+
struct drm_panel_follower panel_follower;
69+
bool is_panel_follower;
6470
};
6571

6672
static int nvt_ts_read_data(struct i2c_client *client, u8 reg, u8 *data, int count)
@@ -97,6 +103,9 @@ static irqreturn_t nvt_ts_irq(int irq, void *dev_id)
97103
bool active;
98104
u8 *touch;
99105

106+
if (!data->input)
107+
return IRQ_HANDLED;
108+
100109
error = nvt_ts_read_data(data->client, NVT_TS_TOUCH_START, data->buf,
101110
data->max_touches * NVT_TS_TOUCH_SIZE);
102111
if (error)
@@ -155,6 +164,7 @@ static int nvt_ts_start(struct input_dev *dev)
155164

156165
enable_irq(data->client->irq);
157166
gpiod_set_value_cansleep(data->reset_gpio, 0);
167+
msleep(100); // TODO: is it really needed? probably
158168

159169
return 0;
160170
}
@@ -172,6 +182,9 @@ static int nvt_ts_suspend(struct device *dev)
172182
{
173183
struct nvt_ts_data *data = i2c_get_clientdata(to_i2c_client(dev));
174184

185+
if (data->is_panel_follower)
186+
return 0;
187+
175188
mutex_lock(&data->input->mutex);
176189
if (input_device_enabled(data->input))
177190
nvt_ts_stop(data->input);
@@ -184,6 +197,9 @@ static int nvt_ts_resume(struct device *dev)
184197
{
185198
struct nvt_ts_data *data = i2c_get_clientdata(to_i2c_client(dev));
186199

200+
if (data->is_panel_follower)
201+
return 0;
202+
187203
mutex_lock(&data->input->mutex);
188204
if (input_device_enabled(data->input))
189205
nvt_ts_start(data->input);
@@ -192,32 +208,17 @@ static int nvt_ts_resume(struct device *dev)
192208
return 0;
193209
}
194210

195-
static DEFINE_SIMPLE_DEV_PM_OPS(nvt_ts_pm_ops, nvt_ts_suspend, nvt_ts_resume);
196-
197-
static int nvt_ts_probe(struct i2c_client *client)
211+
static int nvt_ts_initial_power_on_and_register_inputdev(struct nvt_ts_data *data)
198212
{
199-
struct device *dev = &client->dev;
213+
struct device *dev = &data->client->dev;
200214
int error, width, height, irq_type;
201-
struct nvt_ts_data *data;
202215
const struct nvt_ts_i2c_chip_data *chip;
203216
struct input_dev *input;
204217

205-
if (!client->irq) {
206-
dev_err(dev, "Error no irq specified\n");
207-
return -EINVAL;
208-
}
209-
210-
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
211-
if (!data)
212-
return -ENOMEM;
213-
214-
chip = device_get_match_data(&client->dev);
218+
chip = device_get_match_data(dev);
215219
if (!chip)
216220
return -EINVAL;
217221

218-
data->client = client;
219-
i2c_set_clientdata(client, data);
220-
221222
/*
222223
* VCC is the analog voltage supply
223224
* IOVCC is the digital voltage supply
@@ -278,7 +279,7 @@ static int nvt_ts_probe(struct i2c_client *client)
278279
if (!input)
279280
return -ENOMEM;
280281

281-
input->name = client->name;
282+
input->name = data->client->name;
282283
input->id.bustype = BUS_I2C;
283284
input->open = nvt_ts_start;
284285
input->close = nvt_ts_stop;
@@ -295,10 +296,11 @@ static int nvt_ts_probe(struct i2c_client *client)
295296
data->input = input;
296297
input_set_drvdata(input, data);
297298

298-
error = devm_request_threaded_irq(dev, client->irq, NULL, nvt_ts_irq,
299+
error = devm_request_threaded_irq(dev, data->client->irq, NULL,
300+
nvt_ts_irq,
299301
IRQF_ONESHOT | IRQF_NO_AUTOEN |
300302
nvt_ts_irq_type[irq_type],
301-
client->name, data);
303+
data->client->name, data);
302304
if (error) {
303305
dev_err(dev, "failed to request irq: %d\n", error);
304306
return error;
@@ -313,6 +315,91 @@ static int nvt_ts_probe(struct i2c_client *client)
313315
return 0;
314316
}
315317

318+
static int on_novatek_panel_prepared(struct drm_panel_follower *follower)
319+
{
320+
struct nvt_ts_data *data = container_of(follower, struct nvt_ts_data, panel_follower);
321+
int ret;
322+
dev_info(&data->client->dev, "%s\n", __func__); // REMOVEME
323+
324+
/* Is this the first power on? */
325+
if (!data->input) {
326+
dev_info(&data->client->dev, "doing initial power on\n"); // REMOVEME
327+
ret = nvt_ts_initial_power_on_and_register_inputdev(data);
328+
if (ret)
329+
return ret;
330+
}
331+
332+
mutex_lock(&data->input->mutex);
333+
if (input_device_enabled(data->input))
334+
nvt_ts_start(data->input);
335+
mutex_unlock(&data->input->mutex);
336+
337+
return 0;
338+
}
339+
340+
static int on_novatek_panel_unpreparing(struct drm_panel_follower *follower)
341+
{
342+
struct nvt_ts_data *data = container_of(follower, struct nvt_ts_data, panel_follower);
343+
dev_info(&data->client->dev, "%s\n", __func__); // REMOVEME
344+
345+
mutex_lock(&data->input->mutex);
346+
if (input_device_enabled(data->input))
347+
nvt_ts_stop(data->input);
348+
mutex_unlock(&data->input->mutex);
349+
350+
return 0;
351+
}
352+
353+
static const struct drm_panel_follower_funcs nvt_ts_follower_funcs = {
354+
.panel_prepared = on_novatek_panel_prepared,
355+
.panel_unpreparing = on_novatek_panel_unpreparing,
356+
};
357+
358+
static DEFINE_SIMPLE_DEV_PM_OPS(nvt_ts_pm_ops, nvt_ts_suspend, nvt_ts_resume);
359+
360+
static int nvt_ts_probe(struct i2c_client *client)
361+
{
362+
struct device *dev = &client->dev;
363+
struct nvt_ts_data *data;
364+
365+
if (!client->irq) {
366+
dev_err(dev, "Error no irq specified\n");
367+
return -EINVAL;
368+
}
369+
370+
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
371+
if (!data)
372+
return -ENOMEM;
373+
374+
data->client = client;
375+
i2c_set_clientdata(client, data);
376+
377+
/* check if "panel = <&...>" is set in DT */
378+
if (drm_is_panel_follower(dev)) {
379+
/* register self as follower */
380+
dev_info(dev, "probing in follower mode\n"); // REMOVEME
381+
data->is_panel_follower = true;
382+
data->panel_follower.funcs = &nvt_ts_follower_funcs;
383+
drm_panel_add_follower(dev, &data->panel_follower);
384+
/*
385+
* In this mode, we can't do anything more at this moment.
386+
* Need to wait for callbacks from panel.
387+
*/
388+
return 0;
389+
}
390+
391+
dev_info(dev, "probing in normal mode\n"); // REMOVEME
392+
return nvt_ts_initial_power_on_and_register_inputdev(data);
393+
}
394+
395+
static void nvt_ts_remove(struct i2c_client *client)
396+
{
397+
struct nvt_ts_data *data = i2c_get_clientdata(client);
398+
399+
if (data->is_panel_follower)
400+
drm_panel_remove_follower(&data->panel_follower);
401+
}
402+
316403
static const struct nvt_ts_i2c_chip_data nvt_nt11205_ts_data = {
317404
.wake_type = 0x05,
318405
.chip_id = 0x05,
@@ -344,6 +431,7 @@ static struct i2c_driver nvt_ts_driver = {
344431
.of_match_table = nvt_ts_of_match,
345432
},
346433
.probe = nvt_ts_probe,
434+
.remove = nvt_ts_remove,
347435
.id_table = nvt_ts_i2c_id,
348436
};
349437

0 commit comments

Comments
 (0)