1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <ddk/binding.h>
6#include <ddk/debug.h>
7#include <ddk/protocol/platform-defs.h>
8#include <ddk/protocol/platform-device.h>
9#include <fbl/auto_call.h>
10#include <fbl/auto_lock.h>
11
12#include "gt92xx.h"
13
14namespace goodix {
15// Configuration data
16// first two bytes contain starting register address (part of i2c transaction)
17static uint8_t conf_data[] = {
18    GT_REG_CONFIG_DATA >> 8, GT_REG_CONFIG_DATA & 0xff,
19    0x5C, 0x00, 0x04, 0x58, 0x02, 0x05, 0xBD, 0xC0,
20    0x00, 0x08, 0x1E, 0x05, 0x50, 0x32, 0x05, 0x0B,
21    0x00, 0x00, 0x00, 0x00, 0x40, 0x12, 0x00, 0x17,
22    0x17, 0x19, 0x12, 0x8D, 0x2D, 0x0F, 0x3F, 0x41,
23    0xB2, 0x04, 0x00, 0x00, 0x00, 0xBC, 0x03, 0x1D,
24    0x1E, 0x80, 0x01, 0x00, 0x14, 0x46, 0x00, 0x00,
25    0x00, 0x00, 0x00, 0x37, 0x55, 0x8F, 0xC5, 0x02,
26    0x07, 0x11, 0x00, 0x04, 0x8A, 0x39, 0x00, 0x81,
27    0x3E, 0x00, 0x78, 0x44, 0x00, 0x71, 0x4A, 0x00,
28    0x6A, 0x51, 0x00, 0x6A, 0x00, 0x00, 0x00, 0x00,
29    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
30    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
31    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
32    0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
33    0x1C, 0x1A, 0x18, 0x16, 0x14, 0x12, 0x10, 0x0E,
34    0x0C, 0x0A, 0x08, 0x06, 0x04, 0x02, 0x00, 0x00,
35    0xFF, 0xFF, 0x1F, 0xE7, 0xFF, 0xFF, 0xFF, 0x0F,
36    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2A, 0x29,
37    0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21,
38    0x20, 0x1F, 0x1E, 0x0C, 0x0B, 0x0A, 0x09, 0x08,
39    0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
40    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
41    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
42    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
43    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
44    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
45    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
46    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
47    0x00, 0x00, 0x6C, 0x01};
48
49int Gt92xxDevice::Thread() {
50    zx_status_t status;
51    zxlogf(INFO, "gt92xx: entering irq thread\n");
52    while (true) {
53        status = irq_.wait(nullptr);
54        if (!running_.load()) {
55            return ZX_OK;
56        }
57        if (status != ZX_OK) {
58            zxlogf(ERROR, "gt92xx: Interrupt error %d\n", status);
59        }
60        uint8_t touch_stat = Read(GT_REG_TOUCH_STATUS);
61        if (touch_stat & 0x80) {
62            uint8_t num_reports = touch_stat & 0x0f;
63            FingerReport reports[kMaxPoints];
64            // Read touch reports
65            zx_status_t status =
66                Read(GT_REG_REPORTS, reinterpret_cast<uint8_t*>(&reports),
67                     static_cast<uint8_t>(sizeof(FingerReport) * kMaxPoints));
68            if (status == ZX_OK) {
69                fbl::AutoLock lock(&proxy_lock_);
70                gt_rpt_.rpt_id = GT92XX_RPT_ID_TOUCH;
71                gt_rpt_.contact_count = num_reports;
72                // We are reusing same HID report as ft3x77 to simplify astro integration
73                // so we need to copy from device format to HID structure format
74                for (uint32_t i = 0; i < kMaxPoints; i++) {
75                    gt_rpt_.fingers[i].finger_id = reports[i].id;
76                    gt_rpt_.fingers[i].x = reports[i].x;
77                    gt_rpt_.fingers[i].y = reports[i].y;
78                }
79                if (proxy_.is_valid()) {
80                    proxy_.IoQueue(reinterpret_cast<uint8_t*>(&gt_rpt_), sizeof(gt92xx_touch_t));
81                }
82            }
83            // Clear the touch status
84            Write(GT_REG_TOUCH_STATUS, 0);
85        }
86    }
87    zxlogf(INFO, "gt92xx: exiting\n");
88    return 0;
89}
90
91zx_status_t Gt92xxDevice::Create(zx_device_t* device) {
92
93    zxlogf(INFO, "gt92xx: driver started...\n");
94
95    auto pdev = ddk::Pdev::Create(device);
96    if (!pdev) {
97        zxlogf(ERROR, "%s could not acquire platform device\n", __func__);
98        return ZX_ERR_NO_RESOURCES;
99    }
100
101    auto goodix_dev = fbl::make_unique<Gt92xxDevice>(device,
102                                                     fbl::move(pdev->GetI2cChan(0)),
103                                                     fbl::move(pdev->GetGpio(0)),
104                                                     fbl::move(pdev->GetGpio(1)));
105
106    zx_status_t status = goodix_dev->Init();
107    if (status != ZX_OK) {
108        zxlogf(ERROR, "Could not initialize gt92xx hardware %d\n", status);
109        return status;
110    }
111
112    auto thunk = [](void* arg) -> int {
113        return reinterpret_cast<Gt92xxDevice*>(arg)->Thread();
114    };
115
116    auto cleanup = fbl::MakeAutoCall([&]() { goodix_dev->ShutDown(); });
117
118    goodix_dev->running_.store(true);
119    int ret = thrd_create_with_name(&goodix_dev->thread_, thunk,
120                                    goodix_dev.get(),
121                                    "gt92xx-thread");
122    ZX_DEBUG_ASSERT(ret == thrd_success);
123
124    status = goodix_dev->DdkAdd("gt92xx HidDevice\n");
125    if (status != ZX_OK) {
126        zxlogf(ERROR, "gt92xx: Could not create hid device: %d\n", status);
127        return status;
128    } else {
129        zxlogf(INFO, "gt92xx: Added hid device\n");
130    }
131
132    cleanup.cancel();
133
134    // device intentionally leaked as it is now held by DevMgr
135    __UNUSED auto ptr = goodix_dev.release();
136
137    return ZX_OK;
138}
139
140zx_status_t Gt92xxDevice::Init() {
141    // Hardware reset
142    HWReset();
143
144    uint8_t fw = Read(GT_REG_FIRMWARE);
145    if (fw != GT_FIRMWARE_MAGIC) {
146        zxlogf(ERROR, "Invalid gt92xx firmware configuration!\n");
147        return ZX_ERR_BAD_STATE;
148    }
149    // Device requires 50ms delay after this check (per datasheet)
150    zx_nanosleep(zx_deadline_after(ZX_MSEC(50)));
151
152    // Configuration data should span specific set of registers
153    // last register has flag to latch in new configuration, second
154    // to last register holds checksum of register values.
155    // Note: first two bytes of conf_data hold the 16-bit register address where
156    // the write will start.
157    ZX_DEBUG_ASSERT((countof(conf_data) - sizeof(uint16_t)) ==
158                    (GT_REG_CONFIG_REFRESH - GT_REG_CONFIG_DATA + 1));
159
160    // Write conf data to registers
161    zx_status_t status = i2c_.Transact(conf_data, sizeof(conf_data), NULL, 0);
162    if (status != ZX_OK) {
163        return status;
164    }
165    // Device requires 10ms delay to refresh configuration
166    zx_nanosleep(zx_deadline_after(ZX_MSEC(10)));
167    // Clear touch state in case there were spurious touches registered
168    // during startup
169    Write(GT_REG_TOUCH_STATUS, 0);
170
171    status = int_gpio_.GetInterrupt(ZX_INTERRUPT_MODE_EDGE_HIGH, &irq_);
172
173    return status;
174}
175
176void Gt92xxDevice::HWReset() {
177    // Hardware reset will also set the address of the controller to either
178    // 0x14 0r 0x5d.  See the datasheet for explanation of sequence.
179    reset_gpio_.ConfigOut(0); //Make reset pin an output and pull low
180    int_gpio_.ConfigOut(0);   //Make interrupt pin an output and pull low
181
182    // Delay for 100us
183    zx_nanosleep(zx_deadline_after(ZX_USEC(100)));
184
185    reset_gpio_.Write(1); // Release the reset
186    zx_nanosleep(zx_deadline_after(ZX_MSEC(5)));
187    int_gpio_.ConfigIn(0);                        // Make interrupt pin an input again;
188    zx_nanosleep(zx_deadline_after(ZX_MSEC(50))); // Wait for reset to complete
189}
190
191zx_status_t Gt92xxDevice::HidBusQuery(uint32_t options, hid_info_t* info) {
192    if (!info) {
193        return ZX_ERR_INVALID_ARGS;
194    }
195    info->dev_num = 0;
196    info->dev_class = HID_DEV_CLASS_OTHER;
197    info->boot_device = false;
198
199    return ZX_OK;
200}
201
202void Gt92xxDevice::DdkRelease() {
203    delete this;
204}
205
206void Gt92xxDevice::DdkUnbind() {
207    ShutDown();
208    DdkRemove();
209}
210
211zx_status_t Gt92xxDevice::ShutDown() {
212    running_.store(false);
213    irq_.destroy();
214    thrd_join(thread_, NULL);
215    {
216        fbl::AutoLock lock(&proxy_lock_);
217        proxy_.clear();
218    }
219    return ZX_OK;
220}
221
222zx_status_t Gt92xxDevice::HidBusGetDescriptor(uint8_t desc_type, void** data, size_t* len) {
223
224    const uint8_t* desc_ptr;
225    uint8_t* buf;
226    *len = get_gt92xx_report_desc(&desc_ptr);
227    fbl::AllocChecker ac;
228    buf = new (&ac) uint8_t[*len];
229    if (!ac.check()) {
230        return ZX_ERR_NO_MEMORY;
231    }
232    memcpy(buf, desc_ptr, *len);
233    *data = buf;
234    return ZX_OK;
235}
236
237zx_status_t Gt92xxDevice::HidBusGetReport(uint8_t rpt_type, uint8_t rpt_id, void* data,
238                                          size_t len, size_t* out_len) {
239    return ZX_ERR_NOT_SUPPORTED;
240}
241
242zx_status_t Gt92xxDevice::HidBusSetReport(uint8_t rpt_type, uint8_t rpt_id, void* data,
243                                          size_t len) {
244    return ZX_ERR_NOT_SUPPORTED;
245}
246
247zx_status_t Gt92xxDevice::HidBusGetIdle(uint8_t rpt_id, uint8_t* duration) {
248    return ZX_ERR_NOT_SUPPORTED;
249}
250
251zx_status_t Gt92xxDevice::HidBusSetIdle(uint8_t rpt_id, uint8_t duration) {
252    return ZX_ERR_NOT_SUPPORTED;
253}
254
255zx_status_t Gt92xxDevice::HidBusGetProtocol(uint8_t* protocol) {
256    return ZX_ERR_NOT_SUPPORTED;
257}
258
259zx_status_t Gt92xxDevice::HidBusSetProtocol(uint8_t protocol) {
260    return ZX_OK;
261}
262
263void Gt92xxDevice::HidBusStop() {
264    fbl::AutoLock lock(&proxy_lock_);
265    proxy_.clear();
266}
267
268zx_status_t Gt92xxDevice::HidBusStart(ddk::HidBusIfcProxy proxy) {
269    fbl::AutoLock lock(&proxy_lock_);
270    if (proxy_.is_valid()) {
271        zxlogf(ERROR, "gt92xx: Already bound!\n");
272        return ZX_ERR_ALREADY_BOUND;
273    } else {
274        proxy_ = proxy;
275        zxlogf(INFO, "gt92xx: started\n");
276    }
277    return ZX_OK;
278}
279
280uint8_t Gt92xxDevice::Read(uint16_t addr) {
281    uint8_t rbuf;
282    Read(addr, &rbuf, 1);
283    return rbuf;
284}
285
286zx_status_t Gt92xxDevice::Read(uint16_t addr, uint8_t* buf, uint8_t len) {
287    uint8_t tbuf[2];
288    tbuf[0] = static_cast<uint8_t>(addr >> 8);
289    tbuf[1] = static_cast<uint8_t>(addr & 0xff);
290    return i2c_.Transact(tbuf, 2, buf, len);
291}
292
293zx_status_t Gt92xxDevice::Write(uint16_t addr, uint8_t val) {
294    uint8_t tbuf[3];
295    tbuf[0] = static_cast<uint8_t>(addr >> 8);
296    tbuf[1] = static_cast<uint8_t>(addr & 0xff);
297    tbuf[2] = val;
298    return i2c_.Transact(tbuf, 3, NULL, 0);
299}
300
301} // namespace ft
302
303__BEGIN_CDECLS
304
305zx_status_t gt92xx_bind(void* ctx, zx_device_t* device) {
306    return goodix::Gt92xxDevice::Create(device);
307}
308
309static zx_driver_ops_t gt92xx_driver_ops = {
310    .version = DRIVER_OPS_VERSION,
311    .init = nullptr,
312    .bind = gt92xx_bind,
313    .create = nullptr,
314    .release = nullptr,
315};
316
317// clang-format off
318ZIRCON_DRIVER_BEGIN(gt92xx, gt92xx_driver_ops, "zircon", "0.1", 3)
319    BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_GOOGLE),
320    BI_ABORT_IF(NE, BIND_PLATFORM_DEV_PID, PDEV_PID_ASTRO),
321    BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_DID, PDEV_DID_ASTRO_GOODIXTOUCH),
322ZIRCON_DRIVER_END(gt92xx)
323// clang-format on
324__END_CDECLS
325