From patchwork Fri Mar 2 11:35:45 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mitsyanko Igor X-Patchwork-Id: 144224 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id A66CF1007D6 for ; Fri, 2 Mar 2012 22:36:29 +1100 (EST) Received: from localhost ([::1]:50008 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1S3Qmh-0002es-CU for incoming@patchwork.ozlabs.org; Fri, 02 Mar 2012 06:36:27 -0500 Received: from eggs.gnu.org ([208.118.235.92]:56335) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1S3QmO-0002eA-2u for qemu-devel@nongnu.org; Fri, 02 Mar 2012 06:36:15 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1S3QmG-0005WJ-3o for qemu-devel@nongnu.org; Fri, 02 Mar 2012 06:36:07 -0500 Received: from mailout2.w1.samsung.com ([210.118.77.12]:28709) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1S3QmF-0005Vw-O4 for qemu-devel@nongnu.org; Fri, 02 Mar 2012 06:36:00 -0500 Received: from euspt1 (mailout2.w1.samsung.com [210.118.77.12]) by mailout2.w1.samsung.com (iPlanet Messaging Server 5.2 Patch 2 (built Jul 14 2004)) with ESMTP id <0M0900FPK9JXQL@mailout2.w1.samsung.com> for qemu-devel@nongnu.org; Fri, 02 Mar 2012 11:35:58 +0000 (GMT) Received: from dodo.rnd.samsung.ru ([106.109.8.162]) by spt1.w1.samsung.com (iPlanet Messaging Server 5.2 Patch 2 (built Jul 14 2004)) with ESMTPA id <0M0900JK29JL0S@spt1.w1.samsung.com> for qemu-devel@nongnu.org; Fri, 02 Mar 2012 11:35:57 +0000 (GMT) Date: Fri, 02 Mar 2012 15:35:45 +0400 From: Igor Mitsyanko In-reply-to: <1330688145-22944-1-git-send-email-i.mitsyanko@samsung.com> To: qemu-devel@nongnu.org Message-id: <1330688145-22944-6-git-send-email-i.mitsyanko@samsung.com> MIME-version: 1.0 X-Mailer: git-send-email 1.7.4.1 Content-type: TEXT/PLAIN Content-transfer-encoding: 7BIT References: <1330688145-22944-1-git-send-email-i.mitsyanko@samsung.com> X-detected-operating-system: by eggs.gnu.org: Solaris 9.1 X-Received-From: 210.118.77.12 Cc: peter.maydell@linaro.org, Igor Mitsyanko , e.voevodin@samsung.com, kyungmin.park@samsung.com, d.solodkiy@samsung.com, m.kozlov@samsung.com Subject: [Qemu-devel] [PATCH 5/5] hw: add Atmel maxtouch touchscreen implementation X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org And use it for exynos4210 NURI board emulation Signed-off-by: Igor Mitsyanko --- Makefile.target | 1 + hw/exynos4_boards.c | 11 +- hw/maxtouch.c | 1079 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1088 insertions(+), 3 deletions(-) create mode 100644 hw/maxtouch.c diff --git a/Makefile.target b/Makefile.target index 7968120..05ce652 100644 --- a/Makefile.target +++ b/Makefile.target @@ -349,6 +349,7 @@ obj-arm-y += exynos4210_gic.o exynos4210_combiner.o exynos4210.o obj-arm-y += exynos4_boards.o exynos4210_uart.o exynos4210_pwm.o obj-arm-y += exynos4210_pmu.o exynos4210_mct.o exynos4210_fimd.o obj-arm-y += exynos4210_i2c.o exynos4210_gpio.o +obj-arm-y += maxtouch.o obj-arm-y += arm_l2x0.o obj-arm-y += arm_mptimer.o a15mpcore.o obj-arm-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o 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..621775c --- /dev/null +++ b/hw/maxtouch.c @@ -0,0 +1,1079 @@ +/* + * Atmel maXTouch touchscreen emulation + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. All rights reserved. + * Igor Mitsyanko + * + * 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 . + * + */ + +#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: + * + * [MXT i2c address(0x4A) with last bit 0(write data)] + * [LSB of MXT register offset (starting from 0)] + * [MSB of MXT register offset] + * + * + * [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] + * [...........] + * + * + * Atmel maXTouch i2c registers write byte sequence: + * + * [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] + * [...........] + * + */ + +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 = {}; + + type.name = info->name, + type.parent = TYPE_MAXTOUCH, + type.class_init = maxtouch_class_init, + type.class_finalize = maxtouch_class_finalize, + type.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)