Merge branch 'next' into for-linus

Prepare input updates for 5.7 merge window.
This commit is contained in:
Dmitry Torokhov 2020-04-06 20:56:50 -07:00
Родитель 4134252ab7 3a85796296
Коммит cd510679f4
8 изменённых файлов: 655 добавлений и 214 удалений

Просмотреть файл

@ -1,77 +0,0 @@
FocalTech EDT-FT5x06 Polytouch driver
=====================================
There are 5 variants of the chip for various touch panel sizes
FT5206GE1 2.8" .. 3.8"
FT5306DE4 4.3" .. 7"
FT5406EE8 7" .. 8.9"
FT5506EEG 7" .. 8.9"
FT5726NEI 5.7” .. 11.6"
The software interface is identical for all those chips, so that
currently there is no need for the driver to distinguish between the
different chips. Nevertheless distinct compatible strings are used so
that a distinction can be added if necessary without changing the DT
bindings.
Required properties:
- compatible: "edt,edt-ft5206"
or: "edt,edt-ft5306"
or: "edt,edt-ft5406"
or: "edt,edt-ft5506"
or: "evervision,ev-ft5726"
or: "focaltech,ft6236"
- reg: I2C slave address of the chip (0x38)
- interrupts: interrupt specification for the touchdetect
interrupt
Optional properties:
- reset-gpios: GPIO specification for the RESET input
- wake-gpios: GPIO specification for the WAKE input
- vcc-supply: Regulator that supplies the touchscreen
- pinctrl-names: should be "default"
- pinctrl-0: a phandle pointing to the pin settings for the
control gpios
- wakeup-source: If present the device will act as wakeup-source
- threshold: allows setting the "click"-threshold in the range
from 0 to 80.
- gain: allows setting the sensitivity in the range from 0 to
31. Note that lower values indicate higher
sensitivity.
- offset: allows setting the edge compensation in the range from
0 to 31.
- offset-x: Same as offset, but applies only to the horizontal position.
Range from 0 to 80, only supported by evervision,ev-ft5726
devices.
- offset-y: Same as offset, but applies only to the vertical position.
Range from 0 to 80, only supported by evervision,ev-ft5726
devices.
- touchscreen-size-x : See touchscreen.txt
- touchscreen-size-y : See touchscreen.txt
- touchscreen-fuzz-x : See touchscreen.txt
- touchscreen-fuzz-y : See touchscreen.txt
- touchscreen-inverted-x : See touchscreen.txt
- touchscreen-inverted-y : See touchscreen.txt
- touchscreen-swapped-x-y : See touchscreen.txt
Example:
polytouch: edt-ft5x06@38 {
compatible = "edt,edt-ft5406", "edt,edt-ft5x06";
reg = <0x38>;
pinctrl-names = "default";
pinctrl-0 = <&edt_ft5x06_pins>;
interrupt-parent = <&gpio2>;
interrupts = <5 IRQ_TYPE_EDGE_FALLING>;
reset-gpios = <&gpio2 6 GPIO_ACTIVE_LOW>;
wake-gpios = <&gpio4 9 GPIO_ACTIVE_HIGH>;
};

Просмотреть файл

@ -0,0 +1,125 @@
# SPDX-License-Identifier: GPL-2.0
%YAML 1.2
---
$id: http://devicetree.org/schemas/input/touchscreen/edt-ft5x06.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: FocalTech EDT-FT5x06 Polytouch Bindings
description: |
There are 5 variants of the chip for various touch panel sizes
FT5206GE1 2.8" .. 3.8"
FT5306DE4 4.3" .. 7"
FT5406EE8 7" .. 8.9"
FT5506EEG 7" .. 8.9"
FT5726NEI 5.7” .. 11.6"
maintainers:
- Dmitry Torokhov <dmitry.torokhov@gmail.com>
allOf:
- $ref: touchscreen.yaml#
- if:
properties:
compatible:
contains:
enum:
- evervision,ev-ft5726
then:
properties:
offset-x: true
offset-y: true
properties:
compatible:
enum:
- edt,edt-ft5206
- edt,edt-ft5306
- edt,edt-ft5406
- edt,edt-ft5506
- evervision,ev-ft5726
- focaltech,ft6236
reg:
const: 0x38
interrupts:
maxItems: 1
reset-gpios:
maxItems: 1
wake-gpios:
maxItems: 1
wakeup-source: true
vcc-supply:
maxItems: 1
gain:
description: Allows setting the sensitivity in the range from 0 to 31.
Note that lower values indicate higher sensitivity.
allOf:
- $ref: /schemas/types.yaml#/definitions/uint32
- minimum: 0
- maximum: 31
offset:
description: Allows setting the edge compensation in the range from 0 to 31.
allOf:
- $ref: /schemas/types.yaml#/definitions/uint32
- minimum: 0
- maximum: 31
offset-x:
description: Same as offset, but applies only to the horizontal position.
Range from 0 to 80, only supported by evervision,ev-ft5726 devices.
allOf:
- $ref: /schemas/types.yaml#/definitions/uint32
- minimum: 0
- maximum: 80
offset-y:
description: Same as offset, but applies only to the vertical position.
Range from 0 to 80, only supported by evervision,ev-ft5726 devices.
allOf:
- $ref: /schemas/types.yaml#/definitions/uint32
- minimum: 0
- maximum: 80
touchscreen-size-x: true
touchscreen-size-y: true
touchscreen-fuzz-x: true
touchscreen-fuzz-y: true
touchscreen-inverted-x: true
touchscreen-inverted-y: true
touchscreen-swapped-x-y: true
interrupt-controller: true
additionalProperties: false
required:
- compatible
- reg
- interrupts
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
i2c@00000000 {
#address-cells = <1>;
#size-cells = <0>;
edt-ft5x06@38 {
compatible = "edt,edt-ft5406";
reg = <0x38>;
interrupt-parent = <&gpio2>;
interrupts = <5 IRQ_TYPE_EDGE_FALLING>;
reset-gpios = <&gpio2 6 GPIO_ACTIVE_LOW>;
wake-gpios = <&gpio4 9 GPIO_ACTIVE_HIGH>;
};
};
...

Просмотреть файл

@ -21,6 +21,8 @@ properties:
- goodix,gt911 - goodix,gt911
- goodix,gt9110 - goodix,gt9110
- goodix,gt912 - goodix,gt912
- goodix,gt9147
- goodix,gt917s
- goodix,gt927 - goodix,gt927
- goodix,gt9271 - goodix,gt9271
- goodix,gt928 - goodix,gt928

Просмотреть файл

@ -530,6 +530,17 @@ static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = {
DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo LaVie Z"), DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo LaVie Z"),
}, },
}, },
{
/*
* Acer Aspire 5738z
* Touchpad stops working in mux mode when dis- + re-enabled
* with the touchpad enable/disable toggle hotkey
*/
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5738"),
},
},
{ } { }
}; };

Просмотреть файл

@ -1309,6 +1309,7 @@ static int elants_i2c_probe(struct i2c_client *client,
input_set_abs_params(ts->input, ABS_MT_PRESSURE, 0, 255, 0, 0); input_set_abs_params(ts->input, ABS_MT_PRESSURE, 0, 255, 0, 0);
input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->x_res); input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->x_res);
input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->y_res); input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->y_res);
input_abs_set_res(ts->input, ABS_MT_TOUCH_MAJOR, 1);
error = input_register_device(ts->input); error = input_register_device(ts->input);
if (error) { if (error) {

Просмотреть файл

@ -29,14 +29,68 @@
#include <linux/of.h> #include <linux/of.h>
#include <asm/unaligned.h> #include <asm/unaligned.h>
#define GOODIX_GPIO_INT_NAME "irq"
#define GOODIX_GPIO_RST_NAME "reset"
#define GOODIX_MAX_HEIGHT 4096
#define GOODIX_MAX_WIDTH 4096
#define GOODIX_INT_TRIGGER 1
#define GOODIX_CONTACT_SIZE 8
#define GOODIX_MAX_CONTACT_SIZE 9
#define GOODIX_MAX_CONTACTS 10
#define GOODIX_MAX_KEYS 7
#define GOODIX_CONFIG_MIN_LENGTH 186
#define GOODIX_CONFIG_911_LENGTH 186
#define GOODIX_CONFIG_967_LENGTH 228
#define GOODIX_CONFIG_GT9X_LENGTH 240
#define GOODIX_CONFIG_MAX_LENGTH 240
/* Register defines */
#define GOODIX_REG_COMMAND 0x8040
#define GOODIX_CMD_SCREEN_OFF 0x05
#define GOODIX_READ_COOR_ADDR 0x814E
#define GOODIX_GT1X_REG_CONFIG_DATA 0x8050
#define GOODIX_GT9X_REG_CONFIG_DATA 0x8047
#define GOODIX_REG_ID 0x8140
#define GOODIX_BUFFER_STATUS_READY BIT(7)
#define GOODIX_HAVE_KEY BIT(4)
#define GOODIX_BUFFER_STATUS_TIMEOUT 20
#define RESOLUTION_LOC 1
#define MAX_CONTACTS_LOC 5
#define TRIGGER_LOC 6
/* Our special handling for GPIO accesses through ACPI is x86 specific */
#if defined CONFIG_X86 && defined CONFIG_ACPI
#define ACPI_GPIO_SUPPORT
#endif
struct goodix_ts_data; struct goodix_ts_data;
enum goodix_irq_pin_access_method {
IRQ_PIN_ACCESS_NONE,
IRQ_PIN_ACCESS_GPIO,
IRQ_PIN_ACCESS_ACPI_GPIO,
IRQ_PIN_ACCESS_ACPI_METHOD,
};
struct goodix_chip_data { struct goodix_chip_data {
u16 config_addr; u16 config_addr;
int config_len; int config_len;
int (*check_config)(struct goodix_ts_data *, const struct firmware *); int (*check_config)(struct goodix_ts_data *ts, const u8 *cfg, int len);
void (*calc_config_checksum)(struct goodix_ts_data *ts);
}; };
struct goodix_chip_id {
const char *id;
const struct goodix_chip_data *data;
};
#define GOODIX_ID_MAX_LEN 4
struct goodix_ts_data { struct goodix_ts_data {
struct i2c_client *client; struct i2c_client *client;
struct input_dev *input_dev; struct input_dev *input_dev;
@ -48,71 +102,72 @@ struct goodix_ts_data {
struct regulator *vddio; struct regulator *vddio;
struct gpio_desc *gpiod_int; struct gpio_desc *gpiod_int;
struct gpio_desc *gpiod_rst; struct gpio_desc *gpiod_rst;
u16 id; int gpio_count;
int gpio_int_idx;
char id[GOODIX_ID_MAX_LEN + 1];
u16 version; u16 version;
const char *cfg_name; const char *cfg_name;
bool reset_controller_at_probe;
bool load_cfg_from_disk;
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;
u8 config[GOODIX_CONFIG_MAX_LENGTH];
unsigned short keymap[GOODIX_MAX_KEYS];
}; };
#define GOODIX_GPIO_INT_NAME "irq"
#define GOODIX_GPIO_RST_NAME "reset"
#define GOODIX_MAX_HEIGHT 4096
#define GOODIX_MAX_WIDTH 4096
#define GOODIX_INT_TRIGGER 1
#define GOODIX_CONTACT_SIZE 8
#define GOODIX_MAX_CONTACT_SIZE 9
#define GOODIX_MAX_CONTACTS 10
#define GOODIX_CONFIG_MAX_LENGTH 240
#define GOODIX_CONFIG_911_LENGTH 186
#define GOODIX_CONFIG_967_LENGTH 228
/* Register defines */
#define GOODIX_REG_COMMAND 0x8040
#define GOODIX_CMD_SCREEN_OFF 0x05
#define GOODIX_READ_COOR_ADDR 0x814E
#define GOODIX_GT1X_REG_CONFIG_DATA 0x8050
#define GOODIX_GT9X_REG_CONFIG_DATA 0x8047
#define GOODIX_REG_ID 0x8140
#define GOODIX_BUFFER_STATUS_READY BIT(7)
#define GOODIX_BUFFER_STATUS_TIMEOUT 20
#define RESOLUTION_LOC 1
#define MAX_CONTACTS_LOC 5
#define TRIGGER_LOC 6
static int goodix_check_cfg_8(struct goodix_ts_data *ts, static int goodix_check_cfg_8(struct goodix_ts_data *ts,
const struct firmware *cfg); const u8 *cfg, int len);
static int goodix_check_cfg_16(struct goodix_ts_data *ts, static int goodix_check_cfg_16(struct goodix_ts_data *ts,
const struct firmware *cfg); const u8 *cfg, int len);
static void goodix_calc_cfg_checksum_8(struct goodix_ts_data *ts);
static void goodix_calc_cfg_checksum_16(struct goodix_ts_data *ts);
static const struct goodix_chip_data gt1x_chip_data = { static const struct goodix_chip_data gt1x_chip_data = {
.config_addr = GOODIX_GT1X_REG_CONFIG_DATA, .config_addr = GOODIX_GT1X_REG_CONFIG_DATA,
.config_len = GOODIX_CONFIG_MAX_LENGTH, .config_len = GOODIX_CONFIG_GT9X_LENGTH,
.check_config = goodix_check_cfg_16, .check_config = goodix_check_cfg_16,
.calc_config_checksum = goodix_calc_cfg_checksum_16,
}; };
static const struct goodix_chip_data gt911_chip_data = { static const struct goodix_chip_data gt911_chip_data = {
.config_addr = GOODIX_GT9X_REG_CONFIG_DATA, .config_addr = GOODIX_GT9X_REG_CONFIG_DATA,
.config_len = GOODIX_CONFIG_911_LENGTH, .config_len = GOODIX_CONFIG_911_LENGTH,
.check_config = goodix_check_cfg_8, .check_config = goodix_check_cfg_8,
.calc_config_checksum = goodix_calc_cfg_checksum_8,
}; };
static const struct goodix_chip_data gt967_chip_data = { static const struct goodix_chip_data gt967_chip_data = {
.config_addr = GOODIX_GT9X_REG_CONFIG_DATA, .config_addr = GOODIX_GT9X_REG_CONFIG_DATA,
.config_len = GOODIX_CONFIG_967_LENGTH, .config_len = GOODIX_CONFIG_967_LENGTH,
.check_config = goodix_check_cfg_8, .check_config = goodix_check_cfg_8,
.calc_config_checksum = goodix_calc_cfg_checksum_8,
}; };
static const struct goodix_chip_data gt9x_chip_data = { static const struct goodix_chip_data gt9x_chip_data = {
.config_addr = GOODIX_GT9X_REG_CONFIG_DATA, .config_addr = GOODIX_GT9X_REG_CONFIG_DATA,
.config_len = GOODIX_CONFIG_MAX_LENGTH, .config_len = GOODIX_CONFIG_GT9X_LENGTH,
.check_config = goodix_check_cfg_8, .check_config = goodix_check_cfg_8,
.calc_config_checksum = goodix_calc_cfg_checksum_8,
};
static const struct goodix_chip_id goodix_chip_ids[] = {
{ .id = "1151", .data = &gt1x_chip_data },
{ .id = "5663", .data = &gt1x_chip_data },
{ .id = "5688", .data = &gt1x_chip_data },
{ .id = "917S", .data = &gt1x_chip_data },
{ .id = "911", .data = &gt911_chip_data },
{ .id = "9271", .data = &gt911_chip_data },
{ .id = "9110", .data = &gt911_chip_data },
{ .id = "927", .data = &gt911_chip_data },
{ .id = "928", .data = &gt911_chip_data },
{ .id = "912", .data = &gt967_chip_data },
{ .id = "9147", .data = &gt967_chip_data },
{ .id = "967", .data = &gt967_chip_data },
{ }
}; };
static const unsigned long goodix_irq_flags[] = { static const unsigned long goodix_irq_flags[] = {
@ -168,6 +223,22 @@ static const struct dmi_system_id nine_bytes_report[] = {
{} {}
}; };
/*
* Those tablets have their x coordinate inverted
*/
static const struct dmi_system_id inverted_x_screen[] = {
#if defined(CONFIG_DMI) && defined(CONFIG_X86)
{
.ident = "Cube I15-TC",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Cube"),
DMI_MATCH(DMI_PRODUCT_NAME, "I15-TC")
},
},
#endif
{}
};
/** /**
* goodix_i2c_read - read data from a register of the i2c slave device. * goodix_i2c_read - read data from a register of the i2c slave device.
* *
@ -235,28 +306,16 @@ static int goodix_i2c_write_u8(struct i2c_client *client, u16 reg, u8 value)
return goodix_i2c_write(client, reg, &value, sizeof(value)); return goodix_i2c_write(client, reg, &value, sizeof(value));
} }
static const struct goodix_chip_data *goodix_get_chip_data(u16 id) static const struct goodix_chip_data *goodix_get_chip_data(const char *id)
{ {
switch (id) { unsigned int i;
case 1151:
case 5663:
case 5688:
return &gt1x_chip_data;
case 911: for (i = 0; goodix_chip_ids[i].id; i++) {
case 9271: if (!strcmp(goodix_chip_ids[i].id, id))
case 9110: return goodix_chip_ids[i].data;
case 927:
case 928:
return &gt911_chip_data;
case 912:
case 967:
return &gt967_chip_data;
default:
return &gt9x_chip_data;
} }
return &gt9x_chip_data;
} }
static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data) static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data)
@ -264,6 +323,13 @@ static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data)
unsigned long max_timeout; unsigned long max_timeout;
int touch_num; int touch_num;
int error; int error;
u16 addr = GOODIX_READ_COOR_ADDR;
/*
* We are going to read 1-byte header,
* ts->contact_size * max(1, touch_num) bytes of coordinates
* and 1-byte footer which contains the touch-key code.
*/
const int header_contact_keycode_size = 1 + ts->contact_size + 1;
/* /*
* The 'buffer status' bit, which indicates that the data is valid, is * The 'buffer status' bit, which indicates that the data is valid, is
@ -272,8 +338,8 @@ static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data)
*/ */
max_timeout = jiffies + msecs_to_jiffies(GOODIX_BUFFER_STATUS_TIMEOUT); max_timeout = jiffies + msecs_to_jiffies(GOODIX_BUFFER_STATUS_TIMEOUT);
do { do {
error = goodix_i2c_read(ts->client, GOODIX_READ_COOR_ADDR, error = goodix_i2c_read(ts->client, addr, data,
data, ts->contact_size + 1); header_contact_keycode_size);
if (error) { if (error) {
dev_err(&ts->client->dev, "I2C transfer error: %d\n", dev_err(&ts->client->dev, "I2C transfer error: %d\n",
error); error);
@ -286,11 +352,10 @@ static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data)
return -EPROTO; return -EPROTO;
if (touch_num > 1) { if (touch_num > 1) {
data += 1 + ts->contact_size; addr += header_contact_keycode_size;
data += header_contact_keycode_size;
error = goodix_i2c_read(ts->client, error = goodix_i2c_read(ts->client,
GOODIX_READ_COOR_ADDR + addr, data,
1 + ts->contact_size,
data,
ts->contact_size * ts->contact_size *
(touch_num - 1)); (touch_num - 1));
if (error) if (error)
@ -307,7 +372,7 @@ static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data)
* The Goodix panel will send spurious interrupts after a * The Goodix panel will send spurious interrupts after a
* 'finger up' event, which will always cause a timeout. * 'finger up' event, which will always cause a timeout.
*/ */
return 0; return -ENOMSG;
} }
static void goodix_ts_report_touch_8b(struct goodix_ts_data *ts, u8 *coor_data) static void goodix_ts_report_touch_8b(struct goodix_ts_data *ts, u8 *coor_data)
@ -340,6 +405,25 @@ static void goodix_ts_report_touch_9b(struct goodix_ts_data *ts, u8 *coor_data)
input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, input_w); input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, input_w);
} }
static void goodix_ts_report_key(struct goodix_ts_data *ts, u8 *data)
{
int touch_num;
u8 key_value;
int i;
if (data[0] & GOODIX_HAVE_KEY) {
touch_num = data[0] & 0x0f;
key_value = data[1 + ts->contact_size * touch_num];
for (i = 0; i < GOODIX_MAX_KEYS; i++)
if (key_value & BIT(i))
input_report_key(ts->input_dev,
ts->keymap[i], 1);
} else {
for (i = 0; i < GOODIX_MAX_KEYS; i++)
input_report_key(ts->input_dev, ts->keymap[i], 0);
}
}
/** /**
* goodix_process_events - Process incoming events * goodix_process_events - Process incoming events
* *
@ -350,7 +434,7 @@ static void goodix_ts_report_touch_9b(struct goodix_ts_data *ts, u8 *coor_data)
*/ */
static void goodix_process_events(struct goodix_ts_data *ts) static void goodix_process_events(struct goodix_ts_data *ts)
{ {
u8 point_data[1 + GOODIX_MAX_CONTACT_SIZE * GOODIX_MAX_CONTACTS]; u8 point_data[2 + GOODIX_MAX_CONTACT_SIZE * GOODIX_MAX_CONTACTS];
int touch_num; int touch_num;
int i; int i;
@ -358,11 +442,7 @@ static void goodix_process_events(struct goodix_ts_data *ts)
if (touch_num < 0) if (touch_num < 0)
return; return;
/* goodix_ts_report_key(ts, point_data);
* Bit 4 of the first byte reports the status of the capacitive
* Windows/Home button.
*/
input_report_key(ts->input_dev, KEY_LEFTMETA, point_data[0] & BIT(4));
for (i = 0; i < touch_num; i++) for (i = 0; i < touch_num; i++)
if (ts->contact_size == 9) if (ts->contact_size == 9)
@ -406,22 +486,21 @@ static int goodix_request_irq(struct goodix_ts_data *ts)
ts->irq_flags, ts->client->name, ts); ts->irq_flags, ts->client->name, ts);
} }
static int goodix_check_cfg_8(struct goodix_ts_data *ts, static int goodix_check_cfg_8(struct goodix_ts_data *ts, const u8 *cfg, int len)
const struct firmware *cfg)
{ {
int i, raw_cfg_len = cfg->size - 2; int i, raw_cfg_len = len - 2;
u8 check_sum = 0; u8 check_sum = 0;
for (i = 0; i < raw_cfg_len; i++) for (i = 0; i < raw_cfg_len; i++)
check_sum += cfg->data[i]; check_sum += cfg[i];
check_sum = (~check_sum) + 1; check_sum = (~check_sum) + 1;
if (check_sum != cfg->data[raw_cfg_len]) { if (check_sum != cfg[raw_cfg_len]) {
dev_err(&ts->client->dev, dev_err(&ts->client->dev,
"The checksum of the config fw is not correct"); "The checksum of the config fw is not correct");
return -EINVAL; return -EINVAL;
} }
if (cfg->data[raw_cfg_len + 1] != 1) { if (cfg[raw_cfg_len + 1] != 1) {
dev_err(&ts->client->dev, dev_err(&ts->client->dev,
"Config fw must have Config_Fresh register set"); "Config fw must have Config_Fresh register set");
return -EINVAL; return -EINVAL;
@ -430,22 +509,35 @@ static int goodix_check_cfg_8(struct goodix_ts_data *ts,
return 0; return 0;
} }
static int goodix_check_cfg_16(struct goodix_ts_data *ts, static void goodix_calc_cfg_checksum_8(struct goodix_ts_data *ts)
const struct firmware *cfg)
{ {
int i, raw_cfg_len = cfg->size - 3; int i, raw_cfg_len = ts->chip->config_len - 2;
u8 check_sum = 0;
for (i = 0; i < raw_cfg_len; i++)
check_sum += ts->config[i];
check_sum = (~check_sum) + 1;
ts->config[raw_cfg_len] = check_sum;
ts->config[raw_cfg_len + 1] = 1; /* Set "config_fresh" bit */
}
static int goodix_check_cfg_16(struct goodix_ts_data *ts, const u8 *cfg,
int len)
{
int i, raw_cfg_len = len - 3;
u16 check_sum = 0; u16 check_sum = 0;
for (i = 0; i < raw_cfg_len; i += 2) for (i = 0; i < raw_cfg_len; i += 2)
check_sum += get_unaligned_be16(&cfg->data[i]); check_sum += get_unaligned_be16(&cfg[i]);
check_sum = (~check_sum) + 1; check_sum = (~check_sum) + 1;
if (check_sum != get_unaligned_be16(&cfg->data[raw_cfg_len])) { if (check_sum != get_unaligned_be16(&cfg[raw_cfg_len])) {
dev_err(&ts->client->dev, dev_err(&ts->client->dev,
"The checksum of the config fw is not correct"); "The checksum of the config fw is not correct");
return -EINVAL; return -EINVAL;
} }
if (cfg->data[raw_cfg_len + 2] != 1) { if (cfg[raw_cfg_len + 2] != 1) {
dev_err(&ts->client->dev, dev_err(&ts->client->dev,
"Config fw must have Config_Fresh register set"); "Config fw must have Config_Fresh register set");
return -EINVAL; return -EINVAL;
@ -454,22 +546,35 @@ static int goodix_check_cfg_16(struct goodix_ts_data *ts,
return 0; return 0;
} }
static void goodix_calc_cfg_checksum_16(struct goodix_ts_data *ts)
{
int i, raw_cfg_len = ts->chip->config_len - 3;
u16 check_sum = 0;
for (i = 0; i < raw_cfg_len; i += 2)
check_sum += get_unaligned_be16(&ts->config[i]);
check_sum = (~check_sum) + 1;
put_unaligned_be16(check_sum, &ts->config[raw_cfg_len]);
ts->config[raw_cfg_len + 2] = 1; /* Set "config_fresh" bit */
}
/** /**
* goodix_check_cfg - Checks if config fw is valid * goodix_check_cfg - Checks if config fw is valid
* *
* @ts: goodix_ts_data pointer * @ts: goodix_ts_data pointer
* @cfg: firmware config data * @cfg: firmware config data
*/ */
static int goodix_check_cfg(struct goodix_ts_data *ts, static int goodix_check_cfg(struct goodix_ts_data *ts, const u8 *cfg, int len)
const struct firmware *cfg)
{ {
if (cfg->size > GOODIX_CONFIG_MAX_LENGTH) { if (len < GOODIX_CONFIG_MIN_LENGTH ||
len > GOODIX_CONFIG_MAX_LENGTH) {
dev_err(&ts->client->dev, dev_err(&ts->client->dev,
"The length of the config fw is not correct"); "The length of the config fw is not correct");
return -EINVAL; return -EINVAL;
} }
return ts->chip->check_config(ts, cfg); return ts->chip->check_config(ts, cfg, len);
} }
/** /**
@ -478,17 +583,15 @@ static int goodix_check_cfg(struct goodix_ts_data *ts,
* @ts: goodix_ts_data pointer * @ts: goodix_ts_data pointer
* @cfg: config firmware to write to device * @cfg: config firmware to write to device
*/ */
static int goodix_send_cfg(struct goodix_ts_data *ts, static int goodix_send_cfg(struct goodix_ts_data *ts, const u8 *cfg, int len)
const struct firmware *cfg)
{ {
int error; int error;
error = goodix_check_cfg(ts, cfg); error = goodix_check_cfg(ts, cfg, len);
if (error) if (error)
return error; return error;
error = goodix_i2c_write(ts->client, ts->chip->config_addr, cfg->data, error = goodix_i2c_write(ts->client, ts->chip->config_addr, cfg, len);
cfg->size);
if (error) { if (error) {
dev_err(&ts->client->dev, "Failed to write config data: %d", dev_err(&ts->client->dev, "Failed to write config data: %d",
error); error);
@ -502,17 +605,93 @@ static int goodix_send_cfg(struct goodix_ts_data *ts,
return 0; return 0;
} }
#ifdef ACPI_GPIO_SUPPORT
static int goodix_pin_acpi_direction_input(struct goodix_ts_data *ts)
{
acpi_handle handle = ACPI_HANDLE(&ts->client->dev);
acpi_status status;
status = acpi_evaluate_object(handle, "INTI", NULL, NULL);
return ACPI_SUCCESS(status) ? 0 : -EIO;
}
static int goodix_pin_acpi_output_method(struct goodix_ts_data *ts, int value)
{
acpi_handle handle = ACPI_HANDLE(&ts->client->dev);
acpi_status status;
status = acpi_execute_simple_method(handle, "INTO", value);
return ACPI_SUCCESS(status) ? 0 : -EIO;
}
#else
static int goodix_pin_acpi_direction_input(struct goodix_ts_data *ts)
{
dev_err(&ts->client->dev,
"%s called on device without ACPI support\n", __func__);
return -EINVAL;
}
static int goodix_pin_acpi_output_method(struct goodix_ts_data *ts, int value)
{
dev_err(&ts->client->dev,
"%s called on device without ACPI support\n", __func__);
return -EINVAL;
}
#endif
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);
case IRQ_PIN_ACCESS_ACPI_GPIO:
/*
* The IRQ pin triggers on a falling edge, so its gets marked
* as active-low, use output_raw to avoid the value inversion.
*/
return gpiod_direction_output_raw(ts->gpiod_int, value);
case IRQ_PIN_ACCESS_ACPI_METHOD:
return goodix_pin_acpi_output_method(ts, 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);
case IRQ_PIN_ACCESS_ACPI_GPIO:
return gpiod_direction_input(ts->gpiod_int);
case IRQ_PIN_ACCESS_ACPI_METHOD:
return goodix_pin_acpi_direction_input(ts);
}
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;
@ -536,7 +715,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;
@ -560,6 +739,124 @@ static int goodix_reset(struct goodix_ts_data *ts)
return 0; return 0;
} }
#ifdef ACPI_GPIO_SUPPORT
#include <asm/cpu_device_id.h>
#include <asm/intel-family.h>
static const struct x86_cpu_id baytrail_cpu_ids[] = {
{ X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_SILVERMONT, X86_FEATURE_ANY, },
{}
};
static inline bool is_byt(void)
{
const struct x86_cpu_id *id = x86_match_cpu(baytrail_cpu_ids);
return !!id;
}
static const struct acpi_gpio_params first_gpio = { 0, 0, false };
static const struct acpi_gpio_params second_gpio = { 1, 0, false };
static const struct acpi_gpio_mapping acpi_goodix_int_first_gpios[] = {
{ GOODIX_GPIO_INT_NAME "-gpios", &first_gpio, 1 },
{ GOODIX_GPIO_RST_NAME "-gpios", &second_gpio, 1 },
{ },
};
static const struct acpi_gpio_mapping acpi_goodix_int_last_gpios[] = {
{ GOODIX_GPIO_RST_NAME "-gpios", &first_gpio, 1 },
{ GOODIX_GPIO_INT_NAME "-gpios", &second_gpio, 1 },
{ },
};
static const struct acpi_gpio_mapping acpi_goodix_reset_only_gpios[] = {
{ GOODIX_GPIO_RST_NAME "-gpios", &first_gpio, 1 },
{ },
};
static int goodix_resource(struct acpi_resource *ares, void *data)
{
struct goodix_ts_data *ts = data;
struct device *dev = &ts->client->dev;
struct acpi_resource_gpio *gpio;
switch (ares->type) {
case ACPI_RESOURCE_TYPE_GPIO:
gpio = &ares->data.gpio;
if (gpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT) {
if (ts->gpio_int_idx == -1) {
ts->gpio_int_idx = ts->gpio_count;
} else {
dev_err(dev, "More then one GpioInt resource, ignoring ACPI GPIO resources\n");
ts->gpio_int_idx = -2;
}
}
ts->gpio_count++;
break;
default:
break;
}
return 0;
}
/*
* This function gets called in case we fail to get the irq GPIO directly
* because the ACPI tables lack GPIO-name to APCI _CRS index mappings
* (no _DSD UUID daffd814-6eba-4d8c-8a91-bc9bbf4aa301 data).
* In that case we add our own mapping and then goodix_get_gpio_config()
* retries to get the GPIOs based on the added mapping.
*/
static int goodix_add_acpi_gpio_mappings(struct goodix_ts_data *ts)
{
const struct acpi_gpio_mapping *gpio_mapping = NULL;
struct device *dev = &ts->client->dev;
LIST_HEAD(resources);
int ret;
ts->gpio_count = 0;
ts->gpio_int_idx = -1;
ret = acpi_dev_get_resources(ACPI_COMPANION(dev), &resources,
goodix_resource, ts);
if (ret < 0) {
dev_err(dev, "Error getting ACPI resources: %d\n", ret);
return ret;
}
acpi_dev_free_resource_list(&resources);
if (ts->gpio_count == 2 && ts->gpio_int_idx == 0) {
ts->irq_pin_access_method = IRQ_PIN_ACCESS_ACPI_GPIO;
gpio_mapping = acpi_goodix_int_first_gpios;
} else if (ts->gpio_count == 2 && ts->gpio_int_idx == 1) {
ts->irq_pin_access_method = IRQ_PIN_ACCESS_ACPI_GPIO;
gpio_mapping = acpi_goodix_int_last_gpios;
} else if (ts->gpio_count == 1 && ts->gpio_int_idx == -1 &&
acpi_has_method(ACPI_HANDLE(dev), "INTI") &&
acpi_has_method(ACPI_HANDLE(dev), "INTO")) {
dev_info(dev, "Using ACPI INTI and INTO methods for IRQ pin access\n");
ts->irq_pin_access_method = IRQ_PIN_ACCESS_ACPI_METHOD;
gpio_mapping = acpi_goodix_reset_only_gpios;
} else if (is_byt() && ts->gpio_count == 2 && ts->gpio_int_idx == -1) {
dev_info(dev, "No ACPI GpioInt resource, assuming that the GPIO order is reset, int\n");
ts->irq_pin_access_method = IRQ_PIN_ACCESS_ACPI_GPIO;
gpio_mapping = acpi_goodix_int_last_gpios;
} else {
dev_warn(dev, "Unexpected ACPI resources: gpio_count %d, gpio_int_idx %d\n",
ts->gpio_count, ts->gpio_int_idx);
return -EINVAL;
}
return devm_acpi_dev_add_driver_gpios(dev, gpio_mapping);
}
#else
static int goodix_add_acpi_gpio_mappings(struct goodix_ts_data *ts)
{
return -EINVAL;
}
#endif /* CONFIG_X86 && CONFIG_ACPI */
/** /**
* goodix_get_gpio_config - Get GPIO config from ACPI/DT * goodix_get_gpio_config - Get GPIO config from ACPI/DT
* *
@ -570,6 +867,7 @@ static int goodix_get_gpio_config(struct goodix_ts_data *ts)
int error; int error;
struct device *dev; struct device *dev;
struct gpio_desc *gpiod; struct gpio_desc *gpiod;
bool added_acpi_mappings = false;
if (!ts->client) if (!ts->client)
return -EINVAL; return -EINVAL;
@ -593,6 +891,7 @@ static int goodix_get_gpio_config(struct goodix_ts_data *ts)
return error; return error;
} }
retry_get_irq_gpio:
/* Get the interrupt GPIO pin number */ /* Get the interrupt GPIO pin number */
gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_INT_NAME, GPIOD_IN); gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_INT_NAME, GPIOD_IN);
if (IS_ERR(gpiod)) { if (IS_ERR(gpiod)) {
@ -602,6 +901,11 @@ static int goodix_get_gpio_config(struct goodix_ts_data *ts)
GOODIX_GPIO_INT_NAME, error); GOODIX_GPIO_INT_NAME, error);
return error; return error;
} }
if (!gpiod && has_acpi_companion(dev) && !added_acpi_mappings) {
added_acpi_mappings = true;
if (goodix_add_acpi_gpio_mappings(ts) == 0)
goto retry_get_irq_gpio;
}
ts->gpiod_int = gpiod; ts->gpiod_int = gpiod;
@ -617,6 +921,31 @@ static int goodix_get_gpio_config(struct goodix_ts_data *ts)
ts->gpiod_rst = gpiod; ts->gpiod_rst = gpiod;
switch (ts->irq_pin_access_method) {
case IRQ_PIN_ACCESS_ACPI_GPIO:
/*
* We end up here if goodix_add_acpi_gpio_mappings() has
* called devm_acpi_dev_add_driver_gpios() because the ACPI
* tables did not contain name to index mappings.
* Check that we successfully got both GPIOs after we've
* added our own acpi_gpio_mapping and if we did not get both
* GPIOs reset irq_pin_access_method to IRQ_PIN_ACCESS_NONE.
*/
if (!ts->gpiod_int || !ts->gpiod_rst)
ts->irq_pin_access_method = IRQ_PIN_ACCESS_NONE;
break;
case IRQ_PIN_ACCESS_ACPI_METHOD:
if (!ts->gpiod_rst)
ts->irq_pin_access_method = IRQ_PIN_ACCESS_NONE;
break;
default:
if (ts->gpiod_int && ts->gpiod_rst) {
ts->reset_controller_at_probe = true;
ts->load_cfg_from_disk = true;
ts->irq_pin_access_method = IRQ_PIN_ACCESS_GPIO;
}
}
return 0; return 0;
} }
@ -629,12 +958,11 @@ static int goodix_get_gpio_config(struct goodix_ts_data *ts)
*/ */
static void goodix_read_config(struct goodix_ts_data *ts) static void goodix_read_config(struct goodix_ts_data *ts)
{ {
u8 config[GOODIX_CONFIG_MAX_LENGTH];
int x_max, y_max; int x_max, y_max;
int error; int error;
error = goodix_i2c_read(ts->client, ts->chip->config_addr, error = goodix_i2c_read(ts->client, ts->chip->config_addr,
config, ts->chip->config_len); ts->config, ts->chip->config_len);
if (error) { if (error) {
dev_warn(&ts->client->dev, "Error reading config: %d\n", dev_warn(&ts->client->dev, "Error reading config: %d\n",
error); error);
@ -643,15 +971,17 @@ static void goodix_read_config(struct goodix_ts_data *ts)
return; return;
} }
ts->int_trigger_type = config[TRIGGER_LOC] & 0x03; ts->int_trigger_type = ts->config[TRIGGER_LOC] & 0x03;
ts->max_touch_num = config[MAX_CONTACTS_LOC] & 0x0f; ts->max_touch_num = ts->config[MAX_CONTACTS_LOC] & 0x0f;
x_max = get_unaligned_le16(&config[RESOLUTION_LOC]); x_max = get_unaligned_le16(&ts->config[RESOLUTION_LOC]);
y_max = get_unaligned_le16(&config[RESOLUTION_LOC + 2]); y_max = get_unaligned_le16(&ts->config[RESOLUTION_LOC + 2]);
if (x_max && y_max) { if (x_max && y_max) {
input_abs_set_max(ts->input_dev, ABS_MT_POSITION_X, x_max - 1); input_abs_set_max(ts->input_dev, ABS_MT_POSITION_X, x_max - 1);
input_abs_set_max(ts->input_dev, ABS_MT_POSITION_Y, y_max - 1); input_abs_set_max(ts->input_dev, ABS_MT_POSITION_Y, y_max - 1);
} }
ts->chip->calc_config_checksum(ts);
} }
/** /**
@ -663,7 +993,7 @@ static int goodix_read_version(struct goodix_ts_data *ts)
{ {
int error; int error;
u8 buf[6]; u8 buf[6];
char id_str[5]; char id_str[GOODIX_ID_MAX_LEN + 1];
error = goodix_i2c_read(ts->client, GOODIX_REG_ID, buf, sizeof(buf)); error = goodix_i2c_read(ts->client, GOODIX_REG_ID, buf, sizeof(buf));
if (error) { if (error) {
@ -671,14 +1001,13 @@ static int goodix_read_version(struct goodix_ts_data *ts)
return error; return error;
} }
memcpy(id_str, buf, 4); memcpy(id_str, buf, GOODIX_ID_MAX_LEN);
id_str[4] = 0; id_str[GOODIX_ID_MAX_LEN] = 0;
if (kstrtou16(id_str, 10, &ts->id)) strscpy(ts->id, id_str, GOODIX_ID_MAX_LEN + 1);
ts->id = 0x1001;
ts->version = get_unaligned_le16(&buf[4]); ts->version = get_unaligned_le16(&buf[4]);
dev_info(&ts->client->dev, "ID %d, version: %04x\n", ts->id, dev_info(&ts->client->dev, "ID %s, version: %04x\n", ts->id,
ts->version); ts->version);
return 0; return 0;
@ -722,6 +1051,7 @@ static int goodix_i2c_test(struct i2c_client *client)
static int goodix_configure_dev(struct goodix_ts_data *ts) static int goodix_configure_dev(struct goodix_ts_data *ts)
{ {
int error; int error;
int i;
ts->int_trigger_type = GOODIX_INT_TRIGGER; ts->int_trigger_type = GOODIX_INT_TRIGGER;
ts->max_touch_num = GOODIX_MAX_CONTACTS; ts->max_touch_num = GOODIX_MAX_CONTACTS;
@ -736,11 +1066,23 @@ static int goodix_configure_dev(struct goodix_ts_data *ts)
ts->input_dev->phys = "input/ts"; ts->input_dev->phys = "input/ts";
ts->input_dev->id.bustype = BUS_I2C; ts->input_dev->id.bustype = BUS_I2C;
ts->input_dev->id.vendor = 0x0416; ts->input_dev->id.vendor = 0x0416;
ts->input_dev->id.product = ts->id; if (kstrtou16(ts->id, 10, &ts->input_dev->id.product))
ts->input_dev->id.product = 0x1001;
ts->input_dev->id.version = ts->version; ts->input_dev->id.version = ts->version;
ts->input_dev->keycode = ts->keymap;
ts->input_dev->keycodesize = sizeof(ts->keymap[0]);
ts->input_dev->keycodemax = GOODIX_MAX_KEYS;
/* Capacitive Windows/Home button on some devices */ /* Capacitive Windows/Home button on some devices */
input_set_capability(ts->input_dev, EV_KEY, KEY_LEFTMETA); for (i = 0; i < GOODIX_MAX_KEYS; ++i) {
if (i == 0)
ts->keymap[i] = KEY_LEFTMETA;
else
ts->keymap[i] = KEY_F1 + (i - 1);
input_set_capability(ts->input_dev, EV_KEY, ts->keymap[i]);
}
input_set_capability(ts->input_dev, EV_ABS, ABS_MT_POSITION_X); input_set_capability(ts->input_dev, EV_ABS, ABS_MT_POSITION_X);
input_set_capability(ts->input_dev, EV_ABS, ABS_MT_POSITION_Y); input_set_capability(ts->input_dev, EV_ABS, ABS_MT_POSITION_Y);
@ -780,6 +1122,12 @@ static int goodix_configure_dev(struct goodix_ts_data *ts)
"Non-standard 9-bytes report format quirk\n"); "Non-standard 9-bytes report format quirk\n");
} }
if (dmi_check_system(inverted_x_screen)) {
ts->prop.invert_x = true;
dev_dbg(&ts->client->dev,
"Applying 'inverted x screen' quirk\n");
}
error = input_mt_init_slots(ts->input_dev, ts->max_touch_num, error = input_mt_init_slots(ts->input_dev, ts->max_touch_num,
INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
if (error) { if (error) {
@ -820,7 +1168,7 @@ static void goodix_config_cb(const struct firmware *cfg, void *ctx)
if (cfg) { if (cfg) {
/* send device configuration to the firmware */ /* send device configuration to the firmware */
error = goodix_send_cfg(ts, cfg); error = goodix_send_cfg(ts, cfg->data, cfg->size);
if (error) if (error)
goto err_release_cfg; goto err_release_cfg;
} }
@ -889,7 +1237,8 @@ static int goodix_ts_probe(struct i2c_client *client,
if (error) if (error)
return error; return error;
if (ts->gpiod_int && ts->gpiod_rst) { reset:
if (ts->reset_controller_at_probe) {
/* reset the controller */ /* reset the controller */
error = goodix_reset(ts); error = goodix_reset(ts);
if (error) { if (error) {
@ -900,6 +1249,12 @@ static int goodix_ts_probe(struct i2c_client *client,
error = goodix_i2c_test(client); error = goodix_i2c_test(client);
if (error) { if (error) {
if (!ts->reset_controller_at_probe &&
ts->irq_pin_access_method != IRQ_PIN_ACCESS_NONE) {
/* Retry after a controller reset */
ts->reset_controller_at_probe = true;
goto reset;
}
dev_err(&client->dev, "I2C communication failure: %d\n", error); dev_err(&client->dev, "I2C communication failure: %d\n", error);
return error; return error;
} }
@ -912,10 +1267,10 @@ 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->load_cfg_from_disk) {
/* 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_%s_cfg.bin", ts->id);
if (!ts->cfg_name) if (!ts->cfg_name)
return -ENOMEM; return -ENOMEM;
@ -943,7 +1298,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->load_cfg_from_disk)
wait_for_completion(&ts->firmware_loading_complete); wait_for_completion(&ts->firmware_loading_complete);
return 0; return 0;
@ -955,19 +1310,20 @@ static int __maybe_unused goodix_suspend(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->load_cfg_from_disk)
wait_for_completion(&ts->firmware_loading_complete);
/* 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;
} }
wait_for_completion(&ts->firmware_loading_complete);
/* Free IRQ as IRQ pin is used as output in the suspend sequence */ /* Free IRQ as IRQ pin is used as output in the suspend sequence */
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;
@ -979,7 +1335,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;
} }
@ -997,9 +1353,10 @@ static int __maybe_unused goodix_resume(struct device *dev)
{ {
struct i2c_client *client = to_i2c_client(dev); struct i2c_client *client = to_i2c_client(dev);
struct goodix_ts_data *ts = i2c_get_clientdata(client); struct goodix_ts_data *ts = i2c_get_clientdata(client);
u8 config_ver;
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;
} }
@ -1008,7 +1365,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;
@ -1018,6 +1375,27 @@ static int __maybe_unused goodix_resume(struct device *dev)
if (error) if (error)
return error; return error;
error = goodix_i2c_read(ts->client, ts->chip->config_addr,
&config_ver, 1);
if (error)
dev_warn(dev, "Error reading config version: %d, resetting controller\n",
error);
else if (config_ver != ts->config[0])
dev_info(dev, "Config version mismatch %d != %d, resetting controller\n",
config_ver, ts->config[0]);
if (error != 0 || config_ver != ts->config[0]) {
error = goodix_reset(ts);
if (error) {
dev_err(dev, "Controller reset failed.\n");
return error;
}
error = goodix_send_cfg(ts, ts->config, ts->chip->config_len);
if (error)
return error;
}
error = goodix_request_irq(ts); error = goodix_request_irq(ts);
if (error) if (error)
return error; return error;
@ -1050,6 +1428,8 @@ static const struct of_device_id goodix_of_match[] = {
{ .compatible = "goodix,gt911" }, { .compatible = "goodix,gt911" },
{ .compatible = "goodix,gt9110" }, { .compatible = "goodix,gt9110" },
{ .compatible = "goodix,gt912" }, { .compatible = "goodix,gt912" },
{ .compatible = "goodix,gt9147" },
{ .compatible = "goodix,gt917s" },
{ .compatible = "goodix,gt927" }, { .compatible = "goodix,gt927" },
{ .compatible = "goodix,gt9271" }, { .compatible = "goodix,gt9271" },
{ .compatible = "goodix,gt928" }, { .compatible = "goodix,gt928" },

Просмотреть файл

@ -66,7 +66,7 @@ void touchscreen_parse_properties(struct input_dev *input, bool multitouch,
{ {
struct device *dev = input->dev.parent; struct device *dev = input->dev.parent;
struct input_absinfo *absinfo; struct input_absinfo *absinfo;
unsigned int axis; unsigned int axis, axis_x, axis_y;
unsigned int minimum, maximum, fuzz; unsigned int minimum, maximum, fuzz;
bool data_present; bool data_present;
@ -74,33 +74,34 @@ void touchscreen_parse_properties(struct input_dev *input, bool multitouch,
if (!input->absinfo) if (!input->absinfo)
return; return;
axis = multitouch ? ABS_MT_POSITION_X : ABS_X; axis_x = multitouch ? ABS_MT_POSITION_X : ABS_X;
axis_y = multitouch ? ABS_MT_POSITION_Y : ABS_Y;
data_present = touchscreen_get_prop_u32(dev, "touchscreen-min-x", data_present = touchscreen_get_prop_u32(dev, "touchscreen-min-x",
input_abs_get_min(input, axis), input_abs_get_min(input, axis_x),
&minimum) | &minimum) |
touchscreen_get_prop_u32(dev, "touchscreen-size-x", touchscreen_get_prop_u32(dev, "touchscreen-size-x",
input_abs_get_max(input, input_abs_get_max(input,
axis) + 1, axis_x) + 1,
&maximum) | &maximum) |
touchscreen_get_prop_u32(dev, "touchscreen-fuzz-x", touchscreen_get_prop_u32(dev, "touchscreen-fuzz-x",
input_abs_get_fuzz(input, axis), input_abs_get_fuzz(input, axis_x),
&fuzz); &fuzz);
if (data_present) if (data_present)
touchscreen_set_params(input, axis, minimum, maximum - 1, fuzz); touchscreen_set_params(input, axis_x, minimum, maximum - 1, fuzz);
axis = multitouch ? ABS_MT_POSITION_Y : ABS_Y;
data_present = touchscreen_get_prop_u32(dev, "touchscreen-min-y", data_present = touchscreen_get_prop_u32(dev, "touchscreen-min-y",
input_abs_get_min(input, axis), input_abs_get_min(input, axis_y),
&minimum) | &minimum) |
touchscreen_get_prop_u32(dev, "touchscreen-size-y", touchscreen_get_prop_u32(dev, "touchscreen-size-y",
input_abs_get_max(input, input_abs_get_max(input,
axis) + 1, axis_y) + 1,
&maximum) | &maximum) |
touchscreen_get_prop_u32(dev, "touchscreen-fuzz-y", touchscreen_get_prop_u32(dev, "touchscreen-fuzz-y",
input_abs_get_fuzz(input, axis), input_abs_get_fuzz(input, axis_y),
&fuzz); &fuzz);
if (data_present) if (data_present)
touchscreen_set_params(input, axis, minimum, maximum - 1, fuzz); touchscreen_set_params(input, axis_y, minimum, maximum - 1, fuzz);
axis = multitouch ? ABS_MT_PRESSURE : ABS_PRESSURE; axis = multitouch ? ABS_MT_PRESSURE : ABS_PRESSURE;
data_present = touchscreen_get_prop_u32(dev, data_present = touchscreen_get_prop_u32(dev,
@ -117,15 +118,13 @@ void touchscreen_parse_properties(struct input_dev *input, bool multitouch,
if (!prop) if (!prop)
return; return;
axis = multitouch ? ABS_MT_POSITION_X : ABS_X; prop->max_x = input_abs_get_max(input, axis_x);
prop->max_y = input_abs_get_max(input, axis_y);
prop->max_x = input_abs_get_max(input, axis);
prop->max_y = input_abs_get_max(input, axis + 1);
prop->invert_x = prop->invert_x =
device_property_read_bool(dev, "touchscreen-inverted-x"); device_property_read_bool(dev, "touchscreen-inverted-x");
if (prop->invert_x) { if (prop->invert_x) {
absinfo = &input->absinfo[axis]; absinfo = &input->absinfo[axis_x];
absinfo->maximum -= absinfo->minimum; absinfo->maximum -= absinfo->minimum;
absinfo->minimum = 0; absinfo->minimum = 0;
} }
@ -133,7 +132,7 @@ void touchscreen_parse_properties(struct input_dev *input, bool multitouch,
prop->invert_y = prop->invert_y =
device_property_read_bool(dev, "touchscreen-inverted-y"); device_property_read_bool(dev, "touchscreen-inverted-y");
if (prop->invert_y) { if (prop->invert_y) {
absinfo = &input->absinfo[axis + 1]; absinfo = &input->absinfo[axis_y];
absinfo->maximum -= absinfo->minimum; absinfo->maximum -= absinfo->minimum;
absinfo->minimum = 0; absinfo->minimum = 0;
} }
@ -141,7 +140,7 @@ void touchscreen_parse_properties(struct input_dev *input, bool multitouch,
prop->swap_x_y = prop->swap_x_y =
device_property_read_bool(dev, "touchscreen-swapped-x-y"); device_property_read_bool(dev, "touchscreen-swapped-x-y");
if (prop->swap_x_y) if (prop->swap_x_y)
swap(input->absinfo[axis], input->absinfo[axis + 1]); swap(input->absinfo[axis_x], input->absinfo[axis_y]);
} }
EXPORT_SYMBOL(touchscreen_parse_properties); EXPORT_SYMBOL(touchscreen_parse_properties);

Просмотреть файл

@ -1,4 +1,4 @@
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
/* /*
* Input event codes * Input event codes
* *