]>
Commit | Line | Data |
---|---|---|
fd5a3b33 | 1 | /* |
87283515 | 2 | * TI ADS7846 / TSC2046 chip emulation. |
fd5a3b33 AZ |
3 | * |
4 | * Copyright (c) 2006 Openedhand Ltd. | |
5 | * Written by Andrzej Zaborowski <balrog@zabor.org> | |
6 | * | |
7 | * This code is licensed under the GNU GPL v2. | |
6b620ca3 PB |
8 | * |
9 | * Contributions after 2012-01-13 are licensed under the terms of the | |
10 | * GNU GPL, version 2 or (at your option) any later version. | |
fd5a3b33 AZ |
11 | */ |
12 | ||
47df5154 | 13 | #include "qemu/osdep.h" |
64552b6b | 14 | #include "hw/irq.h" |
8fd06719 | 15 | #include "hw/ssi/ssi.h" |
d6454270 | 16 | #include "migration/vmstate.h" |
0b8fa32f | 17 | #include "qemu/module.h" |
28ecbaee | 18 | #include "ui/console.h" |
db1015e9 | 19 | #include "qom/object.h" |
fd5a3b33 | 20 | |
db1015e9 | 21 | struct ADS7846State { |
ec7e429b | 22 | SSIPeripheral ssidev; |
fd5a3b33 AZ |
23 | qemu_irq interrupt; |
24 | ||
25 | int input[8]; | |
26 | int pressure; | |
27 | int noise; | |
28 | ||
29 | int cycle; | |
30 | int output; | |
db1015e9 | 31 | }; |
fd5a3b33 | 32 | |
213f63df | 33 | #define TYPE_ADS7846 "ads7846" |
8063396b | 34 | OBJECT_DECLARE_SIMPLE_TYPE(ADS7846State, ADS7846) |
213f63df | 35 | |
fd5a3b33 | 36 | /* Control-byte bitfields */ |
48805df9 YF |
37 | #define CB_PD0 (1 << 0) |
38 | #define CB_PD1 (1 << 1) | |
39 | #define CB_SER (1 << 2) | |
40 | #define CB_MODE (1 << 3) | |
41 | #define CB_A0 (1 << 4) | |
42 | #define CB_A1 (1 << 5) | |
43 | #define CB_A2 (1 << 6) | |
44 | #define CB_START (1 << 7) | |
45 | ||
46 | #define X_AXIS_DMAX 3470 | |
47 | #define X_AXIS_MIN 290 | |
48 | #define Y_AXIS_DMAX 3450 | |
49 | #define Y_AXIS_MIN 200 | |
50 | ||
51 | #define ADS_VBAT 2000 | |
52 | #define ADS_VAUX 2000 | |
53 | #define ADS_TEMP0 2000 | |
54 | #define ADS_TEMP1 3000 | |
55 | #define ADS_XPOS(x, y) (X_AXIS_MIN + ((X_AXIS_DMAX * (x)) >> 15)) | |
56 | #define ADS_YPOS(x, y) (Y_AXIS_MIN + ((Y_AXIS_DMAX * (y)) >> 15)) | |
57 | #define ADS_Z1POS(x, y) 600 | |
58 | #define ADS_Z2POS(x, y) (600 + 6000 / ADS_XPOS(x, y)) | |
fd5a3b33 | 59 | |
bc24a225 | 60 | static void ads7846_int_update(ADS7846State *s) |
fd5a3b33 AZ |
61 | { |
62 | if (s->interrupt) | |
63 | qemu_set_irq(s->interrupt, s->pressure == 0); | |
64 | } | |
65 | ||
ec7e429b | 66 | static uint32_t ads7846_transfer(SSIPeripheral *dev, uint32_t value) |
fd5a3b33 | 67 | { |
213f63df | 68 | ADS7846State *s = ADS7846(dev); |
fd5a3b33 AZ |
69 | |
70 | switch (s->cycle ++) { | |
71 | case 0: | |
72 | if (!(value & CB_START)) { | |
73 | s->cycle = 0; | |
74 | break; | |
75 | } | |
76 | ||
77 | s->output = s->input[(value >> 4) & 7]; | |
78 | ||
79 | /* Imitate the ADC noise, some drivers expect this. */ | |
80 | s->noise = (s->noise + 3) & 7; | |
81 | switch ((value >> 4) & 7) { | |
82 | case 1: s->output += s->noise ^ 2; break; | |
83 | case 3: s->output += s->noise ^ 0; break; | |
84 | case 4: s->output += s->noise ^ 7; break; | |
85 | case 5: s->output += s->noise ^ 5; break; | |
86 | } | |
87 | ||
88 | if (value & CB_MODE) | |
48805df9 | 89 | s->output >>= 4; /* 8 bits instead of 12 */ |
fd5a3b33 AZ |
90 | |
91 | break; | |
92 | case 1: | |
93 | s->cycle = 0; | |
94 | break; | |
95 | } | |
a984a69e | 96 | return s->output; |
fd5a3b33 AZ |
97 | } |
98 | ||
99 | static void ads7846_ts_event(void *opaque, | |
100 | int x, int y, int z, int buttons_state) | |
101 | { | |
bc24a225 | 102 | ADS7846State *s = opaque; |
fd5a3b33 AZ |
103 | |
104 | if (buttons_state) { | |
611d7189 AZ |
105 | x = 0x7fff - x; |
106 | s->input[1] = ADS_XPOS(x, y); | |
fd5a3b33 AZ |
107 | s->input[3] = ADS_Z1POS(x, y); |
108 | s->input[4] = ADS_Z2POS(x, y); | |
611d7189 | 109 | s->input[5] = ADS_YPOS(x, y); |
fd5a3b33 AZ |
110 | } |
111 | ||
112 | if (s->pressure == !buttons_state) { | |
113 | s->pressure = !!buttons_state; | |
114 | ||
aa941b94 | 115 | ads7846_int_update(s); |
fd5a3b33 AZ |
116 | } |
117 | } | |
118 | ||
aefe2129 | 119 | static int ads7856_post_load(void *opaque, int version_id) |
aa941b94 | 120 | { |
aefe2129 | 121 | ADS7846State *s = opaque; |
aa941b94 AZ |
122 | |
123 | s->pressure = 0; | |
124 | ads7846_int_update(s); | |
aa941b94 AZ |
125 | return 0; |
126 | } | |
127 | ||
aefe2129 JQ |
128 | static const VMStateDescription vmstate_ads7846 = { |
129 | .name = "ads7846", | |
66530953 PC |
130 | .version_id = 1, |
131 | .minimum_version_id = 1, | |
aefe2129 | 132 | .post_load = ads7856_post_load, |
af0f07df | 133 | .fields = (const VMStateField[]) { |
ec7e429b | 134 | VMSTATE_SSI_PERIPHERAL(ssidev, ADS7846State), |
aefe2129 JQ |
135 | VMSTATE_INT32_ARRAY(input, ADS7846State, 8), |
136 | VMSTATE_INT32(noise, ADS7846State), | |
137 | VMSTATE_INT32(cycle, ADS7846State), | |
138 | VMSTATE_INT32(output, ADS7846State), | |
139 | VMSTATE_END_OF_LIST() | |
140 | } | |
141 | }; | |
142 | ||
ec7e429b | 143 | static void ads7846_realize(SSIPeripheral *d, Error **errp) |
fd5a3b33 | 144 | { |
1a7d9ee6 | 145 | DeviceState *dev = DEVICE(d); |
213f63df | 146 | ADS7846State *s = ADS7846(d); |
fd5a3b33 | 147 | |
1a7d9ee6 | 148 | qdev_init_gpio_out(dev, &s->interrupt, 1); |
fd5a3b33 | 149 | |
48805df9 YF |
150 | s->input[0] = ADS_TEMP0; /* TEMP0 */ |
151 | s->input[2] = ADS_VBAT; /* VBAT */ | |
152 | s->input[6] = ADS_VAUX; /* VAUX */ | |
153 | s->input[7] = ADS_TEMP1; /* TEMP1 */ | |
fd5a3b33 AZ |
154 | |
155 | /* We want absolute coordinates */ | |
156 | qemu_add_mouse_event_handler(ads7846_ts_event, s, 1, | |
157 | "QEMU ADS7846-driven Touchscreen"); | |
158 | ||
159 | ads7846_int_update(s); | |
aa941b94 | 160 | |
99b16e8e | 161 | vmstate_register_any(NULL, &vmstate_ads7846, s); |
a984a69e | 162 | } |
aa941b94 | 163 | |
cd6c4cf2 AL |
164 | static void ads7846_class_init(ObjectClass *klass, void *data) |
165 | { | |
be3701ea | 166 | DeviceClass *dc = DEVICE_CLASS(klass); |
ec7e429b | 167 | SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass); |
cd6c4cf2 | 168 | |
7673bb4c | 169 | k->realize = ads7846_realize; |
cd6c4cf2 | 170 | k->transfer = ads7846_transfer; |
be3701ea | 171 | set_bit(DEVICE_CATEGORY_INPUT, dc->categories); |
cd6c4cf2 AL |
172 | } |
173 | ||
8c43a6f0 | 174 | static const TypeInfo ads7846_info = { |
213f63df | 175 | .name = TYPE_ADS7846, |
ec7e429b | 176 | .parent = TYPE_SSI_PERIPHERAL, |
39bffca2 AL |
177 | .instance_size = sizeof(ADS7846State), |
178 | .class_init = ads7846_class_init, | |
a984a69e PB |
179 | }; |
180 | ||
83f7d43a | 181 | static void ads7846_register_types(void) |
a984a69e | 182 | { |
39bffca2 | 183 | type_register_static(&ads7846_info); |
fd5a3b33 | 184 | } |
a984a69e | 185 | |
83f7d43a | 186 | type_init(ads7846_register_types) |