[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH V2 3/3] hw: add Atmel maxtouch touchscreen implement
From: |
Igor Mitsyanko |
Subject: |
[Qemu-devel] [PATCH V2 3/3] hw: add Atmel maxtouch touchscreen implementation |
Date: |
Thu, 15 Mar 2012 11:35:24 +0400 |
And use it for exynos4210 NURI board emulation
Signed-off-by: Igor Mitsyanko <address@hidden>
---
Makefile.objs | 1 +
default-configs/arm-softmmu.mak | 1 +
hw/exynos4_boards.c | 11 +-
hw/maxtouch.c | 1079 +++++++++++++++++++++++++++++++++++++++
4 files changed, 1089 insertions(+), 3 deletions(-)
create mode 100644 hw/maxtouch.c
diff --git a/Makefile.objs b/Makefile.objs
index 226b01d..0171957 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -248,6 +248,7 @@ hw-obj-$(CONFIG_SMARTCARD) += usb/dev-smartcard-reader.o
ccid-card-passthru.o
hw-obj-$(CONFIG_SMARTCARD_NSS) += ccid-card-emulated.o
hw-obj-$(CONFIG_USB_REDIR) += usb/redirect.o
hw-obj-$(CONFIG_I8259) += i8259_common.o i8259.o
+hw-obj-$(CONFIG_MAXTOUCH) += maxtouch.o
# PPC devices
hw-obj-$(CONFIG_PREP_PCI) += prep_pci.o
diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index e542b4f..7666748 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -27,3 +27,4 @@ CONFIG_SMC91C111=y
CONFIG_DS1338=y
CONFIG_PFLASH_CFI01=y
CONFIG_PFLASH_CFI02=y
+CONFIG_MAXTOUCH=y
diff --git a/hw/exynos4_boards.c b/hw/exynos4_boards.c
index b438361..f851026 100644
--- a/hw/exynos4_boards.c
+++ b/hw/exynos4_boards.c
@@ -28,6 +28,7 @@
#include "exec-memory.h"
#include "exynos4210.h"
#include "boards.h"
+#include "i2c.h"
#undef DEBUG
@@ -44,6 +45,7 @@
#endif
#define SMDK_LAN9118_BASE_ADDR 0x05000000
+#define MAXTOUCH_TS_I2C_ADDR 0x4a
typedef enum Exynos4BoardType {
EXYNOS4_BOARD_NURI,
@@ -134,9 +136,12 @@ static void nuri_init(ram_addr_t ram_size,
const char *kernel_filename, const char *kernel_cmdline,
const char *initrd_filename, const char *cpu_model)
{
- exynos4_boards_init_common(kernel_filename, kernel_cmdline,
- initrd_filename, EXYNOS4_BOARD_NURI);
-
+ Exynos4210State *cpu = exynos4_boards_init_common(kernel_filename,
+ kernel_cmdline, initrd_filename, EXYNOS4_BOARD_NURI);
+ DeviceState *dev =
+ i2c_create_slave(cpu->i2c_if[3], "maxtouch.var0",
MAXTOUCH_TS_I2C_ADDR);
+ qdev_connect_gpio_out(dev, 0, qdev_get_gpio_in(cpu->gpio2x,
+ EXYNOS4210_GPIO2X_LINE(GPX0, 4)));
arm_load_kernel(first_cpu, &exynos4_board_binfo);
}
diff --git a/hw/maxtouch.c b/hw/maxtouch.c
new file mode 100644
index 0000000..0c37d30
--- /dev/null
+++ b/hw/maxtouch.c
@@ -0,0 +1,1079 @@
+/*
+ * Atmel maXTouch touchscreen emulation
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd. All rights reserved.
+ * Igor Mitsyanko <address@hidden>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "i2c.h"
+#include "console.h"
+
+#ifndef MXT_DEBUG
+#define MXT_DEBUG 0
+#endif
+
+/* Fifo length must be a power of 2 */
+#define MXT_MESSAGE_FIFO_LEN 16
+/* Maxtouch supports up to 10 concurrent touches, but we emulate 3 since common
+ * PC mouse has only 3 buttons. Exact meaning of each touch (each mouse button
+ * press) is defined by target userspace application only */
+#define MXT_NUM_OF_TOUCHES 3
+#define MXT_CRC_POLY 0x80001B
+#define MXT_CRC_SIZE 3
+/* Maximum value of x and y coordinate in QEMU mouse event callback */
+#define MXT_QEMU_MAX_COORD 0x7FFF
+
+/* Each maXTouch device consists of a certain number of subdevices (objects)
+ * with code names like T5, T6, T9, e.t.c. Each object implements only a
portion
+ * of maXTouch functionality. For example, touch detection is performed
+ * by T9 object, but information about touch state changes is generated (and
can
+ * be read) only in T5 object.
+ * Various variants of maXTouch can have different set of objects placed at
+ * different addresses within maXtouch address space. Composition of objects
+ * is described by mandatory Object Table which starts at address 0x7.
+ * Length of object table (i.e. number of objects) of this exact variant of
+ * maXTouch can be read from address 0x6 */
+#define MXT_OBJTBL_ENTRY_LEN 6
+/* Offsets within one object table entry */
+/* Object type code */
+#define MXT_OBJTBL_TYPE 0x0
+/* Start address of object registers within maxTouch address space */
+#define MXT_OBJTBL_START_LSB 0x1
+#define MXT_OBJTBL_START_MSB 0x2
+/* Number of object's registers (actually, this field contains size-1) */
+#define MXT_OBJTBL_SIZE 0x3
+/* Number of instances of this object (contains instances-1, so value 0 means
+ * one instance). All instances are placed continuously in memory */
+#define MXT_OBJTBL_INSTANCES 0x4
+/* Number of messages ID's object can generate in T5 object. For example,
+ * T9 will generate messages with different ID's for each separate touch */
+#define MXT_OBJTBL_REPORT_IDS 0x5
+
+/* Object types */
+enum {
+ MXT_GEN_MESSAGE_T5 = 0, MXT_GEN_COMMAND_T6,
+ MXT_GEN_POWER_T7, MXT_GEN_ACQUIRE_T8,
+ MXT_TOUCH_MULTI_T9, MXT_TOUCH_KEYARRAY_T15,
+ MXT_SPT_COMMSCONFIG_T18, MXT_SPT_GPIOPWM_T19,
+ MXT_PROCI_GRIPFACE_T20, MXT_PROCG_NOISE_T22,
+ MXT_TOUCH_PROXIMITY_T23, MXT_PROCI_ONETOUCH_T24,
+ MXT_SPT_SELFTEST_T25, MXT_PROCI_TWOTOUCH_T27,
+ MXT_SPT_CTECONFIG_T28, MXT_DEBUG_DIAGNOSTIC_T37,
+ MXT_NUM_OF_OBJECT_TYPES
+};
+
+static const uint8_t mxt_obj_types_list[MXT_NUM_OF_OBJECT_TYPES] = {
+ [MXT_GEN_MESSAGE_T5] = 5, [MXT_GEN_COMMAND_T6] = 6,
+ [MXT_GEN_POWER_T7] = 7, [MXT_GEN_ACQUIRE_T8] = 8,
+ [MXT_TOUCH_MULTI_T9] = 9, [MXT_TOUCH_KEYARRAY_T15] = 15,
+ [MXT_SPT_COMMSCONFIG_T18] = 18, [MXT_SPT_GPIOPWM_T19] = 19,
+ [MXT_PROCI_GRIPFACE_T20] = 20, [MXT_PROCG_NOISE_T22] = 22,
+ [MXT_TOUCH_PROXIMITY_T23] = 23, [MXT_PROCI_ONETOUCH_T24] = 24,
+ [MXT_SPT_SELFTEST_T25] = 25, [MXT_PROCI_TWOTOUCH_T27] = 27,
+ [MXT_SPT_CTECONFIG_T28] = 28, [MXT_DEBUG_DIAGNOSTIC_T37] = 37
+};
+
+static const uint8_t mxt_obj_sizes[MXT_NUM_OF_OBJECT_TYPES] = {
+ [MXT_GEN_MESSAGE_T5] = 10, [MXT_GEN_COMMAND_T6] = 6,
+ [MXT_GEN_POWER_T7] = 3, [MXT_GEN_ACQUIRE_T8] = 8,
+ [MXT_TOUCH_MULTI_T9] = 31, [MXT_TOUCH_KEYARRAY_T15] = 11,
+ [MXT_SPT_COMMSCONFIG_T18] = 2, [MXT_SPT_GPIOPWM_T19] = 16,
+ [MXT_PROCI_GRIPFACE_T20] = 12, [MXT_PROCG_NOISE_T22] = 17,
+ [MXT_TOUCH_PROXIMITY_T23] = 15, [MXT_PROCI_ONETOUCH_T24] = 19,
+ [MXT_SPT_SELFTEST_T25] = 14, [MXT_PROCI_TWOTOUCH_T27] = 7,
+ [MXT_SPT_CTECONFIG_T28] = 6, [MXT_DEBUG_DIAGNOSTIC_T37] = 128
+};
+
+#define MXT_INFO_START 0x00
+#define MXT_INFO_SIZE 7
+#define MXT_INFO_END (MXT_INFO_START + MXT_INFO_SIZE - 1)
+#define MXT_OBJTBL_START (MXT_INFO_START + MXT_INFO_SIZE)
+#define MXT_OBJTBL_END(s) \
+ (MXT_OBJTBL_START + (s->mxt_info[MXT_OBJ_NUM]) * MXT_OBJTBL_ENTRY_LEN - 1)
+#define MXT_CRC_START(s) \
+ (MXT_OBJTBL_START + (s->mxt_info[MXT_OBJ_NUM]) * MXT_OBJTBL_ENTRY_LEN)
+#define MXT_CRC_END(s) \
+ (MXT_OBJTBL_START + (s->mxt_info[MXT_OBJ_NUM]) * MXT_OBJTBL_ENTRY_LEN + \
+ MXT_CRC_SIZE - 1)
+#define MXT_OBJECTS_START(s) (s->obj_tbl[0].start_addr)
+
+/* MXT info Registers */
+#define MXT_FAMILY_ID 0x00
+#define MXT_VARIANT_ID 0x01
+#define MXT_VERSION 0x02
+#define MXT_BUILD 0x03
+#define MXT_MATRIX_X_SIZE 0x04
+#define MXT_MATRIX_Y_SIZE 0x05
+#define MXT_OBJ_NUM 0x06
+
+/* Registers within Multitouch T9 object */
+#define MXT_OBJ_T9_CTRL 0
+#define MXT_OBJ_T9_XORIGIN 1
+#define MXT_OBJ_T9_YORIGIN 2
+#define MXT_OBJ_T9_XSIZE 3
+#define MXT_OBJ_T9_YSIZE 4
+#define MXT_OBJ_T9_BLEN 6
+#define MXT_OBJ_T9_TCHTHR 7
+#define MXT_OBJ_T9_TCHDI 8
+#define MXT_OBJ_T9_ORIENT 9
+#define MXT_OBJ_T9_MOVHYSTI 11
+#define MXT_OBJ_T9_MOVHYSTN 12
+#define MXT_OBJ_T9_NUMTOUCH 14
+#define MXT_OBJ_T9_MRGHYST 15
+#define MXT_OBJ_T9_MRGTHR 16
+#define MXT_OBJ_T9_AMPHYST 17
+#define MXT_OBJ_T9_XRANGE_LSB 18
+#define MXT_OBJ_T9_XRANGE_MSB 19
+#define MXT_OBJ_T9_YRANGE_LSB 20
+#define MXT_OBJ_T9_YRANGE_MSB 21
+#define MXT_OBJ_T9_XLOCLIP 22
+#define MXT_OBJ_T9_XHICLIP 23
+#define MXT_OBJ_T9_YLOCLIP 24
+#define MXT_OBJ_T9_YHICLIP 25
+#define MXT_OBJ_T9_XEDGECTRL 26
+#define MXT_OBJ_T9_XEDGEDIST 27
+#define MXT_OBJ_T9_YEDGECTRL 28
+#define MXT_OBJ_T9_YEDGEDIST 29
+#define MXT_OBJ_T9_JUMPLIMIT 30
+
+/* Multitouch T9 messages status flags */
+#define MXT_T9_STAT_MOVE (1 << 4)
+#define MXT_T9_STAT_RELEASE (1 << 5)
+#define MXT_T9_STAT_PRESS (1 << 6)
+#define MXT_T9_STAT_DETECT (1 << 7)
+
+/* Multitouch T9 orient bits */
+#define MXT_T9_XY_SWITCH (1 << 0)
+
+/* Fields of T5 message */
+#define MXT_OBJ_T5_REPORTID 0
+#define MXT_OBJ_T5_STATUS 1
+#define MXT_OBJ_T5_XPOSMSH 2
+#define MXT_OBJ_T5_YPOSMSH 3
+#define MXT_OBJ_T5_XYPOSLSH 4
+#define MXT_OBJ_T5_AREA 5
+#define MXT_OBJ_T5_PRESSURE 6
+#define MXT_OBJ_T5_UNKNOWN 7
+#define MXT_OBJ_T5_CHECKSUM 8
+
+#if MXT_MESSAGE_FIFO_LEN & (MXT_MESSAGE_FIFO_LEN - 1)
+#error Message fifo length must be a power of 2
+#endif
+
+/* An entry of object description table */
+typedef struct MXTObjTblEntry {
+ uint8_t type;
+ uint16_t start_addr;
+ uint8_t size;
+ uint8_t instances;
+ uint8_t num_report_ids;
+} MXTObjTblEntry;
+
+typedef struct ObjConfig {
+ uint8_t type;
+ uint8_t instances;
+} ObjConfig;
+
+typedef struct MXTVariantInfo {
+ const char *name;
+ const uint8_t *mxt_variant_info;
+ const ObjConfig *mxt_variant_obj_list;
+} MXTVariantInfo;
+
+#define TYPE_MAXTOUCH "maxtouch"
+#define MXT(obj) \
+ OBJECT_CHECK(MXTState, (obj), TYPE_MAXTOUCH)
+#define MXT_CLASS(klass) \
+ OBJECT_CLASS_CHECK(MXTClass, (klass), TYPE_MAXTOUCH)
+#define MXT_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(MXTClass, (obj), TYPE_MAXTOUCH)
+
+/* Definitions for variant #0 of Atmel maXTouch */
+#define TYPE_MXT_VARIANT0 "maxtouch.var0"
+
+static ObjConfig mxt_variant0_objlist[] = {
+ { .type = MXT_GEN_MESSAGE_T5, .instances = 0 },
+ { .type = MXT_GEN_COMMAND_T6, .instances = 0 },
+ { .type = MXT_GEN_POWER_T7, .instances = 0 },
+ { .type = MXT_GEN_ACQUIRE_T8, .instances = 0 },
+ { .type = MXT_TOUCH_MULTI_T9, .instances = 0 },
+ { .type = MXT_TOUCH_KEYARRAY_T15, .instances = 0 },
+ { .type = MXT_SPT_GPIOPWM_T19, .instances = 0 },
+ { .type = MXT_PROCI_GRIPFACE_T20, .instances = 0 },
+ { .type = MXT_PROCG_NOISE_T22, .instances = 0 },
+ { .type = MXT_TOUCH_PROXIMITY_T23, .instances = 0 },
+ { .type = MXT_PROCI_ONETOUCH_T24, .instances = 0 },
+ { .type = MXT_SPT_SELFTEST_T25, .instances = 0 },
+ { .type = MXT_PROCI_TWOTOUCH_T27, .instances = 0 },
+ { .type = MXT_SPT_CTECONFIG_T28, .instances = 0 },
+};
+
+static const uint8_t mxt_variant0_info[MXT_INFO_SIZE] = {
+ [MXT_FAMILY_ID] = 0x80,
+ [MXT_VARIANT_ID] = 0x0,
+ [MXT_VERSION] = 0x1,
+ [MXT_BUILD] = 0x1,
+ [MXT_MATRIX_X_SIZE] = 16,
+ [MXT_MATRIX_Y_SIZE] = 14,
+ [MXT_OBJ_NUM] = sizeof(mxt_variant0_objlist) / sizeof(ObjConfig),
+};
+
+/* Definitions for variant #1 of Atmel maXTouch */
+#define TYPE_MXT_VARIANT1 "maxtouch.var1"
+
+static ObjConfig mxt_variant1_objlist[] = {
+ { .type = MXT_GEN_MESSAGE_T5, .instances = 0 },
+ { .type = MXT_GEN_COMMAND_T6, .instances = 0 },
+ { .type = MXT_GEN_POWER_T7, .instances = 0 },
+ { .type = MXT_GEN_ACQUIRE_T8, .instances = 0 },
+ { .type = MXT_TOUCH_MULTI_T9, .instances = 0 },
+ { .type = MXT_SPT_COMMSCONFIG_T18, .instances = 0 },
+ { .type = MXT_PROCI_GRIPFACE_T20, .instances = 0 },
+ { .type = MXT_PROCG_NOISE_T22, .instances = 0 },
+ { .type = MXT_SPT_CTECONFIG_T28, .instances = 0 },
+ { .type = MXT_DEBUG_DIAGNOSTIC_T37, .instances = 0 },
+};
+
+static const uint8_t mxt_variant1_info[MXT_INFO_SIZE] = {
+ [MXT_FAMILY_ID] = 0x80,
+ [MXT_VARIANT_ID] = 0x1,
+ [MXT_VERSION] = 0x1,
+ [MXT_BUILD] = 0x1,
+ [MXT_MATRIX_X_SIZE] = 16,
+ [MXT_MATRIX_Y_SIZE] = 14,
+ [MXT_OBJ_NUM] = sizeof(mxt_variant1_objlist) / sizeof(ObjConfig),
+};
+
+static const MXTVariantInfo mxt_variants_info_array[] = {
+ {
+ .name = TYPE_MXT_VARIANT0,
+ .mxt_variant_info = mxt_variant0_info,
+ .mxt_variant_obj_list = mxt_variant0_objlist
+ },
+ {
+ .name = TYPE_MXT_VARIANT1,
+ .mxt_variant_info = mxt_variant1_info,
+ .mxt_variant_obj_list = mxt_variant1_objlist
+ }
+};
+
+#define MXT_NUM_OF_VARIANTS \
+ (sizeof(mxt_variants_info_array) / sizeof(MXTVariantInfo))
+
+/* Generate Message T5 message format */
+typedef struct MXTMessage {
+ uint8_t reportid;
+ uint8_t status;
+ uint8_t xpos_msh;
+ uint8_t ypos_msh;
+ uint8_t xypos_lsh;
+ uint8_t area;
+ uint8_t pressure;
+ uint8_t checksum;
+} MXTMessage;
+
+/* Possible MXT i2c-related states */
+enum {
+ idle = 0,
+ start_bit_write,
+ start_bit_read,
+ next_is_reg_lsb,
+ next_is_reg_msb,
+ register_selected,
+ send_zero_next,
+ reading_data,
+};
+
+typedef struct MXTClass {
+ I2CSlaveClass parent_class;
+
+ const uint8_t *mxt_info;
+ MXTObjTblEntry *obj_tbl;
+ uint8_t crc[MXT_CRC_SIZE];
+ uint16_t end_addr;
+ /* This is used to speed things up a little */
+ uint16_t t5_address;
+ uint16_t t9_address;
+} MXTClass;
+
+typedef struct MXTState {
+ I2CSlave i2c;
+ QEMUPutMouseEntry *mouse;
+ qemu_irq nCHG; /* line state changes to low level to signal new event
*/
+ uint32_t i2c_state;
+ uint8_t *objects;
+
+ MXTMessage msg_fifo[MXT_MESSAGE_FIFO_LEN];
+ uint8_t fifo_get;
+ uint8_t fifo_add;
+ bool fifo_lock;
+
+ uint16_t selected_reg;
+ uint16_t scale_coef_x;
+ uint16_t scale_coef_y;
+ uint16_t x_offset;
+ uint16_t y_offset;
+ uint16_t x_curr;
+ uint16_t y_curr;
+ uint8_t touches;
+} MXTState;
+
+static const VMStateDescription mxt_message_vmstate = {
+ .name = "mxt-message",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(reportid, MXTMessage),
+ VMSTATE_UINT8(status, MXTMessage),
+ VMSTATE_UINT8(xpos_msh, MXTMessage),
+ VMSTATE_UINT8(ypos_msh, MXTMessage),
+ VMSTATE_UINT8(xypos_lsh, MXTMessage),
+ VMSTATE_UINT8(area, MXTMessage),
+ VMSTATE_UINT8(pressure, MXTMessage),
+ VMSTATE_UINT8(checksum, MXTMessage),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+
+#if MXT_DEBUG
+#define DPRINT(fmt, args...) \
+ do {fprintf(stderr, "QEMU MXT: "fmt, ## args); } while (0)
+#define DPRINT_SMPL(fmt, args...) \
+ do {fprintf(stderr, fmt, ## args); } while (0)
+#define ERRPRINT(fmt, args...) \
+ do {fprintf(stderr, "QEMU MXT ERROR: "fmt, ## args); } while (0)
+
+static char dbg_reg_buf[50];
+
+static const char *dbg_multitoucht9_regs[] = {
+ [MXT_OBJ_T9_CTRL] = "CTRL", [MXT_OBJ_T9_XORIGIN] = "XORIGIN",
+ [MXT_OBJ_T9_YORIGIN] = "YORIGIN", [MXT_OBJ_T9_XSIZE] = "XSIZE",
+ [MXT_OBJ_T9_YSIZE] = "YSIZE", [5] = "REG_5",
+ [MXT_OBJ_T9_BLEN] = "BLEN", [MXT_OBJ_T9_TCHTHR] = "TCHTHR",
+ [MXT_OBJ_T9_TCHDI] = "TCHDI", [MXT_OBJ_T9_ORIENT] = "ORIENT",
+ [10] = "REG_10", [MXT_OBJ_T9_MOVHYSTI] =
"MOVHYSTI",
+ [MXT_OBJ_T9_MOVHYSTN] = "MOVHYSTN", [13] = "REG_13",
+ [MXT_OBJ_T9_NUMTOUCH] = "NUMTOUCH",
+ [MXT_OBJ_T9_MRGHYST] = "MRGHYST", [MXT_OBJ_T9_MRGTHR] = "MRGTHR",
+ [MXT_OBJ_T9_AMPHYST] = "AMPHYST", [MXT_OBJ_T9_XRANGE_LSB] =
"XRANGE_L",
+ [MXT_OBJ_T9_XRANGE_MSB] = "XRANGE_H", [MXT_OBJ_T9_YRANGE_LSB] =
"YRANGE_L",
+ [MXT_OBJ_T9_YRANGE_MSB] = "YRANGE_H", [MXT_OBJ_T9_XLOCLIP] = "XLOCLIP",
+ [MXT_OBJ_T9_XHICLIP] = "XHICLIP", [MXT_OBJ_T9_YLOCLIP] = "YLOCLIP",
+ [MXT_OBJ_T9_YHICLIP] = "YHICLIP", [MXT_OBJ_T9_XEDGECTRL] =
"XEDGECTRL",
+ [MXT_OBJ_T9_XEDGEDIST] = "XEDGEDIST", [MXT_OBJ_T9_YEDGECTRL] =
"YEDGECTRL",
+ [MXT_OBJ_T9_YEDGEDIST] = "YEDGEDIST", [MXT_OBJ_T9_JUMPLIMIT] =
"JUMPLIMIT",
+};
+
+static const char *dbg_gen_message_t5_regs[] = {
+ [MXT_OBJ_T5_REPORTID] = "REPORTID", [MXT_OBJ_T5_STATUS] = "STATUS",
+ [MXT_OBJ_T5_XPOSMSH] = "XPOSMSH", [MXT_OBJ_T5_YPOSMSH] = "YPOSMSH",
+ [MXT_OBJ_T5_XYPOSLSH] = "XYPOSLSH", [MXT_OBJ_T5_AREA] = "AREA",
+ [MXT_OBJ_T5_PRESSURE] = "PRESSURE", [MXT_OBJ_T5_UNKNOWN] = "REG_7",
+ [MXT_OBJ_T5_CHECKSUM] = "CHECKSUM", [9] = "REG_9"
+};
+
+static const char *dbg_mxt_info_regs[] = {
+ [MXT_FAMILY_ID] = "FAMILY_ID", [MXT_VARIANT_ID] = "VARIANT_ID",
+ [MXT_VERSION] = "VERSION", [MXT_BUILD] = "BUILD",
+ [MXT_MATRIX_X_SIZE] = "MATRIX_X_SIZE",
+ [MXT_MATRIX_Y_SIZE] = "MATRIX_Y_SIZE",
+ [MXT_OBJ_NUM] = "OBJ_NUM",
+};
+
+static const char *dbg_mxt_obj_name(unsigned type)
+{
+ switch (type) {
+ case 5:
+ return "GEN_MESSAGE_T5";
+ case 6:
+ return "GEN_COMMAND_T6";
+ case 7:
+ return "GEN_POWER_T7";
+ case 8:
+ return "GEN_ACQUIRE_T8";
+ case 9:
+ return "TOUCH_MULTI_T9";
+ case 15:
+ return "TOUCH_KEYARRAY_T15";
+ case 18:
+ return "SPT_COMMSCONFIG_T18";
+ case 19:
+ return "SPT_GPIOPWM_T19";
+ case 20:
+ return "PROCI_GRIPFACE_T20";
+ case 22:
+ return "PROCG_NOISE_T22";
+ case 23:
+ return "TOUCH_PROXIMITY_T23";
+ case 24:
+ return "PROCI_ONETOUCH_T24";
+ case 25:
+ return "SPT_SELFTEST_T25";
+ case 27:
+ return "PROCI_TWOTOUCH_T27";
+ case 28:
+ return "SPT_CTECONFIG_T28";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static char *mxt_get_reg_name(MXTState *s, unsigned int offset)
+{
+ MXTClass *k = MXT_GET_CLASS(s);
+ unsigned i;
+
+ if ((offset >= k->t5_address) &&
+ (offset < (k->t5_address + mxt_obj_sizes[MXT_GEN_MESSAGE_T5]))) {
+ i = (offset - k->t5_address);
+ snprintf(dbg_reg_buf, sizeof(dbg_reg_buf), "MESSAGE_T5[%s]",
+ dbg_gen_message_t5_regs[i]);
+ } else if ((offset >= k->t9_address) && (offset < k->t9_address +
+ mxt_obj_sizes[MXT_TOUCH_MULTI_T9])) {
+ i = (offset - k->t9_address);
+ snprintf(dbg_reg_buf, sizeof(dbg_reg_buf), "MULTITOUCH_T9[%s]",
+ dbg_multitoucht9_regs[i]);
+ } else if (offset <= MXT_INFO_END) {
+ i = (offset - MXT_INFO_START);
+ snprintf(dbg_reg_buf, sizeof(dbg_reg_buf), "INFO[%s]",
+ dbg_mxt_info_regs[i]);
+ } else if (offset <= MXT_OBJTBL_END(k)) {
+ i = (offset - MXT_OBJTBL_START) / MXT_OBJTBL_ENTRY_LEN;
+ switch ((offset - MXT_OBJTBL_START) % MXT_OBJTBL_ENTRY_LEN) {
+ case MXT_OBJTBL_TYPE:
+ snprintf(dbg_reg_buf, sizeof(dbg_reg_buf), "OBJTBL_%u TYPE", i);
+ break;
+ case MXT_OBJTBL_START_LSB:
+ snprintf(dbg_reg_buf, sizeof(dbg_reg_buf), "OBJTBL_%u ADDR_LSB",
i);
+ break;
+ case MXT_OBJTBL_START_MSB:
+ snprintf(dbg_reg_buf, sizeof(dbg_reg_buf), "OBJTBL_%u ADDR_MSB",
i);
+ break;
+ case MXT_OBJTBL_SIZE:
+ snprintf(dbg_reg_buf, sizeof(dbg_reg_buf), "OBJTBL_%u SIZE", i);
+ break;
+ case MXT_OBJTBL_INSTANCES:
+ snprintf(dbg_reg_buf, sizeof(dbg_reg_buf),
+ "OBJTBL_%u INSTANCES", i);
+ break;
+ case MXT_OBJTBL_REPORT_IDS:
+ snprintf(dbg_reg_buf, sizeof(dbg_reg_buf),
+ "OBJTBL_%u REPORT_IDS", i);
+ break;
+ }
+ } else if (offset <= MXT_CRC_END(k)) {
+ i = offset - MXT_CRC_START(k);
+ snprintf(dbg_reg_buf, sizeof(dbg_reg_buf), "CRC[%i]", i);
+ } else if (offset <= k->end_addr) {
+ for (i = 0; i < k->mxt_info[MXT_OBJ_NUM]; i++) {
+ if (offset >= (k->obj_tbl[i].start_addr + k->obj_tbl[i].size + 1))
{
+ continue;
+ }
+ snprintf(dbg_reg_buf, sizeof(dbg_reg_buf), "%s[%u]",
+ dbg_mxt_obj_name(k->obj_tbl[i].type),
+ offset - k->obj_tbl[i].start_addr);
+ break;
+ }
+ } else {
+ snprintf(dbg_reg_buf, sizeof(dbg_reg_buf), "UNKNOWN");
+ }
+ return dbg_reg_buf;
+}
+
+#else
+#define DPRINT(fmt, args...) do { } while (0)
+#define DPRINT_SMPL(fmt, args...) do { } while (0)
+#define ERRPRINT(fmt, args...) \
+ do {fprintf(stderr, "QEMU MXT ERROR: "fmt, ## args); } while (0)
+#endif
+
+static void
+mxt_mouse_event(void *opaque, int x, int y, int z, int buttons_state)
+{
+ MXTState *s = (MXTState *)opaque;
+ uint16_t x_new, y_new;
+ unsigned i;
+ bool state_changed = false;
+
+ /* Check that message buffer is not full */
+ if (s->fifo_lock ||
+ ((s->fifo_add + 1) & (MXT_MESSAGE_FIFO_LEN - 1)) == s->fifo_get) {
+ return;
+ }
+
+ x_new = (x + s->x_offset) / s->scale_coef_x;
+ y_new = (y + s->y_offset) / s->scale_coef_y;
+
+ for (i = 1; i <= MXT_NUM_OF_TOUCHES; i++) {
+ if ((s->touches & (1 << (i - 1))) != (buttons_state & (1 << (i - 1))))
{
+ if (buttons_state & (1 << (i - 1))) {
+ /* Generate press event message */
+ s->msg_fifo[s->fifo_add].status =
+ MXT_T9_STAT_DETECT | MXT_T9_STAT_PRESS;
+ s->msg_fifo[s->fifo_add].area = 0x20;
+ s->msg_fifo[s->fifo_add].pressure = 0x10;
+ } else {
+ /* Generate release event message */
+ s->msg_fifo[s->fifo_add].status = MXT_T9_STAT_RELEASE;
+ s->msg_fifo[s->fifo_add].area = 0x0;
+ s->msg_fifo[s->fifo_add].pressure = 0x0;
+ }
+ } else if ((s->touches & (1 << (i - 1))) &&
+ (s->x_curr != x_new || s->y_curr != y_new)) {
+ s->msg_fifo[s->fifo_add].status =
+ MXT_T9_STAT_DETECT | MXT_T9_STAT_MOVE;
+ s->msg_fifo[s->fifo_add].area = 0x20;
+ s->msg_fifo[s->fifo_add].pressure = 0x10;
+ } else {
+ continue;
+ }
+
+ s->msg_fifo[s->fifo_add].xpos_msh = x_new >> 4;
+ s->msg_fifo[s->fifo_add].ypos_msh = y_new >> 4;
+ s->msg_fifo[s->fifo_add].xypos_lsh =
+ (y_new & 0xF) | ((x_new << 4) & 0xF0);
+ s->msg_fifo[s->fifo_add].reportid = i;
+ s->msg_fifo[s->fifo_add].checksum = 0;
+ s->fifo_add = (s->fifo_add + 1) & (MXT_MESSAGE_FIFO_LEN - 1);
+ state_changed = true;
+ }
+
+ if (state_changed) {
+ s->touches = buttons_state;
+ s->x_curr = x_new;
+ s->y_curr = y_new;
+ /* CHG line changes to low and new message is generated in
+ * gen_message_t5 subsystem when touch event occurs. CHG line
+ * changes back to high only after all messages have been read from
+ * gen_message_t5 subsystem */
+ qemu_irq_lower(s->nCHG);
+ }
+}
+
+/* Read field of current message in message FIFO */
+static uint8_t mxt_read_message_field(MXTState *s, unsigned field)
+{
+ uint8_t ret;
+
+ /* If there are no messages, return dummy message with REPORTID=0xFF */
+ if (s->fifo_get == s->fifo_add) {
+ s->fifo_lock = true;
+ if (field == MXT_OBJ_T5_CHECKSUM) {
+ qemu_irq_raise(s->nCHG);
+ s->fifo_lock = false;
+ }
+ return 0xFF;
+ }
+
+ switch (field) {
+ case MXT_OBJ_T5_REPORTID:
+ return s->msg_fifo[s->fifo_get].reportid;
+ case MXT_OBJ_T5_STATUS:
+ return s->msg_fifo[s->fifo_get].status;
+ case MXT_OBJ_T5_XPOSMSH:
+ return s->msg_fifo[s->fifo_get].xpos_msh;
+ case MXT_OBJ_T5_YPOSMSH:
+ return s->msg_fifo[s->fifo_get].ypos_msh;
+ case MXT_OBJ_T5_XYPOSLSH:
+ return s->msg_fifo[s->fifo_get].xypos_lsh;
+ case MXT_OBJ_T5_AREA:
+ return s->msg_fifo[s->fifo_get].area;
+ case MXT_OBJ_T5_PRESSURE:
+ return s->msg_fifo[s->fifo_get].pressure;
+ case MXT_OBJ_T5_UNKNOWN:
+ /* Linux driver doesn't use this field */
+ return 0;
+ case MXT_OBJ_T5_CHECKSUM:
+ ret = s->msg_fifo[s->fifo_get].checksum;
+ s->fifo_get = (s->fifo_get + 1) & (MXT_MESSAGE_FIFO_LEN - 1);
+ return ret;
+ }
+ return 0;
+}
+
+static int mxt_read_info_reg(MXTClass *k, unsigned int offset)
+{
+ unsigned i;
+
+ if (offset <= MXT_INFO_END) {
+ return k->mxt_info[offset - MXT_INFO_START];
+ } else if (offset <= MXT_OBJTBL_END(k)) {
+ i = (offset - MXT_OBJTBL_START) / MXT_OBJTBL_ENTRY_LEN;
+ switch ((offset - MXT_OBJTBL_START) % MXT_OBJTBL_ENTRY_LEN) {
+ case MXT_OBJTBL_TYPE:
+ return k->obj_tbl[i].type;
+ case MXT_OBJTBL_START_LSB:
+ return (uint8_t)k->obj_tbl[i].start_addr;
+ case MXT_OBJTBL_START_MSB:
+ return (uint8_t)(k->obj_tbl[i].start_addr >> 8);
+ case MXT_OBJTBL_SIZE:
+ return k->obj_tbl[i].size;
+ case MXT_OBJTBL_INSTANCES:
+ return k->obj_tbl[i].instances;
+ case MXT_OBJTBL_REPORT_IDS:
+ return k->obj_tbl[i].num_report_ids;
+ }
+ } else if (offset <= MXT_CRC_END(k)) {
+ return k->crc[offset - MXT_CRC_START(k)];
+ }
+
+ return -1;
+}
+
+static inline void mxt_calc_x_scalecoef(MXTState *s)
+{
+ MXTClass *k = MXT_GET_CLASS(s);
+ uint16_t div, tmp;
+ uint8_t *t9 = &s->objects[k->t9_address - MXT_OBJECTS_START(k)];
+ uint16_t x_max = t9[MXT_OBJ_T9_XRANGE_LSB] |
+ (t9[MXT_OBJ_T9_XRANGE_MSB] << 8);
+
+ if (x_max == 0) {
+ if (t9[MXT_OBJ_T9_ORIENT] & MXT_T9_XY_SWITCH) {
+ s->scale_coef_y = 0xFFFF;
+ s->y_offset = 0;
+ } else {
+ s->scale_coef_x = 0xFFFF;
+ s->x_offset = 0;
+ }
+ return;
+ }
+
+ div = MXT_QEMU_MAX_COORD / x_max + 1;
+ tmp = x_max * div;
+ /* Divide by 4 if XRANGE less then 1024 */
+ if ((t9[MXT_OBJ_T9_YRANGE_MSB] & 0xC) == 0) {
+ div >>= 2;
+ div++;
+ tmp = x_max * (div << 2);
+ }
+
+ if (t9[MXT_OBJ_T9_ORIENT] & MXT_T9_XY_SWITCH) {
+ s->scale_coef_y = div;
+ s->y_offset = tmp - MXT_QEMU_MAX_COORD;
+ } else {
+ s->scale_coef_x = div;
+ s->x_offset = tmp - MXT_QEMU_MAX_COORD;
+ }
+}
+
+static inline void mxt_calc_y_scalecoef(MXTState *s)
+{
+ MXTClass *k = MXT_GET_CLASS(s);
+ uint16_t div, tmp;
+ uint8_t *t9 = &s->objects[k->t9_address - MXT_OBJECTS_START(k)];
+ uint16_t y_max =
+ t9[MXT_OBJ_T9_YRANGE_LSB] | (t9[MXT_OBJ_T9_YRANGE_MSB] << 8);
+
+ if (y_max == 0) {
+ if (t9[MXT_OBJ_T9_ORIENT] & MXT_T9_XY_SWITCH) {
+ s->scale_coef_x = 0xFFFF;
+ s->x_offset = 0;
+ } else {
+ s->scale_coef_y = 0xFFFF;
+ s->y_offset = 0;
+ }
+ return;
+ }
+
+ div = MXT_QEMU_MAX_COORD / y_max + 1;
+ tmp = y_max * div;
+ /* Divide by 4 if YRANGE less then 1024 */
+ if ((t9[MXT_OBJ_T9_YRANGE_MSB] & 0xC) == 0) {
+ div >>= 2;
+ div++;
+ tmp = y_max * (div << 2);
+ }
+
+ if (t9[MXT_OBJ_T9_ORIENT] & MXT_T9_XY_SWITCH) {
+ s->scale_coef_x = div;
+ s->x_offset = tmp - MXT_QEMU_MAX_COORD;
+ } else {
+ s->scale_coef_y = div;
+ s->y_offset = tmp - MXT_QEMU_MAX_COORD;
+ }
+}
+
+static void mxt_write_to_t9(MXTState *s, unsigned int offset, uint8_t val)
+{
+ MXTClass *k = MXT_GET_CLASS(s);
+ uint16_t addr = k->t9_address - MXT_OBJECTS_START(k) + offset;
+
+ s->objects[addr] = val;
+
+ switch (offset) {
+ case MXT_OBJ_T9_CTRL:
+ if ((s->objects[addr] == 0x83) && !(s->mouse)) {
+ s->mouse =
+ qemu_add_mouse_event_handler(mxt_mouse_event, s, 1,
"maxtouch");
+ qemu_activate_mouse_event_handler(s->mouse);
+ } else if (s->objects[addr] == 0 && s->mouse) {
+ qemu_remove_mouse_event_handler(s->mouse);
+ s->mouse = NULL;
+ }
+ break;
+ case MXT_OBJ_T9_XRANGE_LSB: case MXT_OBJ_T9_XRANGE_MSB:
+ mxt_calc_x_scalecoef(s);
+ break;
+ case MXT_OBJ_T9_YRANGE_LSB: case MXT_OBJ_T9_YRANGE_MSB:
+ mxt_calc_y_scalecoef(s);
+ break;
+ case MXT_OBJ_T9_ORIENT:
+ mxt_calc_x_scalecoef(s);
+ mxt_calc_y_scalecoef(s);
+ break;
+ }
+}
+
+/* Atmel maXTouch i2c registers read byte sequence:
+ * <Start bit>
+ * [MXT i2c address(0x4A) with last bit 0(write data)]
+ * [LSB of MXT register offset (starting from 0)]
+ * [MSB of MXT register offset]
+ * <Stop bit>
+ * <Start bit>
+ * [MXT address(0x4A) with last bit 1(read data)]
+ * [MXT sends 0x0]
+ * [MXT sends value of register offset]
+ * [MXT sends value of register offset+1]
+ * [MXT sends value of register offset+2]
+ * [...........]
+ * <Stop bit>
+ *
+ * Atmel maXTouch i2c registers write byte sequence:
+ * <Start bit>
+ * [MXT address(0x4A) with last bit 0(write data)]
+ * [LSB of MXT register offset (starting from 0)]
+ * [MSB of MXT register offset]
+ * [value to write into register with specified offset]
+ * [value to write into register with specified offset+1]
+ * [value to write into register with specified offset+2]
+ * [...........]
+ * <Stop bit>
+ */
+
+static int mxt_i2c_read(I2CSlave *i2c)
+{
+ MXTState *s = MXT(i2c);
+ MXTClass *k = MXT_GET_CLASS(s);
+ int ret;
+
+ switch (s->i2c_state) {
+ case send_zero_next:
+ ret = 0;
+ s->i2c_state = reading_data;
+ break;
+ case reading_data:
+ if ((s->selected_reg >= k->t5_address) && (s->selected_reg <
+ (k->t5_address + mxt_obj_sizes[MXT_GEN_MESSAGE_T5]))) {
+ /* This is first because this will succeed almost always */
+ ret = mxt_read_message_field(s, s->selected_reg - k->t5_address);
+ } else if (s->selected_reg <= MXT_CRC_END(k)) {
+ ret = mxt_read_info_reg(k, s->selected_reg);
+ } else if (s->selected_reg <= k->end_addr) {
+ ret = s->objects[s->selected_reg - MXT_OBJECTS_START(k)];
+ } else {
+ ERRPRINT("register with address 0x%04x doesn't exist\n",
+ s->selected_reg);
+ ret = -1;
+ }
+ DPRINT("Sending %s(0x%02x) -> 0x%02x\n",
+ mxt_get_reg_name(s, s->selected_reg), s->selected_reg, ret);
+ s->selected_reg++;
+ break;
+ default:
+ ERRPRINT("data read request in wrong state!\n");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static int mxt_i2c_write(I2CSlave *i2c, uint8_t data)
+{
+ MXTState *s = MXT(i2c);
+ MXTClass *k = MXT_GET_CLASS(s);
+
+ switch (s->i2c_state) {
+ case start_bit_write:
+ /* data is i2c slave address, we can ignore it */
+ s->i2c_state = next_is_reg_lsb;
+ break;
+ case start_bit_read:
+ /* data is i2c slave address, we can ignore it */
+ s->i2c_state = send_zero_next;
+ break;
+ case next_is_reg_lsb:
+ s->selected_reg = (s->selected_reg & 0xFF00) | data;
+ s->i2c_state = next_is_reg_msb;
+ break;
+ case next_is_reg_msb:
+ s->selected_reg = (s->selected_reg & 0x00FF) | (data << 8);
+ DPRINT("Selected register 0x%04x\n", s->selected_reg);
+ s->i2c_state = register_selected;
+ break;
+ case register_selected:
+ DPRINT("Writing %s <- 0x%02x\n",
+ mxt_get_reg_name(s, s->selected_reg), data);
+ if ((s->selected_reg >= k->t9_address) && (s->selected_reg <
+ k->t9_address + mxt_obj_sizes[MXT_TOUCH_MULTI_T9])) {
+ mxt_write_to_t9(s, s->selected_reg - k->t9_address, data);
+ } else if ((s->selected_reg >= MXT_OBJECTS_START(k)) &&
+ (s->selected_reg <= k->end_addr)) {
+ s->objects[s->selected_reg - MXT_OBJECTS_START(k)] = data;
+ } else {
+ ERRPRINT("can't write to register with address 0x%04x\n",
+ s->selected_reg);
+ return -1;
+ }
+ s->selected_reg++;
+ break;
+ default:
+ ERRPRINT("data write request in wrong state!\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void mxt_i2c_event(I2CSlave *i2c, enum i2c_event event)
+{
+ MXTState *s = MXT(i2c);
+
+ switch (event) {
+ case I2C_START_RECV:
+ DPRINT("I2C start bit appeared: reading data\n");
+ s->i2c_state = start_bit_read;
+ break;
+ case I2C_START_SEND:
+ DPRINT("I2C start bit appeared: writing data\n");
+ s->i2c_state = start_bit_write;
+ break;
+ case I2C_FINISH:
+ DPRINT("I2C stop bit received\n");
+ s->i2c_state = idle;
+ break;
+ default:
+ break;
+ }
+}
+
+static void mxt_init(Object *obj)
+{
+ MXTState *s = MXT(obj);
+ MXTClass *k = MXT_GET_CLASS(obj);
+ unsigned i;
+ uint16_t objects_len = 0;
+
+ for (i = 0; i < k->mxt_info[MXT_OBJ_NUM]; i++) {
+ objects_len += (k->obj_tbl[i].size + 1) * (k->obj_tbl[i].instances +
1);
+ }
+ s->objects = g_malloc0(objects_len);
+
+ s->i2c_state = idle;
+ s->selected_reg = 0;
+ s->fifo_add = s->fifo_get = 0;
+ s->fifo_lock = false;
+ s->scale_coef_x = 0xFFFF;
+ s->scale_coef_y = 0xFFFF;
+ s->x_offset = 0;
+ s->y_offset = 0;
+ s->x_curr = 0;
+ s->y_curr = 0;
+ s->touches = 0;
+ s->mouse = NULL;
+
+ qdev_init_gpio_out(DEVICE(obj), &s->nCHG, 1);
+ qemu_irq_raise(s->nCHG);
+}
+
+static void mxt_finalize(Object *obj)
+{
+ MXTState *s = MXT(obj);
+
+ if (s->mouse) {
+ qemu_remove_mouse_event_handler(s->mouse);
+ s->mouse = NULL;
+ }
+
+ g_free(s->objects);
+}
+
+static int mxt_i2c_init(I2CSlave *i2c)
+{
+ return 0;
+}
+
+static inline uint32_t mxt_crc24(uint32_t crc, uint8_t byte1, uint8_t byte2)
+{
+ uint32_t ret = (crc << 1) ^ ((byte2 << 8) | byte1);
+
+ if (ret & 0x1000000) {
+ ret ^= MXT_CRC_POLY;
+ }
+
+ return ret;
+}
+
+static void mxt_calculate_crc(MXTClass *k)
+{
+ unsigned i;
+ uint32_t crc = 0;
+
+ for (i = 0; i < MXT_OBJTBL_END(k); i += 2) {
+ crc =
+ mxt_crc24(crc, mxt_read_info_reg(k, i), mxt_read_info_reg(k, i +
1));
+ }
+
+ crc = mxt_crc24(crc, mxt_read_info_reg(k, i), 0) & 0x00FFFFFF;
+ k->crc[0] = crc & 0xFF;
+ k->crc[1] = (crc >> 8) & 0xFF;
+ k->crc[2] = (crc >> 16) & 0xFF;
+}
+
+static void
+mxt_init_object_table(MXTClass *k, const ObjConfig *list)
+{
+ MXTObjTblEntry *tbl = k->obj_tbl;
+ unsigned i, tbl_len = k->mxt_info[MXT_OBJ_NUM];
+
+ for (i = 0; i < tbl_len; i++) {
+ tbl[i].type = mxt_obj_types_list[list[i].type];
+ tbl[i].size = mxt_obj_sizes[list[i].type] - 1;
+ tbl[i].instances = list[i].instances;
+ tbl[i].num_report_ids =
+ (list[i].type == MXT_TOUCH_MULTI_T9) ? MXT_NUM_OF_TOUCHES : 0;
+ if (i == 0) {
+ tbl[i].start_addr = MXT_OBJTBL_START + tbl_len *
+ MXT_OBJTBL_ENTRY_LEN + MXT_CRC_SIZE;
+ } else {
+ tbl[i].start_addr = tbl[i-1].start_addr +
+ (tbl[i-1].size + 1) * (tbl[i-1].instances + 1);
+ }
+ if (list[i].type == MXT_GEN_MESSAGE_T5) {
+ k->t5_address = tbl[i].start_addr;
+ } else if (list[i].type == MXT_TOUCH_MULTI_T9) {
+ k->t9_address = tbl[i].start_addr;
+ }
+ }
+
+ k->end_addr = tbl[i-1].start_addr + tbl[i-1].size;
+ /* T5 and T9 objects are mandatory */
+ assert(k->t5_address);
+ assert(k->t9_address);
+}
+
+static int mxt_post_load(void *opaque, int ver_id)
+{
+ MXTState *s = (MXTState *)opaque;
+ MXTClass *k = MXT_GET_CLASS(s);
+ uint16_t addr = k->t9_address - MXT_OBJECTS_START(k) + MXT_OBJ_T9_CTRL;
+
+ if ((s->objects[addr] == 0x83) && !(s->mouse)) {
+ s->mouse =
+ qemu_add_mouse_event_handler(mxt_mouse_event, s, 1, "maxtouch");
+ qemu_activate_mouse_event_handler(s->mouse);
+ }
+
+ return 0;
+}
+
+static const VMStateDescription mxt_vmstate = {
+ .name = "mxt",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .post_load = mxt_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(i2c_state, MXTState),
+ VMSTATE_STRUCT_ARRAY(msg_fifo, MXTState, MXT_MESSAGE_FIFO_LEN, 1,
+ mxt_message_vmstate, MXTMessage),
+ VMSTATE_UINT8(fifo_get, MXTState),
+ VMSTATE_UINT8(fifo_add, MXTState),
+ VMSTATE_BOOL(fifo_lock, MXTState),
+ VMSTATE_UINT16(selected_reg, MXTState),
+ VMSTATE_UINT16(scale_coef_x, MXTState),
+ VMSTATE_UINT16(scale_coef_y, MXTState),
+ VMSTATE_UINT16(x_offset, MXTState),
+ VMSTATE_UINT16(y_offset, MXTState),
+ VMSTATE_UINT16(x_curr, MXTState),
+ VMSTATE_UINT16(y_curr, MXTState),
+ VMSTATE_UINT8(touches, MXTState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void maxtouch_class_init(ObjectClass *klass, void *data)
+{
+ I2CSlaveClass *i2c = I2C_SLAVE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ MXTClass *k = MXT_CLASS(klass);
+ const MXTVariantInfo *info = (const MXTVariantInfo *)data;
+
+ dc->vmsd = &mxt_vmstate;
+ i2c->init = mxt_i2c_init;
+ i2c->event = mxt_i2c_event;
+ i2c->recv = mxt_i2c_read;
+ i2c->send = mxt_i2c_write;
+
+ k->mxt_info = info->mxt_variant_info;
+ k->obj_tbl = g_new0(MXTObjTblEntry, k->mxt_info[MXT_OBJ_NUM]);
+ mxt_init_object_table(k, info->mxt_variant_obj_list);
+ mxt_calculate_crc(k);
+}
+
+static void maxtouch_class_finalize(ObjectClass *klass, void *data)
+{
+ MXTClass *k = MXT_CLASS(klass);
+
+ g_free(k->obj_tbl);
+}
+
+static void mxt_register_type(const MXTVariantInfo *info)
+{
+ TypeInfo type = {
+ .name = info->name,
+ .parent = TYPE_MAXTOUCH,
+ .class_init = maxtouch_class_init,
+ .class_finalize = maxtouch_class_finalize,
+ .class_data = (void *)info
+ };
+
+ type_register(&type);
+}
+
+static TypeInfo maxtouch_type_info = {
+ .name = TYPE_MAXTOUCH,
+ .parent = TYPE_I2C_SLAVE,
+ .instance_size = sizeof(MXTState),
+ .instance_init = mxt_init,
+ .instance_finalize = mxt_finalize,
+ .abstract = true,
+ .class_size = sizeof(MXTClass),
+};
+
+static void mxt_types_init(void)
+{
+ unsigned i;
+
+ type_register_static(&maxtouch_type_info);
+ for (i = 0; i < MXT_NUM_OF_VARIANTS; i++) {
+ mxt_register_type(&mxt_variants_info_array[i]);
+ }
+}
+
+type_init(mxt_types_init)
--
1.7.4.1
[Qemu-devel] [PATCH V2 3/3] hw: add Atmel maxtouch touchscreen implementation,
Igor Mitsyanko <=
[Qemu-devel] [PATCH V2 2/3] exynos4210: add exynos4210 GPIO implementation, Igor Mitsyanko, 2012/03/15