[astro][hid] Goodix touch driver

TEST: test on astro with hidtouch
Change-Id: I780befb0ffdf83680af7124f0bb6adb33f6f5cf1
diff --git a/system/dev/board/astro/astro-touch.c b/system/dev/board/astro/astro-touch.c
index 2cf66f4..d79c9ec 100644
--- a/system/dev/board/astro/astro-touch.c
+++ b/system/dev/board/astro/astro-touch.c
@@ -26,7 +26,7 @@
 
 static const pbus_i2c_channel_t ft3x27_touch_i2c[] = {
     {
-        .bus_id = 1,
+        .bus_id = ASTRO_I2C_2,
         .address = 0x38,
     },
 };
@@ -42,6 +42,25 @@
     .gpio_count = countof(touch_gpios),
 };
 
+static const pbus_i2c_channel_t gt92xx_touch_i2c[] = {
+    {
+        .bus_id = ASTRO_I2C_2,
+        .address = 0x5d,
+    },
+};
+
+static pbus_dev_t gt92xx_touch_dev = {
+    .name = "gt92xx-touch",
+    .vid = PDEV_VID_GOOGLE,
+    .pid = PDEV_PID_ASTRO,
+    .did = PDEV_DID_ASTRO_GOODIXTOUCH,
+    .i2c_channels = gt92xx_touch_i2c,
+    .i2c_channel_count = countof(gt92xx_touch_i2c),
+    .gpios = touch_gpios,
+    .gpio_count = countof(touch_gpios),
+};
+
+
 zx_status_t astro_touch_init(aml_bus_t* bus) {
 
     //Check the display ID pin to determine which driver device to add
@@ -56,8 +75,11 @@
     */
     gpio_impl_read(&bus->gpio, S905D2_GPIOH(5), &gpio_state);
     if (gpio_state) {
-        zxlogf(INFO, "Innolux/Goodix screen not supported at this time\n");
-        return ZX_OK;
+        zx_status_t status = pbus_device_add(&bus->pbus, &gt92xx_touch_dev);
+        if (status != ZX_OK) {
+            zxlogf(INFO, "astro_touch_init(gt92xx): pbus_device_add failed: %d\n", status);
+            return status;
+        }
     } else {
         zx_status_t status = pbus_device_add(&bus->pbus, &ft3x27_touch_dev);
         if (status != ZX_OK) {
diff --git a/system/dev/input/goodix/gt92xx.cpp b/system/dev/input/goodix/gt92xx.cpp
new file mode 100644
index 0000000..07665ab
--- /dev/null
+++ b/system/dev/input/goodix/gt92xx.cpp
@@ -0,0 +1,324 @@
+// Copyright 2018 The Fuchsia 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 <ddk/binding.h>
+#include <ddk/debug.h>
+#include <ddk/protocol/platform-defs.h>
+#include <ddk/protocol/platform-device.h>
+#include <fbl/auto_call.h>
+#include <fbl/auto_lock.h>
+
+#include "gt92xx.h"
+
+namespace goodix {
+// Configuration data
+// first two bytes contain starting register address (part of i2c transaction)
+static uint8_t conf_data[] = {
+    GT_REG_CONFIG_DATA >> 8, GT_REG_CONFIG_DATA & 0xff,
+    0x5C, 0x00, 0x04, 0x58, 0x02, 0x05, 0xBD, 0xC0,
+    0x00, 0x08, 0x1E, 0x05, 0x50, 0x32, 0x05, 0x0B,
+    0x00, 0x00, 0x00, 0x00, 0x40, 0x12, 0x00, 0x17,
+    0x17, 0x19, 0x12, 0x8D, 0x2D, 0x0F, 0x3F, 0x41,
+    0xB2, 0x04, 0x00, 0x00, 0x00, 0xBC, 0x03, 0x1D,
+    0x1E, 0x80, 0x01, 0x00, 0x14, 0x46, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x37, 0x55, 0x8F, 0xC5, 0x02,
+    0x07, 0x11, 0x00, 0x04, 0x8A, 0x39, 0x00, 0x81,
+    0x3E, 0x00, 0x78, 0x44, 0x00, 0x71, 0x4A, 0x00,
+    0x6A, 0x51, 0x00, 0x6A, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+    0x1C, 0x1A, 0x18, 0x16, 0x14, 0x12, 0x10, 0x0E,
+    0x0C, 0x0A, 0x08, 0x06, 0x04, 0x02, 0x00, 0x00,
+    0xFF, 0xFF, 0x1F, 0xE7, 0xFF, 0xFF, 0xFF, 0x0F,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2A, 0x29,
+    0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21,
+    0x20, 0x1F, 0x1E, 0x0C, 0x0B, 0x0A, 0x09, 0x08,
+    0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x6C, 0x01};
+
+int Gt92xxDevice::Thread() {
+    zx_status_t status;
+    zxlogf(INFO, "gt92xx: entering irq thread\n");
+    while (true) {
+        status = irq_.wait(nullptr);
+        if (!running_.load()) {
+            return ZX_OK;
+        }
+        if (status != ZX_OK) {
+            zxlogf(ERROR, "gt92xx: Interrupt error %d\n", status);
+        }
+        uint8_t touch_stat = Read(GT_REG_TOUCH_STATUS);
+        if (touch_stat & 0x80) {
+            uint8_t num_reports = touch_stat & 0x0f;
+            FingerReport reports[kMaxPoints];
+            // Read touch reports
+            zx_status_t status =
+                Read(GT_REG_REPORTS, reinterpret_cast<uint8_t*>(&reports),
+                     static_cast<uint8_t>(sizeof(FingerReport) * kMaxPoints));
+            if (status == ZX_OK) {
+                fbl::AutoLock lock(&proxy_lock_);
+                gt_rpt_.rpt_id = GT92XX_RPT_ID_TOUCH;
+                gt_rpt_.contact_count = num_reports;
+                // We are reusing same HID report as ft3x77 to simplify astro integration
+                // so we need to copy from device format to HID structure format
+                for (uint32_t i = 0; i < kMaxPoints; i++) {
+                    gt_rpt_.fingers[i].finger_id = reports[i].id;
+                    gt_rpt_.fingers[i].x = reports[i].x;
+                    gt_rpt_.fingers[i].y = reports[i].y;
+                }
+                if (proxy_.is_valid()) {
+                    proxy_.IoQueue(reinterpret_cast<uint8_t*>(&gt_rpt_), sizeof(gt92xx_touch_t));
+                }
+            }
+            // Clear the touch status
+            Write(GT_REG_TOUCH_STATUS, 0);
+        }
+    }
+    zxlogf(INFO, "gt92xx: exiting\n");
+    return 0;
+}
+
+zx_status_t Gt92xxDevice::Create(zx_device_t* device) {
+
+    zxlogf(INFO, "gt92xx: driver started...\n");
+
+    auto pdev = ddk::Pdev::Create(device);
+    if (!pdev) {
+        zxlogf(ERROR, "%s could not acquire platform device\n", __func__);
+        return ZX_ERR_NO_RESOURCES;
+    }
+
+    auto goodix_dev = fbl::make_unique<Gt92xxDevice>(device,
+                                                     fbl::move(pdev->GetI2cChan(0)),
+                                                     fbl::move(pdev->GetGpio(0)),
+                                                     fbl::move(pdev->GetGpio(1)));
+
+    zx_status_t status = goodix_dev->Init();
+    if (status != ZX_OK) {
+        zxlogf(ERROR, "Could not initialize gt92xx hardware %d\n", status);
+        return status;
+    }
+
+    auto thunk = [](void* arg) -> int {
+        return reinterpret_cast<Gt92xxDevice*>(arg)->Thread();
+    };
+
+    auto cleanup = fbl::MakeAutoCall([&]() { goodix_dev->ShutDown(); });
+
+    goodix_dev->running_.store(true);
+    int ret = thrd_create_with_name(&goodix_dev->thread_, thunk,
+                                    goodix_dev.get(),
+                                    "gt92xx-thread");
+    ZX_DEBUG_ASSERT(ret == thrd_success);
+
+    status = goodix_dev->DdkAdd("gt92xx HidDevice\n");
+    if (status != ZX_OK) {
+        zxlogf(ERROR, "gt92xx: Could not create hid device: %d\n", status);
+        return status;
+    } else {
+        zxlogf(INFO, "gt92xx: Added hid device\n");
+    }
+
+    cleanup.cancel();
+
+    // device intentionally leaked as it is now held by DevMgr
+    __UNUSED auto ptr = goodix_dev.release();
+
+    return ZX_OK;
+}
+
+zx_status_t Gt92xxDevice::Init() {
+    // Hardware reset
+    HWReset();
+
+    uint8_t fw = Read(GT_REG_FIRMWARE);
+    if (fw != GT_FIRMWARE_MAGIC) {
+        zxlogf(ERROR, "Invalid gt92xx firmware configuration!\n");
+        return ZX_ERR_BAD_STATE;
+    }
+    // Device requires 50ms delay after this check (per datasheet)
+    zx_nanosleep(zx_deadline_after(ZX_MSEC(50)));
+
+    // Configuration data should span specific set of registers
+    // last register has flag to latch in new configuration, second
+    // to last register holds checksum of register values.
+    // Note: first two bytes of conf_data hold the 16-bit register address where
+    // the write will start.
+    ZX_DEBUG_ASSERT((countof(conf_data) - sizeof(uint16_t)) ==
+                    (GT_REG_CONFIG_REFRESH - GT_REG_CONFIG_DATA + 1));
+
+    // Write conf data to registers
+    zx_status_t status = i2c_.Transact(conf_data, sizeof(conf_data), NULL, 0);
+    if (status != ZX_OK) {
+        return status;
+    }
+    // Device requires 10ms delay to refresh configuration
+    zx_nanosleep(zx_deadline_after(ZX_MSEC(10)));
+    // Clear touch state in case there were spurious touches registered
+    // during startup
+    Write(GT_REG_TOUCH_STATUS, 0);
+
+    status = int_gpio_.GetInterrupt(ZX_INTERRUPT_MODE_EDGE_HIGH, &irq_);
+
+    return status;
+}
+
+void Gt92xxDevice::HWReset() {
+    // Hardware reset will also set the address of the controller to either
+    // 0x14 0r 0x5d.  See the datasheet for explanation of sequence.
+    reset_gpio_.ConfigOut(0); //Make reset pin an output and pull low
+    int_gpio_.ConfigOut(0);   //Make interrupt pin an output and pull low
+
+    // Delay for 100us
+    zx_nanosleep(zx_deadline_after(ZX_USEC(100)));
+
+    reset_gpio_.Write(1); // Release the reset
+    zx_nanosleep(zx_deadline_after(ZX_MSEC(5)));
+    int_gpio_.ConfigIn(0);                        // Make interrupt pin an input again;
+    zx_nanosleep(zx_deadline_after(ZX_MSEC(50))); // Wait for reset to complete
+}
+
+zx_status_t Gt92xxDevice::HidBusQuery(uint32_t options, hid_info_t* info) {
+    if (!info) {
+        return ZX_ERR_INVALID_ARGS;
+    }
+    info->dev_num = 0;
+    info->dev_class = HID_DEV_CLASS_OTHER;
+    info->boot_device = false;
+
+    return ZX_OK;
+}
+
+void Gt92xxDevice::DdkRelease() {
+    delete this;
+}
+
+void Gt92xxDevice::DdkUnbind() {
+    ShutDown();
+    DdkRemove();
+}
+
+zx_status_t Gt92xxDevice::ShutDown() {
+    running_.store(false);
+    irq_.destroy();
+    thrd_join(thread_, NULL);
+    {
+        fbl::AutoLock lock(&proxy_lock_);
+        proxy_.clear();
+    }
+    return ZX_OK;
+}
+
+zx_status_t Gt92xxDevice::HidBusGetDescriptor(uint8_t desc_type, void** data, size_t* len) {
+
+    const uint8_t* desc_ptr;
+    uint8_t* buf;
+    *len = get_gt92xx_report_desc(&desc_ptr);
+    fbl::AllocChecker ac;
+    buf = new (&ac) uint8_t[*len];
+    if (!ac.check()) {
+        return ZX_ERR_NO_MEMORY;
+    }
+    memcpy(buf, desc_ptr, *len);
+    *data = buf;
+    return ZX_OK;
+}
+
+zx_status_t Gt92xxDevice::HidBusGetReport(uint8_t rpt_type, uint8_t rpt_id, void* data,
+                                          size_t len, size_t* out_len) {
+    return ZX_ERR_NOT_SUPPORTED;
+}
+
+zx_status_t Gt92xxDevice::HidBusSetReport(uint8_t rpt_type, uint8_t rpt_id, void* data,
+                                          size_t len) {
+    return ZX_ERR_NOT_SUPPORTED;
+}
+
+zx_status_t Gt92xxDevice::HidBusGetIdle(uint8_t rpt_id, uint8_t* duration) {
+    return ZX_ERR_NOT_SUPPORTED;
+}
+
+zx_status_t Gt92xxDevice::HidBusSetIdle(uint8_t rpt_id, uint8_t duration) {
+    return ZX_ERR_NOT_SUPPORTED;
+}
+
+zx_status_t Gt92xxDevice::HidBusGetProtocol(uint8_t* protocol) {
+    return ZX_ERR_NOT_SUPPORTED;
+}
+
+zx_status_t Gt92xxDevice::HidBusSetProtocol(uint8_t protocol) {
+    return ZX_OK;
+}
+
+void Gt92xxDevice::HidBusStop() {
+    fbl::AutoLock lock(&proxy_lock_);
+    proxy_.clear();
+}
+
+zx_status_t Gt92xxDevice::HidBusStart(ddk::HidBusIfcProxy proxy) {
+    fbl::AutoLock lock(&proxy_lock_);
+    if (proxy_.is_valid()) {
+        zxlogf(ERROR, "gt92xx: Already bound!\n");
+        return ZX_ERR_ALREADY_BOUND;
+    } else {
+        proxy_ = proxy;
+        zxlogf(INFO, "gt92xx: started\n");
+    }
+    return ZX_OK;
+}
+
+uint8_t Gt92xxDevice::Read(uint16_t addr) {
+    uint8_t rbuf;
+    Read(addr, &rbuf, 1);
+    return rbuf;
+}
+
+zx_status_t Gt92xxDevice::Read(uint16_t addr, uint8_t* buf, uint8_t len) {
+    uint8_t tbuf[2];
+    tbuf[0] = static_cast<uint8_t>(addr >> 8);
+    tbuf[1] = static_cast<uint8_t>(addr & 0xff);
+    return i2c_.Transact(tbuf, 2, buf, len);
+}
+
+zx_status_t Gt92xxDevice::Write(uint16_t addr, uint8_t val) {
+    uint8_t tbuf[3];
+    tbuf[0] = static_cast<uint8_t>(addr >> 8);
+    tbuf[1] = static_cast<uint8_t>(addr & 0xff);
+    tbuf[2] = val;
+    return i2c_.Transact(tbuf, 3, NULL, 0);
+}
+
+} // namespace ft
+
+__BEGIN_CDECLS
+
+zx_status_t gt92xx_bind(void* ctx, zx_device_t* device) {
+    return goodix::Gt92xxDevice::Create(device);
+}
+
+static zx_driver_ops_t gt92xx_driver_ops = {
+    .version = DRIVER_OPS_VERSION,
+    .init = nullptr,
+    .bind = gt92xx_bind,
+    .create = nullptr,
+    .release = nullptr,
+};
+
+// clang-format off
+ZIRCON_DRIVER_BEGIN(gt92xx, gt92xx_driver_ops, "zircon", "0.1", 3)
+    BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_GOOGLE),
+    BI_ABORT_IF(NE, BIND_PLATFORM_DEV_PID, PDEV_PID_ASTRO),
+    BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_DID, PDEV_DID_ASTRO_GOODIXTOUCH),
+ZIRCON_DRIVER_END(gt92xx)
+// clang-format on
+__END_CDECLS
diff --git a/system/dev/input/goodix/gt92xx.h b/system/dev/input/goodix/gt92xx.h
new file mode 100644
index 0000000..88001f8
--- /dev/null
+++ b/system/dev/input/goodix/gt92xx.h
@@ -0,0 +1,103 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#pragma once
+
+#include <ddk/device.h>
+#include <ddk/protocol/gpio.h>
+#include <ddk/protocol/i2c.h>
+#include <ddktl/device.h>
+#include <ddktl/pdev.h>
+#include <ddktl/protocol/hidbus.h>
+#include <fbl/atomic.h>
+#include <fbl/mutex.h>
+#include <hid/gt92xx.h>
+#include <lib/zx/interrupt.h>
+#include <threads.h>
+#include <zircon/types.h>
+
+// clang-format off
+#define GT_REG_SLEEP            0x8040
+#define GT_REG_CONFIG_DATA      0x8047
+#define GT_REG_MAX_X_LO         0x8048
+#define GT_REG_MAX_X_HI         0x8049
+#define GT_REG_MAX_Y_LO         0x804a
+#define GT_REG_MAX_Y_HI         0x804b
+#define GT_REG_NUM_FINGERS      0x804c
+
+#define GT_REG_CONFIG_REFRESH   0x812a
+#define GT_REG_VERSION          0x8140
+#define GT_REG_SENSOR_ID        0x814a
+#define GT_REG_TOUCH_STATUS     0x814e
+#define GT_REG_REPORTS          0x814f
+
+#define GT_REG_FIRMWARE         0x41e4
+#define GT_FIRMWARE_MAGIC       0xbe
+// clang-format on
+
+namespace goodix {
+
+class Gt92xxDevice : public ddk::Device<Gt92xxDevice, ddk::Unbindable>,
+                     public ddk::HidBusProtocol<Gt92xxDevice> {
+public:
+    Gt92xxDevice(zx_device_t* device, ddk::I2cChannel i2c,
+                 ddk::GpioPin intr, ddk::GpioPin reset)
+        : ddk::Device<Gt92xxDevice, ddk::Unbindable>(device),
+          i2c_(fbl::move(i2c)), int_gpio_(fbl::move(intr)),
+          reset_gpio_(fbl::move(reset)){};
+
+    static zx_status_t Create(zx_device_t* device);
+
+    void DdkRelease();
+    void DdkUnbind() __TA_EXCLUDES(proxy_lock_);
+
+    // HidBus required methods
+    void HidBusStop();
+    zx_status_t HidBusGetDescriptor(uint8_t desc_type, void** data, size_t* len);
+    zx_status_t HidBusGetReport(uint8_t rpt_type, uint8_t rpt_id, void* data,
+                                size_t len, size_t* out_len);
+    zx_status_t HidBusSetReport(uint8_t rpt_type, uint8_t rpt_id, void* data,
+                                size_t len);
+    zx_status_t HidBusGetIdle(uint8_t rpt_id, uint8_t* duration);
+    zx_status_t HidBusSetIdle(uint8_t rpt_id, uint8_t duration);
+    zx_status_t HidBusGetProtocol(uint8_t* protocol);
+    zx_status_t HidBusSetProtocol(uint8_t protocol);
+    zx_status_t HidBusStart(ddk::HidBusIfcProxy proxy) __TA_EXCLUDES(proxy_lock_);
+    zx_status_t HidBusQuery(uint32_t options, hid_info_t* info) __TA_EXCLUDES(proxy_lock_);
+
+private:
+    // Format of data as it is read from the device
+    struct FingerReport {
+        uint8_t id;
+        uint16_t x;
+        uint16_t y;
+        uint16_t size;
+        uint8_t reserved;
+    } __PACKED;
+
+    static constexpr uint32_t kMaxPoints = 5;
+
+    zx_status_t ShutDown() __TA_EXCLUDES(proxy_lock_);
+    // performs hardware reset using gpio
+    void HWReset();
+    zx_status_t Init();
+
+    uint8_t Read(uint16_t addr);
+    zx_status_t Read(uint16_t addr, uint8_t* buf, uint8_t len);
+    zx_status_t Write(uint16_t addr, uint8_t val);
+
+    int Thread();
+
+    const ddk::I2cChannel i2c_;
+    const ddk::GpioPin int_gpio_;
+    const ddk::GpioPin reset_gpio_;
+
+    gt92xx_touch_t gt_rpt_ __TA_GUARDED(proxy_lock_);
+    zx::interrupt irq_;
+    thrd_t thread_;
+    fbl::atomic<bool> running_;
+    fbl::Mutex proxy_lock_;
+    ddk::HidBusIfcProxy proxy_ __TA_GUARDED(proxy_lock_);
+};
+}
\ No newline at end of file
diff --git a/system/dev/input/goodix/rules.mk b/system/dev/input/goodix/rules.mk
new file mode 100644
index 0000000..aa03610
--- /dev/null
+++ b/system/dev/input/goodix/rules.mk
@@ -0,0 +1,26 @@
+# Copyright 2018 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_TYPE := driver
+
+MODULE_SRCS := \
+    $(LOCAL_DIR)/gt92xx.cpp \
+
+MODULE_STATIC_LIBS := \
+    system/ulib/ddk \
+    system/ulib/ddktl \
+    system/ulib/hid \
+    system/ulib/zxcpp \
+    system/ulib/fbl \
+    system/ulib/zx \
+    system/ulib/sync \
+    system/ulib/hid
+
+MODULE_LIBS := system/ulib/driver system/ulib/zircon system/ulib/c
+
+include make/module.mk
diff --git a/system/ulib/hid/gt92xx.c b/system/ulib/hid/gt92xx.c
new file mode 100644
index 0000000..932d75f
--- /dev/null
+++ b/system/ulib/hid/gt92xx.c
@@ -0,0 +1,182 @@
+// Copyright 2018 The Fuchsia 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 <hid/gt92xx.h>
+#include <zircon/errors.h>
+#include <string.h>
+
+static const uint8_t gt92xx_touch_report_desc[] = {
+    0x05, 0x0D,        // Usage Page (Digitizer)
+    0x09, 0x04,        // Usage (Touch Screen)
+    0xA1, 0x01,        // Collection (Application)
+    0x85, 0x01,        //   Report ID (1)
+    0x09, 0x22,        //   Usage (Finger)
+    0xA1, 0x02,        //   Collection (Logical)
+    0x09, 0x42,        //     Usage (Tip Switch)
+    0x15, 0x00,        //     Logical Minimum (0)
+    0x25, 0x01,        //     Logical Maximum (1)
+    0x75, 0x01,        //     Report Size (1)
+    0x95, 0x01,        //     Report Count (1)
+    0x81, 0x02,        //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+    0x75, 0x01,        //     Report Size (1)
+    0x81, 0x03,        //     Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+    0x75, 0x06,        //     Report Size (6)
+    0x09, 0x51,        //     Usage (0x51)
+    0x25, 0x3F,        //     Logical Maximum (63)
+    0x81, 0x02,        //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+    0x95, 0x01,        //     Report Count (1)
+    0x05, 0x01,        //     Usage Page (Generic Desktop Ctrls)
+    0xA4,              //     Push
+    0x26, 0x58, 0x02,  //       Logical Maximum (600)
+    0x75, 0x10,        //       Report Size (16)
+    0x09, 0x30,        //       Usage (X)
+    0x95, 0x01,        //       Report Count (1)
+    0x81, 0x02,        //       Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+    0x26, 0x00, 0x04,  //       Logical Maximum (1024)
+    0x09, 0x31,        //       Usage (Y)
+    0x81, 0x02,        //       Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+    0xB4,              //     Pop
+    0xC0,              //   End Collection
+    0x05, 0x0D,        //   Usage Page (Digitizer)
+    0x09, 0x22,        //   Usage (Finger)
+    0xA1, 0x02,        //   Collection (Logical)
+    0x09, 0x42,        //     Usage (Tip Switch)
+    0x15, 0x00,        //     Logical Minimum (0)
+    0x25, 0x01,        //     Logical Maximum (1)
+    0x75, 0x01,        //     Report Size (1)
+    0x95, 0x01,        //     Report Count (1)
+    0x81, 0x02,        //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+    0x75, 0x01,        //     Report Size (1)
+    0x81, 0x03,        //     Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+    0x75, 0x06,        //     Report Size (6)
+    0x09, 0x51,        //     Usage (0x51)
+    0x25, 0x3F,        //     Logical Maximum (63)
+    0x81, 0x02,        //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+    0x95, 0x01,        //     Report Count (1)
+    0x05, 0x01,        //     Usage Page (Generic Desktop Ctrls)
+    0xA4,              //     Push
+    0x26, 0x58, 0x02,  //       Logical Maximum (600)
+    0x75, 0x10,        //       Report Size (16)
+    0x09, 0x30,        //       Usage (X)
+    0x95, 0x01,        //       Report Count (1)
+    0x81, 0x02,        //       Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+    0x26, 0x00, 0x04,  //       Logical Maximum (1024)
+    0x09, 0x31,        //       Usage (Y)
+    0x81, 0x02,        //       Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+    0xB4,              //     Pop
+    0xC0,              //   End Collection
+    0x05, 0x0D,        //   Usage Page (Digitizer)
+    0x09, 0x22,        //   Usage (Finger)
+    0xA1, 0x02,        //   Collection (Logical)
+    0x09, 0x42,        //     Usage (Tip Switch)
+    0x15, 0x00,        //     Logical Minimum (0)
+    0x25, 0x01,        //     Logical Maximum (1)
+    0x75, 0x01,        //     Report Size (1)
+    0x95, 0x01,        //     Report Count (1)
+    0x81, 0x02,        //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+    0x75, 0x01,        //     Report Size (1)
+    0x81, 0x03,        //     Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+    0x75, 0x06,        //     Report Size (6)
+    0x09, 0x51,        //     Usage (0x51)
+    0x25, 0x3F,        //     Logical Maximum (63)
+    0x81, 0x02,        //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+    0x95, 0x01,        //     Report Count (1)
+    0x05, 0x01,        //     Usage Page (Generic Desktop Ctrls)
+    0xA4,              //     Push
+    0x26, 0x58, 0x02,  //       Logical Maximum (600)
+    0x75, 0x10,        //       Report Size (16)
+    0x09, 0x30,        //       Usage (X)
+    0x95, 0x01,        //       Report Count (1)
+    0x81, 0x02,        //       Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+    0x26, 0x00, 0x04,  //       Logical Maximum (1024)
+    0x09, 0x31,        //       Usage (Y)
+    0x81, 0x02,        //       Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+    0xB4,              //     Pop
+    0xC0,              //   End Collection
+    0x05, 0x0D,        //   Usage Page (Digitizer)
+    0x09, 0x22,        //   Usage (Finger)
+    0xA1, 0x02,        //   Collection (Logical)
+    0x09, 0x42,        //     Usage (Tip Switch)
+    0x15, 0x00,        //     Logical Minimum (0)
+    0x25, 0x01,        //     Logical Maximum (1)
+    0x75, 0x01,        //     Report Size (1)
+    0x95, 0x01,        //     Report Count (1)
+    0x81, 0x02,        //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+    0x75, 0x01,        //     Report Size (1)
+    0x81, 0x03,        //     Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+    0x75, 0x06,        //     Report Size (6)
+    0x09, 0x51,        //     Usage (0x51)
+    0x25, 0x3F,        //     Logical Maximum (63)
+    0x81, 0x02,        //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+    0x95, 0x01,        //     Report Count (1)
+    0x05, 0x01,        //     Usage Page (Generic Desktop Ctrls)
+    0xA4,              //     Push
+    0x26, 0x58, 0x02,  //       Logical Maximum (600)
+    0x75, 0x10,        //       Report Size (16)
+    0x09, 0x30,        //       Usage (X)
+    0x95, 0x01,        //       Report Count (1)
+    0x81, 0x02,        //       Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+    0x26, 0x00, 0x04,  //       Logical Maximum (1024)
+    0x09, 0x31,        //       Usage (Y)
+    0x81, 0x02,        //       Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+    0xB4,              //     Pop
+    0xC0,              //   End Collection
+    0x05, 0x0D,        //   Usage Page (Digitizer)
+    0x09, 0x22,        //   Usage (Finger)
+    0xA1, 0x02,        //   Collection (Logical)
+    0x09, 0x42,        //     Usage (Tip Switch)
+    0x15, 0x00,        //     Logical Minimum (0)
+    0x25, 0x01,        //     Logical Maximum (1)
+    0x75, 0x01,        //     Report Size (1)
+    0x95, 0x01,        //     Report Count (1)
+    0x81, 0x02,        //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+    0x75, 0x01,        //     Report Size (1)
+    0x81, 0x03,        //     Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+    0x75, 0x06,        //     Report Size (6)
+    0x09, 0x51,        //     Usage (0x51)
+    0x25, 0x3F,        //     Logical Maximum (63)
+    0x81, 0x02,        //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+    0x95, 0x01,        //     Report Count (1)
+    0x05, 0x01,        //     Usage Page (Generic Desktop Ctrls)
+    0xA4,              //     Push
+    0x26, 0x58, 0x02,  //       Logical Maximum (600)
+    0x75, 0x10,        //       Report Size (16)
+    0x09, 0x30,        //       Usage (X)
+    0x95, 0x01,        //       Report Count (1)
+    0x81, 0x02,        //       Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+    0x26, 0x00, 0x04,  //       Logical Maximum (1024)
+    0x09, 0x31,        //       Usage (Y)
+    0x81, 0x02,        //       Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+    0xB4,              //     Pop
+    0xC0,              //   End Collection
+    0x05, 0x0D,        //   Usage Page (Digitizer)
+    0x09, 0x54,        //   Usage (0x54)
+    0x25, 0x05,        //   Logical Maximum (5)
+    0x95, 0x01,        //   Report Count (1)
+    0x75, 0x08,        //   Report Size (8)
+    0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+    0xC0,              // End Collection
+};
+
+bool is_gt92xx_touch_report_desc(const uint8_t* data, size_t len) {
+    if (!data)
+        return false;
+
+    if (len != sizeof(gt92xx_touch_report_desc))
+        return false;
+
+    return (memcmp(data, gt92xx_touch_report_desc, len) == 0);
+}
+
+zx_status_t setup_gt92xx_touch(int fd) {
+    if (fd < 0)
+        return ZX_ERR_INVALID_ARGS;
+
+    return ZX_OK;
+}
+
+size_t get_gt92xx_report_desc(const uint8_t** buf) {
+    *buf = gt92xx_touch_report_desc;
+    return sizeof(gt92xx_touch_report_desc);
+}
\ No newline at end of file
diff --git a/system/ulib/hid/include/hid/gt92xx.h b/system/ulib/hid/include/hid/gt92xx.h
new file mode 100644
index 0000000..c35eecc
--- /dev/null
+++ b/system/ulib/hid/include/hid/gt92xx.h
@@ -0,0 +1,33 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#pragma once
+
+#include <zircon/compiler.h>
+#include <zircon/types.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+__BEGIN_CDECLS
+
+#define GT92XX_RPT_ID_TOUCH 1
+
+typedef struct gt92xx_finger {
+    uint8_t finger_id;
+    uint16_t x;
+    uint16_t y;
+} __PACKED gt92xx_finger_t;
+
+typedef struct gt92xx_touch {
+    uint8_t rpt_id;
+    gt92xx_finger_t fingers[5];
+    uint8_t contact_count;  // will be zero for reports for fingers 6-10
+} __PACKED gt92xx_touch_t;
+
+bool is_gt92xx_touch_report_desc(const uint8_t* data, size_t len);
+zx_status_t setup_gt92xx_touch(int fd);
+
+size_t get_gt92xx_report_desc(const uint8_t** buf);
+
+__END_CDECLS
diff --git a/system/ulib/hid/rules.mk b/system/ulib/hid/rules.mk
index 47a2782..85b761b 100644
--- a/system/ulib/hid/rules.mk
+++ b/system/ulib/hid/rules.mk
@@ -13,6 +13,7 @@
     $(LOCAL_DIR)/egalax.c \
     $(LOCAL_DIR)/eyoyo.c \
     $(LOCAL_DIR)/ft3x27.c \
+    $(LOCAL_DIR)/gt92xx.c \
     $(LOCAL_DIR)/hid.c \
     $(LOCAL_DIR)/keymaps.c \
     $(LOCAL_DIR)/paradise.c \