4
* Copyright (c) 2006 Andrzej Zaborowski <balrog@zabor.org>
5
* Copyright (C) 2008 Nokia Corporation
7
* This program is free software; you can redistribute it and/or
8
* modify it under the terms of the GNU General Public License as
9
* published by the Free Software Foundation; either version 2 or
10
* (at your option) version 3 of the License.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
24
#include "qemu-timer.h"
28
#define TSC_CUT_RESOLUTION(value, p) ((value) >> (16 - (p ? 12 : 10)))
30
struct tsc2005_state_s {
31
qemu_irq pint; /* Combination of the nPENIRQ and DAV signals */
38
int state, reg, irq, command;
62
TSC_MODE_XYZ_SCAN = 0x0,
80
static const uint16_t mode_regs[16] = {
81
0xf000, /* X, Y, Z scan */
82
0xc000, /* X, Y scan */
89
0x0800, /* AUX scan */
92
0x0080, /* Short-circuit test */
93
0x0000, /* Reserved */
94
0x0000, /* X+, X- drivers */
95
0x0000, /* Y+, Y- drivers */
96
0x0000, /* Y+, X- drivers */
99
#define X_TRANSFORM(s) \
100
((s->y * s->tr[0] - s->x * s->tr[1]) / s->tr[2] + s->tr[3])
101
#define Y_TRANSFORM(s) \
102
((s->y * s->tr[4] - s->x * s->tr[5]) / s->tr[6] + s->tr[7])
103
#define Z1_TRANSFORM(s) \
104
((400 - ((s)->x >> 7) + ((s)->pressure << 10)) << 4)
105
#define Z2_TRANSFORM(s) \
106
((4000 + ((s)->y >> 7) - ((s)->pressure << 10)) << 4)
108
#define AUX_VAL (700 << 4) /* +/- 3 at 12-bit */
109
#define TEMP1_VAL (1264 << 4) /* +/- 5 at 12-bit */
110
#define TEMP2_VAL (1531 << 4) /* +/- 5 at 12-bit */
112
static uint16_t tsc2005_read(struct tsc2005_state_s *s, int reg)
118
s->dav &= ~mode_regs[TSC_MODE_X];
119
return TSC_CUT_RESOLUTION(X_TRANSFORM(s), s->precision) +
122
s->dav &= ~mode_regs[TSC_MODE_Y];
124
return TSC_CUT_RESOLUTION(Y_TRANSFORM(s), s->precision) ^
128
return TSC_CUT_RESOLUTION(Z1_TRANSFORM(s), s->precision) -
132
return TSC_CUT_RESOLUTION(Z2_TRANSFORM(s), s->precision) |
136
s->dav &= ~mode_regs[TSC_MODE_AUX];
137
return TSC_CUT_RESOLUTION(AUX_VAL, s->precision);
139
case 0x5: /* TEMP1 */
140
s->dav &= ~mode_regs[TSC_MODE_TEMP1];
141
return TSC_CUT_RESOLUTION(TEMP1_VAL, s->precision) -
143
case 0x6: /* TEMP2 */
145
s->dav &= ~mode_regs[TSC_MODE_TEMP2];
146
return TSC_CUT_RESOLUTION(TEMP2_VAL, s->precision) ^
149
case 0x7: /* Status */
150
ret = s->dav | (s->reset << 7) | (s->pdst << 2) | 0x0;
151
s->dav &= ~(mode_regs[TSC_MODE_X_TEST] | mode_regs[TSC_MODE_Y_TEST] |
152
mode_regs[TSC_MODE_TS_TEST]);
156
case 0x8: /* AUX high treshold */
157
return s->aux_thr[1];
158
case 0x9: /* AUX low treshold */
159
return s->aux_thr[0];
161
case 0xa: /* TEMP high treshold */
162
return s->temp_thr[1];
163
case 0xb: /* TEMP low treshold */
164
return s->temp_thr[0];
167
return (s->pressure << 15) | ((!s->busy) << 14) |
168
(s->nextprecision << 13) | s->timing[0];
172
return (s->pin_func << 14) | s->filter;
174
case 0xf: /* Function select status */
175
return s->function >= 0 ? 1 << s->function : 0;
178
/* Never gets here */
182
static void tsc2005_write(struct tsc2005_state_s *s, int reg, uint16_t data)
185
case 0x8: /* AUX high treshold */
186
s->aux_thr[1] = data;
188
case 0x9: /* AUX low treshold */
189
s->aux_thr[0] = data;
192
case 0xa: /* TEMP high treshold */
193
s->temp_thr[1] = data;
195
case 0xb: /* TEMP low treshold */
196
s->temp_thr[0] = data;
200
s->host_mode = data >> 15;
201
if (s->enabled != !(data & 0x4000)) {
202
s->enabled = !(data & 0x4000);
203
fprintf(stderr, "%s: touchscreen sense %sabled\n",
204
__FUNCTION__, s->enabled ? "en" : "dis");
205
if (s->busy && !s->enabled)
206
qemu_del_timer(s->timer);
207
s->busy &= s->enabled;
209
s->nextprecision = (data >> 13) & 1;
210
s->timing[0] = data & 0x1fff;
211
if ((s->timing[0] >> 11) == 3)
212
fprintf(stderr, "%s: illegal conversion clock setting\n",
216
s->timing[1] = data & 0xf07;
219
s->pin_func = (data >> 14) & 3;
220
s->filter = data & 0x3fff;
224
fprintf(stderr, "%s: write into read-only register %x\n",
229
/* This handles most of the chip's logic. */
230
static void tsc2005_pin_update(struct tsc2005_state_s *s)
235
switch (s->pin_func) {
237
pin_state = !s->pressure && !!s->dav;
245
pin_state = !s->pressure;
248
if (pin_state != s->irq) {
250
qemu_set_irq(s->pint, s->irq);
253
switch (s->nextfunction) {
254
case TSC_MODE_XYZ_SCAN:
255
case TSC_MODE_XY_SCAN:
256
if (!s->host_mode && s->dav)
261
case TSC_MODE_AUX_SCAN:
273
case TSC_MODE_X_TEST:
274
case TSC_MODE_Y_TEST:
275
case TSC_MODE_TS_TEST:
280
case TSC_MODE_RESERVED:
281
case TSC_MODE_XX_DRV:
282
case TSC_MODE_YY_DRV:
283
case TSC_MODE_YX_DRV:
288
if (!s->enabled || s->busy)
292
s->precision = s->nextprecision;
293
s->function = s->nextfunction;
294
s->pdst = !s->pnd0; /* Synchronised on internal clock */
295
expires = qemu_get_clock(vm_clock) + (ticks_per_sec >> 7);
296
qemu_mod_timer(s->timer, expires);
299
static void tsc2005_reset(struct tsc2005_state_s *s)
305
s->nextprecision = 0;
315
s->temp_thr[0] = 0x000;
316
s->temp_thr[1] = 0xfff;
317
s->aux_thr[0] = 0x000;
318
s->aux_thr[1] = 0xfff;
320
tsc2005_pin_update(s);
323
static uint8_t tsc2005_txrx_word(void *opaque, uint8_t value)
325
struct tsc2005_state_s *s = opaque;
328
switch (s->state ++) {
332
if (value & (1 << 1))
335
s->nextfunction = (value >> 3) & 0xf;
336
s->nextprecision = (value >> 2) & 1;
337
if (s->enabled != !(value & 1)) {
338
s->enabled = !(value & 1);
339
fprintf(stderr, "%s: touchscreen sense %sabled\n",
340
__FUNCTION__, s->enabled ? "en" : "dis");
341
if (s->busy && !s->enabled)
342
qemu_del_timer(s->timer);
343
s->busy &= s->enabled;
345
tsc2005_pin_update(s);
351
s->reg = (value >> 3) & 0xf;
352
s->pnd0 = (value >> 1) & 1;
353
s->command = value & 1;
357
s->data = tsc2005_read(s, s->reg);
358
tsc2005_pin_update(s);
367
ret = (s->data >> 8) & 0xff;
369
s->data |= value << 8;
374
ret = s->data & 0xff;
377
tsc2005_write(s, s->reg, s->data);
378
tsc2005_pin_update(s);
388
uint32_t tsc2005_txrx(void *opaque, uint32_t value, int len)
395
ret |= tsc2005_txrx_word(opaque, (value >> len) & 0xff) << len;
401
static void tsc2005_timer_tick(void *opaque)
403
struct tsc2005_state_s *s = opaque;
405
/* Timer ticked -- a set of conversions has been finished. */
411
s->dav |= mode_regs[s->function];
413
tsc2005_pin_update(s);
416
static void tsc2005_touchscreen_event(void *opaque,
417
int x, int y, int z, int buttons_state)
419
struct tsc2005_state_s *s = opaque;
426
s->pressure = !!buttons_state;
429
* Note: We would get better responsiveness in the guest by
430
* signaling TS events immediately, but for now we simulate
431
* the first conversion delay for sake of correctness.
433
if (p != s->pressure)
434
tsc2005_pin_update(s);
437
static void tsc2005_save(QEMUFile *f, void *opaque)
439
struct tsc2005_state_s *s = (struct tsc2005_state_s *) opaque;
442
qemu_put_be16(f, s->x);
443
qemu_put_be16(f, s->y);
444
qemu_put_byte(f, s->pressure);
446
qemu_put_byte(f, s->state);
447
qemu_put_byte(f, s->reg);
448
qemu_put_byte(f, s->command);
450
qemu_put_byte(f, s->irq);
451
qemu_put_be16s(f, &s->dav);
452
qemu_put_be16s(f, &s->data);
454
qemu_put_timer(f, s->timer);
455
qemu_put_byte(f, s->enabled);
456
qemu_put_byte(f, s->host_mode);
457
qemu_put_byte(f, s->function);
458
qemu_put_byte(f, s->nextfunction);
459
qemu_put_byte(f, s->precision);
460
qemu_put_byte(f, s->nextprecision);
461
qemu_put_be16(f, s->filter);
462
qemu_put_byte(f, s->pin_func);
463
qemu_put_be16(f, s->timing[0]);
464
qemu_put_be16(f, s->timing[1]);
465
qemu_put_be16s(f, &s->temp_thr[0]);
466
qemu_put_be16s(f, &s->temp_thr[1]);
467
qemu_put_be16s(f, &s->aux_thr[0]);
468
qemu_put_be16s(f, &s->aux_thr[1]);
469
qemu_put_be32(f, s->noise);
470
qemu_put_byte(f, s->reset);
471
qemu_put_byte(f, s->pdst);
472
qemu_put_byte(f, s->pnd0);
474
for (i = 0; i < 8; i ++)
475
qemu_put_be32(f, s->tr[i]);
478
static int tsc2005_load(QEMUFile *f, void *opaque, int version_id)
480
struct tsc2005_state_s *s = (struct tsc2005_state_s *) opaque;
483
s->x = qemu_get_be16(f);
484
s->y = qemu_get_be16(f);
485
s->pressure = qemu_get_byte(f);
487
s->state = qemu_get_byte(f);
488
s->reg = qemu_get_byte(f);
489
s->command = qemu_get_byte(f);
491
s->irq = qemu_get_byte(f);
492
qemu_get_be16s(f, &s->dav);
493
qemu_get_be16s(f, &s->data);
495
qemu_get_timer(f, s->timer);
496
s->enabled = qemu_get_byte(f);
497
s->host_mode = qemu_get_byte(f);
498
s->function = qemu_get_byte(f);
499
s->nextfunction = qemu_get_byte(f);
500
s->precision = qemu_get_byte(f);
501
s->nextprecision = qemu_get_byte(f);
502
s->filter = qemu_get_be16(f);
503
s->pin_func = qemu_get_byte(f);
504
s->timing[0] = qemu_get_be16(f);
505
s->timing[1] = qemu_get_be16(f);
506
qemu_get_be16s(f, &s->temp_thr[0]);
507
qemu_get_be16s(f, &s->temp_thr[1]);
508
qemu_get_be16s(f, &s->aux_thr[0]);
509
qemu_get_be16s(f, &s->aux_thr[1]);
510
s->noise = qemu_get_be32(f);
511
s->reset = qemu_get_byte(f);
512
s->pdst = qemu_get_byte(f);
513
s->pnd0 = qemu_get_byte(f);
515
for (i = 0; i < 8; i ++)
516
s->tr[i] = qemu_get_be32(f);
518
s->busy = qemu_timer_pending(s->timer);
519
tsc2005_pin_update(s);
524
void *tsc2005_init(qemu_irq pintdav)
526
struct tsc2005_state_s *s;
528
s = (struct tsc2005_state_s *)
529
qemu_mallocz(sizeof(struct tsc2005_state_s));
533
s->precision = s->nextprecision = 0;
534
s->timer = qemu_new_timer(vm_clock, tsc2005_timer_tick, s);
549
qemu_add_mouse_event_handler(tsc2005_touchscreen_event, s, 1,
550
"QEMU TSC2005-driven Touchscreen");
552
qemu_register_reset((void *) tsc2005_reset, s);
553
register_savevm("tsc2005", -1, 0, tsc2005_save, tsc2005_load, s);
559
* Use tslib generated calibration data to generate ADC input values
560
* from the touchscreen. Assuming 12-bit precision was used during
563
void tsc2005_set_transform(void *opaque, struct mouse_transform_info_s *info)
565
struct tsc2005_state_s *s = (struct tsc2005_state_s *) opaque;
567
/* This version assumes touchscreen X & Y axis are parallel or
568
* perpendicular to LCD's X & Y axis in some way. */
569
if (abs(info->a[0]) > abs(info->a[1])) {
571
s->tr[1] = -info->a[6] * info->x;
572
s->tr[2] = info->a[0];
573
s->tr[3] = -info->a[2] / info->a[0];
574
s->tr[4] = info->a[6] * info->y;
576
s->tr[6] = info->a[4];
577
s->tr[7] = -info->a[5] / info->a[4];
579
s->tr[0] = info->a[6] * info->y;
581
s->tr[2] = info->a[1];
582
s->tr[3] = -info->a[2] / info->a[1];
584
s->tr[5] = -info->a[6] * info->x;
585
s->tr[6] = info->a[3];
586
s->tr[7] = -info->a[5] / info->a[3];