diff options
Diffstat (limited to 'driver')
-rw-r--r-- | driver/build.mk | 1 | ||||
-rw-r--r-- | driver/touchpad_gt7288.c | 229 | ||||
-rw-r--r-- | driver/touchpad_gt7288.h | 76 |
3 files changed, 306 insertions, 0 deletions
diff --git a/driver/build.mk b/driver/build.mk index e110e41667..67a082fab1 100644 --- a/driver/build.mk +++ b/driver/build.mk @@ -108,6 +108,7 @@ driver-$(CONFIG_TEMP_SENSOR_TMP432)+=temp_sensor/tmp432.o driver-$(CONFIG_TEMP_SENSOR_TMP468)+=temp_sensor/tmp468.o # Touchpads +driver-$(CONFIG_TOUCHPAD_GT7288)+=touchpad_gt7288.o driver-$(CONFIG_TOUCHPAD_ELAN)+=touchpad_elan.o driver-$(CONFIG_TOUCHPAD_ST)+=touchpad_st.o diff --git a/driver/touchpad_gt7288.c b/driver/touchpad_gt7288.c new file mode 100644 index 0000000000..0b04cfbf5c --- /dev/null +++ b/driver/touchpad_gt7288.c @@ -0,0 +1,229 @@ +/* Copyright 2019 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <stdbool.h> + +#include "common.h" +#include "console.h" +#include "hooks.h" +#include "i2c.h" +#include "task.h" +#include "touchpad_gt7288.h" +#include "util.h" + +/* Define this to enable various warning messages during report parsing. */ +#undef DEBUG_CHECKS + +#define CPRINTS(format, args...) cprints(CC_TOUCHPAD, format, ## args) + +#define RETURN_ERROR(fn) do { \ + int error = (fn); \ + if (error != EC_SUCCESS) \ + return error; \ +} while (0) + +#define GT7288_SLAVE_ADDRESS 0x14 + +#define GT7288_REPORT_ID_PTP 0x04 + +#define GT7288_BUTTON_STATE_UP 0x80 +#define GT7288_BUTTON_STATE_DOWN 0x81 + +#define GT7288_REG_HID_DESCRIPTOR 0x0001 +#define GT7288_REG_REPORT_DESCRIPTOR 0x0002 + +#define GT7288_HID_DESCRIPTOR_LENGTH 0x1E +#define GT7288_REPORT_DESCRIPTOR_LENGTH 0x1AE +#define GT7288_REPORT_LENGTH 16 + +/** + * gt7288_read_desc() - Read a descriptor using the Conventional Read Mode. + * @register_id: the register containing the descriptor to read. + * @data: the data that is read. + * @max_length: the maximum length of data. + * + * Return: EC_SUCCESS or an error code. + */ +static int gt7288_read_desc(uint16_t register_id, uint8_t *data, + size_t max_length) +{ + uint8_t reg_bytes[] = { + register_id & 0xFF, (register_id & 0xFF00) >> 8 + }; + return i2c_xfer(CONFIG_TOUCHPAD_I2C_PORT, GT7288_SLAVE_ADDRESS, + reg_bytes, sizeof(reg_bytes), data, max_length); +} + +int gt7288_get_version_info(struct gt7288_version_info *info) +{ + uint8_t data[GT7288_HID_DESCRIPTOR_LENGTH]; + + RETURN_ERROR(gt7288_read_desc(GT7288_REG_HID_DESCRIPTOR, data, + sizeof(data))); + info->product_id = UINT16_FROM_BYTE_ARRAY_LE(data, 22); + info->version_id = UINT16_FROM_BYTE_ARRAY_LE(data, 24); + return EC_SUCCESS; +} + +static void gt7288_translate_contact(const uint8_t *data, + struct gt7288_contact *contact) +{ + if (IS_ENABLED(DEBUG_CHECKS)) { + uint8_t report_id = data[2]; + + if (report_id != GT7288_REPORT_ID_PTP) { + CPRINTS("WARNING: unexpected report ID 0x%02X (expected 0x%02X).", + report_id, GT7288_REPORT_ID_PTP); + } + } + + contact->id = data[3] >> 4; + /* Note: these bits appear to be in the wrong order in the programming + * guide, verified by experimentation. + */ + contact->tip = (data[3] & BIT(1)) >> 1; + contact->confidence = data[3] & BIT(0); + contact->x = UINT16_FROM_BYTE_ARRAY_LE(data, 4); + contact->y = UINT16_FROM_BYTE_ARRAY_LE(data, 6); +} + +static int gt7288_read(uint8_t *data, size_t max_length) +{ + return i2c_xfer(CONFIG_TOUCHPAD_I2C_PORT, GT7288_SLAVE_ADDRESS, + NULL, 0, data, max_length); +} + +int gt7288_read_ptp_report(struct gt7288_ptp_report *report) +{ + size_t i; + uint8_t data[GT7288_REPORT_LENGTH]; + + RETURN_ERROR(gt7288_read(data, sizeof(data))); + report->timestamp = UINT16_FROM_BYTE_ARRAY_LE(data, 8); + + if (data[10] > GT7288_MAX_CONTACTS) { + if (IS_ENABLED(DEBUG_CHECKS)) + CPRINTS("ERROR: too many contacts (%d > %d).", + data[10], GT7288_MAX_CONTACTS); + return EC_ERROR_HW_INTERNAL; + } + report->num_contacts = data[10]; + + if (IS_ENABLED(DEBUG_CHECKS) && data[11] != GT7288_BUTTON_STATE_UP && + data[11] != GT7288_BUTTON_STATE_DOWN) { + CPRINTS("WARNING: unexpected button state 0x%02X (expected 0x%02X or 0x%02X).", + data[11], GT7288_BUTTON_STATE_UP, + GT7288_BUTTON_STATE_DOWN); + } + report->button_down = data[11] == GT7288_BUTTON_STATE_DOWN; + + gt7288_translate_contact(data, &report->contacts[0]); + + for (i = 1; i < report->num_contacts; i++) { + RETURN_ERROR(gt7288_read(data, sizeof(data))); + gt7288_translate_contact(data, &report->contacts[i]); + } + return EC_SUCCESS; +} + +#ifdef CONFIG_CMD_GT7288 +static int command_gt7288_read_desc(int argc, char **argv) +{ + uint16_t register_id; + long parsed_arg; + char *end; + int i; + uint8_t data[GT7288_HID_DESCRIPTOR_LENGTH]; + + if (argc != 2) + return EC_ERROR_PARAM_COUNT; + + parsed_arg = strtoi(argv[1], &end, 0); + if (parsed_arg < 0 || parsed_arg > UINT16_MAX || end == argv[1]) + return EC_ERROR_PARAM1; + register_id = parsed_arg; + + RETURN_ERROR(gt7288_read_desc(register_id, data, sizeof(data))); + + ccprintf("Data: "); + for (i = 0; i < sizeof(data); i++) + ccprintf("%02X ", data[i]); + ccprintf("\n"); + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(gt7288_desc, command_gt7288_read_desc, + "register", + "Read a descriptor on the GT7288"); + +static int command_gt7288_read_report_descriptor(int argc, char **argv) +{ + int i; + uint8_t data[64]; + size_t bytes_read = 0; + + if (argc != 1) + return EC_ERROR_PARAM_COUNT; + + /* The report descriptor is bigger than the Maxim I2C code can handle in + * one go, so we have to split it into chunks. + */ + RETURN_ERROR(gt7288_read_desc(GT7288_REG_REPORT_DESCRIPTOR, NULL, 0)); + ccprintf("Report descriptor: "); + while (bytes_read < GT7288_REPORT_DESCRIPTOR_LENGTH) { + size_t bytes_to_read = + MIN(GT7288_REPORT_DESCRIPTOR_LENGTH - bytes_read, + sizeof(data)); + RETURN_ERROR(gt7288_read(data, bytes_to_read)); + + for (i = 0; i < sizeof(data); i++) + ccprintf("%02X ", data[i]); + + bytes_read += bytes_to_read; + } + ccprintf("\n"); + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(gt7288_repdesc, command_gt7288_read_report_descriptor, + "", "Read the report descriptor on the GT7288"); + +static int command_gt7288_ver(int argc, char **argv) +{ + struct gt7288_version_info info; + + if (argc != 1) + return EC_ERROR_PARAM_COUNT; + + RETURN_ERROR(gt7288_get_version_info(&info)); + ccprintf("Product ID: 0x%04X\n", info.product_id); + ccprintf("Version ID: 0x%04X\n", info.version_id); + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(gt7288_ver, command_gt7288_ver, "", + "Read version information from the GT7288"); + +static int command_gt7288_report(int argc, char **argv) +{ + int i; + struct gt7288_ptp_report report; + + RETURN_ERROR(gt7288_read_ptp_report(&report)); + ccprintf("Timestamp %d, button %s, %d contacts\n", report.timestamp, + report.button_down ? "down" : "up", report.num_contacts); + if (report.num_contacts == 0) + return EC_SUCCESS; + + ccprintf("ID, X, Y, tip, confidence\n"); + for (i = 0; i < report.num_contacts; i++) { + struct gt7288_contact *contact = &report.contacts[i]; + + ccprintf("%2d, %4d, %4d, %3d, %10d\n", contact->id, contact->x, + contact->y, contact->tip, contact->confidence); + } + + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(gt7288_rep, command_gt7288_report, "", + "Read a report from the GT7288."); +#endif /* CONFIG_CMD_GT7288 */ diff --git a/driver/touchpad_gt7288.h b/driver/touchpad_gt7288.h new file mode 100644 index 0000000000..28a2e37d73 --- /dev/null +++ b/driver/touchpad_gt7288.h @@ -0,0 +1,76 @@ +/* Copyright 2019 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* Driver for the Goodix GT7288 touch controller. */ + +#ifndef __CROS_EC_TOUCHPAD_GT7288_H +#define __CROS_EC_TOUCHPAD_GT7288_H + +#include <stdbool.h> +#include <stddef.h> + +/* The maximum number of contacts that can be reported at once. */ +#define GT7288_MAX_CONTACTS 5 + +/** + * struct gt7288_version_info - version information for the chip. + * @product_id: HID product ID (0x01F0 for touchpads, 0x01F1 for touchscreens). + * @version_id: the firmware version. For touchpads equipped with a fingerprint + * sensor, the MSB will be 1. + */ +struct gt7288_version_info { + uint16_t product_id; + uint16_t version_id; +}; + +/** + * gt7288_get_version_info() - Reads version information from the GT7288. + * @info: the version information. + * + * Return: EC_SUCCESS or an error code. + */ +int gt7288_get_version_info(struct gt7288_version_info *info); + +/** + * struct gt7288_contact - data describing a single contact. + * @id: a 4-bit ID that uniquely identifies the contact during its lifecycle. + * @x: the absolute X coordinate. + * @y: the absolute Y coordinate. + * @tip: whether the fingertip is touching the pad. (Currently always true.) + * @confidence: whether the controller considers the touch a finger (true) or + * palm (false). + */ +struct gt7288_contact { + uint8_t id; + uint16_t x; + uint16_t y; + bool tip; + bool confidence; +}; + +/** + * struct gt7288_report - data from a complete report in PTP mode. + * @timestamp: a relative timestamp, in units of 100µs. + * @num_contacts: the number of contacts on the pad. + * @button_down: whether the button is pressed. + * @contacts: an array of structs describing the individual contacts. + */ +struct gt7288_ptp_report { + uint16_t timestamp; + size_t num_contacts; + bool button_down; + struct gt7288_contact contacts[GT7288_MAX_CONTACTS]; +}; + +/** + * gt7288_read_ptp_report() - Reads a complete report, when the GT7288 is in PTP + * mode. + * @report: the report that is read. + * + * Return: EC_SUCCESS or an error code. + */ +int gt7288_read_ptp_report(struct gt7288_ptp_report *report); + +#endif /* __CROS_EC_TOUCHPAD_GT7288_H */ |