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