[cleo][touch] Enable touch driver for Cleo

According to the Linux driver source the FT6336
(used in Cleo) and the FT3x27 series share the
same registers and report format. The only driver
change necessary was to work around the MT8167 I2C
transfer length limitation.

Test: added logging, reported cordinates matched
      touch activity with one and two fingers.
Change-Id: I6e62732241329e8d303bb679eaf8664a33cd8c97
diff --git a/system/dev/board/astro/astro-touch.c b/system/dev/board/astro/astro-touch.c
index bbaa2bc..0154740 100644
--- a/system/dev/board/astro/astro-touch.c
+++ b/system/dev/board/astro/astro-touch.c
@@ -33,9 +33,8 @@
 
 static pbus_dev_t ft3x27_touch_dev = {
     .name = "ft3x27-touch",
-    .vid = PDEV_VID_GOOGLE,
-    .pid = PDEV_PID_ASTRO,
-    .did = PDEV_DID_ASTRO_FOCALTOUCH,
+    .vid = PDEV_VID_GENERIC,
+    .did = PDEV_DID_FOCALTOUCH,
     .i2c_channel_list = ft3x27_touch_i2c,
     .i2c_channel_count = countof(ft3x27_touch_i2c),
     .gpio_list = touch_gpios,
diff --git a/system/dev/board/mt8167s_ref/mt8167-touch.cpp b/system/dev/board/mt8167s_ref/mt8167-touch.cpp
new file mode 100644
index 0000000..db97f27
--- /dev/null
+++ b/system/dev/board/mt8167s_ref/mt8167-touch.cpp
@@ -0,0 +1,113 @@
+// 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 <limits.h>
+
+#include <ddk/debug.h>
+#include <ddk/device.h>
+#include <ddk/platform-defs.h>
+#include <ddk/protocol/platform/bus.h>
+#include <ddktl/mmio.h>
+#include <fbl/algorithm.h>
+#include <hwreg/bitfields.h>
+#include <soc/mt8167/mt8167-hw.h>
+
+#include "mt8167.h"
+
+namespace {
+
+constexpr uintptr_t kPmicBaseAligned =
+    fbl::round_down<uintptr_t, uintptr_t>(MT8167_PMIC_WRAP_BASE, PAGE_SIZE);
+constexpr size_t kPmicOffset = MT8167_PMIC_WRAP_BASE - kPmicBaseAligned;
+constexpr size_t kPmicSizeAligned =
+    fbl::round_up<size_t, size_t>(kPmicOffset + MT8167_PMIC_WRAP_SIZE, PAGE_SIZE);
+
+constexpr uint32_t kDigLdoCon7 = 0x285;
+constexpr uint16_t kVgp1Enable = 0x8000;
+
+}  // namespace
+
+namespace board_mt8167 {
+
+class PmicCmd : public hwreg::RegisterBase<PmicCmd, uint32_t> {
+public:
+    static auto Get() { return hwreg::RegisterAddr<PmicCmd>(0xa0 + kPmicOffset); }
+
+    DEF_BIT(31, write);
+    DEF_FIELD(30, 16, addr);
+    DEF_FIELD(15, 0, data);
+};
+
+class PmicReadData : public hwreg::RegisterBase<PmicReadData, uint32_t> {
+public:
+    static constexpr uint32_t kStateIdle  = 0;
+
+    static auto Get() { return hwreg::RegisterAddr<PmicReadData>(0xa4 + kPmicOffset); }
+
+    DEF_FIELD(18, 16, status);
+};
+
+zx_status_t Mt8167::TouchInit() {
+    pdev_board_info_t info;
+    zx_status_t status = pbus_.GetBoardInfo(&info);
+    if (status != ZX_OK) {
+        zxlogf(ERROR, "%s: GetBoardInfo failed\n", __FILE__);
+        return status;
+    }
+
+    if (info.vid != PDEV_VID_GOOGLE || info.pid != PDEV_PID_CLEO) {
+        return ZX_OK;
+    }
+
+    static constexpr pbus_gpio_t touch_gpios[] = {
+        {
+            .gpio = MT8167_GPIO_TOUCH_INT
+        },
+        {
+            .gpio = MT8167_GPIO_TOUCH_RST
+        },
+    };
+
+    static constexpr pbus_i2c_channel_t touch_i2cs[] = {
+        {
+            .bus_id = 0,
+            .address = 0x38
+        },
+    };
+
+    pbus_dev_t touch_dev = {};
+    touch_dev.name = "touch";
+    touch_dev.vid = PDEV_VID_GENERIC;
+    touch_dev.did = PDEV_DID_FOCALTOUCH;
+    touch_dev.i2c_channel_list = touch_i2cs;
+    touch_dev.i2c_channel_count = countof(touch_i2cs);
+    touch_dev.gpio_list = touch_gpios;
+    touch_dev.gpio_count = countof(touch_gpios);
+
+    zx::unowned_resource root_resource(get_root_resource());
+    std::optional<ddk::MmioBuffer> pmic_mmio;
+    status = ddk::MmioBuffer::Create(kPmicBaseAligned, kPmicSizeAligned, *root_resource,
+                                     ZX_CACHE_POLICY_UNCACHED_DEVICE, &pmic_mmio);
+    if (status != ZX_OK) {
+        zxlogf(ERROR, "%s: Failed to enable VGP1 regulator: %d\n", __FUNCTION__, status);
+        return status;
+    }
+
+    while (PmicReadData::Get().ReadFrom(&(*pmic_mmio)).status() != PmicReadData::kStateIdle) {}
+
+    PmicCmd::Get()
+        .FromValue(0)
+        .set_write(1)
+        .set_addr(kDigLdoCon7)
+        .set_data(kVgp1Enable)
+        .WriteTo(&(*pmic_mmio));
+
+    if ((status = pbus_.DeviceAdd(&touch_dev)) != ZX_OK) {
+        zxlogf(ERROR, "%s: Failed to add touch device: %d\n", __FUNCTION__, status);
+    }
+
+    return status;
+}
+
+}  // namespace board_mt8167
diff --git a/system/dev/board/mt8167s_ref/mt8167.cpp b/system/dev/board/mt8167s_ref/mt8167.cpp
index fe6a96b..f201968 100644
--- a/system/dev/board/mt8167s_ref/mt8167.cpp
+++ b/system/dev/board/mt8167s_ref/mt8167.cpp
@@ -86,6 +86,11 @@
     if (UsbInit() != ZX_OK) {
         zxlogf(ERROR, "UsbInit() failed\n");
     }
+    // TouchInit() must be called before ThermalInit() as the touch driver uses the PMIC wrapper to
+    // enable the VGP1 regulator.
+    if (TouchInit() != ZX_OK) {
+        zxlogf(ERROR, "TouchInit() failed\n");
+    }
     if (ThermalInit() != ZX_OK) {
         zxlogf(ERROR, "ThermalInit() failed\n");
     }
diff --git a/system/dev/board/mt8167s_ref/mt8167.h b/system/dev/board/mt8167s_ref/mt8167.h
index 5601ad8..0da7003 100644
--- a/system/dev/board/mt8167s_ref/mt8167.h
+++ b/system/dev/board/mt8167s_ref/mt8167.h
@@ -53,6 +53,7 @@
     zx_status_t ClkInit();
     zx_status_t UsbInit();
     zx_status_t ThermalInit();
+    zx_status_t TouchInit();
     int Thread();
 
     ddk::PBusProtocolProxy pbus_;
diff --git a/system/dev/board/mt8167s_ref/rules.mk b/system/dev/board/mt8167s_ref/rules.mk
index 31837df..a96023d 100644
--- a/system/dev/board/mt8167s_ref/rules.mk
+++ b/system/dev/board/mt8167s_ref/rules.mk
@@ -21,6 +21,7 @@
     $(LOCAL_DIR)/mt8167-clk.cpp \
     $(LOCAL_DIR)/mt8167-usb.cpp \
     $(LOCAL_DIR)/mt8167-thermal.cpp \
+    $(LOCAL_DIR)/mt8167-touch.cpp \
 
 MODULE_STATIC_LIBS := \
     system/dev/lib/mt8167 \
diff --git a/system/dev/input/focaltech/focaltech.c b/system/dev/input/focaltech/focaltech.c
index b855d0b..9b46157 100644
--- a/system/dev/input/focaltech/focaltech.c
+++ b/system/dev/input/focaltech/focaltech.c
@@ -15,8 +15,8 @@
 
 // clang-format off
 ZIRCON_DRIVER_BEGIN(focaltech_touch, focaltech_driver_ops, "focaltech-touch", "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_FOCALTOUCH),
+    BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_PDEV),
+    BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_GENERIC),
+    BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_DID, PDEV_DID_FOCALTOUCH),
 ZIRCON_DRIVER_END(focaltech_touch)
 // clang-format on
diff --git a/system/dev/input/focaltech/ft3x27.cpp b/system/dev/input/focaltech/ft3x27.cpp
index 29c2a9b..65afb77 100644
--- a/system/dev/input/focaltech/ft3x27.cpp
+++ b/system/dev/input/focaltech/ft3x27.cpp
@@ -5,6 +5,7 @@
 #include <ddk/debug.h>
 #include <ddk/protocol/platform/device.h>
 #include <ddk/protocol/i2c-lib.h>
+#include <fbl/algorithm.h>
 #include <fbl/auto_call.h>
 #include <fbl/auto_lock.h>
 #include <fbl/ref_counted.h>
@@ -235,13 +236,23 @@
     return rbuf;
 }
 
-zx_status_t Ft3x27Device::Read(uint8_t addr, uint8_t* buf, uint8_t len) {
+zx_status_t Ft3x27Device::Read(uint8_t addr, uint8_t* buf, size_t len) {
+    // TODO(bradenkell): Remove this workaround when transfers of more than 8 bytes are supported on
+    // the MT8167.
+    while (len > 0) {
+        size_t readlen = fbl::min(len, kMaxI2cTransferLength);
 
-    zx_status_t status = i2c_write_read_sync(&i2c_, &addr, 1, buf, len);
-    if (status != ZX_OK) {
-        zxlogf(ERROR, "Failed to read i2c - %d\n", status);
-        return status;
+        zx_status_t status = i2c_write_read_sync(&i2c_, &addr, 1, buf, readlen);
+        if (status != ZX_OK) {
+            zxlogf(ERROR, "Failed to read i2c - %d\n", status);
+            return status;
+        }
+
+        addr = static_cast<uint8_t>(addr + readlen);
+        buf += readlen;
+        len -= readlen;
     }
+
     return ZX_OK;
 }
 } //namespace ft
diff --git a/system/dev/input/focaltech/ft3x27.h b/system/dev/input/focaltech/ft3x27.h
index cd44fe0..0a91208 100644
--- a/system/dev/input/focaltech/ft3x27.h
+++ b/system/dev/input/focaltech/ft3x27.h
@@ -96,11 +96,13 @@
     //  them) on the i2c bus.  This is not the HID report size.
     static constexpr uint32_t kFingerRptSize = 6;
 
+    static constexpr size_t kMaxI2cTransferLength = 8;
+
     zx_status_t InitPdev();
     zx_status_t ShutDown() __TA_EXCLUDES(proxy_lock_);
 
     uint8_t Read(uint8_t addr);
-    zx_status_t Read(uint8_t addr, uint8_t* buf, uint8_t len);
+    zx_status_t Read(uint8_t addr, uint8_t* buf, size_t len);
 
     int Thread();
 
diff --git a/system/dev/lib/mt8167/include/soc/mt8167/mt8167-hw.h b/system/dev/lib/mt8167/include/soc/mt8167/mt8167-hw.h
index 1dad2cb..b6829bc 100644
--- a/system/dev/lib/mt8167/include/soc/mt8167/mt8167-hw.h
+++ b/system/dev/lib/mt8167/include/soc/mt8167/mt8167-hw.h
@@ -114,6 +114,7 @@
 #define MT8167_I2C_CNT                                      3
 #define MT8167_GPIO_EINT_MAX                                131
 
-#define MT8167_GPIO_MSDC0_RST                               114
-
 #define MT8167_GPIO_MT7668_PMU_EN                           2
+#define MT8167_GPIO_TOUCH_INT                               8
+#define MT8167_GPIO_TOUCH_RST                               9
+#define MT8167_GPIO_MSDC0_RST                               114
diff --git a/system/ulib/ddk/include/ddk/platform-defs.h b/system/ulib/ddk/include/ddk/platform-defs.h
index b621e43..410f219 100644
--- a/system/ulib/ddk/include/ddk/platform-defs.h
+++ b/system/ulib/ddk/include/ddk/platform-defs.h
@@ -33,6 +33,7 @@
 #define PDEV_DID_MUSB_PERIPHERAL    20  // MUSB in peripheral role
 #define PDEV_DID_MUSB_HOST          21  // MUSB in host role
 #define PDEV_DID_DUMMY_DISPLAY      22  // Dummy display
+#define PDEV_DID_FOCALTOUCH         23  // FocalTech touch device
 
 // QEMU emulator
 #define PDEV_VID_QEMU               1
@@ -65,8 +66,7 @@
 #define PDEV_DID_GAUSS_AUDIO_OUT    2
 #define PDEV_DID_GAUSS_I2C_TEST     3
 #define PDEV_DID_GAUSS_LED          4
-#define PDEV_DID_ASTRO_FOCALTOUCH   5
-#define PDEV_DID_ASTRO_GOODIXTOUCH  6
+#define PDEV_DID_ASTRO_GOODIXTOUCH  5
 
 // Khadas
 #define PDEV_VID_KHADAS             4