Commit e4dcff1f6e7582f76c2c9990b1d9111bbc8e26ef
Committed by
Thomas Gleixner
1 parent
f4ad1ebd7a
Exists in
master
and in
39 other branches
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 |