Commit 49db3997 authored by Hans de Goede's avatar Hans de Goede Committed by Dmitry Torokhov

Input: goodix - refactor IRQ pin GPIO accesses

Suspending Goodix touchscreens requires changing the interrupt pin to
output before sending them a power-down command. Followed by wiggling
the interrupt pin to wake the device up, after which it is put back
in input mode.

So far we have only effectively supported this on devices which use
devicetree. On X86 ACPI platforms both looking up the pins; and using a
pin as both IRQ and GPIO is a bit more complicated. E.g. on some devices
we cannot directly access the IRQ pin as GPIO and we need to call ACPI
methods to control it instead.

This commit adds a new irq_pin_access_method field to the goodix_chip_data
struct and adds goodix_irq_direction_output and goodix_irq_direction_input
helpers which together abstract the GPIO accesses to the IRQ pin.

This is a preparation patch for adding support for properly suspending the
touchscreen on X86 ACPI platforms.

BugLink: https://bugzilla.redhat.com/show_bug.cgi?id=1786317
BugLink: https://github.com/nexus511/gpd-ubuntu-packages/issues/10
BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=199207Signed-off-by: default avatarHans de Goede <hdegoede@redhat.com>
Reviewed-by: default avatarBastien Nocera <hadess@hadess.net>
Link: https://lore.kernel.org/r/20200307121505.3707-1-hdegoede@redhat.comSigned-off-by: default avatarDmitry Torokhov <dmitry.torokhov@gmail.com>
parent 06170671
...@@ -31,6 +31,11 @@ ...@@ -31,6 +31,11 @@
struct goodix_ts_data; struct goodix_ts_data;
enum goodix_irq_pin_access_method {
IRQ_PIN_ACCESS_NONE,
IRQ_PIN_ACCESS_GPIO,
};
struct goodix_chip_data { struct goodix_chip_data {
u16 config_addr; u16 config_addr;
int config_len; int config_len;
...@@ -53,6 +58,7 @@ struct goodix_ts_data { ...@@ -53,6 +58,7 @@ struct goodix_ts_data {
const char *cfg_name; const char *cfg_name;
struct completion firmware_loading_complete; struct completion firmware_loading_complete;
unsigned long irq_flags; unsigned long irq_flags;
enum goodix_irq_pin_access_method irq_pin_access_method;
unsigned int contact_size; unsigned int contact_size;
}; };
...@@ -518,17 +524,48 @@ static int goodix_send_cfg(struct goodix_ts_data *ts, ...@@ -518,17 +524,48 @@ static int goodix_send_cfg(struct goodix_ts_data *ts,
return 0; return 0;
} }
static int goodix_irq_direction_output(struct goodix_ts_data *ts,
int value)
{
switch (ts->irq_pin_access_method) {
case IRQ_PIN_ACCESS_NONE:
dev_err(&ts->client->dev,
"%s called without an irq_pin_access_method set\n",
__func__);
return -EINVAL;
case IRQ_PIN_ACCESS_GPIO:
return gpiod_direction_output(ts->gpiod_int, value);
}
return -EINVAL; /* Never reached */
}
static int goodix_irq_direction_input(struct goodix_ts_data *ts)
{
switch (ts->irq_pin_access_method) {
case IRQ_PIN_ACCESS_NONE:
dev_err(&ts->client->dev,
"%s called without an irq_pin_access_method set\n",
__func__);
return -EINVAL;
case IRQ_PIN_ACCESS_GPIO:
return gpiod_direction_input(ts->gpiod_int);
}
return -EINVAL; /* Never reached */
}
static int goodix_int_sync(struct goodix_ts_data *ts) static int goodix_int_sync(struct goodix_ts_data *ts)
{ {
int error; int error;
error = gpiod_direction_output(ts->gpiod_int, 0); error = goodix_irq_direction_output(ts, 0);
if (error) if (error)
return error; return error;
msleep(50); /* T5: 50ms */ msleep(50); /* T5: 50ms */
error = gpiod_direction_input(ts->gpiod_int); error = goodix_irq_direction_input(ts);
if (error) if (error)
return error; return error;
...@@ -552,7 +589,7 @@ static int goodix_reset(struct goodix_ts_data *ts) ...@@ -552,7 +589,7 @@ static int goodix_reset(struct goodix_ts_data *ts)
msleep(20); /* T2: > 10ms */ msleep(20); /* T2: > 10ms */
/* HIGH: 0x28/0x29, LOW: 0xBA/0xBB */ /* HIGH: 0x28/0x29, LOW: 0xBA/0xBB */
error = gpiod_direction_output(ts->gpiod_int, ts->client->addr == 0x14); error = goodix_irq_direction_output(ts, ts->client->addr == 0x14);
if (error) if (error)
return error; return error;
...@@ -633,6 +670,9 @@ static int goodix_get_gpio_config(struct goodix_ts_data *ts) ...@@ -633,6 +670,9 @@ static int goodix_get_gpio_config(struct goodix_ts_data *ts)
ts->gpiod_rst = gpiod; ts->gpiod_rst = gpiod;
if (ts->gpiod_int && ts->gpiod_rst)
ts->irq_pin_access_method = IRQ_PIN_ACCESS_GPIO;
return 0; return 0;
} }
...@@ -911,7 +951,7 @@ static int goodix_ts_probe(struct i2c_client *client, ...@@ -911,7 +951,7 @@ static int goodix_ts_probe(struct i2c_client *client,
if (error) if (error)
return error; return error;
if (ts->gpiod_int && ts->gpiod_rst) { if (ts->irq_pin_access_method == IRQ_PIN_ACCESS_GPIO) {
/* reset the controller */ /* reset the controller */
error = goodix_reset(ts); error = goodix_reset(ts);
if (error) { if (error) {
...@@ -934,7 +974,7 @@ static int goodix_ts_probe(struct i2c_client *client, ...@@ -934,7 +974,7 @@ static int goodix_ts_probe(struct i2c_client *client,
ts->chip = goodix_get_chip_data(ts->id); ts->chip = goodix_get_chip_data(ts->id);
if (ts->gpiod_int && ts->gpiod_rst) { if (ts->irq_pin_access_method == IRQ_PIN_ACCESS_GPIO) {
/* update device config */ /* update device config */
ts->cfg_name = devm_kasprintf(&client->dev, GFP_KERNEL, ts->cfg_name = devm_kasprintf(&client->dev, GFP_KERNEL,
"goodix_%d_cfg.bin", ts->id); "goodix_%d_cfg.bin", ts->id);
...@@ -965,7 +1005,7 @@ static int goodix_ts_remove(struct i2c_client *client) ...@@ -965,7 +1005,7 @@ static int goodix_ts_remove(struct i2c_client *client)
{ {
struct goodix_ts_data *ts = i2c_get_clientdata(client); struct goodix_ts_data *ts = i2c_get_clientdata(client);
if (ts->gpiod_int && ts->gpiod_rst) if (ts->irq_pin_access_method == IRQ_PIN_ACCESS_GPIO)
wait_for_completion(&ts->firmware_loading_complete); wait_for_completion(&ts->firmware_loading_complete);
return 0; return 0;
...@@ -978,7 +1018,7 @@ static int __maybe_unused goodix_suspend(struct device *dev) ...@@ -978,7 +1018,7 @@ static int __maybe_unused goodix_suspend(struct device *dev)
int error; int error;
/* We need gpio pins to suspend/resume */ /* We need gpio pins to suspend/resume */
if (!ts->gpiod_int || !ts->gpiod_rst) { if (ts->irq_pin_access_method == IRQ_PIN_ACCESS_NONE) {
disable_irq(client->irq); disable_irq(client->irq);
return 0; return 0;
} }
...@@ -989,7 +1029,7 @@ static int __maybe_unused goodix_suspend(struct device *dev) ...@@ -989,7 +1029,7 @@ static int __maybe_unused goodix_suspend(struct device *dev)
goodix_free_irq(ts); goodix_free_irq(ts);
/* Output LOW on the INT pin for 5 ms */ /* Output LOW on the INT pin for 5 ms */
error = gpiod_direction_output(ts->gpiod_int, 0); error = goodix_irq_direction_output(ts, 0);
if (error) { if (error) {
goodix_request_irq(ts); goodix_request_irq(ts);
return error; return error;
...@@ -1001,7 +1041,7 @@ static int __maybe_unused goodix_suspend(struct device *dev) ...@@ -1001,7 +1041,7 @@ static int __maybe_unused goodix_suspend(struct device *dev)
GOODIX_CMD_SCREEN_OFF); GOODIX_CMD_SCREEN_OFF);
if (error) { if (error) {
dev_err(&ts->client->dev, "Screen off command failed\n"); dev_err(&ts->client->dev, "Screen off command failed\n");
gpiod_direction_input(ts->gpiod_int); goodix_irq_direction_input(ts);
goodix_request_irq(ts); goodix_request_irq(ts);
return -EAGAIN; return -EAGAIN;
} }
...@@ -1021,7 +1061,7 @@ static int __maybe_unused goodix_resume(struct device *dev) ...@@ -1021,7 +1061,7 @@ static int __maybe_unused goodix_resume(struct device *dev)
struct goodix_ts_data *ts = i2c_get_clientdata(client); struct goodix_ts_data *ts = i2c_get_clientdata(client);
int error; int error;
if (!ts->gpiod_int || !ts->gpiod_rst) { if (ts->irq_pin_access_method == IRQ_PIN_ACCESS_NONE) {
enable_irq(client->irq); enable_irq(client->irq);
return 0; return 0;
} }
...@@ -1030,7 +1070,7 @@ static int __maybe_unused goodix_resume(struct device *dev) ...@@ -1030,7 +1070,7 @@ static int __maybe_unused goodix_resume(struct device *dev)
* Exit sleep mode by outputting HIGH level to INT pin * Exit sleep mode by outputting HIGH level to INT pin
* for 2ms~5ms. * for 2ms~5ms.
*/ */
error = gpiod_direction_output(ts->gpiod_int, 1); error = goodix_irq_direction_output(ts, 1);
if (error) if (error)
return error; return error;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment