Revision a5d7eb65 hw/tsc210x.c
b/hw/tsc210x.c | ||
---|---|---|
1 | 1 |
/* |
2 | 2 |
* TI TSC2102 (touchscreen/sensors/audio controller) emulator. |
3 |
* TI TSC2301 (touchscreen/sensors/keypad). |
|
3 | 4 |
* |
4 | 5 |
* Copyright (c) 2006 Andrzej Zaborowski <balrog@zabor.org> |
6 |
* Copyright (C) 2008 Nokia Corporation |
|
5 | 7 |
* |
6 | 8 |
* This program is free software; you can redistribute it and/or |
7 | 9 |
* modify it under the terms of the GNU General Public License as |
8 |
* published by the Free Software Foundation; either version 2 of
|
|
9 |
* the License, or (at your option) any later version.
|
|
10 |
* published by the Free Software Foundation; either version 2 or
|
|
11 |
* (at your option) version 3 of the License.
|
|
10 | 12 |
* |
11 | 13 |
* This program is distributed in the hope that it will be useful, |
12 | 14 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
... | ... | |
35 | 37 |
|
36 | 38 |
struct tsc210x_state_s { |
37 | 39 |
qemu_irq pint; |
40 |
qemu_irq kbint; |
|
41 |
qemu_irq davint; |
|
38 | 42 |
QEMUTimer *timer; |
39 | 43 |
QEMUSoundCard card; |
40 | 44 |
struct uwire_slave_s chip; |
41 | 45 |
struct i2s_codec_s codec; |
42 | 46 |
uint8_t in_fifo[16384]; |
43 | 47 |
uint8_t out_fifo[16384]; |
48 |
uint16_t model; |
|
44 | 49 |
|
45 | 50 |
int x, y; |
46 | 51 |
int pressure; |
... | ... | |
64 | 69 |
uint16_t audio_ctrl1; |
65 | 70 |
uint16_t audio_ctrl2; |
66 | 71 |
uint16_t audio_ctrl3; |
67 |
uint16_t pll[2];
|
|
72 |
uint16_t pll[3];
|
|
68 | 73 |
uint16_t volume; |
69 | 74 |
int64_t volume_change; |
70 | 75 |
int softstep; |
... | ... | |
78 | 83 |
int i2s_rx_rate; |
79 | 84 |
int i2s_tx_rate; |
80 | 85 |
AudioState *audio; |
86 |
|
|
87 |
int tr[8]; |
|
88 |
|
|
89 |
struct { |
|
90 |
uint16_t down; |
|
91 |
uint16_t mask; |
|
92 |
int scan; |
|
93 |
int debounce; |
|
94 |
int mode; |
|
95 |
int intr; |
|
96 |
} kb; |
|
81 | 97 |
}; |
82 | 98 |
|
83 | 99 |
static const int resolution[4] = { 12, 8, 10, 12 }; |
... | ... | |
118 | 134 |
0x0000, /* Y+, X- drivers */ |
119 | 135 |
}; |
120 | 136 |
|
121 |
/* |
|
122 |
* Convert screen coordinates to arbitrary values that the |
|
123 |
* touchscreen in my Palm Tungsten E device returns. |
|
124 |
* This shouldn't really matter (because the guest system |
|
125 |
* should calibrate the touchscreen anyway), but let's |
|
126 |
* imitate some real hardware. |
|
127 |
*/ |
|
128 |
#define X_TRANSFORM(value) \ |
|
129 |
((3850 - ((int) (value) * (3850 - 250) / 32768)) << 4) |
|
130 |
#define Y_TRANSFORM(value) \ |
|
131 |
((150 + ((int) (value) * (3037 - 150) / 32768)) << 4) |
|
137 |
#define X_TRANSFORM(s) \ |
|
138 |
((s->y * s->tr[0] - s->x * s->tr[1]) / s->tr[2] + s->tr[3]) |
|
139 |
#define Y_TRANSFORM(s) \ |
|
140 |
((s->y * s->tr[4] - s->x * s->tr[5]) / s->tr[6] + s->tr[7]) |
|
132 | 141 |
#define Z1_TRANSFORM(s) \ |
133 | 142 |
((400 - ((s)->x >> 7) + ((s)->pressure << 10)) << 4) |
134 | 143 |
#define Z2_TRANSFORM(s) \ |
... | ... | |
161 | 170 |
s->audio_ctrl3 = 0x0000; |
162 | 171 |
s->pll[0] = 0x1004; |
163 | 172 |
s->pll[1] = 0x0000; |
173 |
s->pll[2] = 0x1fff; |
|
164 | 174 |
s->volume = 0xffff; |
165 | 175 |
s->dac_power = 0x8540; |
166 | 176 |
s->softstep = 1; |
... | ... | |
190 | 200 |
s->i2s_tx_rate = 0; |
191 | 201 |
s->i2s_rx_rate = 0; |
192 | 202 |
|
203 |
s->kb.scan = 1; |
|
204 |
s->kb.debounce = 0; |
|
205 |
s->kb.mask = 0x0000; |
|
206 |
s->kb.mode = 3; |
|
207 |
s->kb.intr = 0; |
|
208 |
|
|
193 | 209 |
qemu_set_irq(s->pint, !s->irq); |
210 |
qemu_set_irq(s->davint, !s->dav); |
|
211 |
qemu_irq_raise(s->kbint); |
|
194 | 212 |
} |
195 | 213 |
|
196 | 214 |
struct tsc210x_rate_info_s { |
... | ... | |
344 | 362 |
switch (reg) { |
345 | 363 |
case 0x00: /* X */ |
346 | 364 |
s->dav &= 0xfbff; |
347 |
return TSC_CUT_RESOLUTION(X_TRANSFORM(s->x), s->precision) +
|
|
365 |
return TSC_CUT_RESOLUTION(X_TRANSFORM(s), s->precision) + |
|
348 | 366 |
(s->noise & 3); |
349 | 367 |
|
350 | 368 |
case 0x01: /* Y */ |
351 | 369 |
s->noise ++; |
352 | 370 |
s->dav &= 0xfdff; |
353 |
return TSC_CUT_RESOLUTION(Y_TRANSFORM(s->y), s->precision) ^
|
|
371 |
return TSC_CUT_RESOLUTION(Y_TRANSFORM(s), s->precision) ^ |
|
354 | 372 |
(s->noise & 3); |
355 | 373 |
|
356 | 374 |
case 0x02: /* Z1 */ |
... | ... | |
364 | 382 |
(s->noise & 3); |
365 | 383 |
|
366 | 384 |
case 0x04: /* KPData */ |
385 |
if ((s->model & 0xff00) == 0x2300) { |
|
386 |
if (s->kb.intr && (s->kb.mode & 2)) { |
|
387 |
s->kb.intr = 0; |
|
388 |
qemu_irq_raise(s->kbint); |
|
389 |
} |
|
390 |
return s->kb.down; |
|
391 |
} |
|
392 |
|
|
367 | 393 |
return 0xffff; |
368 | 394 |
|
369 | 395 |
case 0x05: /* BAT1 */ |
... | ... | |
414 | 440 |
return (s->pressure << 15) | ((!s->busy) << 14) | |
415 | 441 |
(s->nextfunction << 10) | (s->nextprecision << 8) | s->filter; |
416 | 442 |
|
417 |
case 0x01: /* Status */ |
|
418 |
return (s->pin_func << 14) | ((!s->enabled) << 13) | |
|
419 |
(s->host_mode << 12) | ((!!s->dav) << 11) | s->dav; |
|
443 |
case 0x01: /* Status / Keypad Control */ |
|
444 |
if ((s->model & 0xff00) == 0x2100) |
|
445 |
return (s->pin_func << 14) | ((!s->enabled) << 13) | |
|
446 |
(s->host_mode << 12) | ((!!s->dav) << 11) | s->dav; |
|
447 |
else |
|
448 |
return (s->kb.intr << 15) | ((s->kb.scan || !s->kb.down) << 14) | |
|
449 |
(s->kb.debounce << 11); |
|
450 |
|
|
451 |
case 0x02: /* DAC Control */ |
|
452 |
if ((s->model & 0xff00) == 0x2300) |
|
453 |
return s->dac_power & 0x8000; |
|
454 |
else |
|
455 |
goto bad_reg; |
|
420 | 456 |
|
421 | 457 |
case 0x03: /* Reference */ |
422 | 458 |
return s->ref; |
... | ... | |
427 | 463 |
case 0x05: /* Configuration */ |
428 | 464 |
return s->timing; |
429 | 465 |
|
466 |
case 0x06: /* Secondary configuration */ |
|
467 |
if ((s->model & 0xff00) == 0x2100) |
|
468 |
goto bad_reg; |
|
469 |
return ((!s->dav) << 15) | ((s->kb.mode & 1) << 14) | s->pll[2]; |
|
470 |
|
|
471 |
case 0x10: /* Keypad Mask */ |
|
472 |
if ((s->model & 0xff00) == 0x2100) |
|
473 |
goto bad_reg; |
|
474 |
return s->kb.mask; |
|
475 |
|
|
430 | 476 |
default: |
477 |
bad_reg: |
|
431 | 478 |
#ifdef TSC_VERBOSE |
432 | 479 |
fprintf(stderr, "tsc2102_control_register_read: " |
433 | 480 |
"no such register: 0x%02x\n", reg); |
... | ... | |
556 | 603 |
s->filter = value & 0xff; |
557 | 604 |
return; |
558 | 605 |
|
559 |
case 0x01: /* Status */ |
|
560 |
s->pin_func = value >> 14; |
|
606 |
case 0x01: /* Status / Keypad Control */ |
|
607 |
if ((s->model & 0xff00) == 0x2100) |
|
608 |
s->pin_func = value >> 14; |
|
609 |
else { |
|
610 |
s->kb.scan = (value >> 14) & 1; |
|
611 |
s->kb.debounce = (value >> 11) & 7; |
|
612 |
if (s->kb.intr && s->kb.scan) { |
|
613 |
s->kb.intr = 0; |
|
614 |
qemu_irq_raise(s->kbint); |
|
615 |
} |
|
616 |
} |
|
561 | 617 |
return; |
562 | 618 |
|
619 |
case 0x02: /* DAC Control */ |
|
620 |
if ((s->model & 0xff00) == 0x2300) { |
|
621 |
s->dac_power &= 0x7fff; |
|
622 |
s->dac_power |= 0x8000 & value; |
|
623 |
} else |
|
624 |
goto bad_reg; |
|
625 |
break; |
|
626 |
|
|
563 | 627 |
case 0x03: /* Reference */ |
564 | 628 |
s->ref = value & 0x1f; |
565 | 629 |
return; |
... | ... | |
586 | 650 |
#endif |
587 | 651 |
return; |
588 | 652 |
|
653 |
case 0x06: /* Secondary configuration */ |
|
654 |
if ((s->model & 0xff00) == 0x2100) |
|
655 |
goto bad_reg; |
|
656 |
s->kb.mode = value >> 14; |
|
657 |
s->pll[2] = value & 0x3ffff; |
|
658 |
return; |
|
659 |
|
|
660 |
case 0x10: /* Keypad Mask */ |
|
661 |
if ((s->model & 0xff00) == 0x2100) |
|
662 |
goto bad_reg; |
|
663 |
s->kb.mask = value; |
|
664 |
return; |
|
665 |
|
|
589 | 666 |
default: |
667 |
bad_reg: |
|
590 | 668 |
#ifdef TSC_VERBOSE |
591 | 669 |
fprintf(stderr, "tsc2102_control_register_write: " |
592 | 670 |
"no such register: 0x%02x\n", reg); |
... | ... | |
785 | 863 |
return; |
786 | 864 |
} |
787 | 865 |
|
788 |
if (!s->enabled || s->busy) |
|
866 |
if (!s->enabled || s->busy || s->dav)
|
|
789 | 867 |
return; |
790 | 868 |
|
791 | 869 |
s->busy = 1; |
... | ... | |
805 | 883 |
switch (s->page) { |
806 | 884 |
case TSC_DATA_REGISTERS_PAGE: |
807 | 885 |
ret = tsc2102_data_register_read(s, s->offset); |
886 |
if (!s->dav) |
|
887 |
qemu_irq_raise(s->davint); |
|
808 | 888 |
break; |
809 | 889 |
case TSC_CONTROL_REGISTERS_PAGE: |
810 | 890 |
ret = tsc2102_control_register_read(s, s->offset); |
... | ... | |
859 | 939 |
} |
860 | 940 |
} |
861 | 941 |
|
942 |
uint32_t tsc210x_txrx(void *opaque, uint32_t value) |
|
943 |
{ |
|
944 |
struct tsc210x_state_s *s = opaque; |
|
945 |
uint32_t ret = 0; |
|
946 |
|
|
947 |
/* TODO: sequential reads etc - how do we make sure the host doesn't |
|
948 |
* unintentionally read out a conversion result from a register while |
|
949 |
* transmitting the command word of the next command? */ |
|
950 |
if (!value || (s->state && s->command)) |
|
951 |
ret = tsc210x_read(s); |
|
952 |
if (value || (s->state && !s->command)) |
|
953 |
tsc210x_write(s, value); |
|
954 |
|
|
955 |
return ret; |
|
956 |
} |
|
957 |
|
|
862 | 958 |
static void tsc210x_timer_tick(void *opaque) |
863 | 959 |
{ |
864 | 960 |
struct tsc210x_state_s *s = opaque; |
... | ... | |
871 | 967 |
s->busy = 0; |
872 | 968 |
s->dav |= mode_regs[s->function]; |
873 | 969 |
tsc210x_pin_update(s); |
970 |
qemu_irq_lower(s->davint); |
|
874 | 971 |
} |
875 | 972 |
|
876 | 973 |
static void tsc210x_touchscreen_event(void *opaque, |
... | ... | |
1001 | 1098 |
|
1002 | 1099 |
s->busy = qemu_timer_pending(s->timer); |
1003 | 1100 |
qemu_set_irq(s->pint, !s->irq); |
1101 |
qemu_set_irq(s->davint, !s->dav); |
|
1004 | 1102 |
|
1005 | 1103 |
return 0; |
1006 | 1104 |
} |
... | ... | |
1020 | 1118 |
s->precision = s->nextprecision = 0; |
1021 | 1119 |
s->timer = qemu_new_timer(vm_clock, tsc210x_timer_tick, s); |
1022 | 1120 |
s->pint = pint; |
1121 |
s->model = 0x2102; |
|
1023 | 1122 |
s->name = "tsc2102"; |
1024 | 1123 |
s->audio = audio; |
1025 | 1124 |
|
1125 |
s->tr[0] = 0; |
|
1126 |
s->tr[1] = 1; |
|
1127 |
s->tr[2] = 0; |
|
1128 |
s->tr[3] = 1; |
|
1129 |
s->tr[4] = 1; |
|
1130 |
s->tr[5] = 0; |
|
1131 |
s->tr[6] = 0; |
|
1132 |
s->tr[7] = 1; |
|
1133 |
|
|
1026 | 1134 |
s->chip.opaque = s; |
1027 | 1135 |
s->chip.send = (void *) tsc210x_write; |
1028 | 1136 |
s->chip.receive = (void *) tsc210x_read; |
... | ... | |
1048 | 1156 |
return &s->chip; |
1049 | 1157 |
} |
1050 | 1158 |
|
1159 |
struct uwire_slave_s *tsc2301_init(qemu_irq penirq, qemu_irq kbirq, |
|
1160 |
qemu_irq dav, AudioState *audio) |
|
1161 |
{ |
|
1162 |
struct tsc210x_state_s *s; |
|
1163 |
|
|
1164 |
s = (struct tsc210x_state_s *) |
|
1165 |
qemu_mallocz(sizeof(struct tsc210x_state_s)); |
|
1166 |
memset(s, 0, sizeof(struct tsc210x_state_s)); |
|
1167 |
s->x = 400; |
|
1168 |
s->y = 240; |
|
1169 |
s->pressure = 0; |
|
1170 |
s->precision = s->nextprecision = 0; |
|
1171 |
s->timer = qemu_new_timer(vm_clock, tsc210x_timer_tick, s); |
|
1172 |
s->pint = penirq; |
|
1173 |
s->kbint = kbirq; |
|
1174 |
s->davint = dav; |
|
1175 |
s->model = 0x2301; |
|
1176 |
s->name = "tsc2301"; |
|
1177 |
s->audio = audio; |
|
1178 |
|
|
1179 |
s->tr[0] = 0; |
|
1180 |
s->tr[1] = 1; |
|
1181 |
s->tr[2] = 0; |
|
1182 |
s->tr[3] = 1; |
|
1183 |
s->tr[4] = 1; |
|
1184 |
s->tr[5] = 0; |
|
1185 |
s->tr[6] = 0; |
|
1186 |
s->tr[7] = 1; |
|
1187 |
|
|
1188 |
s->chip.opaque = s; |
|
1189 |
s->chip.send = (void *) tsc210x_write; |
|
1190 |
s->chip.receive = (void *) tsc210x_read; |
|
1191 |
|
|
1192 |
s->codec.opaque = s; |
|
1193 |
s->codec.tx_swallow = (void *) tsc210x_i2s_swallow; |
|
1194 |
s->codec.set_rate = (void *) tsc210x_i2s_set_rate; |
|
1195 |
s->codec.in.fifo = s->in_fifo; |
|
1196 |
s->codec.out.fifo = s->out_fifo; |
|
1197 |
|
|
1198 |
tsc210x_reset(s); |
|
1199 |
|
|
1200 |
qemu_add_mouse_event_handler(tsc210x_touchscreen_event, s, 1, |
|
1201 |
"QEMU TSC2301-driven Touchscreen"); |
|
1202 |
|
|
1203 |
if (s->audio) |
|
1204 |
AUD_register_card(s->audio, s->name, &s->card); |
|
1205 |
|
|
1206 |
qemu_register_reset((void *) tsc210x_reset, s); |
|
1207 |
register_savevm(s->name, tsc2102_iid ++, 0, |
|
1208 |
tsc210x_save, tsc210x_load, s); |
|
1209 |
|
|
1210 |
return &s->chip; |
|
1211 |
} |
|
1212 |
|
|
1051 | 1213 |
struct i2s_codec_s *tsc210x_codec(struct uwire_slave_s *chip) |
1052 | 1214 |
{ |
1053 | 1215 |
struct tsc210x_state_s *s = (struct tsc210x_state_s *) chip->opaque; |
1054 | 1216 |
|
1055 | 1217 |
return &s->codec; |
1056 | 1218 |
} |
1219 |
|
|
1220 |
/* |
|
1221 |
* Use tslib generated calibration data to generate ADC input values |
|
1222 |
* from the touchscreen. Assuming 12-bit precision was used during |
|
1223 |
* tslib calibration. |
|
1224 |
*/ |
|
1225 |
void tsc210x_set_transform(struct uwire_slave_s *chip, |
|
1226 |
struct mouse_transform_info_s *info) |
|
1227 |
{ |
|
1228 |
struct tsc210x_state_s *s = (struct tsc210x_state_s *) chip->opaque; |
|
1229 |
#if 0 |
|
1230 |
int64_t ltr[8]; |
|
1231 |
|
|
1232 |
ltr[0] = (int64_t) info->a[1] * info->y; |
|
1233 |
ltr[1] = (int64_t) info->a[4] * info->x; |
|
1234 |
ltr[2] = (int64_t) info->a[1] * info->a[3] - |
|
1235 |
(int64_t) info->a[4] * info->a[0]; |
|
1236 |
ltr[3] = (int64_t) info->a[2] * info->a[4] - |
|
1237 |
(int64_t) info->a[5] * info->a[1]; |
|
1238 |
ltr[4] = (int64_t) info->a[0] * info->y; |
|
1239 |
ltr[5] = (int64_t) info->a[3] * info->x; |
|
1240 |
ltr[6] = (int64_t) info->a[4] * info->a[0] - |
|
1241 |
(int64_t) info->a[1] * info->a[3]; |
|
1242 |
ltr[7] = (int64_t) info->a[2] * info->a[3] - |
|
1243 |
(int64_t) info->a[5] * info->a[0]; |
|
1244 |
|
|
1245 |
/* Avoid integer overflow */ |
|
1246 |
s->tr[0] = ltr[0] >> 11; |
|
1247 |
s->tr[1] = ltr[1] >> 11; |
|
1248 |
s->tr[2] = muldiv64(ltr[2], 1, info->a[6]); |
|
1249 |
s->tr[3] = muldiv64(ltr[3], 1 << 4, ltr[2]); |
|
1250 |
s->tr[4] = ltr[4] >> 11; |
|
1251 |
s->tr[5] = ltr[5] >> 11; |
|
1252 |
s->tr[6] = muldiv64(ltr[6], 1, info->a[6]); |
|
1253 |
s->tr[7] = muldiv64(ltr[7], 1 << 4, ltr[6]); |
|
1254 |
#else |
|
1255 |
|
|
1256 |
/* This version assumes touchscreen X & Y axis are parallel or |
|
1257 |
* perpendicular to LCD's X & Y axis in some way. */ |
|
1258 |
if (abs(info->a[0]) > abs(info->a[1])) { |
|
1259 |
s->tr[0] = 0; |
|
1260 |
s->tr[1] = -info->a[6] * info->x; |
|
1261 |
s->tr[2] = info->a[0]; |
|
1262 |
s->tr[3] = -info->a[2] / info->a[0]; |
|
1263 |
s->tr[4] = info->a[6] * info->y; |
|
1264 |
s->tr[5] = 0; |
|
1265 |
s->tr[6] = info->a[4]; |
|
1266 |
s->tr[7] = -info->a[5] / info->a[4]; |
|
1267 |
} else { |
|
1268 |
s->tr[0] = info->a[6] * info->y; |
|
1269 |
s->tr[1] = 0; |
|
1270 |
s->tr[2] = info->a[1]; |
|
1271 |
s->tr[3] = -info->a[2] / info->a[1]; |
|
1272 |
s->tr[4] = 0; |
|
1273 |
s->tr[5] = -info->a[6] * info->x; |
|
1274 |
s->tr[6] = info->a[3]; |
|
1275 |
s->tr[7] = -info->a[5] / info->a[3]; |
|
1276 |
} |
|
1277 |
|
|
1278 |
s->tr[0] >>= 11; |
|
1279 |
s->tr[1] >>= 11; |
|
1280 |
s->tr[3] <<= 4; |
|
1281 |
s->tr[4] >>= 11; |
|
1282 |
s->tr[5] >>= 11; |
|
1283 |
s->tr[7] <<= 4; |
|
1284 |
#endif |
|
1285 |
} |
|
1286 |
|
|
1287 |
void tsc210x_key_event(struct uwire_slave_s *chip, int key, int down) |
|
1288 |
{ |
|
1289 |
struct tsc210x_state_s *s = (struct tsc210x_state_s *) chip->opaque; |
|
1290 |
|
|
1291 |
if (down) |
|
1292 |
s->kb.down |= 1 << key; |
|
1293 |
else |
|
1294 |
s->kb.down &= ~(1 << key); |
|
1295 |
|
|
1296 |
if (down && (s->kb.down & ~s->kb.mask) && !s->kb.intr) { |
|
1297 |
s->kb.intr = 1; |
|
1298 |
qemu_irq_lower(s->kbint); |
|
1299 |
} else if (s->kb.intr && !(s->kb.down & ~s->kb.mask) && |
|
1300 |
!(s->kb.mode & 1)) { |
|
1301 |
s->kb.intr = 0; |
|
1302 |
qemu_irq_raise(s->kbint); |
|
1303 |
} |
|
1304 |
} |
Also available in: Unified diff