2 * AT42QT602240 Touchscreen
4 * Copyright (c) 2010 Samsung Electronics.
5 * Contributed by Alexey Merkulov <steelart@ispras.ru>
6 * Dmitry Zhurikhin <zhur@ispras.ru>
7 * Vladimir Monakhov <vmonakhov@ispras.ru>
9 * NB: Only features used in the kernel driver is implemented currently.
13 #include "i2c-addressable.h"
16 #define QT602240_DEBUG_DELTAS 2
17 #define QT602240_DEBUG_REFERENCES 3
18 #define QT602240_DEBUG_CTERANGE 26
19 #define QT602240_GEN_MESSAGE 5
20 #define QT602240_GEN_COMMAND 6
21 #define QT602240_GEN_POWER 7
22 #define QT602240_GEN_ACQUIRE 8
23 #define QT602240_TOUCH_MULTI 9
24 #define QT602240_TOUCH_KEYARRAY 15
25 #define QT602240_PROCI_GRIPFACE 20
26 #define QT602240_PROCG_NOISE 22
27 #define QT602240_PROCI_ONETOUCH 24
28 #define QT602240_PROCI_TWOTOUCH 27
29 #define QT602240_SPT_GPIOPWM 19
30 #define QT602240_SPT_SELFTEST 25
31 #define QT602240_SPT_CTECONFIG 28
34 #define QT602240_NORMAL 0x0
35 #define QT602240_DIAGONAL 0x1
36 #define QT602240_HORIZONTAL_FLIP 0x2
37 #define QT602240_ROTATED_90_COUNTER 0x3
38 #define QT602240_VERTICAL_FLIP 0x4
39 #define QT602240_ROTATED_90 0x5
40 #define QT602240_ROTATED_180 0x6
41 #define QT602240_DIAGONAL_COUNTER 0x7
44 #define QT602240_SUPPRESS (1 << 1)
45 #define QT602240_AMP (1 << 2)
46 #define QT602240_VECTOR (1 << 3)
47 #define QT602240_MOVE (1 << 4)
48 #define QT602240_RELEASE (1 << 5)
49 #define QT602240_PRESS (1 << 6)
50 #define QT602240_DETECT (1 << 7)
53 #define QT602240_REPORTID 0
54 #define QT602240_MSG_STATUS 1
55 #define QT602240_MSG_XPOSMSB 2
56 #define QT602240_MSG_YPOSMSB 3
57 #define QT602240_MSG_XYPOSLSB 4
58 #define QT602240_MSG_TCHAREA 5
59 #define QT602240_MSG_TCHAMPLITUDE 6
60 #define QT602240_MSG_TCHVECTOR 7
61 #define QT602240_CHECKSUM 8
64 #define OBJECT_TABLE_MAX_SIZE 16
66 #define OBJ_ADDR_TYPE 0
67 #define OBJ_ADDR_START 1
68 #define OBJ_ADDR_SIZE 3
69 #define OBJ_ADDR_INSTANCES 4
70 #define OBJ_ADDR_REPORT_IDS 5
73 /* Size of message queue */
74 #define QT602240_MAX_MESSAGE 10
76 #define QEMUMAXX 0x7FFF
77 #define QEMUMAXY 0x7FFF
79 #define FAMILY_ID_SIZE 1
80 #define VARIANT_ID_SIZE 1
81 #define VERSION_SIZE 1
83 #define MATRIX_X_SIZE_SIZE 1
84 #define MATRIX_Y_SIZE_SIZE 1
85 #define OBJECTS_NUM_SIZE 1
86 #define OBJECT_TABLE_SIZE (OBJECT_TABLE_MAX_SIZE * OBJ_SIZE)
87 #define CHECKSUM_SIZE 1
88 #define MESSAGE_SIZE 9
89 #define MULTITOUCH_SIZE 30
90 #define GENCOMMAND_SIZE 5
91 #define SPT_CTECONFIG_SIZE 5
94 #define VARIANT_ID (FAMILY_ID + FAMILY_ID_SIZE)
95 #define VERSION (VARIANT_ID + VARIANT_ID_SIZE)
96 #define BUILD (VERSION + VERSION_SIZE)
97 #define MATRIX_X_SIZE (BUILD + BUILD_SIZE)
98 #define MATRIX_Y_SIZE (MATRIX_X_SIZE + MATRIX_X_SIZE_SIZE)
99 #define OBJECTS_NUM (MATRIX_Y_SIZE + MATRIX_Y_SIZE_SIZE)
100 #define OBJECT_TABLE (OBJECTS_NUM + OBJECTS_NUM_SIZE)
101 #define CHECKSUM (OBJECT_TABLE + OBJECT_TABLE_SIZE)
102 #define MESSAGE (CHECKSUM + CHECKSUM_SIZE)
103 #define MULTITOUCH (MESSAGE + MESSAGE_SIZE)
104 #define GENCOMMAND (MULTITOUCH + MULTITOUCH_SIZE)
105 #define SPT_CTECONFIG (GENCOMMAND + GENCOMMAND_SIZE)
106 #define TOTAL_SIZE (SPT_CTECONFIG + SPT_CTECONFIG_SIZE)
108 #define MULTITOUCH_CTRL 0
109 #define MULTITOUCH_ORIENT 9
110 #define MULTITOUCH_XRANGE_LSB 18
111 #define MULTITOUCH_XRANGE_MSB 19
112 #define MULTITOUCH_YRANGE_LSB 20
113 #define MULTITOUCH_YRANGE_MSB 21
115 /* This structure closely correspond to the memory map of the real device.
116 * We use this property in read\write functions by directly reading\writing
117 * data at the offsets provided by the driver. It is possible due to proper
118 * filling of ADDR_START field of each object with this object's structure
119 * offset in the next structure and byte-to-byte equivalence of each object
120 * structure to that of the real device. */
121 typedef struct QT602240State {
123 I2CAddressableState i2c_addressable;
125 uint8_t regs[TOTAL_SIZE];
127 /* Supported objects are MESSAGE, MULTITOUCH, SPT_CTECONFIG, GENCOMMAND. */
129 int32_t prev_x, prev_y;
133 /* Messages are stored in a cyclic buffer */
134 int32_t queue_start, queue_end;
135 uint8_t queue[QT602240_MAX_MESSAGE][MESSAGE_SIZE];
137 /* Boundary reported coordinates */
138 uint32_t minx, maxx, miny, maxy, orient;
141 typedef struct QT602240MultitouchMessage {
147 uint8_t tchamplitude;
149 } QT602240MultitouchMessage;
152 /* Add one object to the table */
153 static void qt602240_add_object(QT602240State *s, uint16_t offset,
154 uint8_t size, int type, uint8_t report_ids)
158 for (i = 0; i < OBJECT_TABLE_MAX_SIZE; i++) {
159 if (s->regs[OBJECT_TABLE + i * OBJ_SIZE + OBJ_ADDR_TYPE] == 0) {
161 s->regs[OBJECT_TABLE + i * OBJ_SIZE + OBJ_ADDR_TYPE] = type;
163 s->regs[OBJECT_TABLE + i * OBJ_SIZE + OBJ_ADDR_START] =
166 s->regs[OBJECT_TABLE + i * OBJ_SIZE + OBJ_ADDR_START + 1] =
167 (offset >> 8) & 0xFF;
169 s->regs[OBJECT_TABLE + i * OBJ_SIZE + OBJ_ADDR_SIZE] = size - 1;
171 s->regs[OBJECT_TABLE + i * OBJ_SIZE + OBJ_ADDR_INSTANCES] = 0;
173 s->regs[OBJECT_TABLE + i * OBJ_SIZE + OBJ_ADDR_REPORT_IDS] = report_ids;
175 s->regs[OBJECTS_NUM]++;
181 /* Reset to default values */
182 static void qt602240_reset(DeviceState *d)
185 FROM_I2CADDR_SLAVE(QT602240State, I2CADDR_SLAVE_FROM_QDEV(d));
187 s->regs[FAMILY_ID] = 0x80;
188 s->regs[VARIANT_ID] = 0x00;
189 s->regs[VERSION] = 20;
190 s->regs[BUILD] = 0x00;
191 s->regs[MATRIX_X_SIZE] = 16;
192 s->regs[MATRIX_Y_SIZE] = 14;
193 s->regs[OBJECTS_NUM] = 0;
200 qt602240_add_object(s, MESSAGE, MESSAGE_SIZE,
201 QT602240_GEN_MESSAGE, 0);
202 qt602240_add_object(s, MULTITOUCH, MULTITOUCH_SIZE,
203 QT602240_TOUCH_MULTI, 10);
204 qt602240_add_object(s, SPT_CTECONFIG, SPT_CTECONFIG_SIZE,
205 QT602240_SPT_CTECONFIG, 0);
206 qt602240_add_object(s, GENCOMMAND, GENCOMMAND_SIZE,
207 QT602240_GEN_COMMAND, 0);
209 s->regs[MESSAGE + QT602240_REPORTID] = 0xFF;
215 #define OFFESETOF_MEM(s, mem) ((void *)(&(mem)) - (void *)(s))
217 static uint8_t qt602240_read(void *opaque, uint32_t address, uint8_t offset)
219 QT602240State *s = (QT602240State *)opaque;
221 uint32_t reg = address + offset;
223 if (reg > TOTAL_SIZE) {
224 hw_error("qt602240: bad read offset 0x%x\n", reg);
227 retval = s->regs[reg];
228 if (reg >= MESSAGE + QT602240_REPORTID &&
229 reg <= MESSAGE + QT602240_CHECKSUM) {
230 /* Get message from the queue */
231 if (s->queue_start == s->queue_end) {
235 retval = s->queue[s->queue_start][reg - MESSAGE];
236 /* Here is an assumption that message is read till the end */
237 if (reg == MESSAGE + QT602240_CHECKSUM) {
238 /* Move to the next message from the queue */
239 s->queue_start = (s->queue_start + 1) % QT602240_MAX_MESSAGE;
240 if (s->queue_start != s->queue_end) {
241 qemu_irq_raise(s->irq); // GRS : # of interrupt & # of message does not match
247 return 3; // GRS : Temporary code. Unimplemented reg
252 static void qt602240_write(void *opaque, uint32_t address, uint8_t offset,
255 QT602240State *s = (QT602240State *)opaque;
256 uint32_t reg = address + offset;
258 if (reg >= MULTITOUCH &&
259 reg < MULTITOUCH + MULTITOUCH_SIZE) {
262 if (reg == MULTITOUCH + MULTITOUCH_ORIENT) {
263 s->orient = s->regs[reg] & 1;
266 if (reg == MULTITOUCH + MULTITOUCH_XRANGE_LSB ||
267 reg == MULTITOUCH + MULTITOUCH_XRANGE_MSB) {
268 int res = s->regs[MULTITOUCH + MULTITOUCH_XRANGE_LSB] +
269 (s->regs[MULTITOUCH + MULTITOUCH_XRANGE_MSB] << 8) + 1;
271 if (s->orient == 0) {
278 if (reg == MULTITOUCH + MULTITOUCH_YRANGE_LSB ||
279 reg == MULTITOUCH + MULTITOUCH_YRANGE_MSB) {
280 int res = s->regs[MULTITOUCH + MULTITOUCH_YRANGE_LSB] +
281 (s->regs[MULTITOUCH + MULTITOUCH_YRANGE_MSB] << 8) + 1;
283 if (s->orient == 0) {
290 } else if (reg >= GENCOMMAND &&
291 reg < GENCOMMAND + GENCOMMAND_SIZE) {
293 } else if (reg >= SPT_CTECONFIG &&
294 reg < SPT_CTECONFIG + SPT_CTECONFIG_SIZE) {
297 hw_error("qt602240: bad write offset 0x%x\n", reg);
301 static int qt602240_enabled(QT602240State *s)
303 /* Check for ENABLE and RPTEN bits */
304 return ((s->regs[MULTITOUCH + MULTITOUCH_CTRL] & 0x3) == 0x3);
307 /* Modify the message read by the driver */
308 static void qt602240_msg(QT602240State *s, int x, int y, int status)
310 /* Check if queue is full */
311 if ((s->queue_end + 1) % QT602240_MAX_MESSAGE == s->queue_start) {
312 if(status != QT602240_RELEASE){
313 return; // GRS : Heuristic - Drag does not finish, by dropping release msg
317 memset(s->queue[s->queue_end], 0, MESSAGE_SIZE);
318 s->queue[s->queue_end][QT602240_REPORTID] = 2;
319 s->queue[s->queue_end][QT602240_MSG_XPOSMSB] = x >> 2;
320 s->queue[s->queue_end][QT602240_MSG_YPOSMSB] = y >> 2;
321 s->queue[s->queue_end][QT602240_MSG_XYPOSLSB] =
322 ((x & 3) << 6) | ((y & 3) << 2);
323 s->queue[s->queue_end][QT602240_MSG_STATUS] = status;
324 s->queue[s->queue_end][QT602240_MSG_TCHAREA] = 1;
326 if ((s->queue_end + 1) % QT602240_MAX_MESSAGE != s->queue_start) {
327 s->queue_end = (s->queue_end + 1) % QT602240_MAX_MESSAGE;
328 // GRS : In case of queue full & release msg - drop the last msg instead of release
333 static void qt602240_ts_event(void *opaque,
334 int x, int y, int z, int buttons_state)
336 QT602240State *s = (QT602240State *)opaque;
338 if (!qt602240_enabled(s)) {
342 /* Convert QEMU mouse coordinates to the touchscreen */
343 /* FIXME: should we use configuration data provided by the driver? */
344 y = (s->miny + y * (s->maxy - s->miny) / QEMUMAXY);
345 x = (s->minx + x * (s->maxx - s->minx) / QEMUMAXX);
347 if (s->pressure == !buttons_state) {
349 qt602240_msg(s, x, y, QT602240_PRESS | QT602240_DETECT);
351 qt602240_msg(s, x, y, QT602240_RELEASE);
353 qemu_irq_raise(s->irq);
354 } else if (s->pressure && (x != s->prev_x || y != s->prev_y)) {
355 static int drop_move = 1;
357 qt602240_msg(s, x, y, QT602240_MOVE | QT602240_DETECT);
358 qemu_irq_raise(s->irq);
365 s->pressure = !!buttons_state;
370 static void qt602240_save(QEMUFile *f, void *opaque)
372 QT602240State *s = (QT602240State *)opaque;
375 qemu_put_buffer(f, s->regs, TOTAL_SIZE);
376 qemu_put_sbe32s(f, &s->prev_x);
377 qemu_put_sbe32s(f, &s->prev_y);
378 qemu_put_sbe32s(f, &s->pressure);
379 qemu_put_sbe32s(f, &s->queue_start);
380 qemu_put_sbe32s(f, &s->queue_end);
382 for (i = 0; i < QT602240_MAX_MESSAGE; i++) {
383 qemu_put_buffer(f, s->queue[i], MESSAGE_SIZE);
386 qemu_put_be32s(f, &s->minx);
387 qemu_put_be32s(f, &s->miny);
388 qemu_put_be32s(f, &s->maxx);
389 qemu_put_be32s(f, &s->maxy);
390 qemu_put_be32s(f, &s->orient);
393 static int qt602240_load(QEMUFile *f, void *opaque, int version_id)
395 QT602240State *s = (QT602240State *)opaque;
398 if (version_id != 1) {
402 qemu_get_buffer(f, s->regs, TOTAL_SIZE);
403 qemu_get_sbe32s(f, &s->prev_x);
404 qemu_get_sbe32s(f, &s->prev_y);
405 qemu_get_sbe32s(f, &s->pressure);
406 qemu_get_sbe32s(f, &s->queue_start);
407 qemu_get_sbe32s(f, &s->queue_end);
409 for (i = 0; i < QT602240_MAX_MESSAGE; i++) {
410 qemu_get_buffer(f, s->queue[i], MESSAGE_SIZE);
413 qemu_get_be32s(f, &s->minx);
414 qemu_get_be32s(f, &s->miny);
415 qemu_get_be32s(f, &s->maxx);
416 qemu_get_be32s(f, &s->maxy);
417 qemu_get_be32s(f, &s->orient);
422 static int qt602240_init(I2CAddressableState *s)
424 QT602240State *t = FROM_I2CADDR_SLAVE(QT602240State, s);
426 qdev_init_gpio_out(&s->i2c.qdev, &t->irq, 1);
428 qemu_add_mouse_event_handler(qt602240_ts_event, t, 1,
429 "AT42QT602240 Touchscreen");
430 qt602240_reset(&s->i2c.qdev);
432 register_savevm(&s->i2c.qdev, "qt602240", -1, 1,
433 qt602240_save, qt602240_load, s);
438 static I2CAddressableDeviceInfo qt602240_info = {
439 .i2c.qdev.name = "qt602240",
440 .i2c.qdev.size = sizeof(QT602240State),
441 .i2c.qdev.reset = qt602240_reset,
442 .init = qt602240_init,
443 .read = qt602240_read,
444 .write = qt602240_write,
449 static void qt602240_register_devices(void)
451 i2c_addressable_register_device(&qt602240_info);
454 device_init(qt602240_register_devices)