Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Markus Armbruster and committed by
Thomas Gleixner
e4dcff1f f4ad1ebd

+188 -34
+10
drivers/input/xen-kbdfront.c
··· 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:
+154 -29
drivers/video/xen-fbfront.c
··· 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) 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 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 *); 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 + } 57 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; ··· 92 68 event.update.width = w; 93 69 event.update.height = h; 94 70 95 - prod = info->page->out_prod; 96 71 /* caller ensures !xenfb_queue_full() */ 97 - mb(); /* ensure ring space available */ 98 - XENFB_OUT_RING_REF(info->page, prod) = event; 99 - wmb(); /* ensure ring contents visible */ 100 - info->page->out_prod = prod + 1; 72 + xenfb_send_event(info, &event); 73 + } 101 74 102 - notify_remote_via_irq(info->irq); 75 + static void xenfb_do_resize(struct xenfb_info *info) 76 + { 77 + union xenfb_out_event event; 78 + 79 + memset(&event, 0, sizeof(event)); 80 + event.resize = info->resize; 81 + 82 + /* caller ensures !xenfb_queue_full() */ 83 + xenfb_send_event(info, &event); 103 84 } 104 85 105 86 static int xenfb_queue_full(struct xenfb_info *info) ··· 116 87 return prod - cons == XENFB_OUT_RING_LEN; 117 88 } 118 89 90 + static void xenfb_handle_resize_dpy(struct xenfb_info *info) 91 + { 92 + unsigned long flags; 93 + 94 + spin_lock_irqsave(&info->resize_lock, flags); 95 + if (info->resize_dpy) { 96 + if (!xenfb_queue_full(info)) { 97 + info->resize_dpy = 0; 98 + xenfb_do_resize(info); 99 + } 100 + } 101 + spin_unlock_irqrestore(&info->resize_lock, flags); 102 + } 103 + 119 104 static void xenfb_refresh(struct xenfb_info *info, 120 105 int x1, int y1, int w, int h) 121 106 { 122 107 unsigned long flags; 123 - int y2 = y1 + h - 1; 124 108 int x2 = x1 + w - 1; 109 + int y2 = y1 + h - 1; 110 + 111 + xenfb_handle_resize_dpy(info); 125 112 126 113 if (!info->update_wanted) 127 114 return; ··· 270 225 return res; 271 226 } 272 227 228 + static int 229 + xenfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) 230 + { 231 + struct xenfb_info *xenfb_info; 232 + int required_mem_len; 233 + 234 + xenfb_info = info->par; 235 + 236 + if (!xenfb_info->feature_resize) { 237 + if (var->xres == video[KPARAM_WIDTH] && 238 + var->yres == video[KPARAM_HEIGHT] && 239 + var->bits_per_pixel == xenfb_info->page->depth) { 240 + return 0; 241 + } 242 + return -EINVAL; 243 + } 244 + 245 + /* Can't resize past initial width and height */ 246 + if (var->xres > video[KPARAM_WIDTH] || var->yres > video[KPARAM_HEIGHT]) 247 + return -EINVAL; 248 + 249 + required_mem_len = var->xres * var->yres * xenfb_info->page->depth / 8; 250 + if (var->bits_per_pixel == xenfb_info->page->depth && 251 + var->xres <= info->fix.line_length / (XENFB_DEPTH / 8) && 252 + required_mem_len <= info->fix.smem_len) { 253 + var->xres_virtual = var->xres; 254 + var->yres_virtual = var->yres; 255 + return 0; 256 + } 257 + return -EINVAL; 258 + } 259 + 260 + static int xenfb_set_par(struct fb_info *info) 261 + { 262 + struct xenfb_info *xenfb_info; 263 + unsigned long flags; 264 + 265 + xenfb_info = info->par; 266 + 267 + spin_lock_irqsave(&xenfb_info->resize_lock, flags); 268 + xenfb_info->resize.type = XENFB_TYPE_RESIZE; 269 + xenfb_info->resize.width = info->var.xres; 270 + xenfb_info->resize.height = info->var.yres; 271 + xenfb_info->resize.stride = info->fix.line_length; 272 + xenfb_info->resize.depth = info->var.bits_per_pixel; 273 + xenfb_info->resize.offset = 0; 274 + xenfb_info->resize_dpy = 1; 275 + spin_unlock_irqrestore(&xenfb_info->resize_lock, flags); 276 + return 0; 277 + } 278 + 273 279 static struct fb_ops xenfb_fb_ops = { 274 280 .owner = THIS_MODULE, 275 281 .fb_read = fb_sys_read, ··· 329 233 .fb_fillrect = xenfb_fillrect, 330 234 .fb_copyarea = xenfb_copyarea, 331 235 .fb_imageblit = xenfb_imageblit, 236 + .fb_check_var = xenfb_check_var, 237 + .fb_set_par = xenfb_set_par, 332 238 }; 333 239 334 240 static irqreturn_t xenfb_event_handler(int rq, void *dev_id) ··· 359 261 { 360 262 struct xenfb_info *info; 361 263 struct fb_info *fb_info; 264 + int fb_size; 265 + int val; 362 266 int ret; 363 267 364 268 info = kzalloc(sizeof(*info), GFP_KERNEL); ··· 368 268 xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure"); 369 269 return -ENOMEM; 370 270 } 271 + 272 + /* Limit kernel param videoram amount to what is in xenstore */ 273 + if (xenbus_scanf(XBT_NIL, dev->otherend, "videoram", "%d", &val) == 1) { 274 + if (val < video[KPARAM_MEM]) 275 + video[KPARAM_MEM] = val; 276 + } 277 + 278 + /* If requested res does not fit in available memory, use default */ 279 + fb_size = video[KPARAM_MEM] * 1024 * 1024; 280 + if (video[KPARAM_WIDTH] * video[KPARAM_HEIGHT] * XENFB_DEPTH / 8 281 + > fb_size) { 282 + video[KPARAM_WIDTH] = XENFB_WIDTH; 283 + video[KPARAM_HEIGHT] = XENFB_HEIGHT; 284 + fb_size = XENFB_DEFAULT_FB_LEN; 285 + } 286 + 371 287 dev->dev.driver_data = info; 372 288 info->xbdev = dev; 373 289 info->irq = -1; 374 290 info->x1 = info->y1 = INT_MAX; 375 291 spin_lock_init(&info->dirty_lock); 292 + spin_lock_init(&info->resize_lock); 376 293 377 - info->fb = vmalloc(xenfb_mem_len); 294 + info->fb = vmalloc(fb_size); 378 295 if (info->fb == NULL) 379 296 goto error_nomem; 380 - memset(info->fb, 0, xenfb_mem_len); 297 + memset(info->fb, 0, fb_size); 381 298 382 - info->nr_pages = (xenfb_mem_len + PAGE_SIZE - 1) >> PAGE_SHIFT; 299 + info->nr_pages = (fb_size + PAGE_SIZE - 1) >> PAGE_SHIFT; 383 300 384 301 info->mfns = vmalloc(sizeof(unsigned long) * info->nr_pages); 385 302 if (!info->mfns) ··· 406 289 info->page = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO); 407 290 if (!info->page) 408 291 goto error_nomem; 409 - 410 - xenfb_init_shared_page(info); 411 292 412 293 /* abusing framebuffer_alloc() to allocate pseudo_palette */ 413 294 fb_info = framebuffer_alloc(sizeof(u32) * 256, NULL); ··· 419 304 fb_info->screen_base = info->fb; 420 305 421 306 fb_info->fbops = &xenfb_fb_ops; 422 - fb_info->var.xres_virtual = fb_info->var.xres = info->page->width; 423 - fb_info->var.yres_virtual = fb_info->var.yres = info->page->height; 424 - fb_info->var.bits_per_pixel = info->page->depth; 307 + fb_info->var.xres_virtual = fb_info->var.xres = video[KPARAM_WIDTH]; 308 + fb_info->var.yres_virtual = fb_info->var.yres = video[KPARAM_HEIGHT]; 309 + fb_info->var.bits_per_pixel = XENFB_DEPTH; 425 310 426 311 fb_info->var.red = (struct fb_bitfield){16, 8, 0}; 427 312 fb_info->var.green = (struct fb_bitfield){8, 8, 0}; ··· 433 318 fb_info->var.vmode = FB_VMODE_NONINTERLACED; 434 319 435 320 fb_info->fix.visual = FB_VISUAL_TRUECOLOR; 436 - fb_info->fix.line_length = info->page->line_length; 321 + fb_info->fix.line_length = fb_info->var.xres * XENFB_DEPTH / 8; 437 322 fb_info->fix.smem_start = 0; 438 - fb_info->fix.smem_len = xenfb_mem_len; 323 + fb_info->fix.smem_len = fb_size; 439 324 strcpy(fb_info->fix.id, "xen"); 440 325 fb_info->fix.type = FB_TYPE_PACKED_PIXELS; 441 326 fb_info->fix.accel = FB_ACCEL_NONE; ··· 451 336 452 337 fb_info->fbdefio = &xenfb_defio; 453 338 fb_deferred_io_init(fb_info); 339 + 340 + xenfb_init_shared_page(info, fb_info); 454 341 455 342 ret = register_framebuffer(fb_info); 456 343 if (ret) { ··· 506 389 struct xenfb_info *info = dev->dev.driver_data; 507 390 508 391 xenfb_disconnect_backend(info); 509 - xenfb_init_shared_page(info); 392 + xenfb_init_shared_page(info, info->fb_info); 510 393 return xenfb_connect_backend(dev, info); 511 394 } 512 395 ··· 534 417 return pfn_to_mfn(vmalloc_to_pfn(address)); 535 418 } 536 419 537 - static void xenfb_init_shared_page(struct xenfb_info *info) 420 + static void xenfb_init_shared_page(struct xenfb_info *info, 421 + struct fb_info *fb_info) 538 422 { 539 423 int i; 424 + int epd = PAGE_SIZE / sizeof(info->mfns[0]); 540 425 541 426 for (i = 0; i < info->nr_pages; i++) 542 427 info->mfns[i] = vmalloc_to_mfn(info->fb + i * PAGE_SIZE); 543 428 544 - info->page->pd[0] = vmalloc_to_mfn(info->mfns); 545 - info->page->pd[1] = 0; 546 - info->page->width = XENFB_WIDTH; 547 - info->page->height = XENFB_HEIGHT; 548 - info->page->depth = XENFB_DEPTH; 549 - info->page->line_length = (info->page->depth / 8) * info->page->width; 550 - info->page->mem_length = xenfb_mem_len; 429 + for (i = 0; i * epd < info->nr_pages; i++) 430 + info->page->pd[i] = vmalloc_to_mfn(&info->mfns[i * epd]); 431 + 432 + info->page->width = fb_info->var.xres; 433 + info->page->height = fb_info->var.yres; 434 + info->page->depth = fb_info->var.bits_per_pixel; 435 + info->page->line_length = fb_info->fix.line_length; 436 + info->page->mem_length = fb_info->fix.smem_len; 551 437 info->page->in_cons = info->page->in_prod = 0; 552 438 info->page->out_cons = info->page->out_prod = 0; 553 439 } ··· 650 530 val = 0; 651 531 if (val) 652 532 info->update_wanted = 1; 533 + 534 + if (xenbus_scanf(XBT_NIL, dev->otherend, 535 + "feature-resize", "%d", &val) < 0) 536 + val = 0; 537 + info->feature_resize = val; 653 538 break; 654 539 655 540 case XenbusStateClosing:
+24 -5
include/xen/interface/io/fbif.h
··· 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 ··· 121 105 * Each directory page holds PAGE_SIZE / sizeof(*pd) 122 106 * framebuffer pages, and can thus map up to PAGE_SIZE * 123 107 * PAGE_SIZE / sizeof(*pd) bytes. With PAGE_SIZE == 4096 and 124 - * sizeof(unsigned long) == 4, that's 4 Megs. Two directory 125 - * pages should be enough for a while. 108 + * sizeof(unsigned long) == 4/8, that's 4 Megs 32 bit and 2 109 + * Megs 64 bit. 256 directories give enough room for a 512 110 + * Meg framebuffer with a max resolution of 12,800x10,240. 111 + * Should be enough for a while with room leftover for 112 + * expansion. 126 113 */ 127 - unsigned long pd[2]; 114 + unsigned long pd[256]; 128 115 }; 129 116 130 117 /* 131 - * Wart: xenkbd needs to know resolution. Put it here until a better 132 - * solution is found, but don't leak it to the backend. 118 + * Wart: xenkbd needs to know default resolution. Put it here until a 119 + * better solution is found, but don't leak it to the backend. 133 120 */ 134 121 #ifdef __KERNEL__ 135 122 #define XENFB_WIDTH 800