Tizen 2.1 base
[sdk/emulator/qemu.git] / hw / qt602240_ts.c
1 /*
2  * AT42QT602240 Touchscreen
3  *
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>
8  *
9  * NB: Only features used in the kernel driver is implemented currently.
10  */
11
12 #include "console.h"
13 #include "i2c-addressable.h"
14
15 /* Object types */
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
32
33 /* Orient */
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
42
43 /* Touch status */
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)
51
52 /* Message */
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
62
63 /* Message format */
64 #define OBJECT_TABLE_MAX_SIZE       16
65
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
71 #define OBJ_SIZE                    6
72
73 /* Size of message queue */
74 #define QT602240_MAX_MESSAGE        10
75
76 #define QEMUMAXX                    0x7FFF
77 #define QEMUMAXY                    0x7FFF
78
79 #define FAMILY_ID_SIZE              1
80 #define VARIANT_ID_SIZE             1
81 #define VERSION_SIZE                1
82 #define BUILD_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
92
93 #define FAMILY_ID                   0
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)
107
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
114
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 {
122
123     I2CAddressableState i2c_addressable;
124
125     uint8_t regs[TOTAL_SIZE];
126
127     /* Supported objects are MESSAGE, MULTITOUCH, SPT_CTECONFIG, GENCOMMAND.  */
128
129     int32_t prev_x, prev_y;
130     int32_t pressure;
131     qemu_irq irq;
132
133     /* Messages are stored in a cyclic buffer */
134     int32_t queue_start, queue_end;
135     uint8_t queue[QT602240_MAX_MESSAGE][MESSAGE_SIZE];
136
137     /* Boundary reported coordinates */
138     uint32_t minx, maxx, miny, maxy, orient;
139 } QT602240State;
140
141 typedef struct QT602240MultitouchMessage {
142     uint8_t status;
143     uint8_t xposmsb;
144     uint8_t yposmsb;
145     uint8_t xyposlsb;
146     uint8_t tcharea;
147     uint8_t tchamplitude;
148     uint8_t tchvector;
149 } QT602240MultitouchMessage;
150
151
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)
155 {
156     int i;
157
158     for (i = 0; i < OBJECT_TABLE_MAX_SIZE; i++) {
159         if (s->regs[OBJECT_TABLE + i * OBJ_SIZE + OBJ_ADDR_TYPE] == 0) {
160
161             s->regs[OBJECT_TABLE + i * OBJ_SIZE + OBJ_ADDR_TYPE] = type;
162
163             s->regs[OBJECT_TABLE + i * OBJ_SIZE + OBJ_ADDR_START] =
164                 offset & 0xFF;
165
166             s->regs[OBJECT_TABLE + i * OBJ_SIZE + OBJ_ADDR_START + 1] =
167                 (offset >> 8) & 0xFF;
168
169             s->regs[OBJECT_TABLE + i * OBJ_SIZE + OBJ_ADDR_SIZE] = size - 1;
170
171             s->regs[OBJECT_TABLE + i * OBJ_SIZE + OBJ_ADDR_INSTANCES] = 0;
172
173             s->regs[OBJECT_TABLE + i * OBJ_SIZE + OBJ_ADDR_REPORT_IDS] = report_ids;
174
175             s->regs[OBJECTS_NUM]++;
176             break;
177         }
178     }
179 }
180
181 /* Reset to default values */
182 static void qt602240_reset(DeviceState *d)
183 {
184     QT602240State *s =
185         FROM_I2CADDR_SLAVE(QT602240State, I2CADDR_SLAVE_FROM_QDEV(d));
186
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;
194
195     s->minx = 0;
196     s->maxx = 1;
197     s->miny = 0;
198     s->maxy = 1;
199
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);
208
209     s->regs[MESSAGE + QT602240_REPORTID] = 0xFF;
210
211     s->queue_start = 0;
212     s->queue_end = 0;
213 }
214
215 #define OFFESETOF_MEM(s, mem) ((void *)(&(mem)) - (void *)(s))
216
217 static uint8_t qt602240_read(void *opaque, uint32_t address, uint8_t offset)
218 {
219     QT602240State *s = (QT602240State *)opaque;
220     uint8_t retval;
221     uint32_t reg = address + offset;
222
223     if (reg > TOTAL_SIZE) {
224         hw_error("qt602240: bad read offset 0x%x\n", reg);
225     }
226
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) {
232             /* No messages */
233             return 0xFF;
234         }
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
242             }
243         }
244     }
245
246     if(reg == 127){
247         return 3;  // GRS : Temporary code. Unimplemented reg
248     }
249     return retval;
250 }
251
252 static void qt602240_write(void *opaque, uint32_t address, uint8_t offset,
253                            uint8_t val)
254 {
255     QT602240State *s = (QT602240State *)opaque;
256     uint32_t reg = address + offset;
257
258     if (reg >= MULTITOUCH &&
259         reg < MULTITOUCH + MULTITOUCH_SIZE) {
260         s->regs[reg] = val;
261
262         if (reg == MULTITOUCH + MULTITOUCH_ORIENT) {
263             s->orient = s->regs[reg] & 1;
264         }
265
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;
270
271             if (s->orient == 0) {
272                 s->maxx = res;
273             } else {
274                 s->maxy = res;
275             }
276         }
277
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;
282
283             if (s->orient == 0) {
284                 s->maxy = res;
285             } else {
286                 s->maxx = res;
287             }
288         }
289
290     } else if (reg >= GENCOMMAND &&
291         reg < GENCOMMAND + GENCOMMAND_SIZE) {
292         s->regs[reg] = val;
293     } else if (reg >= SPT_CTECONFIG &&
294         reg < SPT_CTECONFIG + SPT_CTECONFIG_SIZE) {
295         s->regs[reg] = val;
296     } else {
297         hw_error("qt602240: bad write offset 0x%x\n", reg);
298     }
299 }
300
301 static int qt602240_enabled(QT602240State *s)
302 {
303     /* Check for ENABLE and RPTEN bits */
304     return ((s->regs[MULTITOUCH + MULTITOUCH_CTRL] & 0x3) == 0x3);
305 }
306
307 /* Modify the message read by the driver */
308 static void qt602240_msg(QT602240State *s, int x, int y, int status)
309 {
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 
314         }
315     }
316
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;
325
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
329     }
330
331 }
332
333 static void qt602240_ts_event(void *opaque,
334                               int x, int y, int z, int buttons_state)
335 {
336     QT602240State *s = (QT602240State *)opaque;
337
338     if (!qt602240_enabled(s)) {
339         return;
340     }
341
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);
346
347     if (s->pressure == !buttons_state) {
348         if (buttons_state) {
349             qt602240_msg(s, x, y, QT602240_PRESS | QT602240_DETECT);
350         } else {
351             qt602240_msg(s, x, y, QT602240_RELEASE);
352         }
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;
356         if(drop_move){
357             qt602240_msg(s, x, y, QT602240_MOVE | QT602240_DETECT);
358             qemu_irq_raise(s->irq);
359             drop_move = 0;
360         }else{
361             drop_move = 1;
362         }
363     }
364
365     s->pressure = !!buttons_state;
366     s->prev_x = x;
367     s->prev_y = y;
368 }
369
370 static void qt602240_save(QEMUFile *f, void *opaque)
371 {
372     QT602240State *s = (QT602240State *)opaque;
373     int i;
374
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);
381
382     for (i = 0; i < QT602240_MAX_MESSAGE; i++) {
383         qemu_put_buffer(f, s->queue[i], MESSAGE_SIZE);
384     }
385
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);
391 }
392
393 static int qt602240_load(QEMUFile *f, void *opaque, int version_id)
394 {
395     QT602240State *s = (QT602240State *)opaque;
396     int i;
397
398     if (version_id != 1) {
399         return -EINVAL;
400     }
401
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);
408
409     for (i = 0; i < QT602240_MAX_MESSAGE; i++) {
410         qemu_get_buffer(f, s->queue[i], MESSAGE_SIZE);
411     }
412
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);
418
419     return 0;
420 }
421
422 static int qt602240_init(I2CAddressableState *s)
423 {
424     QT602240State *t = FROM_I2CADDR_SLAVE(QT602240State, s);
425
426     qdev_init_gpio_out(&s->i2c.qdev, &t->irq, 1);
427
428     qemu_add_mouse_event_handler(qt602240_ts_event, t, 1,
429                                  "AT42QT602240 Touchscreen");
430     qt602240_reset(&s->i2c.qdev);
431
432     register_savevm(&s->i2c.qdev, "qt602240", -1, 1,
433                     qt602240_save, qt602240_load, s);
434
435     return 0;
436 }
437
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,
445     .size  = 2,
446     .rev   = 0
447 };
448
449 static void qt602240_register_devices(void)
450 {
451     i2c_addressable_register_device(&qt602240_info);
452 }
453
454 device_init(qt602240_register_devices)