Commit e4dcff1f6e7582f76c2c9990b1d9111bbc8e26ef

Authored by Markus Armbruster
Committed by Thomas Gleixner
1 parent f4ad1ebd7a

xen pvfb: Dynamic mode support (screen resizing)

The pvfb backend indicates dynamic mode support by creating node
feature_resize with a non-zero value in its xenstore directory.
xen-fbfront sends a resize notification event on mode change.  Fully
backwards compatible both ways.

Framebuffer size and initial resolution can be controlled through
kernel parameter xen_fbfront.video.  The backend enforces a separate
size limit, which it advertises in node videoram in its xenstore
directory.

xen-kbdfront gets the maximum screen resolution from nodes width and
height in the backend's xenstore directory instead of hardcoding it.

Additional goodie: support for larger framebuffers (512M on a 64-bit
system with 4K pages).

Changing the number of bits per pixels dynamically is not supported,
yet.

Ported from
http://xenbits.xensource.com/linux-2.6.18-xen.hg?rev/92f7b3144f41
http://xenbits.xensource.com/linux-2.6.18-xen.hg?rev/bfc040135633

Signed-off-by: Pat Campbell <plc@novell.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Jeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

Showing 3 changed files with 188 additions and 34 deletions Side-by-side Diff

drivers/input/xen-kbdfront.c
... ... @@ -300,6 +300,16 @@
300 300 */
301 301 if (dev->state != XenbusStateConnected)
302 302 goto InitWait; /* no InitWait seen yet, fudge it */
  303 +
  304 + /* Set input abs params to match backend screen res */
  305 + if (xenbus_scanf(XBT_NIL, info->xbdev->otherend,
  306 + "width", "%d", &val) > 0)
  307 + input_set_abs_params(info->ptr, ABS_X, 0, val, 0, 0);
  308 +
  309 + if (xenbus_scanf(XBT_NIL, info->xbdev->otherend,
  310 + "height", "%d", &val) > 0)
  311 + input_set_abs_params(info->ptr, ABS_Y, 0, val, 0, 0);
  312 +
303 313 break;
304 314  
305 315 case XenbusStateClosing:
drivers/video/xen-fbfront.c
... ... @@ -43,23 +43,47 @@
43 43 struct xenfb_page *page;
44 44 unsigned long *mfns;
45 45 int update_wanted; /* XENFB_TYPE_UPDATE wanted */
  46 + int feature_resize; /* XENFB_TYPE_RESIZE ok */
  47 + struct xenfb_resize resize; /* protected by resize_lock */
  48 + int resize_dpy; /* ditto */
  49 + spinlock_t resize_lock;
46 50  
47 51 struct xenbus_device *xbdev;
48 52 };
49 53  
50   -static u32 xenfb_mem_len = XENFB_WIDTH * XENFB_HEIGHT * XENFB_DEPTH / 8;
  54 +#define XENFB_DEFAULT_FB_LEN (XENFB_WIDTH * XENFB_HEIGHT * XENFB_DEPTH / 8)
51 55  
  56 +enum { KPARAM_MEM, KPARAM_WIDTH, KPARAM_HEIGHT, KPARAM_CNT };
  57 +static int video[KPARAM_CNT] = { 2, XENFB_WIDTH, XENFB_HEIGHT };
  58 +module_param_array(video, int, NULL, 0);
  59 +MODULE_PARM_DESC(video,
  60 + "Video memory size in MB, width, height in pixels (default 2,800,600)");
  61 +
52 62 static void xenfb_make_preferred_console(void);
53 63 static int xenfb_remove(struct xenbus_device *);
54   -static void xenfb_init_shared_page(struct xenfb_info *);
  64 +static void xenfb_init_shared_page(struct xenfb_info *, struct fb_info *);
55 65 static int xenfb_connect_backend(struct xenbus_device *, struct xenfb_info *);
56 66 static void xenfb_disconnect_backend(struct xenfb_info *);
57 67  
  68 +static void xenfb_send_event(struct xenfb_info *info,
  69 + union xenfb_out_event *event)
  70 +{
  71 + u32 prod;
  72 +
  73 + prod = info->page->out_prod;
  74 + /* caller ensures !xenfb_queue_full() */
  75 + mb(); /* ensure ring space available */
  76 + XENFB_OUT_RING_REF(info->page, prod) = *event;
  77 + wmb(); /* ensure ring contents visible */
  78 + info->page->out_prod = prod + 1;
  79 +
  80 + notify_remote_via_irq(info->irq);
  81 +}
  82 +
58 83 static void xenfb_do_update(struct xenfb_info *info,
59 84 int x, int y, int w, int h)
60 85 {
61 86 union xenfb_out_event event;
62   - u32 prod;
63 87  
64 88 memset(&event, 0, sizeof(event));
65 89 event.type = XENFB_TYPE_UPDATE;
66 90  
67 91  
... ... @@ -68,14 +92,19 @@
68 92 event.update.width = w;
69 93 event.update.height = h;
70 94  
71   - prod = info->page->out_prod;
72 95 /* caller ensures !xenfb_queue_full() */
73   - mb(); /* ensure ring space available */
74   - XENFB_OUT_RING_REF(info->page, prod) = event;
75   - wmb(); /* ensure ring contents visible */
76   - info->page->out_prod = prod + 1;
  96 + xenfb_send_event(info, &event);
  97 +}
77 98  
78   - notify_remote_via_irq(info->irq);
  99 +static void xenfb_do_resize(struct xenfb_info *info)
  100 +{
  101 + union xenfb_out_event event;
  102 +
  103 + memset(&event, 0, sizeof(event));
  104 + event.resize = info->resize;
  105 +
  106 + /* caller ensures !xenfb_queue_full() */
  107 + xenfb_send_event(info, &event);
79 108 }
80 109  
81 110 static int xenfb_queue_full(struct xenfb_info *info)
82 111  
83 112  
84 113  
... ... @@ -87,13 +116,29 @@
87 116 return prod - cons == XENFB_OUT_RING_LEN;
88 117 }
89 118  
  119 +static void xenfb_handle_resize_dpy(struct xenfb_info *info)
  120 +{
  121 + unsigned long flags;
  122 +
  123 + spin_lock_irqsave(&info->resize_lock, flags);
  124 + if (info->resize_dpy) {
  125 + if (!xenfb_queue_full(info)) {
  126 + info->resize_dpy = 0;
  127 + xenfb_do_resize(info);
  128 + }
  129 + }
  130 + spin_unlock_irqrestore(&info->resize_lock, flags);
  131 +}
  132 +
90 133 static void xenfb_refresh(struct xenfb_info *info,
91 134 int x1, int y1, int w, int h)
92 135 {
93 136 unsigned long flags;
94   - int y2 = y1 + h - 1;
95 137 int x2 = x1 + w - 1;
  138 + int y2 = y1 + h - 1;
96 139  
  140 + xenfb_handle_resize_dpy(info);
  141 +
97 142 if (!info->update_wanted)
98 143 return;
99 144  
... ... @@ -225,6 +270,57 @@
225 270 return res;
226 271 }
227 272  
  273 +static int
  274 +xenfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
  275 +{
  276 + struct xenfb_info *xenfb_info;
  277 + int required_mem_len;
  278 +
  279 + xenfb_info = info->par;
  280 +
  281 + if (!xenfb_info->feature_resize) {
  282 + if (var->xres == video[KPARAM_WIDTH] &&
  283 + var->yres == video[KPARAM_HEIGHT] &&
  284 + var->bits_per_pixel == xenfb_info->page->depth) {
  285 + return 0;
  286 + }
  287 + return -EINVAL;
  288 + }
  289 +
  290 + /* Can't resize past initial width and height */
  291 + if (var->xres > video[KPARAM_WIDTH] || var->yres > video[KPARAM_HEIGHT])
  292 + return -EINVAL;
  293 +
  294 + required_mem_len = var->xres * var->yres * xenfb_info->page->depth / 8;
  295 + if (var->bits_per_pixel == xenfb_info->page->depth &&
  296 + var->xres <= info->fix.line_length / (XENFB_DEPTH / 8) &&
  297 + required_mem_len <= info->fix.smem_len) {
  298 + var->xres_virtual = var->xres;
  299 + var->yres_virtual = var->yres;
  300 + return 0;
  301 + }
  302 + return -EINVAL;
  303 +}
  304 +
  305 +static int xenfb_set_par(struct fb_info *info)
  306 +{
  307 + struct xenfb_info *xenfb_info;
  308 + unsigned long flags;
  309 +
  310 + xenfb_info = info->par;
  311 +
  312 + spin_lock_irqsave(&xenfb_info->resize_lock, flags);
  313 + xenfb_info->resize.type = XENFB_TYPE_RESIZE;
  314 + xenfb_info->resize.width = info->var.xres;
  315 + xenfb_info->resize.height = info->var.yres;
  316 + xenfb_info->resize.stride = info->fix.line_length;
  317 + xenfb_info->resize.depth = info->var.bits_per_pixel;
  318 + xenfb_info->resize.offset = 0;
  319 + xenfb_info->resize_dpy = 1;
  320 + spin_unlock_irqrestore(&xenfb_info->resize_lock, flags);
  321 + return 0;
  322 +}
  323 +
228 324 static struct fb_ops xenfb_fb_ops = {
229 325 .owner = THIS_MODULE,
230 326 .fb_read = fb_sys_read,
... ... @@ -233,6 +329,8 @@
233 329 .fb_fillrect = xenfb_fillrect,
234 330 .fb_copyarea = xenfb_copyarea,
235 331 .fb_imageblit = xenfb_imageblit,
  332 + .fb_check_var = xenfb_check_var,
  333 + .fb_set_par = xenfb_set_par,
236 334 };
237 335  
238 336 static irqreturn_t xenfb_event_handler(int rq, void *dev_id)
... ... @@ -261,6 +359,8 @@
261 359 {
262 360 struct xenfb_info *info;
263 361 struct fb_info *fb_info;
  362 + int fb_size;
  363 + int val;
264 364 int ret;
265 365  
266 366 info = kzalloc(sizeof(*info), GFP_KERNEL);
267 367  
268 368  
269 369  
270 370  
... ... @@ -268,18 +368,35 @@
268 368 xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure");
269 369 return -ENOMEM;
270 370 }
  371 +
  372 + /* Limit kernel param videoram amount to what is in xenstore */
  373 + if (xenbus_scanf(XBT_NIL, dev->otherend, "videoram", "%d", &val) == 1) {
  374 + if (val < video[KPARAM_MEM])
  375 + video[KPARAM_MEM] = val;
  376 + }
  377 +
  378 + /* If requested res does not fit in available memory, use default */
  379 + fb_size = video[KPARAM_MEM] * 1024 * 1024;
  380 + if (video[KPARAM_WIDTH] * video[KPARAM_HEIGHT] * XENFB_DEPTH / 8
  381 + > fb_size) {
  382 + video[KPARAM_WIDTH] = XENFB_WIDTH;
  383 + video[KPARAM_HEIGHT] = XENFB_HEIGHT;
  384 + fb_size = XENFB_DEFAULT_FB_LEN;
  385 + }
  386 +
271 387 dev->dev.driver_data = info;
272 388 info->xbdev = dev;
273 389 info->irq = -1;
274 390 info->x1 = info->y1 = INT_MAX;
275 391 spin_lock_init(&info->dirty_lock);
  392 + spin_lock_init(&info->resize_lock);
276 393  
277   - info->fb = vmalloc(xenfb_mem_len);
  394 + info->fb = vmalloc(fb_size);
278 395 if (info->fb == NULL)
279 396 goto error_nomem;
280   - memset(info->fb, 0, xenfb_mem_len);
  397 + memset(info->fb, 0, fb_size);
281 398  
282   - info->nr_pages = (xenfb_mem_len + PAGE_SIZE - 1) >> PAGE_SHIFT;
  399 + info->nr_pages = (fb_size + PAGE_SIZE - 1) >> PAGE_SHIFT;
283 400  
284 401 info->mfns = vmalloc(sizeof(unsigned long) * info->nr_pages);
285 402 if (!info->mfns)
... ... @@ -290,8 +407,6 @@
290 407 if (!info->page)
291 408 goto error_nomem;
292 409  
293   - xenfb_init_shared_page(info);
294   -
295 410 /* abusing framebuffer_alloc() to allocate pseudo_palette */
296 411 fb_info = framebuffer_alloc(sizeof(u32) * 256, NULL);
297 412 if (fb_info == NULL)
... ... @@ -304,9 +419,9 @@
304 419 fb_info->screen_base = info->fb;
305 420  
306 421 fb_info->fbops = &xenfb_fb_ops;
307   - fb_info->var.xres_virtual = fb_info->var.xres = info->page->width;
308   - fb_info->var.yres_virtual = fb_info->var.yres = info->page->height;
309   - fb_info->var.bits_per_pixel = info->page->depth;
  422 + fb_info->var.xres_virtual = fb_info->var.xres = video[KPARAM_WIDTH];
  423 + fb_info->var.yres_virtual = fb_info->var.yres = video[KPARAM_HEIGHT];
  424 + fb_info->var.bits_per_pixel = XENFB_DEPTH;
310 425  
311 426 fb_info->var.red = (struct fb_bitfield){16, 8, 0};
312 427 fb_info->var.green = (struct fb_bitfield){8, 8, 0};
313 428  
... ... @@ -318,9 +433,9 @@
318 433 fb_info->var.vmode = FB_VMODE_NONINTERLACED;
319 434  
320 435 fb_info->fix.visual = FB_VISUAL_TRUECOLOR;
321   - fb_info->fix.line_length = info->page->line_length;
  436 + fb_info->fix.line_length = fb_info->var.xres * XENFB_DEPTH / 8;
322 437 fb_info->fix.smem_start = 0;
323   - fb_info->fix.smem_len = xenfb_mem_len;
  438 + fb_info->fix.smem_len = fb_size;
324 439 strcpy(fb_info->fix.id, "xen");
325 440 fb_info->fix.type = FB_TYPE_PACKED_PIXELS;
326 441 fb_info->fix.accel = FB_ACCEL_NONE;
... ... @@ -337,6 +452,8 @@
337 452 fb_info->fbdefio = &xenfb_defio;
338 453 fb_deferred_io_init(fb_info);
339 454  
  455 + xenfb_init_shared_page(info, fb_info);
  456 +
340 457 ret = register_framebuffer(fb_info);
341 458 if (ret) {
342 459 fb_deferred_io_cleanup(fb_info);
... ... @@ -389,7 +506,7 @@
389 506 struct xenfb_info *info = dev->dev.driver_data;
390 507  
391 508 xenfb_disconnect_backend(info);
392   - xenfb_init_shared_page(info);
  509 + xenfb_init_shared_page(info, info->fb_info);
393 510 return xenfb_connect_backend(dev, info);
394 511 }
395 512  
396 513  
397 514  
... ... @@ -417,20 +534,23 @@
417 534 return pfn_to_mfn(vmalloc_to_pfn(address));
418 535 }
419 536  
420   -static void xenfb_init_shared_page(struct xenfb_info *info)
  537 +static void xenfb_init_shared_page(struct xenfb_info *info,
  538 + struct fb_info *fb_info)
421 539 {
422 540 int i;
  541 + int epd = PAGE_SIZE / sizeof(info->mfns[0]);
423 542  
424 543 for (i = 0; i < info->nr_pages; i++)
425 544 info->mfns[i] = vmalloc_to_mfn(info->fb + i * PAGE_SIZE);
426 545  
427   - info->page->pd[0] = vmalloc_to_mfn(info->mfns);
428   - info->page->pd[1] = 0;
429   - info->page->width = XENFB_WIDTH;
430   - info->page->height = XENFB_HEIGHT;
431   - info->page->depth = XENFB_DEPTH;
432   - info->page->line_length = (info->page->depth / 8) * info->page->width;
433   - info->page->mem_length = xenfb_mem_len;
  546 + for (i = 0; i * epd < info->nr_pages; i++)
  547 + info->page->pd[i] = vmalloc_to_mfn(&info->mfns[i * epd]);
  548 +
  549 + info->page->width = fb_info->var.xres;
  550 + info->page->height = fb_info->var.yres;
  551 + info->page->depth = fb_info->var.bits_per_pixel;
  552 + info->page->line_length = fb_info->fix.line_length;
  553 + info->page->mem_length = fb_info->fix.smem_len;
434 554 info->page->in_cons = info->page->in_prod = 0;
435 555 info->page->out_cons = info->page->out_prod = 0;
436 556 }
... ... @@ -530,6 +650,11 @@
530 650 val = 0;
531 651 if (val)
532 652 info->update_wanted = 1;
  653 +
  654 + if (xenbus_scanf(XBT_NIL, dev->otherend,
  655 + "feature-resize", "%d", &val) < 0)
  656 + val = 0;
  657 + info->feature_resize = val;
533 658 break;
534 659  
535 660 case XenbusStateClosing:
include/xen/interface/io/fbif.h
... ... @@ -49,11 +49,27 @@
49 49 int32_t height; /* rect height */
50 50 };
51 51  
  52 +/*
  53 + * Framebuffer resize notification event
  54 + * Capable backend sets feature-resize in xenstore.
  55 + */
  56 +#define XENFB_TYPE_RESIZE 3
  57 +
  58 +struct xenfb_resize {
  59 + uint8_t type; /* XENFB_TYPE_RESIZE */
  60 + int32_t width; /* width in pixels */
  61 + int32_t height; /* height in pixels */
  62 + int32_t stride; /* stride in bytes */
  63 + int32_t depth; /* depth in bits */
  64 + int32_t offset; /* start offset within framebuffer */
  65 +};
  66 +
52 67 #define XENFB_OUT_EVENT_SIZE 40
53 68  
54 69 union xenfb_out_event {
55 70 uint8_t type;
56 71 struct xenfb_update update;
  72 + struct xenfb_resize resize;
57 73 char pad[XENFB_OUT_EVENT_SIZE];
58 74 };
59 75  
60 76  
61 77  
... ... @@ -105,15 +121,18 @@
105 121 * Each directory page holds PAGE_SIZE / sizeof(*pd)
106 122 * framebuffer pages, and can thus map up to PAGE_SIZE *
107 123 * PAGE_SIZE / sizeof(*pd) bytes. With PAGE_SIZE == 4096 and
108   - * sizeof(unsigned long) == 4, that's 4 Megs. Two directory
109   - * pages should be enough for a while.
  124 + * sizeof(unsigned long) == 4/8, that's 4 Megs 32 bit and 2
  125 + * Megs 64 bit. 256 directories give enough room for a 512
  126 + * Meg framebuffer with a max resolution of 12,800x10,240.
  127 + * Should be enough for a while with room leftover for
  128 + * expansion.
110 129 */
111   - unsigned long pd[2];
  130 + unsigned long pd[256];
112 131 };
113 132  
114 133 /*
115   - * Wart: xenkbd needs to know resolution. Put it here until a better
116   - * solution is found, but don't leak it to the backend.
  134 + * Wart: xenkbd needs to know default resolution. Put it here until a
  135 + * better solution is found, but don't leak it to the backend.
117 136 */
118 137 #ifdef __KERNEL__
119 138 #define XENFB_WIDTH 800