diff options
Diffstat (limited to 'drivers/video/xen-fbfront.c')
-rw-r--r-- | drivers/video/xen-fbfront.c | 211 |
1 files changed, 182 insertions, 29 deletions
diff --git a/drivers/video/xen-fbfront.c b/drivers/video/xen-fbfront.c index 619a6f8d65a2..47ed39b52f9c 100644 --- a/drivers/video/xen-fbfront.c +++ b/drivers/video/xen-fbfront.c | |||
@@ -18,6 +18,7 @@ | |||
18 | * frame buffer. | 18 | * frame buffer. |
19 | */ | 19 | */ |
20 | 20 | ||
21 | #include <linux/console.h> | ||
21 | #include <linux/kernel.h> | 22 | #include <linux/kernel.h> |
22 | #include <linux/errno.h> | 23 | #include <linux/errno.h> |
23 | #include <linux/fb.h> | 24 | #include <linux/fb.h> |
@@ -42,37 +43,68 @@ struct xenfb_info { | |||
42 | struct xenfb_page *page; | 43 | struct xenfb_page *page; |
43 | unsigned long *mfns; | 44 | unsigned long *mfns; |
44 | 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; | ||
45 | 50 | ||
46 | struct xenbus_device *xbdev; | 51 | struct xenbus_device *xbdev; |
47 | }; | 52 | }; |
48 | 53 | ||
49 | 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) |
50 | 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 | |||
62 | static void xenfb_make_preferred_console(void); | ||
51 | static int xenfb_remove(struct xenbus_device *); | 63 | static int xenfb_remove(struct xenbus_device *); |
52 | static void xenfb_init_shared_page(struct xenfb_info *); | 64 | static void xenfb_init_shared_page(struct xenfb_info *, struct fb_info *); |
53 | static int xenfb_connect_backend(struct xenbus_device *, struct xenfb_info *); | 65 | static int xenfb_connect_backend(struct xenbus_device *, struct xenfb_info *); |
54 | static void xenfb_disconnect_backend(struct xenfb_info *); | 66 | static void xenfb_disconnect_backend(struct xenfb_info *); |
55 | 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 | |||
56 | static void xenfb_do_update(struct xenfb_info *info, | 83 | static void xenfb_do_update(struct xenfb_info *info, |
57 | int x, int y, int w, int h) | 84 | int x, int y, int w, int h) |
58 | { | 85 | { |
59 | union xenfb_out_event event; | 86 | union xenfb_out_event event; |
60 | u32 prod; | ||
61 | 87 | ||
88 | memset(&event, 0, sizeof(event)); | ||
62 | event.type = XENFB_TYPE_UPDATE; | 89 | event.type = XENFB_TYPE_UPDATE; |
63 | event.update.x = x; | 90 | event.update.x = x; |
64 | event.update.y = y; | 91 | event.update.y = y; |
65 | event.update.width = w; | 92 | event.update.width = w; |
66 | event.update.height = h; | 93 | event.update.height = h; |
67 | 94 | ||
68 | prod = info->page->out_prod; | ||
69 | /* caller ensures !xenfb_queue_full() */ | 95 | /* caller ensures !xenfb_queue_full() */ |
70 | mb(); /* ensure ring space available */ | 96 | xenfb_send_event(info, &event); |
71 | XENFB_OUT_RING_REF(info->page, prod) = event; | 97 | } |
72 | wmb(); /* ensure ring contents visible */ | ||
73 | info->page->out_prod = prod + 1; | ||
74 | 98 | ||
75 | 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); | ||
76 | } | 108 | } |
77 | 109 | ||
78 | static int xenfb_queue_full(struct xenfb_info *info) | 110 | static int xenfb_queue_full(struct xenfb_info *info) |
@@ -84,12 +116,28 @@ static int xenfb_queue_full(struct xenfb_info *info) | |||
84 | return prod - cons == XENFB_OUT_RING_LEN; | 116 | return prod - cons == XENFB_OUT_RING_LEN; |
85 | } | 117 | } |
86 | 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 | |||
87 | static void xenfb_refresh(struct xenfb_info *info, | 133 | static void xenfb_refresh(struct xenfb_info *info, |
88 | int x1, int y1, int w, int h) | 134 | int x1, int y1, int w, int h) |
89 | { | 135 | { |
90 | unsigned long flags; | 136 | unsigned long flags; |
91 | int y2 = y1 + h - 1; | ||
92 | int x2 = x1 + w - 1; | 137 | int x2 = x1 + w - 1; |
138 | int y2 = y1 + h - 1; | ||
139 | |||
140 | xenfb_handle_resize_dpy(info); | ||
93 | 141 | ||
94 | if (!info->update_wanted) | 142 | if (!info->update_wanted) |
95 | return; | 143 | return; |
@@ -222,6 +270,57 @@ static ssize_t xenfb_write(struct fb_info *p, const char __user *buf, | |||
222 | return res; | 270 | return res; |
223 | } | 271 | } |
224 | 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 | |||
225 | static struct fb_ops xenfb_fb_ops = { | 324 | static struct fb_ops xenfb_fb_ops = { |
226 | .owner = THIS_MODULE, | 325 | .owner = THIS_MODULE, |
227 | .fb_read = fb_sys_read, | 326 | .fb_read = fb_sys_read, |
@@ -230,6 +329,8 @@ static struct fb_ops xenfb_fb_ops = { | |||
230 | .fb_fillrect = xenfb_fillrect, | 329 | .fb_fillrect = xenfb_fillrect, |
231 | .fb_copyarea = xenfb_copyarea, | 330 | .fb_copyarea = xenfb_copyarea, |
232 | .fb_imageblit = xenfb_imageblit, | 331 | .fb_imageblit = xenfb_imageblit, |
332 | .fb_check_var = xenfb_check_var, | ||
333 | .fb_set_par = xenfb_set_par, | ||
233 | }; | 334 | }; |
234 | 335 | ||
235 | static irqreturn_t xenfb_event_handler(int rq, void *dev_id) | 336 | static irqreturn_t xenfb_event_handler(int rq, void *dev_id) |
@@ -258,6 +359,8 @@ static int __devinit xenfb_probe(struct xenbus_device *dev, | |||
258 | { | 359 | { |
259 | struct xenfb_info *info; | 360 | struct xenfb_info *info; |
260 | struct fb_info *fb_info; | 361 | struct fb_info *fb_info; |
362 | int fb_size; | ||
363 | int val; | ||
261 | int ret; | 364 | int ret; |
262 | 365 | ||
263 | info = kzalloc(sizeof(*info), GFP_KERNEL); | 366 | info = kzalloc(sizeof(*info), GFP_KERNEL); |
@@ -265,18 +368,35 @@ static int __devinit xenfb_probe(struct xenbus_device *dev, | |||
265 | xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure"); | 368 | xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure"); |
266 | return -ENOMEM; | 369 | return -ENOMEM; |
267 | } | 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 | |||
268 | dev->dev.driver_data = info; | 387 | dev->dev.driver_data = info; |
269 | info->xbdev = dev; | 388 | info->xbdev = dev; |
270 | info->irq = -1; | 389 | info->irq = -1; |
271 | info->x1 = info->y1 = INT_MAX; | 390 | info->x1 = info->y1 = INT_MAX; |
272 | spin_lock_init(&info->dirty_lock); | 391 | spin_lock_init(&info->dirty_lock); |
392 | spin_lock_init(&info->resize_lock); | ||
273 | 393 | ||
274 | info->fb = vmalloc(xenfb_mem_len); | 394 | info->fb = vmalloc(fb_size); |
275 | if (info->fb == NULL) | 395 | if (info->fb == NULL) |
276 | goto error_nomem; | 396 | goto error_nomem; |
277 | memset(info->fb, 0, xenfb_mem_len); | 397 | memset(info->fb, 0, fb_size); |
278 | 398 | ||
279 | info->nr_pages = (xenfb_mem_len + PAGE_SIZE - 1) >> PAGE_SHIFT; | 399 | info->nr_pages = (fb_size + PAGE_SIZE - 1) >> PAGE_SHIFT; |
280 | 400 | ||
281 | info->mfns = vmalloc(sizeof(unsigned long) * info->nr_pages); | 401 | info->mfns = vmalloc(sizeof(unsigned long) * info->nr_pages); |
282 | if (!info->mfns) | 402 | if (!info->mfns) |
@@ -287,8 +407,6 @@ static int __devinit xenfb_probe(struct xenbus_device *dev, | |||
287 | if (!info->page) | 407 | if (!info->page) |
288 | goto error_nomem; | 408 | goto error_nomem; |
289 | 409 | ||
290 | xenfb_init_shared_page(info); | ||
291 | |||
292 | /* abusing framebuffer_alloc() to allocate pseudo_palette */ | 410 | /* abusing framebuffer_alloc() to allocate pseudo_palette */ |
293 | fb_info = framebuffer_alloc(sizeof(u32) * 256, NULL); | 411 | fb_info = framebuffer_alloc(sizeof(u32) * 256, NULL); |
294 | if (fb_info == NULL) | 412 | if (fb_info == NULL) |
@@ -301,9 +419,9 @@ static int __devinit xenfb_probe(struct xenbus_device *dev, | |||
301 | fb_info->screen_base = info->fb; | 419 | fb_info->screen_base = info->fb; |
302 | 420 | ||
303 | fb_info->fbops = &xenfb_fb_ops; | 421 | fb_info->fbops = &xenfb_fb_ops; |
304 | 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]; |
305 | 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]; |
306 | fb_info->var.bits_per_pixel = info->page->depth; | 424 | fb_info->var.bits_per_pixel = XENFB_DEPTH; |
307 | 425 | ||
308 | fb_info->var.red = (struct fb_bitfield){16, 8, 0}; | 426 | fb_info->var.red = (struct fb_bitfield){16, 8, 0}; |
309 | fb_info->var.green = (struct fb_bitfield){8, 8, 0}; | 427 | fb_info->var.green = (struct fb_bitfield){8, 8, 0}; |
@@ -315,9 +433,9 @@ static int __devinit xenfb_probe(struct xenbus_device *dev, | |||
315 | fb_info->var.vmode = FB_VMODE_NONINTERLACED; | 433 | fb_info->var.vmode = FB_VMODE_NONINTERLACED; |
316 | 434 | ||
317 | fb_info->fix.visual = FB_VISUAL_TRUECOLOR; | 435 | fb_info->fix.visual = FB_VISUAL_TRUECOLOR; |
318 | fb_info->fix.line_length = info->page->line_length; | 436 | fb_info->fix.line_length = fb_info->var.xres * XENFB_DEPTH / 8; |
319 | fb_info->fix.smem_start = 0; | 437 | fb_info->fix.smem_start = 0; |
320 | fb_info->fix.smem_len = xenfb_mem_len; | 438 | fb_info->fix.smem_len = fb_size; |
321 | strcpy(fb_info->fix.id, "xen"); | 439 | strcpy(fb_info->fix.id, "xen"); |
322 | fb_info->fix.type = FB_TYPE_PACKED_PIXELS; | 440 | fb_info->fix.type = FB_TYPE_PACKED_PIXELS; |
323 | fb_info->fix.accel = FB_ACCEL_NONE; | 441 | fb_info->fix.accel = FB_ACCEL_NONE; |
@@ -334,6 +452,8 @@ static int __devinit xenfb_probe(struct xenbus_device *dev, | |||
334 | fb_info->fbdefio = &xenfb_defio; | 452 | fb_info->fbdefio = &xenfb_defio; |
335 | fb_deferred_io_init(fb_info); | 453 | fb_deferred_io_init(fb_info); |
336 | 454 | ||
455 | xenfb_init_shared_page(info, fb_info); | ||
456 | |||
337 | ret = register_framebuffer(fb_info); | 457 | ret = register_framebuffer(fb_info); |
338 | if (ret) { | 458 | if (ret) { |
339 | fb_deferred_io_cleanup(fb_info); | 459 | fb_deferred_io_cleanup(fb_info); |
@@ -348,6 +468,7 @@ static int __devinit xenfb_probe(struct xenbus_device *dev, | |||
348 | if (ret < 0) | 468 | if (ret < 0) |
349 | goto error; | 469 | goto error; |
350 | 470 | ||
471 | xenfb_make_preferred_console(); | ||
351 | return 0; | 472 | return 0; |
352 | 473 | ||
353 | error_nomem: | 474 | error_nomem: |
@@ -358,12 +479,34 @@ static int __devinit xenfb_probe(struct xenbus_device *dev, | |||
358 | return ret; | 479 | return ret; |
359 | } | 480 | } |
360 | 481 | ||
482 | static __devinit void | ||
483 | xenfb_make_preferred_console(void) | ||
484 | { | ||
485 | struct console *c; | ||
486 | |||
487 | if (console_set_on_cmdline) | ||
488 | return; | ||
489 | |||
490 | acquire_console_sem(); | ||
491 | for (c = console_drivers; c; c = c->next) { | ||
492 | if (!strcmp(c->name, "tty") && c->index == 0) | ||
493 | break; | ||
494 | } | ||
495 | release_console_sem(); | ||
496 | if (c) { | ||
497 | unregister_console(c); | ||
498 | c->flags |= CON_CONSDEV; | ||
499 | c->flags &= ~CON_PRINTBUFFER; /* don't print again */ | ||
500 | register_console(c); | ||
501 | } | ||
502 | } | ||
503 | |||
361 | static int xenfb_resume(struct xenbus_device *dev) | 504 | static int xenfb_resume(struct xenbus_device *dev) |
362 | { | 505 | { |
363 | struct xenfb_info *info = dev->dev.driver_data; | 506 | struct xenfb_info *info = dev->dev.driver_data; |
364 | 507 | ||
365 | xenfb_disconnect_backend(info); | 508 | xenfb_disconnect_backend(info); |
366 | xenfb_init_shared_page(info); | 509 | xenfb_init_shared_page(info, info->fb_info); |
367 | return xenfb_connect_backend(dev, info); | 510 | return xenfb_connect_backend(dev, info); |
368 | } | 511 | } |
369 | 512 | ||
@@ -391,20 +534,23 @@ static unsigned long vmalloc_to_mfn(void *address) | |||
391 | return pfn_to_mfn(vmalloc_to_pfn(address)); | 534 | return pfn_to_mfn(vmalloc_to_pfn(address)); |
392 | } | 535 | } |
393 | 536 | ||
394 | 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) | ||
395 | { | 539 | { |
396 | int i; | 540 | int i; |
541 | int epd = PAGE_SIZE / sizeof(info->mfns[0]); | ||
397 | 542 | ||
398 | for (i = 0; i < info->nr_pages; i++) | 543 | for (i = 0; i < info->nr_pages; i++) |
399 | info->mfns[i] = vmalloc_to_mfn(info->fb + i * PAGE_SIZE); | 544 | info->mfns[i] = vmalloc_to_mfn(info->fb + i * PAGE_SIZE); |
400 | 545 | ||
401 | info->page->pd[0] = vmalloc_to_mfn(info->mfns); | 546 | for (i = 0; i * epd < info->nr_pages; i++) |
402 | info->page->pd[1] = 0; | 547 | info->page->pd[i] = vmalloc_to_mfn(&info->mfns[i * epd]); |
403 | info->page->width = XENFB_WIDTH; | 548 | |
404 | info->page->height = XENFB_HEIGHT; | 549 | info->page->width = fb_info->var.xres; |
405 | info->page->depth = XENFB_DEPTH; | 550 | info->page->height = fb_info->var.yres; |
406 | info->page->line_length = (info->page->depth / 8) * info->page->width; | 551 | info->page->depth = fb_info->var.bits_per_pixel; |
407 | 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; | ||
408 | info->page->in_cons = info->page->in_prod = 0; | 554 | info->page->in_cons = info->page->in_prod = 0; |
409 | info->page->out_cons = info->page->out_prod = 0; | 555 | info->page->out_cons = info->page->out_prod = 0; |
410 | } | 556 | } |
@@ -504,6 +650,11 @@ InitWait: | |||
504 | val = 0; | 650 | val = 0; |
505 | if (val) | 651 | if (val) |
506 | 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; | ||
507 | break; | 658 | break; |
508 | 659 | ||
509 | case XenbusStateClosing: | 660 | case XenbusStateClosing: |
@@ -547,4 +698,6 @@ static void __exit xenfb_cleanup(void) | |||
547 | module_init(xenfb_init); | 698 | module_init(xenfb_init); |
548 | module_exit(xenfb_cleanup); | 699 | module_exit(xenfb_cleanup); |
549 | 700 | ||
701 | MODULE_DESCRIPTION("Xen virtual framebuffer device frontend"); | ||
550 | MODULE_LICENSE("GPL"); | 702 | MODULE_LICENSE("GPL"); |
703 | MODULE_ALIAS("xen:vfb"); | ||