blob: d74d79749f8216f35736549e5de909d7ffcedec9 [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"
David 'Digit' Turner197e5f72011-01-16 02:16:02 +010014#include "android/utils/debug.h"
David 'Digit' Turner122b3352011-01-16 16:25:10 +010015#include "android/utils/duff.h"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080016#include "goldfish_device.h"
David 'Digit' Turner055ae422010-07-27 11:34:16 -070017#include "console.h"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080018
David 'Digit' Turnerd8a3d5c2011-01-16 01:05:43 +010019/* These values *must* match the platform definitions found under
20 * hardware/libhardware/include/hardware/hardware.h
21 */
22enum {
23 HAL_PIXEL_FORMAT_RGBA_8888 = 1,
24 HAL_PIXEL_FORMAT_RGBX_8888 = 2,
25 HAL_PIXEL_FORMAT_RGB_888 = 3,
26 HAL_PIXEL_FORMAT_RGB_565 = 4,
27 HAL_PIXEL_FORMAT_BGRA_8888 = 5,
28 HAL_PIXEL_FORMAT_RGBA_5551 = 6,
29 HAL_PIXEL_FORMAT_RGBA_4444 = 7,
30};
31
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080032enum {
33 FB_GET_WIDTH = 0x00,
34 FB_GET_HEIGHT = 0x04,
35 FB_INT_STATUS = 0x08,
36 FB_INT_ENABLE = 0x0c,
37 FB_SET_BASE = 0x10,
38 FB_SET_ROTATION = 0x14,
39 FB_SET_BLANK = 0x18,
40 FB_GET_PHYS_WIDTH = 0x1c,
41 FB_GET_PHYS_HEIGHT = 0x20,
David 'Digit' Turner197e5f72011-01-16 02:16:02 +010042 FB_GET_FORMAT = 0x24,
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080043
44 FB_INT_VSYNC = 1U << 0,
45 FB_INT_BASE_UPDATE_DONE = 1U << 1
46};
47
48struct goldfish_fb_state {
49 struct goldfish_device dev;
David 'Digit' Turner055ae422010-07-27 11:34:16 -070050 DisplayState* ds;
David 'Digit' Turnerd8a3d5c2011-01-16 01:05:43 +010051 int pixel_format;
52 int bytes_per_pixel;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080053 uint32_t fb_base;
54 uint32_t base_valid : 1;
55 uint32_t need_update : 1;
56 uint32_t need_int : 1;
57 uint32_t set_rotation : 2;
58 uint32_t blank : 1;
59 uint32_t int_status;
60 uint32_t int_enable;
61 int rotation; /* 0, 1, 2 or 3 */
David 'Digit' Turner055ae422010-07-27 11:34:16 -070062 int dpi;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080063};
64
David 'Digit' Turner055ae422010-07-27 11:34:16 -070065#define GOLDFISH_FB_SAVE_VERSION 2
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080066
67static void goldfish_fb_save(QEMUFile* f, void* opaque)
68{
69 struct goldfish_fb_state* s = opaque;
70
David 'Digit' Turner055ae422010-07-27 11:34:16 -070071 DisplayState* ds = s->ds;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080072
David 'Digit' Turner055ae422010-07-27 11:34:16 -070073 qemu_put_be32(f, ds->surface->width);
74 qemu_put_be32(f, ds->surface->height);
75 qemu_put_be32(f, ds->surface->linesize);
76 qemu_put_byte(f, 0);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080077
78 qemu_put_be32(f, s->fb_base);
79 qemu_put_byte(f, s->base_valid);
80 qemu_put_byte(f, s->need_update);
81 qemu_put_byte(f, s->need_int);
82 qemu_put_byte(f, s->set_rotation);
83 qemu_put_byte(f, s->blank);
84 qemu_put_be32(f, s->int_status);
85 qemu_put_be32(f, s->int_enable);
86 qemu_put_be32(f, s->rotation);
David 'Digit' Turner055ae422010-07-27 11:34:16 -070087 qemu_put_be32(f, s->dpi);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080088}
89
90static int goldfish_fb_load(QEMUFile* f, void* opaque, int version_id)
91{
92 struct goldfish_fb_state* s = opaque;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080093 int ret = -1;
94 int ds_w, ds_h, ds_pitch, ds_rot;
95
96 if (version_id != GOLDFISH_FB_SAVE_VERSION)
97 goto Exit;
98
99 ds_w = qemu_get_be32(f);
100 ds_h = qemu_get_be32(f);
101 ds_pitch = qemu_get_be32(f);
102 ds_rot = qemu_get_byte(f);
103
David 'Digit' Turner055ae422010-07-27 11:34:16 -0700104 DisplayState* ds = s->ds;
105
106 if (ds->surface->width != ds_w ||
107 ds->surface->height != ds_h ||
108 ds->surface->linesize != ds_pitch ||
109 ds_rot != 0)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800110 {
111 /* XXX: We should be able to force a resize/rotation from here ? */
112 fprintf(stderr, "%s: framebuffer dimensions mismatch\n", __FUNCTION__);
113 goto Exit;
114 }
115
116 s->fb_base = qemu_get_be32(f);
117 s->base_valid = qemu_get_byte(f);
118 s->need_update = qemu_get_byte(f);
119 s->need_int = qemu_get_byte(f);
120 s->set_rotation = qemu_get_byte(f);
121 s->blank = qemu_get_byte(f);
122 s->int_status = qemu_get_be32(f);
123 s->int_enable = qemu_get_be32(f);
124 s->rotation = qemu_get_be32(f);
David 'Digit' Turner055ae422010-07-27 11:34:16 -0700125 s->dpi = qemu_get_be32(f);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800126
127 /* force a refresh */
128 s->need_update = 1;
129
130 ret = 0;
131Exit:
132 return ret;
133}
134
David 'Digit' Turnerd8a3d5c2011-01-16 01:05:43 +0100135/* Type used to record a mapping from display surface pixel format to
136 * HAL pixel format */
137typedef struct {
138 int pixel_format; /* HAL pixel format */
139 uint8_t bits;
140 uint8_t bytes;
141 uint32_t rmask, gmask, bmask, amask;
142} FbConfig;
143
144
145/* Return the pixel format of the current framebuffer, based on
146 * the current display surface's pixel format.
147 *
148 * Note that you should not call this function from the device initialization
149 * function, because the display surface will change format before the kernel
150 * start.
151 */
152static int goldfish_fb_get_pixel_format(struct goldfish_fb_state *s)
153{
154 if (s->pixel_format >= 0) {
155 return s->pixel_format;
156 }
157 static const FbConfig fb_configs[] = {
158 { HAL_PIXEL_FORMAT_RGB_565, 16, 2, 0xf800, 0x7e0, 0x1f, 0x0 },
David 'Digit' Turner197e5f72011-01-16 02:16:02 +0100159 { HAL_PIXEL_FORMAT_RGBX_8888, 32, 4, 0xff0000, 0xff00, 0xff, 0x0 },
160 { HAL_PIXEL_FORMAT_RGBA_8888, 32, 4, 0xff0000, 0xff00, 0xff, 0xff000000 },
David 'Digit' Turnerd8a3d5c2011-01-16 01:05:43 +0100161 { -1, }
162 };
163
164 /* Determine HAL pixel format value based on s->ds */
165 struct PixelFormat* pf = &s->ds->surface->pf;
David 'Digit' Turner197e5f72011-01-16 02:16:02 +0100166 if (VERBOSE_CHECK(init)) {
167 printf("%s:%d: display surface,pixel format:\n", __FUNCTION__, __LINE__);
168 printf(" bits/pixel: %d\n", pf->bits_per_pixel);
169 printf(" bytes/pixel: %d\n", pf->bytes_per_pixel);
170 printf(" depth: %d\n", pf->depth);
171 printf(" red: bits=%d mask=0x%x shift=%d max=0x%x\n",
172 pf->rbits, pf->rmask, pf->rshift, pf->rmax);
173 printf(" green: bits=%d mask=0x%x shift=%d max=0x%x\n",
174 pf->gbits, pf->gmask, pf->gshift, pf->gmax);
175 printf(" blue: bits=%d mask=0x%x shift=%d max=0x%x\n",
176 pf->bbits, pf->bmask, pf->bshift, pf->bmax);
177 printf(" alpha: bits=%d mask=0x%x shift=%d max=0x%x\n",
178 pf->abits, pf->amask, pf->ashift, pf->amax);
179 }
David 'Digit' Turnerd8a3d5c2011-01-16 01:05:43 +0100180
David 'Digit' Turnerd8a3d5c2011-01-16 01:05:43 +0100181 s->bytes_per_pixel = pf->bytes_per_pixel;
182 int nn;
183 for (nn = 0; fb_configs[nn].pixel_format >= 0; nn++) {
184 const FbConfig* fbc = &fb_configs[nn];
185 if (pf->bits_per_pixel == fbc->bits &&
186 pf->bytes_per_pixel == fbc->bytes &&
187 pf->rmask == fbc->rmask &&
188 pf->gmask == fbc->gmask &&
189 pf->bmask == fbc->bmask &&
190 pf->amask == fbc->amask) {
191 /* We found it */
192 s->pixel_format = fbc->pixel_format;
193 return s->pixel_format;
194 }
195 }
196 fprintf(stderr, "%s:%d: Unsupported display pixel format (depth=%d, bytespp=%d, bitspp=%d)\n",
197 __FUNCTION__, __LINE__,
198 pf->depth,
199 pf->bytes_per_pixel,
200 pf->bits_per_pixel);
201 exit(1);
202 return -1;
203}
204
205static int goldfish_fb_get_bytes_per_pixel(struct goldfish_fb_state *s)
206{
207 if (s->pixel_format < 0) {
208 (void) goldfish_fb_get_pixel_format(s);
209 }
210 return s->bytes_per_pixel;
211}
212
David 'Digit' Turner055ae422010-07-27 11:34:16 -0700213static int
214pixels_to_mm(int pixels, int dpi)
215{
216 /* dpi = dots / inch
217 ** inch = dots / dpi
218 ** mm / 25.4 = dots / dpi
219 ** mm = (dots * 25.4)/dpi
220 */
221 return (int)(0.5 + 25.4 * pixels / dpi);
222}
223
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800224
225#define STATS 0
226
227#if STATS
228static int stats_counter;
229static long stats_total;
230static int stats_full_updates;
231static long stats_total_full_updates;
232#endif
233
David 'Digit' Turnerd8a3d5c2011-01-16 01:05:43 +0100234/* This structure is used to hold the inputs for
235 * compute_fb_update_rect_linear below.
236 * This corresponds to the source framebuffer and destination
237 * surface pixel buffers.
238 */
239typedef struct {
240 int width;
241 int height;
242 int bytes_per_pixel;
243 const uint8_t* src_pixels;
244 int src_pitch;
245 uint8_t* dst_pixels;
246 int dst_pitch;
247} FbUpdateState;
248
249/* This structure is used to hold the outputs for
250 * compute_fb_update_rect_linear below.
251 * This corresponds to the smalled bounding rectangle of the
252 * latest framebuffer update.
253 */
254typedef struct {
255 int xmin, ymin, xmax, ymax;
256} FbUpdateRect;
257
258/* Determine the smallest bounding rectangle of pixels which changed
259 * between the source (framebuffer) and destination (surface) pixel
260 * buffers.
261 *
262 * Return 0 if there was no change, otherwise, populate '*rect'
263 * and return 1.
264 *
265 * If 'dirty_base' is not 0, it is a physical address that will be
266 * used to speed-up the check using the VGA dirty bits. In practice
267 * this is only used if your kernel driver does not implement.
268 *
269 * This function assumes that the framebuffers are in linear memory.
270 * This may change later when we want to support larger framebuffers
271 * that exceed the max DMA aperture size though.
272 */
273static int
274compute_fb_update_rect_linear(FbUpdateState* fbs,
275 uint32_t dirty_base,
276 FbUpdateRect* rect)
277{
278 int yy;
279 int width = fbs->width;
280 const uint8_t* src_line = fbs->src_pixels;
281 uint8_t* dst_line = fbs->dst_pixels;
282 uint32_t dirty_addr = dirty_base;
283 rect->xmin = rect->ymin = INT_MAX;
284 rect->xmax = rect->ymax = INT_MIN;
285 for (yy = 0; yy < fbs->height; yy++) {
286 int xx1, xx2;
287 /* If dirty_addr is != 0, then use it as a physical address to
288 * use the VGA dirty bits table to speed up the detection of
289 * changed pixels.
290 */
291 if (dirty_addr != 0) {
292 int dirty = 0;
293 int len = fbs->src_pitch;
294
295 while (len > 0) {
296 int len2 = TARGET_PAGE_SIZE - (dirty_addr & (TARGET_PAGE_SIZE-1));
297
298 if (len2 > len)
299 len2 = len;
300
301 dirty |= cpu_physical_memory_get_dirty(dirty_addr, VGA_DIRTY_FLAG);
302 dirty_addr += len2;
303 len -= len2;
304 }
305
306 if (!dirty) { /* this line was not modified, skip to next one */
307 goto NEXT_LINE;
308 }
309 }
310
311 /* Then compute actual bounds of the changed pixels, while
312 * copying them from 'src' to 'dst'. This depends on the pixel depth.
313 */
314 switch (fbs->bytes_per_pixel) {
315 case 2:
316 {
317 const uint16_t* src = (const uint16_t*) src_line;
318 uint16_t* dst = (uint16_t*) dst_line;
319
David 'Digit' Turner122b3352011-01-16 16:25:10 +0100320 xx1 = 0;
321 DUFF4(width, {
322 if (src[xx1] != dst[xx1])
David 'Digit' Turnerd8a3d5c2011-01-16 01:05:43 +0100323 break;
David 'Digit' Turner122b3352011-01-16 16:25:10 +0100324 xx1++;
325 });
David 'Digit' Turnerd8a3d5c2011-01-16 01:05:43 +0100326 if (xx1 == width) {
327 break;
328 }
David 'Digit' Turner122b3352011-01-16 16:25:10 +0100329 xx2 = width-1;
330 DUFF4(xx2-xx1, {
331 if (src[xx2] != dst[xx2])
David 'Digit' Turnerd8a3d5c2011-01-16 01:05:43 +0100332 break;
David 'Digit' Turner122b3352011-01-16 16:25:10 +0100333 xx2--;
334 });
David 'Digit' Turnerd8a3d5c2011-01-16 01:05:43 +0100335#if HOST_WORDS_BIGENDIAN
336 /* Convert the guest little-endian pixels into big-endian ones */
337 int xx = xx1;
David 'Digit' Turner122b3352011-01-16 16:25:10 +0100338 DUFF4(xx2-xx1+1,{
David 'Digit' Turnerd8a3d5c2011-01-16 01:05:43 +0100339 unsigned spix = src[xx];
340 dst[xx] = (uint16_t)((spix << 8) | (spix >> 8));
David 'Digit' Turner122b3352011-01-16 16:25:10 +0100341 xx++;
342 });
David 'Digit' Turnerd8a3d5c2011-01-16 01:05:43 +0100343#else
344 memcpy( dst+xx1, src+xx1, (xx2-xx1+1)*2 );
345#endif
346 break;
347 }
348
349 case 3:
350 {
David 'Digit' Turner122b3352011-01-16 16:25:10 +0100351 xx1 = 0;
352 DUFF4(width, {
David 'Digit' Turnerd8a3d5c2011-01-16 01:05:43 +0100353 int xx = xx1*3;
354 if (src_line[xx+0] != dst_line[xx+0] ||
355 src_line[xx+1] != dst_line[xx+1] ||
356 src_line[xx+2] != dst_line[xx+2]) {
357 break;
358 }
David 'Digit' Turner122b3352011-01-16 16:25:10 +0100359 xx1 ++;
360 });
David 'Digit' Turnerd8a3d5c2011-01-16 01:05:43 +0100361 if (xx1 == width) {
362 break;
363 }
David 'Digit' Turner122b3352011-01-16 16:25:10 +0100364 xx2 = width-1;
365 DUFF4(xx2-xx1,{
David 'Digit' Turnerd8a3d5c2011-01-16 01:05:43 +0100366 int xx = xx2*3;
367 if (src_line[xx+0] != dst_line[xx+0] ||
368 src_line[xx+1] != dst_line[xx+1] ||
369 src_line[xx+2] != dst_line[xx+2]) {
370 break;
371 }
David 'Digit' Turner122b3352011-01-16 16:25:10 +0100372 xx2--;
373 });
David 'Digit' Turnerd8a3d5c2011-01-16 01:05:43 +0100374 memcpy( dst_line+xx1*3, src_line+xx1*3, (xx2-xx1+1)*3 );
375 break;
376 }
377
378 case 4:
379 {
380 const uint32_t* src = (const uint32_t*) src_line;
381 uint32_t* dst = (uint32_t*) dst_line;
382
David 'Digit' Turner122b3352011-01-16 16:25:10 +0100383 xx1 = 0;
384 DUFF4(width, {
David 'Digit' Turnerd8a3d5c2011-01-16 01:05:43 +0100385 if (src[xx1] != dst[xx1]) {
386 break;
387 }
David 'Digit' Turner122b3352011-01-16 16:25:10 +0100388 xx1++;
389 });
David 'Digit' Turnerd8a3d5c2011-01-16 01:05:43 +0100390 if (xx1 == width) {
391 break;
392 }
David 'Digit' Turner122b3352011-01-16 16:25:10 +0100393 xx2 = width-1;
394 DUFF4(xx2-xx1,{
David 'Digit' Turnerd8a3d5c2011-01-16 01:05:43 +0100395 if (src[xx2] != dst[xx2]) {
396 break;
397 }
David 'Digit' Turner122b3352011-01-16 16:25:10 +0100398 xx2--;
399 });
David 'Digit' Turnerd8a3d5c2011-01-16 01:05:43 +0100400#if HOST_WORDS_BIGENDIAN
401 /* Convert the guest little-endian pixels into big-endian ones */
402 int xx = xx1;
David 'Digit' Turner122b3352011-01-16 16:25:10 +0100403 DUFF4(xx2-xx1+1,{
David 'Digit' Turnerd8a3d5c2011-01-16 01:05:43 +0100404 uint32_t spix = src[xx];
405 spix = (spix << 16) | (spix >> 16);
406 spix = ((spix << 8) & 0xff00ff00) | ((spix >> 8) & 0x00ff00ff);
407 dst[xx] = spix;
David 'Digit' Turner122b3352011-01-16 16:25:10 +0100408 xx++;
409 })
David 'Digit' Turnerd8a3d5c2011-01-16 01:05:43 +0100410#else
411 memcpy( dst+xx1, src+xx1, (xx2-xx1+1)*4 );
412#endif
413 break;
414 }
415 default:
416 return 0;
417 }
418 /* Update bounds if pixels on this line were modified */
419 if (xx1 < width) {
420 if (xx1 < rect->xmin) rect->xmin = xx1;
421 if (xx2 > rect->xmax) rect->xmax = xx2;
422 if (yy < rect->ymin) rect->ymin = yy;
423 if (yy > rect->ymax) rect->ymax = yy;
424 }
425 NEXT_LINE:
426 src_line += fbs->src_pitch;
427 dst_line += fbs->dst_pitch;
428 }
429
430 if (rect->ymin > rect->ymax) { /* nothing changed */
431 return 0;
432 }
433
434 /* Always clear the dirty VGA bits */
435 cpu_physical_memory_reset_dirty(dirty_base + rect->ymin * fbs->src_pitch,
436 dirty_base + (rect->ymax+1)* fbs->src_pitch,
437 VGA_DIRTY_FLAG);
438 return 1;
439}
440
441
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800442static void goldfish_fb_update_display(void *opaque)
443{
444 struct goldfish_fb_state *s = (struct goldfish_fb_state *)opaque;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800445 uint32_t base;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800446 uint8_t* dst_line;
447 uint8_t* src_line;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800448 int full_update = 0;
David 'Digit' Turnerd8a3d5c2011-01-16 01:05:43 +0100449 int width, height, pitch;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800450
451 base = s->fb_base;
452 if(base == 0)
453 return;
454
455 if((s->int_enable & FB_INT_VSYNC) && !(s->int_status & FB_INT_VSYNC)) {
456 s->int_status |= FB_INT_VSYNC;
457 goldfish_device_set_irq(&s->dev, 0, 1);
458 }
459
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800460 if(s->need_update) {
461 full_update = 1;
462 if(s->need_int) {
463 s->int_status |= FB_INT_BASE_UPDATE_DONE;
464 if(s->int_enable & FB_INT_BASE_UPDATE_DONE)
465 goldfish_device_set_irq(&s->dev, 0, 1);
466 }
467 s->need_int = 0;
468 s->need_update = 0;
469 }
470
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700471 src_line = qemu_get_ram_ptr( base );
David 'Digit' Turner055ae422010-07-27 11:34:16 -0700472
473 dst_line = s->ds->surface->data;
474 pitch = s->ds->surface->linesize;
475 width = s->ds->surface->width;
476 height = s->ds->surface->height;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800477
David 'Digit' Turnerd8a3d5c2011-01-16 01:05:43 +0100478 FbUpdateState fbs;
479 FbUpdateRect rect;
480
481 fbs.width = width;
482 fbs.height = height;
483 fbs.dst_pixels = dst_line;
484 fbs.dst_pitch = pitch;
485 fbs.bytes_per_pixel = goldfish_fb_get_bytes_per_pixel(s);
486
487 fbs.src_pixels = src_line;
488 fbs.src_pitch = width*s->ds->surface->pf.bytes_per_pixel;
489
490
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800491#if STATS
492 if (full_update)
493 stats_full_updates += 1;
494 if (++stats_counter == 120) {
495 stats_total += stats_counter;
496 stats_total_full_updates += stats_full_updates;
497
498 printf( "full update stats: peak %.2f %% total %.2f %%\n",
499 stats_full_updates*100.0/stats_counter,
500 stats_total_full_updates*100.0/stats_total );
501
502 stats_counter = 0;
503 stats_full_updates = 0;
504 }
505#endif /* STATS */
506
507 if (s->blank)
508 {
509 memset( dst_line, 0, height*pitch );
David 'Digit' Turnerd8a3d5c2011-01-16 01:05:43 +0100510 rect.xmin = 0;
511 rect.ymin = 0;
512 rect.xmax = width-1;
513 rect.ymax = height-1;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800514 }
David 'Digit' Turnerd8a3d5c2011-01-16 01:05:43 +0100515 else
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800516 {
David 'Digit' Turnerd8a3d5c2011-01-16 01:05:43 +0100517 if (full_update) { /* don't use dirty-bits optimization */
518 base = 0;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800519 }
David 'Digit' Turnerd8a3d5c2011-01-16 01:05:43 +0100520 if (compute_fb_update_rect_linear(&fbs, base, &rect) == 0) {
521 return;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800522 }
523 }
524
David 'Digit' Turnerd8a3d5c2011-01-16 01:05:43 +0100525 rect.xmax += 1;
526 rect.ymax += 1;
527#if 0
528 printf("goldfish_fb_update_display (y:%d,h:%d,x=%d,w=%d)\n",
529 rect.ymin, rect.ymax-rect.ymin, rect.xmin, rect.xmax-rect.xmin);
530#endif
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800531
David 'Digit' Turnerd8a3d5c2011-01-16 01:05:43 +0100532 dpy_update(s->ds, rect.xmin, rect.ymin, rect.xmax-rect.xmin, rect.ymax-rect.ymin);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800533}
534
535static void goldfish_fb_invalidate_display(void * opaque)
536{
537 // is this called?
538 struct goldfish_fb_state *s = (struct goldfish_fb_state *)opaque;
539 s->need_update = 1;
540}
541
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800542static uint32_t goldfish_fb_read(void *opaque, target_phys_addr_t offset)
543{
544 uint32_t ret;
545 struct goldfish_fb_state *s = opaque;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700546
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800547 switch(offset) {
548 case FB_GET_WIDTH:
David 'Digit' Turner055ae422010-07-27 11:34:16 -0700549 ret = ds_get_width(s->ds);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800550 //printf("FB_GET_WIDTH => %d\n", ret);
551 return ret;
552
553 case FB_GET_HEIGHT:
David 'Digit' Turner055ae422010-07-27 11:34:16 -0700554 ret = ds_get_height(s->ds);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800555 //printf( "FB_GET_HEIGHT = %d\n", ret);
556 return ret;
557
558 case FB_INT_STATUS:
559 ret = s->int_status & s->int_enable;
560 if(ret) {
561 s->int_status &= ~ret;
562 goldfish_device_set_irq(&s->dev, 0, 0);
563 }
564 return ret;
565
566 case FB_GET_PHYS_WIDTH:
David 'Digit' Turner055ae422010-07-27 11:34:16 -0700567 ret = pixels_to_mm( ds_get_width(s->ds), s->dpi );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800568 //printf( "FB_GET_PHYS_WIDTH => %d\n", ret );
569 return ret;
570
571 case FB_GET_PHYS_HEIGHT:
David 'Digit' Turner055ae422010-07-27 11:34:16 -0700572 ret = pixels_to_mm( ds_get_height(s->ds), s->dpi );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800573 //printf( "FB_GET_PHYS_HEIGHT => %d\n", ret );
574 return ret;
575
David 'Digit' Turner197e5f72011-01-16 02:16:02 +0100576 case FB_GET_FORMAT:
577 return goldfish_fb_get_pixel_format(s);
578
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800579 default:
580 cpu_abort (cpu_single_env, "goldfish_fb_read: Bad offset %x\n", offset);
581 return 0;
582 }
583}
584
585static void goldfish_fb_write(void *opaque, target_phys_addr_t offset,
586 uint32_t val)
587{
588 struct goldfish_fb_state *s = opaque;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700589
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800590 switch(offset) {
591 case FB_INT_ENABLE:
592 s->int_enable = val;
593 goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
594 break;
595 case FB_SET_BASE: {
596 int need_resize = !s->base_valid;
597 s->fb_base = val;
598 s->int_status &= ~FB_INT_BASE_UPDATE_DONE;
599 s->need_update = 1;
600 s->need_int = 1;
601 s->base_valid = 1;
602 if(s->set_rotation != s->rotation) {
603 //printf("FB_SET_BASE: rotation : %d => %d\n", s->rotation, s->set_rotation);
604 s->rotation = s->set_rotation;
605 need_resize = 1;
606 }
607 goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
608 if (need_resize) {
609 //printf("FB_SET_BASE: need resize (rotation=%d)\n", s->rotation );
David 'Digit' Turner055ae422010-07-27 11:34:16 -0700610 dpy_resize(s->ds);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800611 }
612 } break;
613 case FB_SET_ROTATION:
614 //printf( "FB_SET_ROTATION %d\n", val);
615 s->set_rotation = val;
616 break;
617 case FB_SET_BLANK:
618 s->blank = val;
619 s->need_update = 1;
620 break;
621 default:
622 cpu_abort (cpu_single_env, "goldfish_fb_write: Bad offset %x\n", offset);
623 }
624}
625
626static CPUReadMemoryFunc *goldfish_fb_readfn[] = {
627 goldfish_fb_read,
628 goldfish_fb_read,
629 goldfish_fb_read
630};
631
632static CPUWriteMemoryFunc *goldfish_fb_writefn[] = {
633 goldfish_fb_write,
634 goldfish_fb_write,
635 goldfish_fb_write
636};
637
David 'Digit' Turner055ae422010-07-27 11:34:16 -0700638void goldfish_fb_init(int id)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800639{
640 struct goldfish_fb_state *s;
641
642 s = (struct goldfish_fb_state *)qemu_mallocz(sizeof(*s));
643 s->dev.name = "goldfish_fb";
644 s->dev.id = id;
645 s->dev.size = 0x1000;
646 s->dev.irq_count = 1;
647
David 'Digit' Turner055ae422010-07-27 11:34:16 -0700648 s->ds = graphic_console_init(goldfish_fb_update_display,
649 goldfish_fb_invalidate_display,
650 NULL,
651 NULL,
652 s);
653
654 s->dpi = 165; /* XXX: Find better way to get actual value ! */
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800655
David 'Digit' Turnerd8a3d5c2011-01-16 01:05:43 +0100656 /* IMPORTANT: DO NOT COMPUTE s->pixel_format and s->bytes_per_pixel
657 * here because the display surface is going to change later.
658 */
659 s->bytes_per_pixel = 0;
660 s->pixel_format = -1;
661
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800662 goldfish_device_add(&s->dev, goldfish_fb_readfn, goldfish_fb_writefn, s);
663
664 register_savevm( "goldfish_fb", 0, GOLDFISH_FB_SAVE_VERSION,
665 goldfish_fb_save, goldfish_fb_load, s);
666}