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