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 "goldfish_device.h"
     15 #include "framebuffer.h"
     16 
     17 enum {
     18     FB_GET_WIDTH        = 0x00,
     19     FB_GET_HEIGHT       = 0x04,
     20     FB_INT_STATUS       = 0x08,
     21     FB_INT_ENABLE       = 0x0c,
     22     FB_SET_BASE         = 0x10,
     23     FB_SET_ROTATION     = 0x14,
     24     FB_SET_BLANK        = 0x18,
     25     FB_GET_PHYS_WIDTH   = 0x1c,
     26     FB_GET_PHYS_HEIGHT  = 0x20,
     27 
     28     FB_INT_VSYNC             = 1U << 0,
     29     FB_INT_BASE_UPDATE_DONE  = 1U << 1
     30 };
     31 
     32 struct goldfish_fb_state {
     33     struct goldfish_device dev;
     34     QFrameBuffer*  qfbuff;
     35     uint32_t fb_base;
     36     uint32_t base_valid : 1;
     37     uint32_t need_update : 1;
     38     uint32_t need_int : 1;
     39     uint32_t set_rotation : 2;
     40     uint32_t blank : 1;
     41     uint32_t int_status;
     42     uint32_t int_enable;
     43     int      rotation;   /* 0, 1, 2 or 3 */
     44 };
     45 
     46 #define  GOLDFISH_FB_SAVE_VERSION  1
     47 
     48 static void goldfish_fb_save(QEMUFile*  f, void*  opaque)
     49 {
     50     struct goldfish_fb_state*  s = opaque;
     51 
     52     QFrameBuffer*  q = s->qfbuff;
     53 
     54     qemu_put_be32(f, q->width);
     55     qemu_put_be32(f, q->height);
     56     qemu_put_be32(f, q->pitch);
     57     qemu_put_byte(f, q->rotation);
     58 
     59     qemu_put_be32(f, s->fb_base);
     60     qemu_put_byte(f, s->base_valid);
     61     qemu_put_byte(f, s->need_update);
     62     qemu_put_byte(f, s->need_int);
     63     qemu_put_byte(f, s->set_rotation);
     64     qemu_put_byte(f, s->blank);
     65     qemu_put_be32(f, s->int_status);
     66     qemu_put_be32(f, s->int_enable);
     67     qemu_put_be32(f, s->rotation);
     68 }
     69 
     70 static int  goldfish_fb_load(QEMUFile*  f, void*  opaque, int  version_id)
     71 {
     72     struct goldfish_fb_state*  s   = opaque;
     73 
     74     QFrameBuffer*              q   = s->qfbuff;
     75     int                        ret = -1;
     76     int                        ds_w, ds_h, ds_pitch, ds_rot;
     77 
     78     if (version_id != GOLDFISH_FB_SAVE_VERSION)
     79         goto Exit;
     80 
     81     ds_w     = qemu_get_be32(f);
     82     ds_h     = qemu_get_be32(f);
     83     ds_pitch = qemu_get_be32(f);
     84     ds_rot   = qemu_get_byte(f);
     85 
     86     if (q->width != ds_w      ||
     87         q->height != ds_h     ||
     88         q->pitch != ds_pitch  ||
     89         q->rotation != ds_rot )
     90     {
     91         /* XXX: We should be able to force a resize/rotation from here ? */
     92         fprintf(stderr, "%s: framebuffer dimensions mismatch\n", __FUNCTION__);
     93         goto Exit;
     94     }
     95 
     96     s->fb_base      = qemu_get_be32(f);
     97     s->base_valid   = qemu_get_byte(f);
     98     s->need_update  = qemu_get_byte(f);
     99     s->need_int     = qemu_get_byte(f);
    100     s->set_rotation = qemu_get_byte(f);
    101     s->blank        = qemu_get_byte(f);
    102     s->int_status   = qemu_get_be32(f);
    103     s->int_enable   = qemu_get_be32(f);
    104     s->rotation     = qemu_get_be32(f);
    105 
    106     /* force a refresh */
    107     s->need_update = 1;
    108 
    109     ret = 0;
    110 Exit:
    111     return ret;
    112 }
    113 
    114 
    115 #define  STATS  0
    116 
    117 #if STATS
    118 static int   stats_counter;
    119 static long  stats_total;
    120 static int   stats_full_updates;
    121 static long  stats_total_full_updates;
    122 #endif
    123 
    124 static void goldfish_fb_update_display(void *opaque)
    125 {
    126     struct goldfish_fb_state *s = (struct goldfish_fb_state *)opaque;
    127     uint32_t addr;
    128     uint32_t base;
    129 
    130     uint8_t*  dst_line;
    131     uint8_t*  src_line;
    132     int y_first, y_last = 0;
    133     int full_update = 0;
    134     int    width, height, pitch;
    135 
    136     base = s->fb_base;
    137     if(base == 0)
    138         return;
    139 
    140     if((s->int_enable & FB_INT_VSYNC) && !(s->int_status & FB_INT_VSYNC)) {
    141         s->int_status |= FB_INT_VSYNC;
    142         goldfish_device_set_irq(&s->dev, 0, 1);
    143     }
    144 
    145     y_first = -1;
    146     addr  = base;
    147     if(s->need_update) {
    148         full_update = 1;
    149         if(s->need_int) {
    150             s->int_status |= FB_INT_BASE_UPDATE_DONE;
    151             if(s->int_enable & FB_INT_BASE_UPDATE_DONE)
    152                 goldfish_device_set_irq(&s->dev, 0, 1);
    153         }
    154         s->need_int = 0;
    155         s->need_update = 0;
    156     }
    157 
    158     src_line  = qemu_get_ram_ptr( base );
    159     dst_line  = s->qfbuff->pixels;
    160     pitch     = s->qfbuff->pitch;
    161     width     = s->qfbuff->width;
    162     height    = s->qfbuff->height;
    163 
    164 #if STATS
    165     if (full_update)
    166         stats_full_updates += 1;
    167     if (++stats_counter == 120) {
    168         stats_total               += stats_counter;
    169         stats_total_full_updates  += stats_full_updates;
    170 
    171         printf( "full update stats:  peak %.2f %%  total %.2f %%\n",
    172                 stats_full_updates*100.0/stats_counter,
    173                 stats_total_full_updates*100.0/stats_total );
    174 
    175         stats_counter      = 0;
    176         stats_full_updates = 0;
    177     }
    178 #endif /* STATS */
    179 
    180     if (s->blank)
    181     {
    182         memset( dst_line, 0, height*pitch );
    183         y_first = 0;
    184         y_last  = height-1;
    185     }
    186     else if (full_update)
    187     {
    188         int  yy;
    189 
    190         for (yy = 0; yy < height; yy++, dst_line += pitch, src_line += width*2)
    191         {
    192             uint16_t*  src = (uint16_t*) src_line;
    193             uint16_t*  dst = (uint16_t*) dst_line;
    194             int        nn;
    195 
    196             for (nn = 0; nn < width; nn++) {
    197                 unsigned   spix = src[nn];
    198                 unsigned   dpix = dst[nn];
    199 #if HOST_WORDS_BIGENDIAN
    200                 spix = ((spix << 8) | (spix >> 8)) & 0xffff;
    201 #else
    202                 if (spix != dpix)
    203                     break;
    204 #endif
    205             }
    206 
    207             if (nn == width)
    208                 continue;
    209 
    210 #if HOST_WORDS_BIGENDIAN
    211             for ( ; nn < width; nn++ ) {
    212                 unsigned   spix = src[nn];
    213                 dst[nn] = (uint16_t)((spix << 8) | (spix >> 8));
    214             }
    215 #else
    216             memcpy( dst+nn, src+nn, (width-nn)*2 );
    217 #endif
    218 
    219             y_first = (y_first < 0) ? yy : y_first;
    220             y_last  = yy;
    221         }
    222     }
    223     else  /* not a full update, should not happen very often with Android */
    224     {
    225         int  yy;
    226 
    227         for (yy = 0; yy < height; yy++, dst_line += pitch, src_line += width*2)
    228         {
    229             uint16_t*  src   = (uint16_t*) src_line;
    230             uint16_t*  dst   = (uint16_t*) dst_line;
    231             int        len   = width*2;
    232 #if HOST_WORDS_BIGENDIAN
    233             int        nn;
    234 #endif
    235             int        dirty = 0;
    236 
    237             while (len > 0) {
    238                 int  len2 = TARGET_PAGE_SIZE - (addr & (TARGET_PAGE_SIZE-1));
    239 
    240                 if (len2 > len)
    241                     len2 = len;
    242 
    243                 dirty |= cpu_physical_memory_get_dirty(addr, VGA_DIRTY_FLAG);
    244                 addr  += len2;
    245                 len   -= len2;
    246             }
    247 
    248             if (!dirty)
    249                 continue;
    250 
    251 #if HOST_WORDS_BIGENDIAN
    252             for (nn = 0; nn < width; nn++ ) {
    253                 unsigned   spix = src[nn];
    254                 dst[nn] = (uint16_t)((spix << 8) | (spix >> 8));
    255             }
    256 #else
    257             memcpy( dst, src, width*2 );
    258 #endif
    259 
    260             y_first = (y_first < 0) ? yy : y_first;
    261             y_last  = yy;
    262         }
    263     }
    264 
    265     if (y_first < 0)
    266       return;
    267 
    268     y_last += 1;
    269     //printf("goldfish_fb_update_display %d %d, base %x\n", first, last, base);
    270 
    271     cpu_physical_memory_reset_dirty(base + y_first * width * 2,
    272                                     base + y_last * width * 2,
    273                                     VGA_DIRTY_FLAG);
    274 
    275     qframebuffer_update( s->qfbuff, 0, y_first, width, y_last-y_first );
    276 }
    277 
    278 static void goldfish_fb_invalidate_display(void * opaque)
    279 {
    280     // is this called?
    281     struct goldfish_fb_state *s = (struct goldfish_fb_state *)opaque;
    282     s->need_update = 1;
    283 }
    284 
    285 static void  goldfish_fb_detach_display(void*  opaque)
    286 {
    287     struct goldfish_fb_state *s = (struct goldfish_fb_state *)opaque;
    288     s->qfbuff = NULL;
    289 }
    290 
    291 static uint32_t goldfish_fb_read(void *opaque, target_phys_addr_t offset)
    292 {
    293     uint32_t ret;
    294     struct goldfish_fb_state *s = opaque;
    295 
    296     switch(offset) {
    297         case FB_GET_WIDTH:
    298             ret = s->qfbuff->width;
    299             //printf("FB_GET_WIDTH => %d\n", ret);
    300             return ret;
    301 
    302         case FB_GET_HEIGHT:
    303             ret = s->qfbuff->height;
    304             //printf( "FB_GET_HEIGHT = %d\n", ret);
    305             return ret;
    306 
    307         case FB_INT_STATUS:
    308             ret = s->int_status & s->int_enable;
    309             if(ret) {
    310                 s->int_status &= ~ret;
    311                 goldfish_device_set_irq(&s->dev, 0, 0);
    312             }
    313             return ret;
    314 
    315         case FB_GET_PHYS_WIDTH:
    316             ret = s->qfbuff->phys_width_mm;
    317             //printf( "FB_GET_PHYS_WIDTH => %d\n", ret );
    318             return ret;
    319 
    320         case FB_GET_PHYS_HEIGHT:
    321             ret = s->qfbuff->phys_height_mm;
    322             //printf( "FB_GET_PHYS_HEIGHT => %d\n", ret );
    323             return ret;
    324 
    325         default:
    326             cpu_abort (cpu_single_env, "goldfish_fb_read: Bad offset %x\n", offset);
    327             return 0;
    328     }
    329 }
    330 
    331 static void goldfish_fb_write(void *opaque, target_phys_addr_t offset,
    332                         uint32_t val)
    333 {
    334     struct goldfish_fb_state *s = opaque;
    335 
    336     switch(offset) {
    337         case FB_INT_ENABLE:
    338             s->int_enable = val;
    339             goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
    340             break;
    341         case FB_SET_BASE: {
    342             int need_resize = !s->base_valid;
    343             s->fb_base = val;
    344             s->int_status &= ~FB_INT_BASE_UPDATE_DONE;
    345             s->need_update = 1;
    346             s->need_int = 1;
    347             s->base_valid = 1;
    348             if(s->set_rotation != s->rotation) {
    349                 //printf("FB_SET_BASE: rotation : %d => %d\n", s->rotation, s->set_rotation);
    350                 s->rotation = s->set_rotation;
    351                 need_resize = 1;
    352             }
    353             goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
    354             if (need_resize) {
    355                 //printf("FB_SET_BASE: need resize (rotation=%d)\n", s->rotation );
    356                 qframebuffer_rotate( s->qfbuff, s->rotation );
    357             }
    358             } break;
    359         case FB_SET_ROTATION:
    360             //printf( "FB_SET_ROTATION %d\n", val);
    361             s->set_rotation = val;
    362             break;
    363         case FB_SET_BLANK:
    364             s->blank = val;
    365             s->need_update = 1;
    366             break;
    367         default:
    368             cpu_abort (cpu_single_env, "goldfish_fb_write: Bad offset %x\n", offset);
    369     }
    370 }
    371 
    372 static CPUReadMemoryFunc *goldfish_fb_readfn[] = {
    373    goldfish_fb_read,
    374    goldfish_fb_read,
    375    goldfish_fb_read
    376 };
    377 
    378 static CPUWriteMemoryFunc *goldfish_fb_writefn[] = {
    379    goldfish_fb_write,
    380    goldfish_fb_write,
    381    goldfish_fb_write
    382 };
    383 
    384 void goldfish_fb_init(DisplayState *ds, int id)
    385 {
    386     struct goldfish_fb_state *s;
    387 
    388     s = (struct goldfish_fb_state *)qemu_mallocz(sizeof(*s));
    389     s->dev.name = "goldfish_fb";
    390     s->dev.id = id;
    391     s->dev.size = 0x1000;
    392     s->dev.irq_count = 1;
    393 
    394     s->qfbuff = qframebuffer_fifo_get();
    395     qframebuffer_set_producer( s->qfbuff, s,
    396                                goldfish_fb_update_display,
    397                                goldfish_fb_invalidate_display,
    398                                goldfish_fb_detach_display );
    399 
    400     goldfish_device_add(&s->dev, goldfish_fb_readfn, goldfish_fb_writefn, s);
    401 
    402     register_savevm( "goldfish_fb", 0, GOLDFISH_FB_SAVE_VERSION,
    403                      goldfish_fb_save, goldfish_fb_load, s);
    404 }
    405 
    406