summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Boichat <drinkcat@google.com>2016-11-02 11:15:12 +0800
committerchrome-bot <chrome-bot@chromium.org>2016-11-08 23:24:52 -0800
commitde0f53afef1822cd32c917d91452ff6eec5fd17f (patch)
tree0a420a8771ba276b3a0a5e67b705bdd86d7fb222
parent840ba2b6e48f6378e4d70ad29dfa26190019536d (diff)
downloadchrome-ec-de0f53afef1822cd32c917d91452ff6eec5fd17f.tar.gz
driver/touchpad_elan: Basic elan touchpad driver
BRANCH=none BUG=chrome-os-partner:59083 TEST=make BOARD=hammer -j && bash flash_hammer Change-Id: I0ff4f48ff1399e054f745ac13ffacf81dffedeab Reviewed-on: https://chromium-review.googlesource.com/407740 Commit-Ready: Nicolas Boichat <drinkcat@chromium.org> Tested-by: Nicolas Boichat <drinkcat@chromium.org> Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
-rw-r--r--driver/build.mk3
-rw-r--r--driver/touchpad_elan.c245
-rw-r--r--driver/touchpad_elan.h13
-rw-r--r--include/config.h10
-rw-r--r--include/console_channel.inc3
5 files changed, 274 insertions, 0 deletions
diff --git a/driver/build.mk b/driver/build.mk
index 281afc92d0..0a6d109adc 100644
--- a/driver/build.mk
+++ b/driver/build.mk
@@ -74,6 +74,9 @@ driver-$(CONFIG_TEMP_SENSOR_G782)+=temp_sensor/g78x.o
driver-$(CONFIG_TEMP_SENSOR_TMP006)+=temp_sensor/tmp006.o
driver-$(CONFIG_TEMP_SENSOR_TMP432)+=temp_sensor/tmp432.o
+# Touchpads
+driver-$(CONFIG_TOUCHPAD_ELAN)+=touchpad_elan.o
+
# Thermistors
driver-$(CONFIG_THERMISTOR_NCP15WB)+=temp_sensor/thermistor_ncp15wb.o
diff --git a/driver/touchpad_elan.c b/driver/touchpad_elan.c
new file mode 100644
index 0000000000..e6b52fed1e
--- /dev/null
+++ b/driver/touchpad_elan.c
@@ -0,0 +1,245 @@
+/* Copyright 2016 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 "common.h"
+#include "console.h"
+#include "touchpad_elan.h"
+#include "gpio.h"
+#include "i2c.h"
+#include "task.h"
+#include "timer.h"
+#include "util.h"
+
+/* Console output macros */
+#define CPUTS(outstr) cputs(CC_TOUCHPAD, outstr)
+#define CPRINTF(format, args...) cprintf(CC_TOUCHPAD, format, ## args)
+#define CPRINTS(format, args...) cprints(CC_TOUCHPAD, format, ## args)
+
+/******************************************************************************/
+/* How to talk to the controller */
+/******************************************************************************/
+
+#define ETP_I2C_RESET 0x0100
+#define ETP_I2C_WAKE_UP 0x0800
+#define ETP_I2C_SLEEP 0x0801
+#define ETP_I2C_STAND_CMD 0x0005
+#define ETP_I2C_XY_TRACENUM_CMD 0x0105
+#define ETP_I2C_MAX_X_AXIS_CMD 0x0106
+#define ETP_I2C_MAX_Y_AXIS_CMD 0x0107
+#define ETP_I2C_PRESSURE_CMD 0x010A
+#define ETP_I2C_SET_CMD 0x0300
+
+#define ETP_ENABLE_ABS 0x0001
+
+#define ETP_I2C_REPORT_LEN 34
+
+#define ETP_MAX_FINGERS 5
+#define ETP_FINGER_DATA_LEN 5
+
+#define ETP_PRESSURE_OFFSET 25
+#define ETP_FWIDTH_REDUCE 90
+
+#define ETP_REPORT_ID 0x5D
+#define ETP_REPORT_ID_OFFSET 2
+#define ETP_TOUCH_INFO_OFFSET 3
+#define ETP_FINGER_DATA_OFFSET 4
+#define ETP_HOVER_INFO_OFFSET 30
+#define ETP_MAX_REPORT_LEN 34
+
+struct {
+ /* Max X/Y position */
+ uint16_t max_x;
+ uint16_t max_y;
+ /* Scaling factor for finger width/height */
+ uint16_t width_x;
+ uint16_t width_y;
+ /* Pressure adjustment */
+ uint8_t pressure_adj;
+} elan_tp_params;
+
+static int elan_tp_read_cmd(uint16_t reg, uint16_t *val)
+{
+ uint8_t buf[2];
+ int rv;
+
+ buf[0] = reg;
+ buf[1] = reg >> 8;
+
+ i2c_lock(CONFIG_TOUCHPAD_I2C_PORT, 1);
+ rv = i2c_xfer(CONFIG_TOUCHPAD_I2C_PORT, CONFIG_TOUCHPAD_I2C_ADDR,
+ buf, sizeof(buf), (uint8_t *)val, sizeof(*val),
+ I2C_XFER_SINGLE);
+ i2c_lock(CONFIG_TOUCHPAD_I2C_PORT, 0);
+
+ return rv;
+}
+
+static int elan_tp_write_cmd(uint16_t reg, uint16_t val)
+{
+ uint8_t buf[4];
+ int rv;
+
+ buf[0] = reg;
+ buf[1] = reg >> 8;
+ buf[2] = val;
+ buf[3] = val >> 8;
+
+ i2c_lock(CONFIG_TOUCHPAD_I2C_PORT, 1);
+ rv = i2c_xfer(CONFIG_TOUCHPAD_I2C_PORT, CONFIG_TOUCHPAD_I2C_ADDR,
+ buf, sizeof(buf), NULL, 0, I2C_XFER_SINGLE);
+ i2c_lock(CONFIG_TOUCHPAD_I2C_PORT, 0);
+
+ return rv;
+}
+
+static int elan_tp_read_report(void)
+{
+ int rv;
+ uint8_t tp_buf[ETP_I2C_REPORT_LEN];
+ int i;
+ uint8_t touch_info;
+ uint8_t hover_info;
+ uint8_t *finger = tp_buf+ETP_FINGER_DATA_OFFSET;
+
+ i2c_lock(CONFIG_TOUCHPAD_I2C_PORT, 1);
+ rv = i2c_xfer(CONFIG_TOUCHPAD_I2C_PORT, CONFIG_TOUCHPAD_I2C_ADDR,
+ NULL, 0, tp_buf, ETP_I2C_REPORT_LEN, I2C_XFER_SINGLE);
+ i2c_lock(CONFIG_TOUCHPAD_I2C_PORT, 0);
+
+ if (rv) {
+ CPRINTS("read report error");
+ return rv;
+ }
+
+ CPRINTF("[%T ");
+#if 0
+ for (i = 0; i < ETP_I2C_REPORT_LEN; i++)
+ CPRINTF("%02x", tp_buf[i]);
+ CPRINTF(" || ");
+#endif
+
+ if (tp_buf[ETP_REPORT_ID_OFFSET] != ETP_REPORT_ID) {
+ CPRINTS("Invalid report id (%x)", tp_buf[ETP_REPORT_ID_OFFSET]);
+ return -1;
+ }
+
+ touch_info = tp_buf[ETP_TOUCH_INFO_OFFSET];
+ hover_info = tp_buf[ETP_HOVER_INFO_OFFSET];
+
+ if (touch_info & 0x01)
+ CPRINTF("click|");
+ if (hover_info & 0x40)
+ CPRINTF("hover|");
+
+ for (i = 0; i < ETP_MAX_FINGERS; i++) {
+ int valid = touch_info & (1 << (3+i));
+
+ if (valid) {
+ int x = ((finger[0] & 0xf0) << 4) | finger[1];
+ int y = ((finger[0] & 0x0f) << 8) | finger[2];
+ int width = (finger[3] & 0xf0) >> 4;
+ int height = finger[3] & 0x0f;
+ int pressure = finger[4];
+
+ y = elan_tp_params.max_y - y;
+ width = width * elan_tp_params.width_x;
+ height = height * elan_tp_params.width_y;
+ pressure = pressure + elan_tp_params.pressure_adj;
+
+ if (1)
+ CPRINTF("i=%d %d/%d %d/%d %d|", i, x, y,
+ width, height, pressure);
+ finger += ETP_FINGER_DATA_LEN;
+ }
+ }
+ CPRINTF("]\n");
+
+ return 0;
+}
+
+/* Initialize the controller ICs after reset */
+static int elan_tp_init(void)
+{
+ int rv;
+ uint8_t val[2];
+
+ CPRINTS("%s", __func__);
+
+ elan_tp_write_cmd(ETP_I2C_STAND_CMD, ETP_I2C_RESET);
+ msleep(100);
+ i2c_lock(CONFIG_TOUCHPAD_I2C_PORT, 1);
+ rv = i2c_xfer(CONFIG_TOUCHPAD_I2C_PORT, CONFIG_TOUCHPAD_I2C_ADDR,
+ NULL, 0, val, sizeof(val), I2C_XFER_SINGLE);
+ i2c_lock(CONFIG_TOUCHPAD_I2C_PORT, 0);
+
+ CPRINTS("reset rv %d buf=%04x", rv, *((uint16_t *)val));
+ if (rv)
+ goto out;
+
+ /* Read min/max */
+ rv = elan_tp_read_cmd(ETP_I2C_MAX_X_AXIS_CMD, &elan_tp_params.max_x);
+ if (rv)
+ goto out;
+ rv = elan_tp_read_cmd(ETP_I2C_MAX_Y_AXIS_CMD, &elan_tp_params.max_y);
+ if (rv)
+ goto out;
+
+ /* Read min/max */
+ rv = elan_tp_read_cmd(ETP_I2C_XY_TRACENUM_CMD, (uint16_t *)val);
+ if (rv)
+ goto out;
+ if (val[0] == 0 || val[1] == 0) {
+ CPRINTS("Invalid XY_TRACENUM");
+ goto out;
+ }
+
+ /* ETP_FWIDTH_REDUCE reduces the apparent width to avoid treating large
+ * finger as palm. Multiply value by 2 as HID multitouch divides it.
+ */
+ elan_tp_params.width_x =
+ 2 * ((elan_tp_params.max_x / val[0]) - ETP_FWIDTH_REDUCE);
+ elan_tp_params.width_y =
+ 2 * ((elan_tp_params.max_y / val[1]) - ETP_FWIDTH_REDUCE);
+
+ rv = elan_tp_read_cmd(ETP_I2C_PRESSURE_CMD, (uint16_t *)val);
+ if (rv)
+ goto out;
+ elan_tp_params.pressure_adj = (val[0] & 0x10) ? 0 : ETP_PRESSURE_OFFSET;
+
+ CPRINTS("max=%d/%d width=%d/%d adj=%d",
+ elan_tp_params.max_x, elan_tp_params.max_y,
+ elan_tp_params.width_x, elan_tp_params.width_y,
+ elan_tp_params.pressure_adj);
+
+ /* Switch to absolute mode */
+ rv = elan_tp_write_cmd(ETP_I2C_SET_CMD, ETP_ENABLE_ABS);
+ if (rv)
+ goto out;
+
+ /* Sleep control off */
+ rv = elan_tp_write_cmd(ETP_I2C_STAND_CMD, ETP_I2C_WAKE_UP);
+
+out:
+ CPRINTS("%s:%d", __func__, rv);
+ return rv;
+}
+
+void elan_tp_interrupt(enum gpio_signal signal)
+{
+ task_wake(TASK_ID_TOUCHPAD);
+}
+
+void elan_tp_task(void)
+{
+ elan_tp_init();
+
+ gpio_enable_interrupt(GPIO_TOUCHPAD_INT);
+
+ while (1) {
+ task_wait_event(-1);
+
+ elan_tp_read_report();
+ }
+}
diff --git a/driver/touchpad_elan.h b/driver/touchpad_elan.h
new file mode 100644
index 0000000000..4fb6d6e278
--- /dev/null
+++ b/driver/touchpad_elan.h
@@ -0,0 +1,13 @@
+/* Copyright 2016 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.
+ */
+
+/* Elan touchpad driver for Chrome EC */
+
+#ifndef __CROS_EC_TOUCHPAD_ELAN_H
+#define __CROS_EC_TOUCHPAD_ELAN_H
+
+void elan_tp_interrupt(enum gpio_signal signal);
+
+#endif
diff --git a/include/config.h b/include/config.h
index 5cc38a6c14..3f8637e3f8 100644
--- a/include/config.h
+++ b/include/config.h
@@ -1904,6 +1904,16 @@
#undef CONFIG_DPTF
/*****************************************************************************/
+/* Touchpad config */
+
+/* Enable Elan driver */
+#undef CONFIG_TOUCHPAD_ELAN
+
+/* Set I2C port and address (8-bit) */
+#undef CONFIG_TOUCHPAD_I2C_PORT
+#undef CONFIG_TOUCHPAD_I2C_ADDR
+
+/*****************************************************************************/
/* TPM-like configuration */
/* Speak the TPM SPI Hardware Protocol on the SPI slave interface */
diff --git a/include/console_channel.inc b/include/console_channel.inc
index ba48a31563..7c3e8b3c99 100644
--- a/include/console_channel.inc
+++ b/include/console_channel.inc
@@ -66,6 +66,9 @@ CONSOLE_CHANNEL(CC_SWITCH, "switch")
#endif
CONSOLE_CHANNEL(CC_SYSTEM, "system")
CONSOLE_CHANNEL(CC_TASK, "task")
+#ifdef CONFIG_TOUCHPAD_ELAN
+CONSOLE_CHANNEL(CC_TOUCHPAD, "touchpad")
+#endif
#ifdef CONFIG_DPTF
CONSOLE_CHANNEL(CC_DPTF, "dptf")
#endif