Home | History | Annotate | Download | only in hw
      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 
     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 
     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  */
    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 
    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
    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
    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 
    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 
    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 
    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 
    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 
    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