summaryrefslogtreecommitdiff
path: root/driver
diff options
context:
space:
mode:
authorHarry Cutts <hcutts@chromium.org>2019-09-04 13:49:50 -0700
committerCommit Bot <commit-bot@chromium.org>2019-09-18 01:24:36 +0000
commit73f6dc7cc71d98789f96f38fc8b636d78ba1e9b4 (patch)
tree4c96040cd5869ecc1067724d9ffe0e46b1ce8985 /driver
parent9b659a2c48702bcd2e7658878203cbad7fe71ca4 (diff)
downloadchrome-ec-73f6dc7cc71d98789f96f38fc8b636d78ba1e9b4.tar.gz
touchpad_gt7288: Basic driver for Goodix GT7288
A simple driver which allows touch reports and firmware version information to be read. If the appropriate config flag is set, console commands are included for testing. Unlike the other two touchpad drivers already implemented, which simply receive I2C HID events and send them straight out again over USB HID, we want to do some processing on the touchpad data in the board directory. For that reason, this driver leaves handling the touch interrupts up to the user. BRANCH=none BUG=none TEST=With https://crrev.com/c/1716928 patched, run the various host commands and check the output. Change-Id: Ia38e516473b78fb052ae18ca89acc5d815b53bd6 Signed-off-by: Harry Cutts <hcutts@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/1799290 Reviewed-by: Daisuke Nojiri <dnojiri@chromium.org>
Diffstat (limited to 'driver')
-rw-r--r--driver/build.mk1
-rw-r--r--driver/touchpad_gt7288.c229
-rw-r--r--driver/touchpad_gt7288.h76
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 */