summaryrefslogtreecommitdiff
path: root/driver
diff options
context:
space:
mode:
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 */