Patchwork Re: [PATCH 5/5] linux fbdev display driver.

login
register
mail settings
Submitter Julian Pidancet
Date June 16, 2010, 4:22 p.m.
Message ID <4C18FA58.5080808@citrix.com>
Download mbox | patch
Permalink /patch/55912/
State New
Headers show

Comments

Julian Pidancet - June 16, 2010, 4:22 p.m.
On 06/16/2010 01:44 PM, Stefano Stabellini wrote:
> 
> the patch still doesn't use the display allocator interface, but it
> shouldn't be difficult to implement support for it on top of this patch,
> so it is fine by me.
> 

This patch adds display allocator support in the fbdev driver.
This way we avoid memcpying when the guest surface is compatible with the physical surface.

Patch

diff --git a/fbdev.c b/fbdev.c
index 54f2381..83104c0 100644
--- a/fbdev.c
+++ b/fbdev.c
@@ -69,11 +69,10 @@  static int fb_switch_state = FB_ACTIVE;
 static DisplayChangeListener      *dcl;
 static QemuPfConv                 *conv;
 static PixelFormat                fbpf;
-static int                        resize_screen;
-static int                        redraw_screen;
 static int                        cx, cy, cw, ch;
 static int                        debug = 0;
 static Notifier                   exit_notifier;
+uint8_t                           *guest_surface;
 
 /* fwd decls */
 static int fbdev_activate_vt(int tty, int vtno, bool wait);
@@ -786,10 +785,10 @@  static void fbdev_render(DisplayState *ds, int x, int y, int w, int h)
     uint8_t *src;
     int line;
 
-    if (!conv)
+    if (!conv || !guest_surface)
         return;
 
-    src = ds_get_data(ds) + y * ds_get_linesize(ds)
+    src = guest_surface + y * ds_get_linesize(ds)
         + x * ds_get_bytes_per_pixel(ds);
     dst = fb_mem + y * fb_fix.line_length
         + x * fbpf.bytes_per_pixel;
@@ -819,46 +818,50 @@  static void fbdev_update(DisplayState *ds, int x, int y, int w, int h)
     if (fb_switch_state != FB_ACTIVE)
         return;
 
-    if (resize_screen) {
-        if (debug)
-            fprintf(stderr, "%s: handle resize\n", __FUNCTION__);
-        resize_screen = 0;
-        cx = 0; cy = 0;
-        cw = ds_get_width(ds);
-        ch = ds_get_height(ds);
-        if (ds_get_width(ds) < fb_var.xres) {
-            cx = (fb_var.xres - ds_get_width(ds)) / 2;
-        }
-        if (ds_get_height(ds) < fb_var.yres) {
-            cy = (fb_var.yres - ds_get_height(ds)) / 2;
-        }
+    if (guest_surface != NULL) {
+        fbdev_render(ds, x, y, w, h);
+    }
+}
 
-        if (conv) {
-            qemu_pf_conv_put(conv);
-        }
-        conv = qemu_pf_conv_get(&fbpf, &ds->surface->pf);
-        if (conv == NULL) {
-            fprintf(stderr, "fbdev: unsupported PixelFormat conversion\n");
-        }
+static void fbdev_setdata(DisplayState *ds)
+{
+    if (conv) {
+        qemu_pf_conv_put(conv);
     }
 
-    if (redraw_screen) {
-        if (debug)
-            fprintf(stderr, "%s: handle redraw\n", __FUNCTION__);
-        redraw_screen = 0;
-        fbdev_cls();
-        x = 0; y = 0; w = ds_get_width(ds); h = ds_get_height(ds);
+    conv = qemu_pf_conv_get(&fbpf, &ds->surface->pf);
+    if (conv == NULL) {
+        fprintf(stderr, "fbdev: unsupported PixelFormat conversion\n");
     }
 
-    fbdev_render(ds, x, y, w, h);
+    guest_surface = ds_get_data(ds);
 }
 
 static void fbdev_resize(DisplayState *ds)
 {
-    if (debug)
-        fprintf(stderr, "%s: request resize+redraw\n", __FUNCTION__);
-    resize_screen++;
-    redraw_screen++;
+    int is_video_ptr;
+
+    if (fb_switch_state == FB_ACTIVE) {
+        fbdev_cls();
+    }
+
+    is_video_ptr = ds_get_data(ds) >= fb_mem + fb_mem_offset &&
+                   ds_get_data(ds) < fb_mem + fb_fix.smem_len + fb_mem_offset;
+
+    if (ds_get_bits_per_pixel(ds) != fbpf.bits_per_pixel ||
+        ds_get_linesize(ds) != fb_fix.line_length ||
+        !is_video_ptr) {
+        cx = 0; cy = 0;
+        if (ds_get_width(ds) < fb_var.xres) {
+            cx = (fb_var.xres - ds_get_width(ds)) / 2;
+        }
+        if (ds_get_height(ds) < fb_var.yres) {
+            cy = (fb_var.yres - ds_get_height(ds)) / 2;
+        }
+        fbdev_setdata(ds);
+    } else {
+        guest_surface = NULL;
+    }
 }
 
 static void fbdev_refresh(DisplayState *ds)
@@ -866,21 +869,17 @@  static void fbdev_refresh(DisplayState *ds)
     switch (fb_switch_state) {
     case FB_REL_REQ:
         fbdev_switch_release();
+        vga_hw_invalidate();
     case FB_INACTIVE:
         return;
     case FB_ACQ_REQ:
         fbdev_switch_acquire();
-        redraw_screen++;
-        if (debug)
-            fprintf(stderr, "%s: request redraw\n", __FUNCTION__);
+        vga_hw_invalidate();
     case FB_ACTIVE:
         break;
     }
 
     vga_hw_update();
-    if (redraw_screen) {
-        fbdev_update(ds, 0, 0, 0, 0);
-    }
 }
 
 static void fbdev_exit_notifier(Notifier *notifier)
@@ -888,8 +887,58 @@  static void fbdev_exit_notifier(Notifier *notifier)
     fbdev_cleanup();
 }
 
+static DisplaySurface *fbdev_create_displaysurface(int width, int height)
+{
+    DisplaySurface *surface = qemu_mallocz(sizeof (DisplaySurface));
+
+    surface->width = width;
+    surface->height = height;
+
+    surface->pf = fbpf;
+    surface->linesize = fb_fix.line_length;
+
+    if (fb_switch_state == FB_INACTIVE) {
+        surface->flags = QEMU_ALLOCATED_FLAG;
+        surface->data = qemu_mallocz(surface->linesize * surface->height);
+    } else {
+        surface->flags = QEMU_REALPIXELS_FLAG;
+        surface->data = fb_mem;
+
+        if (width < fb_var.xres)
+            surface->data += ((fb_var.xres - width) / 2) * fbpf.bytes_per_pixel;
+        if (height < fb_var.yres)
+            surface->data += ((fb_var.yres - height) / 2) * fb_fix.line_length;
+    }
+
+    return surface;
+}
+
+static void fbdev_free_displaysurface(DisplaySurface *surface)
+{
+    if (surface == NULL)
+        return;
+
+    if (surface->flags & QEMU_ALLOCATED_FLAG) {
+        qemu_free(surface->data);
+    }
+
+    surface->data = NULL;
+
+    qemu_free(surface);
+}
+
+static DisplaySurface *fbdev_resize_displaysurface(DisplaySurface *surface,
+                                                   int width,
+                                                   int height)
+{
+    fbdev_free_displaysurface(surface);
+    return fbdev_create_displaysurface(width, height);
+}
+
 void fbdev_display_init(DisplayState *ds, const char *device)
 {
+    DisplayAllocator *da;
+
     if (dcl != NULL) {
         if (debug)
             fprintf(stderr, "%s: already active\n", __FUNCTION__);
@@ -910,7 +959,17 @@  void fbdev_display_init(DisplayState *ds, const char *device)
     dcl->dpy_update  = fbdev_update;
     dcl->dpy_resize  = fbdev_resize;
     dcl->dpy_refresh = fbdev_refresh;
+    dcl->dpy_setdata = fbdev_setdata;
     register_displaychangelistener(ds, dcl);
+
+    da = qemu_mallocz(sizeof (DisplayAllocator));
+    da->create_displaysurface = fbdev_create_displaysurface;
+    da->resize_displaysurface = fbdev_resize_displaysurface;
+    da->free_displaysurface = fbdev_free_displaysurface;
+
+    if (register_displayallocator(ds, da) == da) {
+        dpy_resize(ds);
+    }
 }
 
 void fbdev_display_uninit(void)