qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH 2/2] hw/input: Add FT5336 touch controller


From: Evgeny Ermakov
Subject: [PATCH 2/2] hw/input: Add FT5336 touch controller
Date: Wed, 30 Nov 2022 15:12:59 +1100

Signed-off-by: Evgeny Ermakov <evgeny.v.ermakov@gmail.com>
---
 include/hw/input/ft5336.h |  14 ++
 hw/input/ft5336.c         | 357 ++++++++++++++++++++++++++++++++++++++
 hw/input/Kconfig          |   4 +
 hw/input/meson.build      |   2 +
 4 files changed, 377 insertions(+)
 create mode 100644 include/hw/input/ft5336.h
 create mode 100644 hw/input/ft5336.c

diff --git a/include/hw/input/ft5336.h b/include/hw/input/ft5336.h
new file mode 100644
index 0000000000..7bef3f9efb
--- /dev/null
+++ b/include/hw/input/ft5336.h
@@ -0,0 +1,14 @@
+/*
+ * FT5336 touch controller
+ *
+ * Copyright (c) 2022 Evgeny Ermakov <evgeny.v.ermakov@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_INPUT_FT5336_H
+#define HW_INPUT_FT5336_H
+
+#define TYPE_FT5336 "ft5336"
+
+#endif
diff --git a/hw/input/ft5336.c b/hw/input/ft5336.c
new file mode 100644
index 0000000000..bacf79201a
--- /dev/null
+++ b/hw/input/ft5336.c
@@ -0,0 +1,357 @@
+/*
+ * FT5336 touch controller
+ *
+ * Copyright (c) 2022 Evgeny Ermakov <evgeny.v.ermakov@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "hw/input/ft5336.h"
+#include "hw/i2c/i2c.h"
+#include "hw/irq.h"
+#include "migration/vmstate.h"
+#include "qemu/module.h"
+#include "qemu/log.h"
+#include "qemu/timer.h"
+#include "ui/input.h"
+#include "qom/object.h"
+
+OBJECT_DECLARE_SIMPLE_TYPE(FT5336TouchState, FT5336)
+
+struct FT5336TouchState {
+    I2CSlave parent_obj;
+
+    uint8_t i2c_cycle;
+    uint8_t reg;
+
+    qemu_irq irq;
+
+    int32_t abs_x;
+    int32_t abs_y;
+    uint16_t touch_x;
+    uint16_t touch_y;
+    bool touch_press;
+
+    bool inte;
+};
+
+/* I2C Slave address of touchscreen FocalTech FT5336 */
+#define FT5336_I2C_SLAVE_ADDRESS            0x70
+
+/* Maximum border values of the touchscreen pad */
+#define FT5336_MAX_WIDTH                    ((uint16_t)480)     /* Touchscreen 
pad max width   */
+#define FT5336_MAX_HEIGHT                   ((uint16_t)272)     /* Touchscreen 
pad max height  */
+
+/* Max detectable simultaneous touches */
+#define FT5336_MAX_DETECTABLE_TOUCH         0x05
+
+
+enum {
+    FT5336_P_XH               = 0x00,
+    FT5336_P_XL               = 0x01,
+    FT5336_P_YH               = 0x02,
+    FT5336_P_YL               = 0x03,
+   /* Values Pn_XH and Pn_YH related */
+#define FT5336_TOUCH_EVT_FLAG_PRESS_DOWN    0x00
+#define FT5336_TOUCH_EVT_FLAG_LIFT_UP       0x01
+#define FT5336_TOUCH_EVT_FLAG_CONTACT       0x02
+#define FT5336_TOUCH_EVT_FLAG_NO_EVENT      0x03
+
+    FT5336_P_WEIGHT           = 0x04,
+    /* Values Pn_WEIGHT related  */
+#define FT5336_TOUCH_WEIGHT_MASK            0xFF
+#define FT5336_TOUCH_WEIGHT_SHIFT           0x00
+
+    FT5336_P_MISC             = 0x05
+    /* Values related to FT5336_Pn_MISC_REG */
+#define FT5336_TOUCH_AREA_MASK              (0x04 << 4))
+#define FT5336_TOUCH_AREA_SHIFT             0x04
+};
+
+enum {
+    FT5336_R_MODE               = 0x00,
+#define FT5336_DEV_MODE_WORKING             0x00
+#define FT5336_DEV_MODE_FACTORY             0x04
+
+#define FT5336_DEV_MODE_MASK                0x07
+#define FT5336_DEV_MODE_SHIFT               0x04
+
+    FT5336_R_GEST_ID            = 0x01,
+#define FT5336_GEST_ID_NO_GESTURE           0x00
+#define FT5336_GEST_ID_MOVE_UP              0x10
+#define FT5336_GEST_ID_MOVE_RIGHT           0x14
+#define FT5336_GEST_ID_MOVE_DOWN            0x18
+#define FT5336_GEST_ID_MOVE_LEFT            0x1C
+#define FT5336_GEST_ID_SINGLE_CLICK         0x20
+#define FT5336_GEST_ID_DOUBLE_CLICK         0x22
+#define FT5336_GEST_ID_ROTATE_CLOCKWISE     0x28
+#define FT5336_GEST_ID_ROTATE_C_CLOCKWISE   0x29
+#define FT5336_GEST_ID_ZOOM_IN              0x40
+#define FT5336_GEST_ID_ZOOM_OUT             0x49
+
+    FT5336_R_STAT               = 0x02,
+#define FT5336_TD_STAT_MASK                 0x0F
+#define FT5336_TD_STAT_SHIFT                0x00
+
+    FT5336_R_P1_BASE            = 0x03,
+    FT5336_R_P2_BASE            = 0x09,
+    FT5336_R_P3_BASE            = 0x0f,
+    FT5336_R_P4_BASE            = 0x15,
+    FT5336_R_P5_BASE            = 0x1b,
+    FT5336_R_P6_BASE            = 0x21,
+    FT5336_R_P7_BASE            = 0x27,
+    FT5336_R_P8_BASE            = 0x2d,
+    FT5336_R_P9_BASE            = 0x33,
+    FT5336_R_P10_BASE           = 0x39,
+
+#define FT5336_TOUCH_EVT_FLAG_SHIFT         0x06
+#define FT5336_TOUCH_EVT_FLAG_MASK          (3 << FT5336_TOUCH_EVT_FLAG_SHIFT))
+
+#define FT5336_TOUCH_POS_MSB_MASK           0x0F
+#define FT5336_TOUCH_POS_MSB_SHIFT          0x00
+
+    /* Values Pn_XL and Pn_YL related */
+#define FT5336_TOUCH_POS_LSB_MASK           0xFF
+#define FT5336_TOUCH_POS_LSB_SHIFT          0x00
+
+    FT5336_R_TH_GROUP           = 0x80,
+    /* Values FT5336_TH_GROUP_REG : threshold related  */
+#define FT5336_THRESHOLD_MASK               0xFF
+#define FT5336_THRESHOLD_SHIFT              0x00
+
+    FT5336_R_TH_DIFF            = 0x85,
+
+    FT5336_R_CTRL               = 0x86,
+    /* Values related to FT5336_CTRL_REG */
+
+    /* Will keep the Active mode when there is no touching */
+#define FT5336_CTRL_KEEP_ACTIVE_MODE        0x00
+
+    /* Switching from Active mode to Monitor mode automatically when there is 
no touching */
+#define FT5336_CTRL_KEEP_AUTO_SWITCH_MONITOR_MODE  0x01
+    FT5336_R_TIMEENTERMONITOR   = 0x87,
+    FT5336_R_PERIODACTIVE       = 0x88,
+    FT5336_R_PERIODMONITOR      = 0x89,
+    FT5336_R_RADIAN_VALUE       = 0x91,
+    FT5336_R_OFFSET_LEFT_RIGHT  = 0x92,
+    FT5336_R_OFFSET_UP_DOWN     = 0x93,
+    FT5336_R_DISTANCE_LEFT      = 0x94,
+    FT5336_R_DISTANCE_UP_DOWN   = 0x95,
+    FT5336_R_DISTANCE_ZOOM      = 0x96,
+
+    FT5336_R_LIB_VER_H          = 0xa1,
+    FT5336_R_LIB_VER_L          = 0xa2,
+    FT5336_R_CIPHER             = 0xa3,
+
+    FT5336_R_GMODE              = 0xa4,
+#define FT5336_G_MODE_INTERRUPT_MASK        0x03
+#define FT5336_G_MODE_INTERRUPT_SHIFT       0x00
+
+    /* Possible values of FT5336_GMODE_REG */
+#define FT5336_G_MODE_INTERRUPT_POLLING     0x00
+#define FT5336_G_MODE_INTERRUPT_TRIGGER     0x01
+
+    FT5336_R_PWR_MODE           = 0xa5,
+    FT5336_R_FIRMID             = 0xa6,
+
+    FT5336_R_CHIP_ID            = 0xa8,
+    /*  Possible values of FT5336_CHIP_ID_REG */
+#define FT5336_ID_VALUE                     0x51
+
+    FT5336_R_RELEASE_CODE_ID    = 0xaf,
+    /* Release code version */
+#define FT5336_RELEASE_CODE_ID_REG          0xAF
+
+    FT5336_R_STATE              = 0xbc,
+};
+
+
+static uint8_t ft5336_touch_read(FT5336TouchState *s, int reg, int byte)
+{
+    switch (reg) {
+    case FT5336_R_CHIP_ID:
+        return FT5336_ID_VALUE;
+    case FT5336_R_STAT:
+        return s->touch_press ? 1 : 0;
+    case FT5336_R_P1_BASE:
+        switch (byte) {
+        case FT5336_P_XH: return extract16(s->touch_x, 8, 8);
+        case FT5336_P_XL: return extract16(s->touch_x, 0, 8);
+        case FT5336_P_YH: return extract16(s->touch_y, 8, 8);
+        case FT5336_P_YL: return extract16(s->touch_y, 0, 8);
+        default:
+            return 0;
+        }
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: unknown register %02x\n", __func__, reg);
+        return 0;
+    }
+}
+
+static void ft5336_touch_write(FT5336TouchState *s, int reg, int byte, uint8_t 
value)
+{
+    switch (reg) {
+    case FT5336_R_GMODE:
+        s->inte = value;
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: unknown register %02x\n", __func__, reg);
+        break;
+    }
+}
+
+static int ft5336_i2c_event(I2CSlave *i2c, enum i2c_event event)
+{
+    FT5336TouchState *s = FT5336(i2c);
+
+    switch (event) {
+    case I2C_START_RECV:
+    case I2C_START_SEND:
+        s->i2c_cycle = 0;
+        break;
+    default:
+        break;
+    }
+
+    return 0;
+}
+
+static uint8_t ft5336_i2c_recv(I2CSlave *i2c)
+{
+    FT5336TouchState *s = FT5336(i2c);
+
+    return ft5336_touch_read(s, s->reg, s->i2c_cycle++);
+}
+
+static int ft5336_i2c_send(I2CSlave *i2c, uint8_t data)
+{
+    FT5336TouchState *s = FT5336(i2c);
+
+    if (!s->i2c_cycle) {
+        s->reg = data;
+    } else {
+        ft5336_touch_write(s, s->reg, s->i2c_cycle - 1, data);
+    }
+    s->i2c_cycle++;
+
+    return 0;
+}
+
+static void ft5336_input_event(DeviceState *dev, QemuConsole *src,
+                               InputEvent *evt)
+{
+    FT5336TouchState *s = FT5336(dev);
+    InputBtnEvent *btn = NULL;
+    InputMoveEvent *move = NULL;
+
+    switch (evt->type) {
+    case INPUT_EVENT_KIND_BTN:
+        btn = evt->u.btn.data;
+        s->touch_press = btn->down;
+        break;
+    case INPUT_EVENT_KIND_ABS:
+        move = evt->u.rel.data;
+        if (move->axis == INPUT_AXIS_X) {
+            s->abs_x = move->value;
+        } else if (move->axis == INPUT_AXIS_Y) {
+            s->abs_y = move->value;
+        }
+        break;
+    default:
+        break;
+    }
+}
+
+static void ft5336_input_sync(DeviceState *dev)
+{
+    FT5336TouchState *s = FT5336(dev);;
+
+    s->touch_x = qemu_input_scale_axis(s->abs_x,
+                                       INPUT_EVENT_ABS_MIN,
+                                       INPUT_EVENT_ABS_MAX, 0, 1777);
+    s->touch_y = qemu_input_scale_axis(s->abs_y,
+                                       INPUT_EVENT_ABS_MIN,
+                                       INPUT_EVENT_ABS_MAX, 0, 1023);
+
+    if (s->touch_press) {
+        if (s->inte) {
+            qemu_irq_pulse(s->irq);
+        }
+    }
+}
+
+static QemuInputHandler ft5336_input_handler = {
+    .name = "QEMU FT5336-driven Touchscreen",
+    .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_ABS,
+    .event = ft5336_input_event,
+    .sync = ft5336_input_sync
+};
+
+static void ft5336_touch_reset_enter(Object *obj, ResetType type)
+{
+    FT5336TouchState *s = FT5336(obj);
+
+    s->inte = false;
+}
+
+static void ft5336_realize(DeviceState *dev, Error **errp)
+{
+    FT5336TouchState *s = FT5336(dev);
+    qdev_init_gpio_out(dev, &s->irq, 1);
+
+    qemu_input_handler_register((DeviceState *) s, &ft5336_input_handler);
+}
+
+static int ft5336_touch_post_load(void *opaque, int version_id)
+{
+    /* FT5336TouchState *s = opaque; */
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_ft5336_touch = {
+    .name = TYPE_FT5336,
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .post_load = ft5336_touch_post_load,
+    .fields = (VMStateField[]) {
+        VMSTATE_I2C_SLAVE(parent_obj, FT5336TouchState),
+        VMSTATE_UINT8(i2c_cycle, FT5336TouchState),
+        VMSTATE_UINT8(reg, FT5336TouchState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void ft5336_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass);
+    ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+    dc->realize = ft5336_realize;
+    dc->vmsd = &vmstate_ft5336_touch;
+    set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
+    rc->phases.enter = ft5336_touch_reset_enter;
+
+    sc->event = ft5336_i2c_event;
+    sc->recv = ft5336_i2c_recv;
+    sc->send = ft5336_i2c_send;
+}
+
+static const TypeInfo ft5336_info = {
+    .name          = TYPE_FT5336,
+    .parent        = TYPE_I2C_SLAVE,
+    .instance_size = sizeof(FT5336TouchState),
+    .class_init    = ft5336_class_init
+};
+
+static void ft5336_register_types(void)
+{
+    type_register_static(&ft5336_info);
+}
+
+type_init(ft5336_register_types)
diff --git a/hw/input/Kconfig b/hw/input/Kconfig
index 55865bb386..73a6f67216 100644
--- a/hw/input/Kconfig
+++ b/hw/input/Kconfig
@@ -46,3 +46,7 @@ config TSC210X
 
 config LASIPS2
     select PS2
+
+config FT5336
+    bool
+    depends on I2C
diff --git a/hw/input/meson.build b/hw/input/meson.build
index 8deb011d4a..c892ecbeb7 100644
--- a/hw/input/meson.build
+++ b/hw/input/meson.build
@@ -16,3 +16,5 @@ softmmu_ss.add(when: 'CONFIG_VHOST_USER_INPUT', if_true: 
files('vhost-user-input
 softmmu_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_keypad.c'))
 softmmu_ss.add(when: 'CONFIG_TSC210X', if_true: files('tsc210x.c'))
 softmmu_ss.add(when: 'CONFIG_LASIPS2', if_true: files('lasips2.c'))
+
+softmmu_ss.add(when: 'CONFIG_FT5336', if_true: files('ft5336.c'))
-- 
2.38.1




reply via email to

[Prev in Thread] Current Thread [Next in Thread]