Commit a5d7eb6534a091566d63f97c8b35c0ac9623d90b
1 parent
827df9f3
Add TSC2301 touchscreen & keypad controller.
Add also a facility for setting up touchscreen calibration data per-board based on calibration data generated by tslib. git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@4214 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
4 changed files
with
290 additions
and
28 deletions
console.h
... | ... | @@ -32,6 +32,14 @@ void kbd_put_keycode(int keycode); |
32 | 32 | void kbd_mouse_event(int dx, int dy, int dz, int buttons_state); |
33 | 33 | int kbd_mouse_is_absolute(void); |
34 | 34 | |
35 | +struct mouse_transform_info_s { | |
36 | + /* Touchscreen resolution */ | |
37 | + int x; | |
38 | + int y; | |
39 | + /* Calibration values as used/generated by tslib */ | |
40 | + int a[7]; | |
41 | +}; | |
42 | + | |
35 | 43 | void do_info_mice(void); |
36 | 44 | void do_mouse_set(int index); |
37 | 45 | ... | ... |
hw/devices.h
... | ... | @@ -16,6 +16,18 @@ uint32_t ads7846_read(void *opaque); |
16 | 16 | void ads7846_write(void *opaque, uint32_t value); |
17 | 17 | struct ads7846_state_s *ads7846_init(qemu_irq penirq); |
18 | 18 | |
19 | +/* tsc210x.c */ | |
20 | +struct uwire_slave_s; | |
21 | +struct mouse_transform_info_s; | |
22 | +struct uwire_slave_s *tsc2102_init(qemu_irq pint, AudioState *audio); | |
23 | +struct uwire_slave_s *tsc2301_init(qemu_irq penirq, qemu_irq kbirq, | |
24 | + qemu_irq dav, AudioState *audio); | |
25 | +struct i2s_codec_s *tsc210x_codec(struct uwire_slave_s *chip); | |
26 | +uint32_t tsc210x_txrx(void *opaque, uint32_t value); | |
27 | +void tsc210x_set_transform(struct uwire_slave_s *chip, | |
28 | + struct mouse_transform_info_s *info); | |
29 | +void tsc210x_key_event(struct uwire_slave_s *chip, int key, int down); | |
30 | + | |
19 | 31 | /* stellaris_input.c */ |
20 | 32 | void stellaris_gamepad_init(int n, qemu_irq *irq, const int *keycode); |
21 | 33 | ... | ... |
hw/omap.h
... | ... | @@ -1125,10 +1125,4 @@ inline static int debug_register_io_memory(int io_index, |
1125 | 1125 | # define cpu_register_io_memory debug_register_io_memory |
1126 | 1126 | # endif |
1127 | 1127 | |
1128 | -/* Not really omap specific, but is the only thing that uses the | |
1129 | - uwire interface. */ | |
1130 | -/* tsc210x.c */ | |
1131 | -struct uwire_slave_s *tsc2102_init(qemu_irq pint, AudioState *audio); | |
1132 | -struct i2s_codec_s *tsc210x_codec(struct uwire_slave_s *chip); | |
1133 | - | |
1134 | 1128 | #endif /* hw_omap_h */ | ... | ... |
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,12 +37,15 @@ |
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,7 +69,7 @@ struct tsc210x_state_s { |
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,6 +83,17 @@ struct tsc210x_state_s { |
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,17 +134,10 @@ static const uint16_t mode_regs[16] = { |
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,6 +170,7 @@ static void tsc210x_reset(struct tsc210x_state_s *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,7 +200,15 @@ static void tsc210x_reset(struct tsc210x_state_s *s) |
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,13 +362,13 @@ static uint16_t tsc2102_data_register_read(struct tsc210x_state_s *s, int reg) |
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,6 +382,14 @@ static uint16_t tsc2102_data_register_read(struct tsc210x_state_s *s, int reg) |
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,9 +440,19 @@ static uint16_t tsc2102_control_register_read( |
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,7 +463,18 @@ static uint16_t tsc2102_control_register_read( |
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,10 +603,27 @@ static void tsc2102_control_register_write( |
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,7 +650,21 @@ static void tsc2102_control_register_write( |
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,7 +863,7 @@ static void tsc210x_pin_update(struct tsc210x_state_s *s) |
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,6 +883,8 @@ static uint16_t tsc210x_read(struct tsc210x_state_s *s) |
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,6 +939,22 @@ static void tsc210x_write(struct tsc210x_state_s *s, uint16_t value) |
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,6 +967,7 @@ static void tsc210x_timer_tick(void *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,6 +1098,7 @@ static int tsc210x_load(QEMUFile *f, void *opaque, int version_id) |
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,9 +1118,19 @@ struct uwire_slave_s *tsc2102_init(qemu_irq pint, AudioState *audio) |
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,9 +1156,149 @@ struct uwire_slave_s *tsc2102_init(qemu_irq pint, AudioState *audio) |
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 | +} | ... | ... |