1 /* 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 "cpu.h"
13 #include "migration/qemu-file.h"
14 #include "android/android.h"
15 #include "android/utils/debug.h"
16 #include "android/utils/duff.h"
17 #include "hw/android/goldfish/device.h"
18 #include "hw/hw.h"
19 #include "ui/console.h"
20
21 /* These values *must* match the platform definitions found under
22 * hardware/libhardware/include/hardware/hardware.h
23 */
24 enum {
25 HAL_PIXEL_FORMAT_RGBA_8888 = 1,
26 HAL_PIXEL_FORMAT_RGBX_8888 = 2,
27 HAL_PIXEL_FORMAT_RGB_888 = 3,
28 HAL_PIXEL_FORMAT_RGB_565 = 4,
29 HAL_PIXEL_FORMAT_BGRA_8888 = 5,
30 HAL_PIXEL_FORMAT_RGBA_5551 = 6,
31 HAL_PIXEL_FORMAT_RGBA_4444 = 7,
32 };
33
34 enum {
35 FB_GET_WIDTH = 0x00,
36 FB_GET_HEIGHT = 0x04,
37 FB_INT_STATUS = 0x08,
38 FB_INT_ENABLE = 0x0c,
39 FB_SET_BASE = 0x10,
40 FB_SET_ROTATION = 0x14,
41 FB_SET_BLANK = 0x18,
42 FB_GET_PHYS_WIDTH = 0x1c,
43 FB_GET_PHYS_HEIGHT = 0x20,
44 FB_GET_FORMAT = 0x24,
45
46 FB_INT_VSYNC = 1U << 0,
47 FB_INT_BASE_UPDATE_DONE = 1U << 1
48 };
49
50 struct goldfish_fb_state {
51 struct goldfish_device dev;
52 DisplayState* ds;
53 int pixel_format;
54 int bytes_per_pixel;
55 uint32_t fb_base;
56 uint32_t base_valid : 1;
57 uint32_t need_update : 1;
58 uint32_t need_int : 1;
59 uint32_t set_rotation : 2;
60 uint32_t blank : 1;
61 uint32_t int_status;
62 uint32_t int_enable;
63 int rotation; /* 0, 1, 2 or 3 */
64 int dpi;
65 };
66
67 #define GOLDFISH_FB_SAVE_VERSION 2
68
goldfish_fb_save(QEMUFile * f,void * opaque)69 static void goldfish_fb_save(QEMUFile* f, void* opaque)
70 {
71 struct goldfish_fb_state* s = opaque;
72
73 DisplayState* ds = s->ds;
74
75 qemu_put_be32(f, ds->surface->width);
76 qemu_put_be32(f, ds->surface->height);
77 qemu_put_be32(f, ds->surface->linesize);
78 qemu_put_byte(f, 0);
79
80 qemu_put_be32(f, s->fb_base);
81 qemu_put_byte(f, s->base_valid);
82 qemu_put_byte(f, s->need_update);
83 qemu_put_byte(f, s->need_int);
84 qemu_put_byte(f, s->set_rotation);
85 qemu_put_byte(f, s->blank);
86 qemu_put_be32(f, s->int_status);
87 qemu_put_be32(f, s->int_enable);
88 qemu_put_be32(f, s->rotation);
89 qemu_put_be32(f, s->dpi);
90 }
91
goldfish_fb_load(QEMUFile * f,void * opaque,int version_id)92 static int goldfish_fb_load(QEMUFile* f, void* opaque, int version_id)
93 {
94 struct goldfish_fb_state* s = opaque;
95 int ret = -1;
96 int ds_w, ds_h, ds_pitch, ds_rot;
97
98 if (version_id != GOLDFISH_FB_SAVE_VERSION)
99 goto Exit;
100
101 ds_w = qemu_get_be32(f);
102 ds_h = qemu_get_be32(f);
103 ds_pitch = qemu_get_be32(f);
104 ds_rot = qemu_get_byte(f);
105
106 DisplayState* ds = s->ds;
107
108 if (ds->surface->width != ds_w ||
109 ds->surface->height != ds_h ||
110 ds->surface->linesize != ds_pitch ||
111 ds_rot != 0)
112 {
113 /* XXX: We should be able to force a resize/rotation from here ? */
114 fprintf(stderr, "%s: framebuffer dimensions mismatch\n", __FUNCTION__);
115 goto Exit;
116 }
117
118 s->fb_base = qemu_get_be32(f);
119 s->base_valid = qemu_get_byte(f);
120 s->need_update = qemu_get_byte(f);
121 s->need_int = qemu_get_byte(f);
122 s->set_rotation = qemu_get_byte(f);
123 s->blank = qemu_get_byte(f);
124 s->int_status = qemu_get_be32(f);
125 s->int_enable = qemu_get_be32(f);
126 s->rotation = qemu_get_be32(f);
127 s->dpi = qemu_get_be32(f);
128
129 /* force a refresh */
130 s->need_update = 1;
131
132 ret = 0;
133 Exit:
134 return ret;
135 }
136
137 /* Type used to record a mapping from display surface pixel format to
138 * HAL pixel format */
139 typedef struct {
140 int pixel_format; /* HAL pixel format */
141 uint8_t bits;
142 uint8_t bytes;
143 uint32_t rmask, gmask, bmask, amask;
144 } FbConfig;
145
146
147 /* Return the pixel format of the current framebuffer, based on
148 * the current display surface's pixel format.
149 *
150 * Note that you should not call this function from the device initialization
151 * function, because the display surface will change format before the kernel
152 * start.
153 */
goldfish_fb_get_pixel_format(struct goldfish_fb_state * s)154 static int goldfish_fb_get_pixel_format(struct goldfish_fb_state *s)
155 {
156 if (s->pixel_format >= 0) {
157 return s->pixel_format;
158 }
159 static const FbConfig fb_configs[] = {
160 { HAL_PIXEL_FORMAT_RGB_565, 16, 2, 0xf800, 0x7e0, 0x1f, 0x0 },
161 { HAL_PIXEL_FORMAT_RGBX_8888, 32, 4, 0xff0000, 0xff00, 0xff, 0x0 },
162 { HAL_PIXEL_FORMAT_RGBA_8888, 32, 4, 0xff0000, 0xff00, 0xff, 0xff000000 },
163 { -1, }
164 };
165
166 /* Determine HAL pixel format value based on s->ds */
167 struct PixelFormat* pf = &s->ds->surface->pf;
168 if (VERBOSE_CHECK(init)) {
169 printf("%s:%d: display surface,pixel format:\n", __FUNCTION__, __LINE__);
170 printf(" bits/pixel: %d\n", pf->bits_per_pixel);
171 printf(" bytes/pixel: %d\n", pf->bytes_per_pixel);
172 printf(" depth: %d\n", pf->depth);
173 printf(" red: bits=%d mask=0x%x shift=%d max=0x%x\n",
174 pf->rbits, pf->rmask, pf->rshift, pf->rmax);
175 printf(" green: bits=%d mask=0x%x shift=%d max=0x%x\n",
176 pf->gbits, pf->gmask, pf->gshift, pf->gmax);
177 printf(" blue: bits=%d mask=0x%x shift=%d max=0x%x\n",
178 pf->bbits, pf->bmask, pf->bshift, pf->bmax);
179 printf(" alpha: bits=%d mask=0x%x shift=%d max=0x%x\n",
180 pf->abits, pf->amask, pf->ashift, pf->amax);
181 }
182
183 s->bytes_per_pixel = pf->bytes_per_pixel;
184 int nn;
185 for (nn = 0; fb_configs[nn].pixel_format >= 0; nn++) {
186 const FbConfig* fbc = &fb_configs[nn];
187 if (pf->bits_per_pixel == fbc->bits &&
188 pf->bytes_per_pixel == fbc->bytes &&
189 pf->rmask == fbc->rmask &&
190 pf->gmask == fbc->gmask &&
191 pf->bmask == fbc->bmask &&
192 pf->amask == fbc->amask) {
193 /* We found it */
194 s->pixel_format = fbc->pixel_format;
195 return s->pixel_format;
196 }
197 }
198 fprintf(stderr, "%s:%d: Unsupported display pixel format (depth=%d, bytespp=%d, bitspp=%d)\n",
199 __FUNCTION__, __LINE__,
200 pf->depth,
201 pf->bytes_per_pixel,
202 pf->bits_per_pixel);
203 exit(1);
204 return -1;
205 }
206
goldfish_fb_get_bytes_per_pixel(struct goldfish_fb_state * s)207 static int goldfish_fb_get_bytes_per_pixel(struct goldfish_fb_state *s)
208 {
209 if (s->pixel_format < 0) {
210 (void) goldfish_fb_get_pixel_format(s);
211 }
212 return s->bytes_per_pixel;
213 }
214
215 static int
pixels_to_mm(int pixels,int dpi)216 pixels_to_mm(int pixels, int dpi)
217 {
218 /* dpi = dots / inch
219 ** inch = dots / dpi
220 ** mm / 25.4 = dots / dpi
221 ** mm = (dots * 25.4)/dpi
222 */
223 return (int)(0.5 + 25.4 * pixels / dpi);
224 }
225
226
227 #define STATS 0
228
229 #if STATS
230 static int stats_counter;
231 static long stats_total;
232 static int stats_full_updates;
233 static long stats_total_full_updates;
234 #endif
235
236 /* This structure is used to hold the inputs for
237 * compute_fb_update_rect_linear below.
238 * This corresponds to the source framebuffer and destination
239 * surface pixel buffers.
240 */
241 typedef struct {
242 int width;
243 int height;
244 int bytes_per_pixel;
245 const uint8_t* src_pixels;
246 int src_pitch;
247 uint8_t* dst_pixels;
248 int dst_pitch;
249 } FbUpdateState;
250
251 /* This structure is used to hold the outputs for
252 * compute_fb_update_rect_linear below.
253 * This corresponds to the smalled bounding rectangle of the
254 * latest framebuffer update.
255 */
256 typedef struct {
257 int xmin, ymin, xmax, ymax;
258 } FbUpdateRect;
259
260 /* Determine the smallest bounding rectangle of pixels which changed
261 * between the source (framebuffer) and destination (surface) pixel
262 * buffers.
263 *
264 * Return 0 if there was no change, otherwise, populate '*rect'
265 * and return 1.
266 *
267 * If 'dirty_base' is not 0, it is a physical address that will be
268 * used to speed-up the check using the VGA dirty bits. In practice
269 * this is only used if your kernel driver does not implement.
270 *
271 * This function assumes that the framebuffers are in linear memory.
272 * This may change later when we want to support larger framebuffers
273 * that exceed the max DMA aperture size though.
274 */
275 static int
compute_fb_update_rect_linear(FbUpdateState * fbs,uint32_t dirty_base,FbUpdateRect * rect)276 compute_fb_update_rect_linear(FbUpdateState* fbs,
277 uint32_t dirty_base,
278 FbUpdateRect* rect)
279 {
280 int yy;
281 int width = fbs->width;
282 const uint8_t* src_line = fbs->src_pixels;
283 uint8_t* dst_line = fbs->dst_pixels;
284 uint32_t dirty_addr = dirty_base;
285 rect->xmin = rect->ymin = INT_MAX;
286 rect->xmax = rect->ymax = INT_MIN;
287 for (yy = 0; yy < fbs->height; yy++) {
288 int xx1, xx2;
289 /* If dirty_addr is != 0, then use it as a physical address to
290 * use the VGA dirty bits table to speed up the detection of
291 * changed pixels.
292 */
293 if (dirty_addr != 0) {
294 int dirty = 0;
295 int len = fbs->src_pitch;
296
297 while (len > 0) {
298 int len2 = TARGET_PAGE_SIZE - (dirty_addr & (TARGET_PAGE_SIZE-1));
299
300 if (len2 > len)
301 len2 = len;
302
303 dirty |= cpu_physical_memory_get_dirty(dirty_addr, VGA_DIRTY_FLAG);
304 dirty_addr += len2;
305 len -= len2;
306 }
307
308 if (!dirty) { /* this line was not modified, skip to next one */
309 goto NEXT_LINE;
310 }
311 }
312
313 /* Then compute actual bounds of the changed pixels, while
314 * copying them from 'src' to 'dst'. This depends on the pixel depth.
315 */
316 switch (fbs->bytes_per_pixel) {
317 case 2:
318 {
319 const uint16_t* src = (const uint16_t*) src_line;
320 uint16_t* dst = (uint16_t*) dst_line;
321
322 xx1 = 0;
323 DUFF4(width, {
324 uint16_t spix = src[xx1];
325 #if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN)
326 spix = (uint16_t)((spix << 8) | (spix >> 8));
327 #endif
328 if (spix != dst[xx1])
329 break;
330 xx1++;
331 });
332 if (xx1 == width) {
333 break;
334 }
335 xx2 = width-1;
336 DUFF4(xx2-xx1, {
337 if (src[xx2] != dst[xx2])
338 break;
339 xx2--;
340 });
341 #if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN)
342 /* Convert the guest pixels into host ones */
343 int xx = xx1;
344 DUFF4(xx2-xx1+1,{
345 unsigned spix = src[xx];
346 dst[xx] = (uint16_t)((spix << 8) | (spix >> 8));
347 xx++;
348 });
349 #else
350 memcpy( dst+xx1, src+xx1, (xx2-xx1+1)*2 );
351 #endif
352 break;
353 }
354
355 case 3:
356 {
357 xx1 = 0;
358 DUFF4(width, {
359 int xx = xx1*3;
360 if (src_line[xx+0] != dst_line[xx+0] ||
361 src_line[xx+1] != dst_line[xx+1] ||
362 src_line[xx+2] != dst_line[xx+2]) {
363 break;
364 }
365 xx1 ++;
366 });
367 if (xx1 == width) {
368 break;
369 }
370 xx2 = width-1;
371 DUFF4(xx2-xx1,{
372 int xx = xx2*3;
373 if (src_line[xx+0] != dst_line[xx+0] ||
374 src_line[xx+1] != dst_line[xx+1] ||
375 src_line[xx+2] != dst_line[xx+2]) {
376 break;
377 }
378 xx2--;
379 });
380 memcpy( dst_line+xx1*3, src_line+xx1*3, (xx2-xx1+1)*3 );
381 break;
382 }
383
384 case 4:
385 {
386 const uint32_t* src = (const uint32_t*) src_line;
387 uint32_t* dst = (uint32_t*) dst_line;
388
389 xx1 = 0;
390 DUFF4(width, {
391 uint32_t spix = src[xx1];
392 #if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN)
393 spix = (spix << 16) | (spix >> 16);
394 spix = ((spix << 8) & 0xff00ff00) | ((spix >> 8) & 0x00ff00ff);
395 #endif
396 if (spix != dst[xx1]) {
397 break;
398 }
399 xx1++;
400 });
401 if (xx1 == width) {
402 break;
403 }
404 xx2 = width-1;
405 DUFF4(xx2-xx1,{
406 if (src[xx2] != dst[xx2]) {
407 break;
408 }
409 xx2--;
410 });
411 #if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN)
412 /* Convert the guest pixels into host ones */
413 int xx = xx1;
414 DUFF4(xx2-xx1+1,{
415 uint32_t spix = src[xx];
416 spix = (spix << 16) | (spix >> 16);
417 spix = ((spix << 8) & 0xff00ff00) | ((spix >> 8) & 0x00ff00ff);
418 dst[xx] = spix;
419 xx++;
420 })
421 #else
422 memcpy( dst+xx1, src+xx1, (xx2-xx1+1)*4 );
423 #endif
424 break;
425 }
426 default:
427 return 0;
428 }
429 /* Update bounds if pixels on this line were modified */
430 if (xx1 < width) {
431 if (xx1 < rect->xmin) rect->xmin = xx1;
432 if (xx2 > rect->xmax) rect->xmax = xx2;
433 if (yy < rect->ymin) rect->ymin = yy;
434 if (yy > rect->ymax) rect->ymax = yy;
435 }
436 NEXT_LINE:
437 src_line += fbs->src_pitch;
438 dst_line += fbs->dst_pitch;
439 }
440
441 if (rect->ymin > rect->ymax) { /* nothing changed */
442 return 0;
443 }
444
445 /* Always clear the dirty VGA bits */
446 cpu_physical_memory_reset_dirty(dirty_base + rect->ymin * fbs->src_pitch,
447 dirty_base + (rect->ymax+1)* fbs->src_pitch,
448 VGA_DIRTY_FLAG);
449 return 1;
450 }
451
452
goldfish_fb_update_display(void * opaque)453 static void goldfish_fb_update_display(void *opaque)
454 {
455 struct goldfish_fb_state *s = (struct goldfish_fb_state *)opaque;
456 uint32_t base;
457 uint8_t* dst_line;
458 uint8_t* src_line;
459 int full_update = 0;
460 int width, height, pitch;
461
462 base = s->fb_base;
463 if(base == 0)
464 return;
465
466 if((s->int_enable & FB_INT_VSYNC) && !(s->int_status & FB_INT_VSYNC)) {
467 s->int_status |= FB_INT_VSYNC;
468 goldfish_device_set_irq(&s->dev, 0, 1);
469 }
470
471 if(s->need_update) {
472 full_update = 1;
473 if(s->need_int) {
474 s->int_status |= FB_INT_BASE_UPDATE_DONE;
475 if(s->int_enable & FB_INT_BASE_UPDATE_DONE)
476 goldfish_device_set_irq(&s->dev, 0, 1);
477 }
478 s->need_int = 0;
479 s->need_update = 0;
480 }
481
482 src_line = qemu_get_ram_ptr( base );
483
484 dst_line = s->ds->surface->data;
485 pitch = s->ds->surface->linesize;
486 width = s->ds->surface->width;
487 height = s->ds->surface->height;
488
489 FbUpdateState fbs;
490 FbUpdateRect rect;
491
492 fbs.width = width;
493 fbs.height = height;
494 fbs.dst_pixels = dst_line;
495 fbs.dst_pitch = pitch;
496 fbs.bytes_per_pixel = goldfish_fb_get_bytes_per_pixel(s);
497
498 fbs.src_pixels = src_line;
499 fbs.src_pitch = width*s->ds->surface->pf.bytes_per_pixel;
500
501
502 #if STATS
503 if (full_update)
504 stats_full_updates += 1;
505 if (++stats_counter == 120) {
506 stats_total += stats_counter;
507 stats_total_full_updates += stats_full_updates;
508
509 printf( "full update stats: peak %.2f %% total %.2f %%\n",
510 stats_full_updates*100.0/stats_counter,
511 stats_total_full_updates*100.0/stats_total );
512
513 stats_counter = 0;
514 stats_full_updates = 0;
515 }
516 #endif /* STATS */
517
518 if (s->blank)
519 {
520 memset( dst_line, 0, height*pitch );
521 rect.xmin = 0;
522 rect.ymin = 0;
523 rect.xmax = width-1;
524 rect.ymax = height-1;
525 }
526 else
527 {
528 if (full_update) { /* don't use dirty-bits optimization */
529 base = 0;
530 }
531 if (compute_fb_update_rect_linear(&fbs, base, &rect) == 0) {
532 return;
533 }
534 }
535
536 rect.xmax += 1;
537 rect.ymax += 1;
538 #if 0
539 printf("goldfish_fb_update_display (y:%d,h:%d,x=%d,w=%d)\n",
540 rect.ymin, rect.ymax-rect.ymin, rect.xmin, rect.xmax-rect.xmin);
541 #endif
542
543 dpy_update(s->ds, rect.xmin, rect.ymin, rect.xmax-rect.xmin, rect.ymax-rect.ymin);
544 }
545
goldfish_fb_invalidate_display(void * opaque)546 static void goldfish_fb_invalidate_display(void * opaque)
547 {
548 // is this called?
549 struct goldfish_fb_state *s = (struct goldfish_fb_state *)opaque;
550 s->need_update = 1;
551 }
552
goldfish_fb_read(void * opaque,hwaddr offset)553 static uint32_t goldfish_fb_read(void *opaque, hwaddr offset)
554 {
555 uint32_t ret;
556 struct goldfish_fb_state *s = opaque;
557
558 switch(offset) {
559 case FB_GET_WIDTH:
560 ret = ds_get_width(s->ds);
561 //printf("FB_GET_WIDTH => %d\n", ret);
562 return ret;
563
564 case FB_GET_HEIGHT:
565 ret = ds_get_height(s->ds);
566 //printf( "FB_GET_HEIGHT = %d\n", ret);
567 return ret;
568
569 case FB_INT_STATUS:
570 ret = s->int_status & s->int_enable;
571 if(ret) {
572 s->int_status &= ~ret;
573 goldfish_device_set_irq(&s->dev, 0, 0);
574 }
575 return ret;
576
577 case FB_GET_PHYS_WIDTH:
578 ret = pixels_to_mm( ds_get_width(s->ds), s->dpi );
579 //printf( "FB_GET_PHYS_WIDTH => %d\n", ret );
580 return ret;
581
582 case FB_GET_PHYS_HEIGHT:
583 ret = pixels_to_mm( ds_get_height(s->ds), s->dpi );
584 //printf( "FB_GET_PHYS_HEIGHT => %d\n", ret );
585 return ret;
586
587 case FB_GET_FORMAT:
588 return goldfish_fb_get_pixel_format(s);
589
590 default:
591 cpu_abort (cpu_single_env, "goldfish_fb_read: Bad offset %x\n", offset);
592 return 0;
593 }
594 }
595
goldfish_fb_write(void * opaque,hwaddr offset,uint32_t val)596 static void goldfish_fb_write(void *opaque, hwaddr offset,
597 uint32_t val)
598 {
599 struct goldfish_fb_state *s = opaque;
600
601 switch(offset) {
602 case FB_INT_ENABLE:
603 s->int_enable = val;
604 goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
605 break;
606 case FB_SET_BASE: {
607 int need_resize = !s->base_valid;
608 s->fb_base = val;
609 s->int_status &= ~FB_INT_BASE_UPDATE_DONE;
610 s->need_update = 1;
611 s->need_int = 1;
612 s->base_valid = 1;
613 if(s->set_rotation != s->rotation) {
614 //printf("FB_SET_BASE: rotation : %d => %d\n", s->rotation, s->set_rotation);
615 s->rotation = s->set_rotation;
616 need_resize = 1;
617 }
618 goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
619 if (need_resize) {
620 //printf("FB_SET_BASE: need resize (rotation=%d)\n", s->rotation );
621 dpy_resize(s->ds);
622 }
623 } break;
624 case FB_SET_ROTATION:
625 //printf( "FB_SET_ROTATION %d\n", val);
626 s->set_rotation = val;
627 break;
628 case FB_SET_BLANK:
629 s->blank = val;
630 s->need_update = 1;
631 break;
632 default:
633 cpu_abort (cpu_single_env, "goldfish_fb_write: Bad offset %x\n", offset);
634 }
635 }
636
637 static CPUReadMemoryFunc *goldfish_fb_readfn[] = {
638 goldfish_fb_read,
639 goldfish_fb_read,
640 goldfish_fb_read
641 };
642
643 static CPUWriteMemoryFunc *goldfish_fb_writefn[] = {
644 goldfish_fb_write,
645 goldfish_fb_write,
646 goldfish_fb_write
647 };
648
goldfish_fb_init(int id)649 void goldfish_fb_init(int id)
650 {
651 struct goldfish_fb_state *s;
652
653 s = (struct goldfish_fb_state *)g_malloc0(sizeof(*s));
654 s->dev.name = "goldfish_fb";
655 s->dev.id = id;
656 s->dev.size = 0x1000;
657 s->dev.irq_count = 1;
658
659 s->ds = graphic_console_init(goldfish_fb_update_display,
660 goldfish_fb_invalidate_display,
661 NULL,
662 NULL,
663 s);
664
665 s->dpi = 165; /* XXX: Find better way to get actual value ! */
666
667 /* IMPORTANT: DO NOT COMPUTE s->pixel_format and s->bytes_per_pixel
668 * here because the display surface is going to change later.
669 */
670 s->bytes_per_pixel = 0;
671 s->pixel_format = -1;
672
673 goldfish_device_add(&s->dev, goldfish_fb_readfn, goldfish_fb_writefn, s);
674
675 register_savevm(NULL,
676 "goldfish_fb",
677 0,
678 GOLDFISH_FB_SAVE_VERSION,
679 goldfish_fb_save,
680 goldfish_fb_load,
681 s);
682 }
683