aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/xen-fbfront.c
diff options
context:
space:
mode:
authorMarkus Armbruster <armbru@redhat.com>2008-05-26 18:31:11 -0400
committerThomas Gleixner <tglx@linutronix.de>2008-05-27 04:11:36 -0400
commite4dcff1f6e7582f76c2c9990b1d9111bbc8e26ef (patch)
treed4a06968700cf64e3f8b7b146512a457ac9e7ece /drivers/video/xen-fbfront.c
parentf4ad1ebd7a0fae2782ef9f76c0b94b536742c3e8 (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/xen-fbfront.c')
-rw-r--r--drivers/video/xen-fbfront.c183
1 files changed, 154 insertions, 29 deletions
diff --git a/drivers/video/xen-fbfront.c b/drivers/video/xen-fbfront.c
index 291eef69559..47ed39b52f9 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
50static 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
56enum { KPARAM_MEM, KPARAM_WIDTH, KPARAM_HEIGHT, KPARAM_CNT };
57static int video[KPARAM_CNT] = { 2, XENFB_WIDTH, XENFB_HEIGHT };
58module_param_array(video, int, NULL, 0);
59MODULE_PARM_DESC(video,
60 "Video memory size in MB, width, height in pixels (default 2,800,600)");
51 61
52static void xenfb_make_preferred_console(void); 62static void xenfb_make_preferred_console(void);
53static int xenfb_remove(struct xenbus_device *); 63static int xenfb_remove(struct xenbus_device *);
54static void xenfb_init_shared_page(struct xenfb_info *); 64static void xenfb_init_shared_page(struct xenfb_info *, struct fb_info *);
55static int xenfb_connect_backend(struct xenbus_device *, struct xenfb_info *); 65static int xenfb_connect_backend(struct xenbus_device *, struct xenfb_info *);
56static void xenfb_disconnect_backend(struct xenfb_info *); 66static void xenfb_disconnect_backend(struct xenfb_info *);
57 67
68static 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
58static void xenfb_do_update(struct xenfb_info *info, 83static 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); 99static 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
81static int xenfb_queue_full(struct xenfb_info *info) 110static 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
119static 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
90static void xenfb_refresh(struct xenfb_info *info, 133static 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
273static int
274xenfb_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
305static 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
228static struct fb_ops xenfb_fb_ops = { 324static 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
238static irqreturn_t xenfb_event_handler(int rq, void *dev_id) 336static 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
420static void xenfb_init_shared_page(struct xenfb_info *info) 537static 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: