blob: 4a0e3359308136a0dbaed215be46a87726b28156 [file] [log] [blame]
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001/* Copyright (C) 2007-2008 The Android Open Source Project
2**
3** This software is licensed under the terms of the GNU General Public
4** License version 2, as published by the Free Software Foundation, and
5** may be copied, distributed, and modified under those terms.
6**
7** This program is distributed in the hope that it will be useful,
8** but WITHOUT ANY WARRANTY; without even the implied warranty of
9** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10** GNU General Public License for more details.
11*/
12#include "qemu_file.h"
13#include "android/android.h"
14#include "goldfish_device.h"
15#include "framebuffer.h"
16
17enum {
18 FB_GET_WIDTH = 0x00,
19 FB_GET_HEIGHT = 0x04,
20 FB_INT_STATUS = 0x08,
21 FB_INT_ENABLE = 0x0c,
22 FB_SET_BASE = 0x10,
23 FB_SET_ROTATION = 0x14,
24 FB_SET_BLANK = 0x18,
25 FB_GET_PHYS_WIDTH = 0x1c,
26 FB_GET_PHYS_HEIGHT = 0x20,
27
28 FB_INT_VSYNC = 1U << 0,
29 FB_INT_BASE_UPDATE_DONE = 1U << 1
30};
31
32struct goldfish_fb_state {
33 struct goldfish_device dev;
34 QFrameBuffer* qfbuff;
35 uint32_t fb_base;
36 uint32_t base_valid : 1;
37 uint32_t need_update : 1;
38 uint32_t need_int : 1;
39 uint32_t set_rotation : 2;
40 uint32_t blank : 1;
41 uint32_t int_status;
42 uint32_t int_enable;
43 int rotation; /* 0, 1, 2 or 3 */
44};
45
46#define GOLDFISH_FB_SAVE_VERSION 1
47
48static void goldfish_fb_save(QEMUFile* f, void* opaque)
49{
50 struct goldfish_fb_state* s = opaque;
51
52 QFrameBuffer* q = s->qfbuff;
53
54 qemu_put_be32(f, q->width);
55 qemu_put_be32(f, q->height);
56 qemu_put_be32(f, q->pitch);
57 qemu_put_byte(f, q->rotation);
58
59 qemu_put_be32(f, s->fb_base);
60 qemu_put_byte(f, s->base_valid);
61 qemu_put_byte(f, s->need_update);
62 qemu_put_byte(f, s->need_int);
63 qemu_put_byte(f, s->set_rotation);
64 qemu_put_byte(f, s->blank);
65 qemu_put_be32(f, s->int_status);
66 qemu_put_be32(f, s->int_enable);
67 qemu_put_be32(f, s->rotation);
68}
69
70static int goldfish_fb_load(QEMUFile* f, void* opaque, int version_id)
71{
72 struct goldfish_fb_state* s = opaque;
73
74 QFrameBuffer* q = s->qfbuff;
75 int ret = -1;
76 int ds_w, ds_h, ds_pitch, ds_rot;
77
78 if (version_id != GOLDFISH_FB_SAVE_VERSION)
79 goto Exit;
80
81 ds_w = qemu_get_be32(f);
82 ds_h = qemu_get_be32(f);
83 ds_pitch = qemu_get_be32(f);
84 ds_rot = qemu_get_byte(f);
85
86 if (q->width != ds_w ||
87 q->height != ds_h ||
88 q->pitch != ds_pitch ||
89 q->rotation != ds_rot )
90 {
91 /* XXX: We should be able to force a resize/rotation from here ? */
92 fprintf(stderr, "%s: framebuffer dimensions mismatch\n", __FUNCTION__);
93 goto Exit;
94 }
95
96 s->fb_base = qemu_get_be32(f);
97 s->base_valid = qemu_get_byte(f);
98 s->need_update = qemu_get_byte(f);
99 s->need_int = qemu_get_byte(f);
100 s->set_rotation = qemu_get_byte(f);
101 s->blank = qemu_get_byte(f);
102 s->int_status = qemu_get_be32(f);
103 s->int_enable = qemu_get_be32(f);
104 s->rotation = qemu_get_be32(f);
105
106 /* force a refresh */
107 s->need_update = 1;
108
109 ret = 0;
110Exit:
111 return ret;
112}
113
114
115#define STATS 0
116
117#if STATS
118static int stats_counter;
119static long stats_total;
120static int stats_full_updates;
121static long stats_total_full_updates;
122#endif
123
124static void goldfish_fb_update_display(void *opaque)
125{
126 struct goldfish_fb_state *s = (struct goldfish_fb_state *)opaque;
127 uint32_t addr;
128 uint32_t base;
129
130 uint8_t* dst_line;
131 uint8_t* src_line;
132 int y_first, y_last = 0;
133 int full_update = 0;
134 int width, height, pitch;
135
136 base = s->fb_base;
137 if(base == 0)
138 return;
139
140 if((s->int_enable & FB_INT_VSYNC) && !(s->int_status & FB_INT_VSYNC)) {
141 s->int_status |= FB_INT_VSYNC;
142 goldfish_device_set_irq(&s->dev, 0, 1);
143 }
144
145 y_first = -1;
146 addr = base;
147 if(s->need_update) {
148 full_update = 1;
149 if(s->need_int) {
150 s->int_status |= FB_INT_BASE_UPDATE_DONE;
151 if(s->int_enable & FB_INT_BASE_UPDATE_DONE)
152 goldfish_device_set_irq(&s->dev, 0, 1);
153 }
154 s->need_int = 0;
155 s->need_update = 0;
156 }
157
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700158 src_line = qemu_get_ram_ptr( base );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800159 dst_line = s->qfbuff->pixels;
160 pitch = s->qfbuff->pitch;
161 width = s->qfbuff->width;
162 height = s->qfbuff->height;
163
164#if STATS
165 if (full_update)
166 stats_full_updates += 1;
167 if (++stats_counter == 120) {
168 stats_total += stats_counter;
169 stats_total_full_updates += stats_full_updates;
170
171 printf( "full update stats: peak %.2f %% total %.2f %%\n",
172 stats_full_updates*100.0/stats_counter,
173 stats_total_full_updates*100.0/stats_total );
174
175 stats_counter = 0;
176 stats_full_updates = 0;
177 }
178#endif /* STATS */
179
180 if (s->blank)
181 {
182 memset( dst_line, 0, height*pitch );
183 y_first = 0;
184 y_last = height-1;
185 }
186 else if (full_update)
187 {
188 int yy;
189
190 for (yy = 0; yy < height; yy++, dst_line += pitch, src_line += width*2)
191 {
192 uint16_t* src = (uint16_t*) src_line;
193 uint16_t* dst = (uint16_t*) dst_line;
194 int nn;
195
196 for (nn = 0; nn < width; nn++) {
197 unsigned spix = src[nn];
198 unsigned dpix = dst[nn];
199#if WORDS_BIGENDIAN
200 spix = ((spix << 8) | (spix >> 8)) & 0xffff;
201#else
202 if (spix != dpix)
203 break;
204#endif
205 }
206
207 if (nn == width)
208 continue;
209
210#if WORDS_BIGENDIAN
211 for ( ; nn < width; nn++ ) {
212 unsigned spix = src[nn];
213 dst[nn] = (uint16_t)((spix << 8) | (spix >> 8));
214 }
215#else
216 memcpy( dst+nn, src+nn, (width-nn)*2 );
217#endif
218
219 y_first = (y_first < 0) ? yy : y_first;
220 y_last = yy;
221 }
222 }
223 else /* not a full update, should not happen very often with Android */
224 {
225 int yy;
226
227 for (yy = 0; yy < height; yy++, dst_line += pitch, src_line += width*2)
228 {
229 uint16_t* src = (uint16_t*) src_line;
230 uint16_t* dst = (uint16_t*) dst_line;
231 int len = width*2;
232#if WORDS_BIGENDIAN
233 int nn;
234#endif
235 int dirty = 0;
236
237 while (len > 0) {
238 int len2 = TARGET_PAGE_SIZE - (addr & (TARGET_PAGE_SIZE-1));
239
240 if (len2 > len)
241 len2 = len;
242
243 dirty |= cpu_physical_memory_get_dirty(addr, VGA_DIRTY_FLAG);
244 addr += len2;
245 len -= len2;
246 }
247
248 if (!dirty)
249 continue;
250
251#if WORDS_BIGENDIAN
252 for (nn = 0; nn < width; nn++ ) {
253 unsigned spix = src[nn];
254 dst[nn] = (uint16_t)((spix << 8) | (spix >> 8));
255 }
256#else
257 memcpy( dst, src, width*2 );
258#endif
259
260 y_first = (y_first < 0) ? yy : y_first;
261 y_last = yy;
262 }
263 }
264
265 if (y_first < 0)
266 return;
267
268 y_last += 1;
269 //printf("goldfish_fb_update_display %d %d, base %x\n", first, last, base);
270
271 cpu_physical_memory_reset_dirty(base + y_first * width * 2,
272 base + y_last * width * 2,
273 VGA_DIRTY_FLAG);
274
275 qframebuffer_update( s->qfbuff, 0, y_first, width, y_last-y_first );
276}
277
278static void goldfish_fb_invalidate_display(void * opaque)
279{
280 // is this called?
281 struct goldfish_fb_state *s = (struct goldfish_fb_state *)opaque;
282 s->need_update = 1;
283}
284
285static void goldfish_fb_detach_display(void* opaque)
286{
287 struct goldfish_fb_state *s = (struct goldfish_fb_state *)opaque;
288 s->qfbuff = NULL;
289}
290
291static uint32_t goldfish_fb_read(void *opaque, target_phys_addr_t offset)
292{
293 uint32_t ret;
294 struct goldfish_fb_state *s = opaque;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700295
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800296 switch(offset) {
297 case FB_GET_WIDTH:
298 ret = s->qfbuff->width;
299 //printf("FB_GET_WIDTH => %d\n", ret);
300 return ret;
301
302 case FB_GET_HEIGHT:
303 ret = s->qfbuff->height;
304 //printf( "FB_GET_HEIGHT = %d\n", ret);
305 return ret;
306
307 case FB_INT_STATUS:
308 ret = s->int_status & s->int_enable;
309 if(ret) {
310 s->int_status &= ~ret;
311 goldfish_device_set_irq(&s->dev, 0, 0);
312 }
313 return ret;
314
315 case FB_GET_PHYS_WIDTH:
316 ret = s->qfbuff->phys_width_mm;
317 //printf( "FB_GET_PHYS_WIDTH => %d\n", ret );
318 return ret;
319
320 case FB_GET_PHYS_HEIGHT:
321 ret = s->qfbuff->phys_height_mm;
322 //printf( "FB_GET_PHYS_HEIGHT => %d\n", ret );
323 return ret;
324
325 default:
326 cpu_abort (cpu_single_env, "goldfish_fb_read: Bad offset %x\n", offset);
327 return 0;
328 }
329}
330
331static void goldfish_fb_write(void *opaque, target_phys_addr_t offset,
332 uint32_t val)
333{
334 struct goldfish_fb_state *s = opaque;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700335
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800336 switch(offset) {
337 case FB_INT_ENABLE:
338 s->int_enable = val;
339 goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
340 break;
341 case FB_SET_BASE: {
342 int need_resize = !s->base_valid;
343 s->fb_base = val;
344 s->int_status &= ~FB_INT_BASE_UPDATE_DONE;
345 s->need_update = 1;
346 s->need_int = 1;
347 s->base_valid = 1;
348 if(s->set_rotation != s->rotation) {
349 //printf("FB_SET_BASE: rotation : %d => %d\n", s->rotation, s->set_rotation);
350 s->rotation = s->set_rotation;
351 need_resize = 1;
352 }
353 goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
354 if (need_resize) {
355 //printf("FB_SET_BASE: need resize (rotation=%d)\n", s->rotation );
356 qframebuffer_rotate( s->qfbuff, s->rotation );
357 }
358 } break;
359 case FB_SET_ROTATION:
360 //printf( "FB_SET_ROTATION %d\n", val);
361 s->set_rotation = val;
362 break;
363 case FB_SET_BLANK:
364 s->blank = val;
365 s->need_update = 1;
366 break;
367 default:
368 cpu_abort (cpu_single_env, "goldfish_fb_write: Bad offset %x\n", offset);
369 }
370}
371
372static CPUReadMemoryFunc *goldfish_fb_readfn[] = {
373 goldfish_fb_read,
374 goldfish_fb_read,
375 goldfish_fb_read
376};
377
378static CPUWriteMemoryFunc *goldfish_fb_writefn[] = {
379 goldfish_fb_write,
380 goldfish_fb_write,
381 goldfish_fb_write
382};
383
384void goldfish_fb_init(DisplayState *ds, int id)
385{
386 struct goldfish_fb_state *s;
387
388 s = (struct goldfish_fb_state *)qemu_mallocz(sizeof(*s));
389 s->dev.name = "goldfish_fb";
390 s->dev.id = id;
391 s->dev.size = 0x1000;
392 s->dev.irq_count = 1;
393
394 s->qfbuff = qframebuffer_fifo_get();
395 qframebuffer_set_producer( s->qfbuff, s,
396 goldfish_fb_update_display,
397 goldfish_fb_invalidate_display,
398 goldfish_fb_detach_display );
399
400 goldfish_device_add(&s->dev, goldfish_fb_readfn, goldfish_fb_writefn, s);
401
402 register_savevm( "goldfish_fb", 0, GOLDFISH_FB_SAVE_VERSION,
403 goldfish_fb_save, goldfish_fb_load, s);
404}
405