diff options
author | Markus Armbruster <armbru@redhat.com> | 2008-05-26 18:31:11 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2008-05-27 04:11:36 -0400 |
commit | e4dcff1f6e7582f76c2c9990b1d9111bbc8e26ef (patch) | |
tree | d4a06968700cf64e3f8b7b146512a457ac9e7ece /drivers/video | |
parent | f4ad1ebd7a0fae2782ef9f76c0b94b536742c3e8 (diff) |
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>
Diffstat (limited to 'drivers/video')
-rw-r--r-- | drivers/video/xen-fbfront.c | 183 |
1 files changed, 154 insertions, 29 deletions
diff --git a/drivers/video/xen-fbfront.c b/drivers/video/xen-fbfront.c index 291eef695594..47ed39b52f9c 100644 --- a/drivers/video/xen-fbfront.c +++ b/drivers/video/xen-fbfront.c | |||
@@ -43,23 +43,47 @@ struct xenfb_info { | |||
43 | struct xenfb_page *page; | 43 | struct xenfb_page *page; |
44 | unsigned long *mfns; | 44 | unsigned long *mfns; |
45 | int update_wanted; /* XENFB_TYPE_UPDATE wanted */ | 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 | struct xenbus_device *xbdev; | 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) |
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)"); | ||
51 | 61 | ||
52 | static void xenfb_make_preferred_console(void); | 62 | static void xenfb_make_preferred_console(void); |
53 | static int xenfb_remove(struct xenbus_device *); | 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 | static int xenfb_connect_backend(struct xenbus_device *, struct xenfb_info *); | 65 | static int xenfb_connect_backend(struct xenbus_device *, struct xenfb_info *); |
56 | static void xenfb_disconnect_backend(struct xenfb_info *); | 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 | static void xenfb_do_update(struct xenfb_info *info, | 83 | static void xenfb_do_update(struct xenfb_info *info, |
59 | int x, int y, int w, int h) | 84 | int x, int y, int w, int h) |
60 | { | 85 | { |
61 | union xenfb_out_event event; | 86 | union xenfb_out_event event; |
62 | u32 prod; | ||
63 | 87 | ||
64 | memset(&event, 0, sizeof(event)); | 88 | memset(&event, 0, sizeof(event)); |
65 | event.type = XENFB_TYPE_UPDATE; | 89 | event.type = XENFB_TYPE_UPDATE; |
@@ -68,14 +92,19 @@ static void xenfb_do_update(struct xenfb_info *info, | |||
68 | event.update.width = w; | 92 | event.update.width = w; |
69 | event.update.height = h; | 93 | event.update.height = h; |
70 | 94 | ||
71 | prod = info->page->out_prod; | ||
72 | /* caller ensures !xenfb_queue_full() */ | 95 | /* caller ensures !xenfb_queue_full() */ |
73 | mb(); /* ensure ring space available */ | 96 | xenfb_send_event(info, &event); |
74 | XENFB_OUT_RING_REF(info->page, prod) = event; | 97 | } |
75 | wmb(); /* ensure ring contents visible */ | ||
76 | info->page->out_prod = prod + 1; | ||
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 | static int xenfb_queue_full(struct xenfb_info *info) | 110 | static int xenfb_queue_full(struct xenfb_info *info) |
@@ -87,12 +116,28 @@ static int xenfb_queue_full(struct xenfb_info *info) | |||
87 | return prod - cons == XENFB_OUT_RING_LEN; | 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 | static void xenfb_refresh(struct xenfb_info *info, | 133 | static void xenfb_refresh(struct xenfb_info *info, |
91 | int x1, int y1, int w, int h) | 134 | int x1, int y1, int w, int h) |
92 | { | 135 | { |
93 | unsigned long flags; | 136 | unsigned long flags; |
94 | int y2 = y1 + h - 1; | ||
95 | int x2 = x1 + w - 1; | 137 | int x2 = x1 + w - 1; |
138 | int y2 = y1 + h - 1; | ||
139 | |||
140 | xenfb_handle_resize_dpy(info); | ||
96 | 141 | ||
97 | if (!info->update_wanted) | 142 | if (!info->update_wanted) |
98 | return; | 143 | return; |
@@ -225,6 +270,57 @@ static ssize_t xenfb_write(struct fb_info *p, const char __user *buf, | |||
225 | return res; | 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 | static struct fb_ops xenfb_fb_ops = { | 324 | static struct fb_ops xenfb_fb_ops = { |
229 | .owner = THIS_MODULE, | 325 | .owner = THIS_MODULE, |
230 | .fb_read = fb_sys_read, | 326 | .fb_read = fb_sys_read, |
@@ -233,6 +329,8 @@ static struct fb_ops xenfb_fb_ops = { | |||
233 | .fb_fillrect = xenfb_fillrect, | 329 | .fb_fillrect = xenfb_fillrect, |
234 | .fb_copyarea = xenfb_copyarea, | 330 | .fb_copyarea = xenfb_copyarea, |
235 | .fb_imageblit = xenfb_imageblit, | 331 | .fb_imageblit = xenfb_imageblit, |
332 | .fb_check_var = xenfb_check_var, | ||
333 | .fb_set_par = xenfb_set_par, | ||
236 | }; | 334 | }; |
237 | 335 | ||
238 | static irqreturn_t xenfb_event_handler(int rq, void *dev_id) | 336 | static irqreturn_t xenfb_event_handler(int rq, void *dev_id) |
@@ -261,6 +359,8 @@ static int __devinit xenfb_probe(struct xenbus_device *dev, | |||
261 | { | 359 | { |
262 | struct xenfb_info *info; | 360 | struct xenfb_info *info; |
263 | struct fb_info *fb_info; | 361 | struct fb_info *fb_info; |
362 | int fb_size; | ||
363 | int val; | ||
264 | int ret; | 364 | int ret; |
265 | 365 | ||
266 | info = kzalloc(sizeof(*info), GFP_KERNEL); | 366 | info = kzalloc(sizeof(*info), GFP_KERNEL); |
@@ -268,18 +368,35 @@ static int __devinit xenfb_probe(struct xenbus_device *dev, | |||
268 | xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure"); | 368 | xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure"); |
269 | return -ENOMEM; | 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 | dev->dev.driver_data = info; | 387 | dev->dev.driver_data = info; |
272 | info->xbdev = dev; | 388 | info->xbdev = dev; |
273 | info->irq = -1; | 389 | info->irq = -1; |
274 | info->x1 = info->y1 = INT_MAX; | 390 | info->x1 = info->y1 = INT_MAX; |
275 | spin_lock_init(&info->dirty_lock); | 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 | if (info->fb == NULL) | 395 | if (info->fb == NULL) |
279 | goto error_nomem; | 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 | info->mfns = vmalloc(sizeof(unsigned long) * info->nr_pages); | 401 | info->mfns = vmalloc(sizeof(unsigned long) * info->nr_pages); |
285 | if (!info->mfns) | 402 | if (!info->mfns) |
@@ -290,8 +407,6 @@ static int __devinit xenfb_probe(struct xenbus_device *dev, | |||
290 | if (!info->page) | 407 | if (!info->page) |
291 | goto error_nomem; | 408 | goto error_nomem; |
292 | 409 | ||
293 | xenfb_init_shared_page(info); | ||
294 | |||
295 | /* abusing framebuffer_alloc() to allocate pseudo_palette */ | 410 | /* abusing framebuffer_alloc() to allocate pseudo_palette */ |
296 | fb_info = framebuffer_alloc(sizeof(u32) * 256, NULL); | 411 | fb_info = framebuffer_alloc(sizeof(u32) * 256, NULL); |
297 | if (fb_info == NULL) | 412 | if (fb_info == NULL) |
@@ -304,9 +419,9 @@ static int __devinit xenfb_probe(struct xenbus_device *dev, | |||
304 | fb_info->screen_base = info->fb; | 419 | fb_info->screen_base = info->fb; |
305 | 420 | ||
306 | fb_info->fbops = &xenfb_fb_ops; | 421 | fb_info->fbops = &xenfb_fb_ops; |
307 | fb_info->var.xres_virtual = fb_info->var.xres = info->page->width; | 422 | fb_info->var.xres_virtual = fb_info->var.xres = video[KPARAM_WIDTH]; |
308 | fb_info->var.yres_virtual = fb_info->var.yres = info->page->height; | 423 | fb_info->var.yres_virtual = fb_info->var.yres = video[KPARAM_HEIGHT]; |
309 | fb_info->var.bits_per_pixel = info->page->depth; | 424 | fb_info->var.bits_per_pixel = XENFB_DEPTH; |
310 | 425 | ||
311 | fb_info->var.red = (struct fb_bitfield){16, 8, 0}; | 426 | fb_info->var.red = (struct fb_bitfield){16, 8, 0}; |
312 | fb_info->var.green = (struct fb_bitfield){8, 8, 0}; | 427 | fb_info->var.green = (struct fb_bitfield){8, 8, 0}; |
@@ -318,9 +433,9 @@ static int __devinit xenfb_probe(struct xenbus_device *dev, | |||
318 | fb_info->var.vmode = FB_VMODE_NONINTERLACED; | 433 | fb_info->var.vmode = FB_VMODE_NONINTERLACED; |
319 | 434 | ||
320 | fb_info->fix.visual = FB_VISUAL_TRUECOLOR; | 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 | fb_info->fix.smem_start = 0; | 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 | strcpy(fb_info->fix.id, "xen"); | 439 | strcpy(fb_info->fix.id, "xen"); |
325 | fb_info->fix.type = FB_TYPE_PACKED_PIXELS; | 440 | fb_info->fix.type = FB_TYPE_PACKED_PIXELS; |
326 | fb_info->fix.accel = FB_ACCEL_NONE; | 441 | fb_info->fix.accel = FB_ACCEL_NONE; |
@@ -337,6 +452,8 @@ static int __devinit xenfb_probe(struct xenbus_device *dev, | |||
337 | fb_info->fbdefio = &xenfb_defio; | 452 | fb_info->fbdefio = &xenfb_defio; |
338 | fb_deferred_io_init(fb_info); | 453 | fb_deferred_io_init(fb_info); |
339 | 454 | ||
455 | xenfb_init_shared_page(info, fb_info); | ||
456 | |||
340 | ret = register_framebuffer(fb_info); | 457 | ret = register_framebuffer(fb_info); |
341 | if (ret) { | 458 | if (ret) { |
342 | fb_deferred_io_cleanup(fb_info); | 459 | fb_deferred_io_cleanup(fb_info); |
@@ -389,7 +506,7 @@ static int xenfb_resume(struct xenbus_device *dev) | |||
389 | struct xenfb_info *info = dev->dev.driver_data; | 506 | struct xenfb_info *info = dev->dev.driver_data; |
390 | 507 | ||
391 | xenfb_disconnect_backend(info); | 508 | xenfb_disconnect_backend(info); |
392 | xenfb_init_shared_page(info); | 509 | xenfb_init_shared_page(info, info->fb_info); |
393 | return xenfb_connect_backend(dev, info); | 510 | return xenfb_connect_backend(dev, info); |
394 | } | 511 | } |
395 | 512 | ||
@@ -417,20 +534,23 @@ static unsigned long vmalloc_to_mfn(void *address) | |||
417 | return pfn_to_mfn(vmalloc_to_pfn(address)); | 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 | int i; | 540 | int i; |
541 | int epd = PAGE_SIZE / sizeof(info->mfns[0]); | ||
423 | 542 | ||
424 | for (i = 0; i < info->nr_pages; i++) | 543 | for (i = 0; i < info->nr_pages; i++) |
425 | info->mfns[i] = vmalloc_to_mfn(info->fb + i * PAGE_SIZE); | 544 | info->mfns[i] = vmalloc_to_mfn(info->fb + i * PAGE_SIZE); |
426 | 545 | ||
427 | info->page->pd[0] = vmalloc_to_mfn(info->mfns); | 546 | for (i = 0; i * epd < info->nr_pages; i++) |
428 | info->page->pd[1] = 0; | 547 | info->page->pd[i] = vmalloc_to_mfn(&info->mfns[i * epd]); |
429 | info->page->width = XENFB_WIDTH; | 548 | |
430 | info->page->height = XENFB_HEIGHT; | 549 | info->page->width = fb_info->var.xres; |
431 | info->page->depth = XENFB_DEPTH; | 550 | info->page->height = fb_info->var.yres; |
432 | info->page->line_length = (info->page->depth / 8) * info->page->width; | 551 | info->page->depth = fb_info->var.bits_per_pixel; |
433 | info->page->mem_length = xenfb_mem_len; | 552 | info->page->line_length = fb_info->fix.line_length; |
553 | info->page->mem_length = fb_info->fix.smem_len; | ||
434 | info->page->in_cons = info->page->in_prod = 0; | 554 | info->page->in_cons = info->page->in_prod = 0; |
435 | info->page->out_cons = info->page->out_prod = 0; | 555 | info->page->out_cons = info->page->out_prod = 0; |
436 | } | 556 | } |
@@ -530,6 +650,11 @@ InitWait: | |||
530 | val = 0; | 650 | val = 0; |
531 | if (val) | 651 | if (val) |
532 | info->update_wanted = 1; | 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 | break; | 658 | break; |
534 | 659 | ||
535 | case XenbusStateClosing: | 660 | case XenbusStateClosing: |