Savevm/loadvm bits for ARM core, the PXA2xx peripherals and Spitz hardware.
[qemu] / hw / ads7846.c
1 /*
2  * TI ADS7846 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     3680
34 #define X_AXIS_MIN      150
35 #define Y_AXIS_DMAX     3640
36 #define Y_AXIS_MIN      190
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         s->input[1] = ADS_YPOS(x, y);
99         s->input[3] = ADS_Z1POS(x, y);
100         s->input[4] = ADS_Z2POS(x, y);
101         s->input[5] = ADS_XPOS(x, y);
102     }
103
104     if (s->pressure == !buttons_state) {
105         s->pressure = !!buttons_state;
106
107         ads7846_int_update(s);
108     }
109 }
110
111 static void ads7846_save(QEMUFile *f, void *opaque)
112 {
113     struct ads7846_state_s *s = (struct ads7846_state_s *) opaque;
114     int i;
115
116     for (i = 0; i < 8; i ++)
117         qemu_put_be32(f, s->input[i]);
118     qemu_put_be32(f, s->noise);
119     qemu_put_be32(f, s->cycle);
120     qemu_put_be32(f, s->output);
121 }
122
123 static int ads7846_load(QEMUFile *f, void *opaque, int version_id)
124 {
125     struct ads7846_state_s *s = (struct ads7846_state_s *) opaque;
126     int i;
127
128     for (i = 0; i < 8; i ++)
129         s->input[i] = qemu_get_be32(f);
130     s->noise = qemu_get_be32(f);
131     s->cycle = qemu_get_be32(f);
132     s->output = qemu_get_be32(f);
133
134     s->pressure = 0;
135     ads7846_int_update(s);
136
137     return 0;
138 }
139
140 static int ads7846_iid = 0;
141
142 struct ads7846_state_s *ads7846_init(qemu_irq penirq)
143 {
144     struct ads7846_state_s *s;
145     s = (struct ads7846_state_s *)
146             qemu_mallocz(sizeof(struct ads7846_state_s));
147     memset(s, 0, sizeof(struct ads7846_state_s));
148
149     s->interrupt = penirq;
150
151     s->input[0] = ADS_TEMP0;    /* TEMP0 */
152     s->input[2] = ADS_VBAT;     /* VBAT */
153     s->input[6] = ADS_VAUX;     /* VAUX */
154     s->input[7] = ADS_TEMP1;    /* TEMP1 */
155
156     /* We want absolute coordinates */
157     qemu_add_mouse_event_handler(ads7846_ts_event, s, 1,
158                     "QEMU ADS7846-driven Touchscreen");
159
160     ads7846_int_update(s);
161
162     register_savevm("ads7846", ads7846_iid ++, 0,
163                     ads7846_save, ads7846_load, s);
164
165     return s;
166 }