]>
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 <[email protected]> | |
6 | * | |
7 | * This code is licensed under the GNU GPL v2. | |
8 | */ | |
9 | ||
87ecb68b PB |
10 | #include "hw.h" |
11 | #include "devices.h" | |
12 | #include "console.h" | |
fd5a3b33 AZ |
13 | |
14 | struct ads7846_state_s { | |
15 | qemu_irq interrupt; | |
16 | ||
17 | int input[8]; | |
18 | int pressure; | |
19 | int noise; | |
20 | ||
21 | int cycle; | |
22 | int output; | |
23 | }; | |
24 | ||
25 | /* Control-byte bitfields */ | |
26 | #define CB_PD0 (1 << 0) | |
27 | #define CB_PD1 (1 << 1) | |
28 | #define CB_SER (1 << 2) | |
29 | #define CB_MODE (1 << 3) | |
30 | #define CB_A0 (1 << 4) | |
31 | #define CB_A1 (1 << 5) | |
32 | #define CB_A2 (1 << 6) | |
33 | #define CB_START (1 << 7) | |
34 | ||
611d7189 AZ |
35 | #define X_AXIS_DMAX 3470 |
36 | #define X_AXIS_MIN 290 | |
37 | #define Y_AXIS_DMAX 3450 | |
38 | #define Y_AXIS_MIN 200 | |
fd5a3b33 AZ |
39 | |
40 | #define ADS_VBAT 2000 | |
41 | #define ADS_VAUX 2000 | |
42 | #define ADS_TEMP0 2000 | |
43 | #define ADS_TEMP1 3000 | |
44 | #define ADS_XPOS(x, y) (X_AXIS_MIN + ((X_AXIS_DMAX * (x)) >> 15)) | |
45 | #define ADS_YPOS(x, y) (Y_AXIS_MIN + ((Y_AXIS_DMAX * (y)) >> 15)) | |
46 | #define ADS_Z1POS(x, y) 600 | |
47 | #define ADS_Z2POS(x, y) (600 + 6000 / ADS_XPOS(x, y)) | |
48 | ||
49 | static void ads7846_int_update(struct ads7846_state_s *s) | |
50 | { | |
51 | if (s->interrupt) | |
52 | qemu_set_irq(s->interrupt, s->pressure == 0); | |
53 | } | |
54 | ||
55 | uint32_t ads7846_read(void *opaque) | |
56 | { | |
57 | struct ads7846_state_s *s = (struct ads7846_state_s *) opaque; | |
58 | ||
59 | return s->output; | |
60 | } | |
61 | ||
62 | void ads7846_write(void *opaque, uint32_t value) | |
63 | { | |
64 | struct ads7846_state_s *s = (struct ads7846_state_s *) opaque; | |
65 | ||
66 | switch (s->cycle ++) { | |
67 | case 0: | |
68 | if (!(value & CB_START)) { | |
69 | s->cycle = 0; | |
70 | break; | |
71 | } | |
72 | ||
73 | s->output = s->input[(value >> 4) & 7]; | |
74 | ||
75 | /* Imitate the ADC noise, some drivers expect this. */ | |
76 | s->noise = (s->noise + 3) & 7; | |
77 | switch ((value >> 4) & 7) { | |
78 | case 1: s->output += s->noise ^ 2; break; | |
79 | case 3: s->output += s->noise ^ 0; break; | |
80 | case 4: s->output += s->noise ^ 7; break; | |
81 | case 5: s->output += s->noise ^ 5; break; | |
82 | } | |
83 | ||
84 | if (value & CB_MODE) | |
85 | s->output >>= 4; /* 8 bits instead of 12 */ | |
86 | ||
87 | break; | |
88 | case 1: | |
89 | s->cycle = 0; | |
90 | break; | |
91 | } | |
92 | } | |
93 | ||
94 | static void ads7846_ts_event(void *opaque, | |
95 | int x, int y, int z, int buttons_state) | |
96 | { | |
97 | struct ads7846_state_s *s = opaque; | |
98 | ||
99 | if (buttons_state) { | |
611d7189 AZ |
100 | x = 0x7fff - x; |
101 | s->input[1] = ADS_XPOS(x, y); | |
fd5a3b33 AZ |
102 | s->input[3] = ADS_Z1POS(x, y); |
103 | s->input[4] = ADS_Z2POS(x, y); | |
611d7189 | 104 | s->input[5] = ADS_YPOS(x, y); |
fd5a3b33 AZ |
105 | } |
106 | ||
107 | if (s->pressure == !buttons_state) { | |
108 | s->pressure = !!buttons_state; | |
109 | ||
aa941b94 | 110 | ads7846_int_update(s); |
fd5a3b33 AZ |
111 | } |
112 | } | |
113 | ||
aa941b94 AZ |
114 | static void ads7846_save(QEMUFile *f, void *opaque) |
115 | { | |
116 | struct ads7846_state_s *s = (struct ads7846_state_s *) opaque; | |
117 | int i; | |
118 | ||
119 | for (i = 0; i < 8; i ++) | |
120 | qemu_put_be32(f, s->input[i]); | |
121 | qemu_put_be32(f, s->noise); | |
122 | qemu_put_be32(f, s->cycle); | |
123 | qemu_put_be32(f, s->output); | |
124 | } | |
125 | ||
126 | static int ads7846_load(QEMUFile *f, void *opaque, int version_id) | |
127 | { | |
128 | struct ads7846_state_s *s = (struct ads7846_state_s *) opaque; | |
129 | int i; | |
130 | ||
131 | for (i = 0; i < 8; i ++) | |
132 | s->input[i] = qemu_get_be32(f); | |
133 | s->noise = qemu_get_be32(f); | |
134 | s->cycle = qemu_get_be32(f); | |
135 | s->output = qemu_get_be32(f); | |
136 | ||
137 | s->pressure = 0; | |
138 | ads7846_int_update(s); | |
139 | ||
140 | return 0; | |
141 | } | |
142 | ||
143 | static int ads7846_iid = 0; | |
144 | ||
fd5a3b33 AZ |
145 | struct ads7846_state_s *ads7846_init(qemu_irq penirq) |
146 | { | |
147 | struct ads7846_state_s *s; | |
148 | s = (struct ads7846_state_s *) | |
149 | qemu_mallocz(sizeof(struct ads7846_state_s)); | |
150 | memset(s, 0, sizeof(struct ads7846_state_s)); | |
151 | ||
152 | s->interrupt = penirq; | |
153 | ||
154 | s->input[0] = ADS_TEMP0; /* TEMP0 */ | |
155 | s->input[2] = ADS_VBAT; /* VBAT */ | |
156 | s->input[6] = ADS_VAUX; /* VAUX */ | |
157 | s->input[7] = ADS_TEMP1; /* TEMP1 */ | |
158 | ||
159 | /* We want absolute coordinates */ | |
160 | qemu_add_mouse_event_handler(ads7846_ts_event, s, 1, | |
161 | "QEMU ADS7846-driven Touchscreen"); | |
162 | ||
163 | ads7846_int_update(s); | |
aa941b94 AZ |
164 | |
165 | register_savevm("ads7846", ads7846_iid ++, 0, | |
166 | ads7846_save, ads7846_load, s); | |
167 | ||
fd5a3b33 AZ |
168 | return s; |
169 | } |