• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "qemu_file.h"
13 #include "android/android.h"
14 #include "android/utils/debug.h"
15 #include "android/utils/duff.h"
16 #include "goldfish_device.h"
17 #include "console.h"
18 
19 /* These values *must* match the platform definitions found under
20  * hardware/libhardware/include/hardware/hardware.h
21  */
22 enum {
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 
32 enum {
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,
42     FB_GET_FORMAT       = 0x24,
43 
44     FB_INT_VSYNC             = 1U << 0,
45     FB_INT_BASE_UPDATE_DONE  = 1U << 1
46 };
47 
48 struct goldfish_fb_state {
49     struct goldfish_device dev;
50     DisplayState*  ds;
51     int      pixel_format;
52     int      bytes_per_pixel;
53     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 */
62     int      dpi;
63 };
64 
65 #define  GOLDFISH_FB_SAVE_VERSION  2
66 
goldfish_fb_save(QEMUFile * f,void * opaque)67 static void goldfish_fb_save(QEMUFile*  f, void*  opaque)
68 {
69     struct goldfish_fb_state*  s = opaque;
70 
71     DisplayState*  ds = s->ds;
72 
73     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);
77 
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);
87     qemu_put_be32(f, s->dpi);
88 }
89 
goldfish_fb_load(QEMUFile * f,void * opaque,int version_id)90 static int  goldfish_fb_load(QEMUFile*  f, void*  opaque, int  version_id)
91 {
92     struct goldfish_fb_state*  s   = opaque;
93     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 
104     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)
110     {
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);
125     s->dpi          = qemu_get_be32(f);
126 
127     /* force a refresh */
128     s->need_update = 1;
129 
130     ret = 0;
131 Exit:
132     return ret;
133 }
134 
135 /* Type used to record a mapping from display surface pixel format to
136  * HAL pixel format */
137 typedef 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  */
goldfish_fb_get_pixel_format(struct goldfish_fb_state * s)152 static 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 },
159         { HAL_PIXEL_FORMAT_RGBX_8888, 32, 4, 0xff0000, 0xff00, 0xff, 0x0 },
160         { HAL_PIXEL_FORMAT_RGBA_8888, 32, 4, 0xff0000, 0xff00, 0xff, 0xff000000 },
161         { -1, }
162     };
163 
164     /* Determine HAL pixel format value based on s->ds */
165     struct PixelFormat* pf = &s->ds->surface->pf;
166     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     }
180 
181     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 
goldfish_fb_get_bytes_per_pixel(struct goldfish_fb_state * s)205 static 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 
213 static int
pixels_to_mm(int pixels,int dpi)214 pixels_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 
224 
225 #define  STATS  0
226 
227 #if STATS
228 static int   stats_counter;
229 static long  stats_total;
230 static int   stats_full_updates;
231 static long  stats_total_full_updates;
232 #endif
233 
234 /* 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  */
239 typedef 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  */
254 typedef 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  */
273 static int
compute_fb_update_rect_linear(FbUpdateState * fbs,uint32_t dirty_base,FbUpdateRect * rect)274 compute_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 
320             xx1 = 0;
321             DUFF4(width, {
322                 uint16_t spix = src[xx1];
323 #if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN)
324                 spix = (uint16_t)((spix << 8) | (spix >> 8));
325 #endif
326                 if (spix != dst[xx1])
327                     break;
328                 xx1++;
329             });
330             if (xx1 == width) {
331                 break;
332             }
333             xx2 = width-1;
334             DUFF4(xx2-xx1, {
335                 if (src[xx2] != dst[xx2])
336                     break;
337                 xx2--;
338             });
339 #if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN)
340             /* Convert the guest pixels into host ones */
341             int xx = xx1;
342             DUFF4(xx2-xx1+1,{
343                 unsigned   spix = src[xx];
344                 dst[xx] = (uint16_t)((spix << 8) | (spix >> 8));
345                 xx++;
346             });
347 #else
348             memcpy( dst+xx1, src+xx1, (xx2-xx1+1)*2 );
349 #endif
350             break;
351         }
352 
353         case 3:
354         {
355             xx1 = 0;
356             DUFF4(width, {
357                 int xx = xx1*3;
358                 if (src_line[xx+0] != dst_line[xx+0] ||
359                     src_line[xx+1] != dst_line[xx+1] ||
360                     src_line[xx+2] != dst_line[xx+2]) {
361                     break;
362                 }
363                 xx1 ++;
364             });
365             if (xx1 == width) {
366                 break;
367             }
368             xx2 = width-1;
369             DUFF4(xx2-xx1,{
370                 int xx = xx2*3;
371                 if (src_line[xx+0] != dst_line[xx+0] ||
372                     src_line[xx+1] != dst_line[xx+1] ||
373                     src_line[xx+2] != dst_line[xx+2]) {
374                     break;
375                 }
376                 xx2--;
377             });
378             memcpy( dst_line+xx1*3, src_line+xx1*3, (xx2-xx1+1)*3 );
379             break;
380         }
381 
382         case 4:
383         {
384             const uint32_t* src = (const uint32_t*) src_line;
385             uint32_t*       dst = (uint32_t*) dst_line;
386 
387             xx1 = 0;
388             DUFF4(width, {
389                 uint32_t spix = src[xx1];
390 #if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN)
391                 spix = (spix << 16) | (spix >> 16);
392                 spix = ((spix << 8) & 0xff00ff00) | ((spix >> 8) & 0x00ff00ff);
393 #endif
394                 if (spix != dst[xx1]) {
395                     break;
396                 }
397                 xx1++;
398             });
399             if (xx1 == width) {
400                 break;
401             }
402             xx2 = width-1;
403             DUFF4(xx2-xx1,{
404                 if (src[xx2] != dst[xx2]) {
405                     break;
406                 }
407                 xx2--;
408             });
409 #if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN)
410             /* Convert the guest pixels into host ones */
411             int xx = xx1;
412             DUFF4(xx2-xx1+1,{
413                 uint32_t   spix = src[xx];
414                 spix = (spix << 16) | (spix >> 16);
415                 spix = ((spix << 8) & 0xff00ff00) | ((spix >> 8) & 0x00ff00ff);
416                 dst[xx] = spix;
417                 xx++;
418             })
419 #else
420             memcpy( dst+xx1, src+xx1, (xx2-xx1+1)*4 );
421 #endif
422             break;
423         }
424         default:
425             return 0;
426         }
427         /* Update bounds if pixels on this line were modified */
428         if (xx1 < width) {
429             if (xx1 < rect->xmin) rect->xmin = xx1;
430             if (xx2 > rect->xmax) rect->xmax = xx2;
431             if (yy < rect->ymin) rect->ymin = yy;
432             if (yy > rect->ymax) rect->ymax = yy;
433         }
434     NEXT_LINE:
435         src_line += fbs->src_pitch;
436         dst_line += fbs->dst_pitch;
437     }
438 
439     if (rect->ymin > rect->ymax) { /* nothing changed */
440         return 0;
441     }
442 
443     /* Always clear the dirty VGA bits */
444     cpu_physical_memory_reset_dirty(dirty_base + rect->ymin * fbs->src_pitch,
445                                     dirty_base + (rect->ymax+1)* fbs->src_pitch,
446                                     VGA_DIRTY_FLAG);
447     return 1;
448 }
449 
450 
goldfish_fb_update_display(void * opaque)451 static void goldfish_fb_update_display(void *opaque)
452 {
453     struct goldfish_fb_state *s = (struct goldfish_fb_state *)opaque;
454     uint32_t base;
455     uint8_t*  dst_line;
456     uint8_t*  src_line;
457     int full_update = 0;
458     int  width, height, pitch;
459 
460     base = s->fb_base;
461     if(base == 0)
462         return;
463 
464     if((s->int_enable & FB_INT_VSYNC) && !(s->int_status & FB_INT_VSYNC)) {
465         s->int_status |= FB_INT_VSYNC;
466         goldfish_device_set_irq(&s->dev, 0, 1);
467     }
468 
469     if(s->need_update) {
470         full_update = 1;
471         if(s->need_int) {
472             s->int_status |= FB_INT_BASE_UPDATE_DONE;
473             if(s->int_enable & FB_INT_BASE_UPDATE_DONE)
474                 goldfish_device_set_irq(&s->dev, 0, 1);
475         }
476         s->need_int = 0;
477         s->need_update = 0;
478     }
479 
480     src_line  = qemu_get_ram_ptr( base );
481 
482     dst_line  = s->ds->surface->data;
483     pitch     = s->ds->surface->linesize;
484     width     = s->ds->surface->width;
485     height    = s->ds->surface->height;
486 
487     FbUpdateState  fbs;
488     FbUpdateRect   rect;
489 
490     fbs.width      = width;
491     fbs.height     = height;
492     fbs.dst_pixels = dst_line;
493     fbs.dst_pitch  = pitch;
494     fbs.bytes_per_pixel = goldfish_fb_get_bytes_per_pixel(s);
495 
496     fbs.src_pixels = src_line;
497     fbs.src_pitch  = width*s->ds->surface->pf.bytes_per_pixel;
498 
499 
500 #if STATS
501     if (full_update)
502         stats_full_updates += 1;
503     if (++stats_counter == 120) {
504         stats_total               += stats_counter;
505         stats_total_full_updates  += stats_full_updates;
506 
507         printf( "full update stats:  peak %.2f %%  total %.2f %%\n",
508                 stats_full_updates*100.0/stats_counter,
509                 stats_total_full_updates*100.0/stats_total );
510 
511         stats_counter      = 0;
512         stats_full_updates = 0;
513     }
514 #endif /* STATS */
515 
516     if (s->blank)
517     {
518         memset( dst_line, 0, height*pitch );
519         rect.xmin = 0;
520         rect.ymin = 0;
521         rect.xmax = width-1;
522         rect.ymax = height-1;
523     }
524     else
525     {
526         if (full_update) { /* don't use dirty-bits optimization */
527             base = 0;
528         }
529         if (compute_fb_update_rect_linear(&fbs, base, &rect) == 0) {
530             return;
531         }
532     }
533 
534     rect.xmax += 1;
535     rect.ymax += 1;
536 #if 0
537     printf("goldfish_fb_update_display (y:%d,h:%d,x=%d,w=%d)\n",
538            rect.ymin, rect.ymax-rect.ymin, rect.xmin, rect.xmax-rect.xmin);
539 #endif
540 
541     dpy_update(s->ds, rect.xmin, rect.ymin, rect.xmax-rect.xmin, rect.ymax-rect.ymin);
542 }
543 
goldfish_fb_invalidate_display(void * opaque)544 static void goldfish_fb_invalidate_display(void * opaque)
545 {
546     // is this called?
547     struct goldfish_fb_state *s = (struct goldfish_fb_state *)opaque;
548     s->need_update = 1;
549 }
550 
goldfish_fb_read(void * opaque,target_phys_addr_t offset)551 static uint32_t goldfish_fb_read(void *opaque, target_phys_addr_t offset)
552 {
553     uint32_t ret;
554     struct goldfish_fb_state *s = opaque;
555 
556     switch(offset) {
557         case FB_GET_WIDTH:
558             ret = ds_get_width(s->ds);
559             //printf("FB_GET_WIDTH => %d\n", ret);
560             return ret;
561 
562         case FB_GET_HEIGHT:
563             ret = ds_get_height(s->ds);
564             //printf( "FB_GET_HEIGHT = %d\n", ret);
565             return ret;
566 
567         case FB_INT_STATUS:
568             ret = s->int_status & s->int_enable;
569             if(ret) {
570                 s->int_status &= ~ret;
571                 goldfish_device_set_irq(&s->dev, 0, 0);
572             }
573             return ret;
574 
575         case FB_GET_PHYS_WIDTH:
576             ret = pixels_to_mm( ds_get_width(s->ds), s->dpi );
577             //printf( "FB_GET_PHYS_WIDTH => %d\n", ret );
578             return ret;
579 
580         case FB_GET_PHYS_HEIGHT:
581             ret = pixels_to_mm( ds_get_height(s->ds), s->dpi );
582             //printf( "FB_GET_PHYS_HEIGHT => %d\n", ret );
583             return ret;
584 
585         case FB_GET_FORMAT:
586             return goldfish_fb_get_pixel_format(s);
587 
588         default:
589             cpu_abort (cpu_single_env, "goldfish_fb_read: Bad offset %x\n", offset);
590             return 0;
591     }
592 }
593 
goldfish_fb_write(void * opaque,target_phys_addr_t offset,uint32_t val)594 static void goldfish_fb_write(void *opaque, target_phys_addr_t offset,
595                         uint32_t val)
596 {
597     struct goldfish_fb_state *s = opaque;
598 
599     switch(offset) {
600         case FB_INT_ENABLE:
601             s->int_enable = val;
602             goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
603             break;
604         case FB_SET_BASE: {
605             int need_resize = !s->base_valid;
606             s->fb_base = val;
607             s->int_status &= ~FB_INT_BASE_UPDATE_DONE;
608             s->need_update = 1;
609             s->need_int = 1;
610             s->base_valid = 1;
611             if(s->set_rotation != s->rotation) {
612                 //printf("FB_SET_BASE: rotation : %d => %d\n", s->rotation, s->set_rotation);
613                 s->rotation = s->set_rotation;
614                 need_resize = 1;
615             }
616             goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
617             if (need_resize) {
618                 //printf("FB_SET_BASE: need resize (rotation=%d)\n", s->rotation );
619                 dpy_resize(s->ds);
620             }
621             } break;
622         case FB_SET_ROTATION:
623             //printf( "FB_SET_ROTATION %d\n", val);
624             s->set_rotation = val;
625             break;
626         case FB_SET_BLANK:
627             s->blank = val;
628             s->need_update = 1;
629             break;
630         default:
631             cpu_abort (cpu_single_env, "goldfish_fb_write: Bad offset %x\n", offset);
632     }
633 }
634 
635 static CPUReadMemoryFunc *goldfish_fb_readfn[] = {
636    goldfish_fb_read,
637    goldfish_fb_read,
638    goldfish_fb_read
639 };
640 
641 static CPUWriteMemoryFunc *goldfish_fb_writefn[] = {
642    goldfish_fb_write,
643    goldfish_fb_write,
644    goldfish_fb_write
645 };
646 
goldfish_fb_init(int id)647 void goldfish_fb_init(int id)
648 {
649     struct goldfish_fb_state *s;
650 
651     s = (struct goldfish_fb_state *)qemu_mallocz(sizeof(*s));
652     s->dev.name = "goldfish_fb";
653     s->dev.id = id;
654     s->dev.size = 0x1000;
655     s->dev.irq_count = 1;
656 
657     s->ds = graphic_console_init(goldfish_fb_update_display,
658                                  goldfish_fb_invalidate_display,
659                                  NULL,
660                                  NULL,
661                                  s);
662 
663     s->dpi = 165;  /* XXX: Find better way to get actual value ! */
664 
665     /* IMPORTANT: DO NOT COMPUTE s->pixel_format and s->bytes_per_pixel
666      * here because the display surface is going to change later.
667      */
668     s->bytes_per_pixel = 0;
669     s->pixel_format    = -1;
670 
671     goldfish_device_add(&s->dev, goldfish_fb_readfn, goldfish_fb_writefn, s);
672 
673     register_savevm( "goldfish_fb", 0, GOLDFISH_FB_SAVE_VERSION,
674                      goldfish_fb_save, goldfish_fb_load, s);
675 }
676