• 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 "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