xen-fbfront.c revision 1ccbf5344c3daef046d2323190cc6807c44f1917
1/* 2 * Xen para-virtual frame buffer device 3 * 4 * Copyright (C) 2005-2006 Anthony Liguori <aliguori@us.ibm.com> 5 * Copyright (C) 2006-2008 Red Hat, Inc., Markus Armbruster <armbru@redhat.com> 6 * 7 * Based on linux/drivers/video/q40fb.c 8 * 9 * This file is subject to the terms and conditions of the GNU General Public 10 * License. See the file COPYING in the main directory of this archive for 11 * more details. 12 */ 13 14/* 15 * TODO: 16 * 17 * Switch to grant tables when they become capable of dealing with the 18 * frame buffer. 19 */ 20 21#include <linux/console.h> 22#include <linux/kernel.h> 23#include <linux/errno.h> 24#include <linux/fb.h> 25#include <linux/module.h> 26#include <linux/vmalloc.h> 27#include <linux/mm.h> 28 29#include <asm/xen/hypervisor.h> 30 31#include <xen/xen.h> 32#include <xen/events.h> 33#include <xen/page.h> 34#include <xen/interface/io/fbif.h> 35#include <xen/interface/io/protocols.h> 36#include <xen/xenbus.h> 37 38struct xenfb_info { 39 unsigned char *fb; 40 struct fb_info *fb_info; 41 int x1, y1, x2, y2; /* dirty rectangle, 42 protected by dirty_lock */ 43 spinlock_t dirty_lock; 44 int nr_pages; 45 int irq; 46 struct xenfb_page *page; 47 unsigned long *mfns; 48 int update_wanted; /* XENFB_TYPE_UPDATE wanted */ 49 int feature_resize; /* XENFB_TYPE_RESIZE ok */ 50 struct xenfb_resize resize; /* protected by resize_lock */ 51 int resize_dpy; /* ditto */ 52 spinlock_t resize_lock; 53 54 struct xenbus_device *xbdev; 55}; 56 57#define XENFB_DEFAULT_FB_LEN (XENFB_WIDTH * XENFB_HEIGHT * XENFB_DEPTH / 8) 58 59enum { KPARAM_MEM, KPARAM_WIDTH, KPARAM_HEIGHT, KPARAM_CNT }; 60static int video[KPARAM_CNT] = { 2, XENFB_WIDTH, XENFB_HEIGHT }; 61module_param_array(video, int, NULL, 0); 62MODULE_PARM_DESC(video, 63 "Video memory size in MB, width, height in pixels (default 2,800,600)"); 64 65static void xenfb_make_preferred_console(void); 66static int xenfb_remove(struct xenbus_device *); 67static void xenfb_init_shared_page(struct xenfb_info *, struct fb_info *); 68static int xenfb_connect_backend(struct xenbus_device *, struct xenfb_info *); 69static void xenfb_disconnect_backend(struct xenfb_info *); 70 71static void xenfb_send_event(struct xenfb_info *info, 72 union xenfb_out_event *event) 73{ 74 u32 prod; 75 76 prod = info->page->out_prod; 77 /* caller ensures !xenfb_queue_full() */ 78 mb(); /* ensure ring space available */ 79 XENFB_OUT_RING_REF(info->page, prod) = *event; 80 wmb(); /* ensure ring contents visible */ 81 info->page->out_prod = prod + 1; 82 83 notify_remote_via_irq(info->irq); 84} 85 86static void xenfb_do_update(struct xenfb_info *info, 87 int x, int y, int w, int h) 88{ 89 union xenfb_out_event event; 90 91 memset(&event, 0, sizeof(event)); 92 event.type = XENFB_TYPE_UPDATE; 93 event.update.x = x; 94 event.update.y = y; 95 event.update.width = w; 96 event.update.height = h; 97 98 /* caller ensures !xenfb_queue_full() */ 99 xenfb_send_event(info, &event); 100} 101 102static void xenfb_do_resize(struct xenfb_info *info) 103{ 104 union xenfb_out_event event; 105 106 memset(&event, 0, sizeof(event)); 107 event.resize = info->resize; 108 109 /* caller ensures !xenfb_queue_full() */ 110 xenfb_send_event(info, &event); 111} 112 113static int xenfb_queue_full(struct xenfb_info *info) 114{ 115 u32 cons, prod; 116 117 prod = info->page->out_prod; 118 cons = info->page->out_cons; 119 return prod - cons == XENFB_OUT_RING_LEN; 120} 121 122static void xenfb_handle_resize_dpy(struct xenfb_info *info) 123{ 124 unsigned long flags; 125 126 spin_lock_irqsave(&info->resize_lock, flags); 127 if (info->resize_dpy) { 128 if (!xenfb_queue_full(info)) { 129 info->resize_dpy = 0; 130 xenfb_do_resize(info); 131 } 132 } 133 spin_unlock_irqrestore(&info->resize_lock, flags); 134} 135 136static void xenfb_refresh(struct xenfb_info *info, 137 int x1, int y1, int w, int h) 138{ 139 unsigned long flags; 140 int x2 = x1 + w - 1; 141 int y2 = y1 + h - 1; 142 143 xenfb_handle_resize_dpy(info); 144 145 if (!info->update_wanted) 146 return; 147 148 spin_lock_irqsave(&info->dirty_lock, flags); 149 150 /* Combine with dirty rectangle: */ 151 if (info->y1 < y1) 152 y1 = info->y1; 153 if (info->y2 > y2) 154 y2 = info->y2; 155 if (info->x1 < x1) 156 x1 = info->x1; 157 if (info->x2 > x2) 158 x2 = info->x2; 159 160 if (xenfb_queue_full(info)) { 161 /* Can't send right now, stash it in the dirty rectangle */ 162 info->x1 = x1; 163 info->x2 = x2; 164 info->y1 = y1; 165 info->y2 = y2; 166 spin_unlock_irqrestore(&info->dirty_lock, flags); 167 return; 168 } 169 170 /* Clear dirty rectangle: */ 171 info->x1 = info->y1 = INT_MAX; 172 info->x2 = info->y2 = 0; 173 174 spin_unlock_irqrestore(&info->dirty_lock, flags); 175 176 if (x1 <= x2 && y1 <= y2) 177 xenfb_do_update(info, x1, y1, x2 - x1 + 1, y2 - y1 + 1); 178} 179 180static void xenfb_deferred_io(struct fb_info *fb_info, 181 struct list_head *pagelist) 182{ 183 struct xenfb_info *info = fb_info->par; 184 struct page *page; 185 unsigned long beg, end; 186 int y1, y2, miny, maxy; 187 188 miny = INT_MAX; 189 maxy = 0; 190 list_for_each_entry(page, pagelist, lru) { 191 beg = page->index << PAGE_SHIFT; 192 end = beg + PAGE_SIZE - 1; 193 y1 = beg / fb_info->fix.line_length; 194 y2 = end / fb_info->fix.line_length; 195 if (y2 >= fb_info->var.yres) 196 y2 = fb_info->var.yres - 1; 197 if (miny > y1) 198 miny = y1; 199 if (maxy < y2) 200 maxy = y2; 201 } 202 xenfb_refresh(info, 0, miny, fb_info->var.xres, maxy - miny + 1); 203} 204 205static struct fb_deferred_io xenfb_defio = { 206 .delay = HZ / 20, 207 .deferred_io = xenfb_deferred_io, 208}; 209 210static int xenfb_setcolreg(unsigned regno, unsigned red, unsigned green, 211 unsigned blue, unsigned transp, 212 struct fb_info *info) 213{ 214 u32 v; 215 216 if (regno > info->cmap.len) 217 return 1; 218 219#define CNVT_TOHW(val, width) ((((val)<<(width))+0x7FFF-(val))>>16) 220 red = CNVT_TOHW(red, info->var.red.length); 221 green = CNVT_TOHW(green, info->var.green.length); 222 blue = CNVT_TOHW(blue, info->var.blue.length); 223 transp = CNVT_TOHW(transp, info->var.transp.length); 224#undef CNVT_TOHW 225 226 v = (red << info->var.red.offset) | 227 (green << info->var.green.offset) | 228 (blue << info->var.blue.offset); 229 230 switch (info->var.bits_per_pixel) { 231 case 16: 232 case 24: 233 case 32: 234 ((u32 *)info->pseudo_palette)[regno] = v; 235 break; 236 } 237 238 return 0; 239} 240 241static void xenfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect) 242{ 243 struct xenfb_info *info = p->par; 244 245 sys_fillrect(p, rect); 246 xenfb_refresh(info, rect->dx, rect->dy, rect->width, rect->height); 247} 248 249static void xenfb_imageblit(struct fb_info *p, const struct fb_image *image) 250{ 251 struct xenfb_info *info = p->par; 252 253 sys_imageblit(p, image); 254 xenfb_refresh(info, image->dx, image->dy, image->width, image->height); 255} 256 257static void xenfb_copyarea(struct fb_info *p, const struct fb_copyarea *area) 258{ 259 struct xenfb_info *info = p->par; 260 261 sys_copyarea(p, area); 262 xenfb_refresh(info, area->dx, area->dy, area->width, area->height); 263} 264 265static ssize_t xenfb_write(struct fb_info *p, const char __user *buf, 266 size_t count, loff_t *ppos) 267{ 268 struct xenfb_info *info = p->par; 269 ssize_t res; 270 271 res = fb_sys_write(p, buf, count, ppos); 272 xenfb_refresh(info, 0, 0, info->page->width, info->page->height); 273 return res; 274} 275 276static int 277xenfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) 278{ 279 struct xenfb_info *xenfb_info; 280 int required_mem_len; 281 282 xenfb_info = info->par; 283 284 if (!xenfb_info->feature_resize) { 285 if (var->xres == video[KPARAM_WIDTH] && 286 var->yres == video[KPARAM_HEIGHT] && 287 var->bits_per_pixel == xenfb_info->page->depth) { 288 return 0; 289 } 290 return -EINVAL; 291 } 292 293 /* Can't resize past initial width and height */ 294 if (var->xres > video[KPARAM_WIDTH] || var->yres > video[KPARAM_HEIGHT]) 295 return -EINVAL; 296 297 required_mem_len = var->xres * var->yres * xenfb_info->page->depth / 8; 298 if (var->bits_per_pixel == xenfb_info->page->depth && 299 var->xres <= info->fix.line_length / (XENFB_DEPTH / 8) && 300 required_mem_len <= info->fix.smem_len) { 301 var->xres_virtual = var->xres; 302 var->yres_virtual = var->yres; 303 return 0; 304 } 305 return -EINVAL; 306} 307 308static int xenfb_set_par(struct fb_info *info) 309{ 310 struct xenfb_info *xenfb_info; 311 unsigned long flags; 312 313 xenfb_info = info->par; 314 315 spin_lock_irqsave(&xenfb_info->resize_lock, flags); 316 xenfb_info->resize.type = XENFB_TYPE_RESIZE; 317 xenfb_info->resize.width = info->var.xres; 318 xenfb_info->resize.height = info->var.yres; 319 xenfb_info->resize.stride = info->fix.line_length; 320 xenfb_info->resize.depth = info->var.bits_per_pixel; 321 xenfb_info->resize.offset = 0; 322 xenfb_info->resize_dpy = 1; 323 spin_unlock_irqrestore(&xenfb_info->resize_lock, flags); 324 return 0; 325} 326 327static struct fb_ops xenfb_fb_ops = { 328 .owner = THIS_MODULE, 329 .fb_read = fb_sys_read, 330 .fb_write = xenfb_write, 331 .fb_setcolreg = xenfb_setcolreg, 332 .fb_fillrect = xenfb_fillrect, 333 .fb_copyarea = xenfb_copyarea, 334 .fb_imageblit = xenfb_imageblit, 335 .fb_check_var = xenfb_check_var, 336 .fb_set_par = xenfb_set_par, 337}; 338 339static irqreturn_t xenfb_event_handler(int rq, void *dev_id) 340{ 341 /* 342 * No in events recognized, simply ignore them all. 343 * If you need to recognize some, see xen-kbdfront's 344 * input_handler() for how to do that. 345 */ 346 struct xenfb_info *info = dev_id; 347 struct xenfb_page *page = info->page; 348 349 if (page->in_cons != page->in_prod) { 350 info->page->in_cons = info->page->in_prod; 351 notify_remote_via_irq(info->irq); 352 } 353 354 /* Flush dirty rectangle: */ 355 xenfb_refresh(info, INT_MAX, INT_MAX, -INT_MAX, -INT_MAX); 356 357 return IRQ_HANDLED; 358} 359 360static int __devinit xenfb_probe(struct xenbus_device *dev, 361 const struct xenbus_device_id *id) 362{ 363 struct xenfb_info *info; 364 struct fb_info *fb_info; 365 int fb_size; 366 int val; 367 int ret; 368 369 info = kzalloc(sizeof(*info), GFP_KERNEL); 370 if (info == NULL) { 371 xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure"); 372 return -ENOMEM; 373 } 374 375 /* Limit kernel param videoram amount to what is in xenstore */ 376 if (xenbus_scanf(XBT_NIL, dev->otherend, "videoram", "%d", &val) == 1) { 377 if (val < video[KPARAM_MEM]) 378 video[KPARAM_MEM] = val; 379 } 380 381 /* If requested res does not fit in available memory, use default */ 382 fb_size = video[KPARAM_MEM] * 1024 * 1024; 383 if (video[KPARAM_WIDTH] * video[KPARAM_HEIGHT] * XENFB_DEPTH / 8 384 > fb_size) { 385 video[KPARAM_WIDTH] = XENFB_WIDTH; 386 video[KPARAM_HEIGHT] = XENFB_HEIGHT; 387 fb_size = XENFB_DEFAULT_FB_LEN; 388 } 389 390 dev_set_drvdata(&dev->dev, info); 391 info->xbdev = dev; 392 info->irq = -1; 393 info->x1 = info->y1 = INT_MAX; 394 spin_lock_init(&info->dirty_lock); 395 spin_lock_init(&info->resize_lock); 396 397 info->fb = vmalloc(fb_size); 398 if (info->fb == NULL) 399 goto error_nomem; 400 memset(info->fb, 0, fb_size); 401 402 info->nr_pages = (fb_size + PAGE_SIZE - 1) >> PAGE_SHIFT; 403 404 info->mfns = vmalloc(sizeof(unsigned long) * info->nr_pages); 405 if (!info->mfns) 406 goto error_nomem; 407 408 /* set up shared page */ 409 info->page = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO); 410 if (!info->page) 411 goto error_nomem; 412 413 /* abusing framebuffer_alloc() to allocate pseudo_palette */ 414 fb_info = framebuffer_alloc(sizeof(u32) * 256, NULL); 415 if (fb_info == NULL) 416 goto error_nomem; 417 418 /* complete the abuse: */ 419 fb_info->pseudo_palette = fb_info->par; 420 fb_info->par = info; 421 422 fb_info->screen_base = info->fb; 423 424 fb_info->fbops = &xenfb_fb_ops; 425 fb_info->var.xres_virtual = fb_info->var.xres = video[KPARAM_WIDTH]; 426 fb_info->var.yres_virtual = fb_info->var.yres = video[KPARAM_HEIGHT]; 427 fb_info->var.bits_per_pixel = XENFB_DEPTH; 428 429 fb_info->var.red = (struct fb_bitfield){16, 8, 0}; 430 fb_info->var.green = (struct fb_bitfield){8, 8, 0}; 431 fb_info->var.blue = (struct fb_bitfield){0, 8, 0}; 432 433 fb_info->var.activate = FB_ACTIVATE_NOW; 434 fb_info->var.height = -1; 435 fb_info->var.width = -1; 436 fb_info->var.vmode = FB_VMODE_NONINTERLACED; 437 438 fb_info->fix.visual = FB_VISUAL_TRUECOLOR; 439 fb_info->fix.line_length = fb_info->var.xres * XENFB_DEPTH / 8; 440 fb_info->fix.smem_start = 0; 441 fb_info->fix.smem_len = fb_size; 442 strcpy(fb_info->fix.id, "xen"); 443 fb_info->fix.type = FB_TYPE_PACKED_PIXELS; 444 fb_info->fix.accel = FB_ACCEL_NONE; 445 446 fb_info->flags = FBINFO_FLAG_DEFAULT; 447 448 ret = fb_alloc_cmap(&fb_info->cmap, 256, 0); 449 if (ret < 0) { 450 framebuffer_release(fb_info); 451 xenbus_dev_fatal(dev, ret, "fb_alloc_cmap"); 452 goto error; 453 } 454 455 fb_info->fbdefio = &xenfb_defio; 456 fb_deferred_io_init(fb_info); 457 458 xenfb_init_shared_page(info, fb_info); 459 460 ret = xenfb_connect_backend(dev, info); 461 if (ret < 0) 462 goto error; 463 464 ret = register_framebuffer(fb_info); 465 if (ret) { 466 fb_deferred_io_cleanup(fb_info); 467 fb_dealloc_cmap(&fb_info->cmap); 468 framebuffer_release(fb_info); 469 xenbus_dev_fatal(dev, ret, "register_framebuffer"); 470 goto error; 471 } 472 info->fb_info = fb_info; 473 474 xenfb_make_preferred_console(); 475 return 0; 476 477 error_nomem: 478 ret = -ENOMEM; 479 xenbus_dev_fatal(dev, ret, "allocating device memory"); 480 error: 481 xenfb_remove(dev); 482 return ret; 483} 484 485static __devinit void 486xenfb_make_preferred_console(void) 487{ 488 struct console *c; 489 490 if (console_set_on_cmdline) 491 return; 492 493 acquire_console_sem(); 494 for (c = console_drivers; c; c = c->next) { 495 if (!strcmp(c->name, "tty") && c->index == 0) 496 break; 497 } 498 release_console_sem(); 499 if (c) { 500 unregister_console(c); 501 c->flags |= CON_CONSDEV; 502 c->flags &= ~CON_PRINTBUFFER; /* don't print again */ 503 register_console(c); 504 } 505} 506 507static int xenfb_resume(struct xenbus_device *dev) 508{ 509 struct xenfb_info *info = dev_get_drvdata(&dev->dev); 510 511 xenfb_disconnect_backend(info); 512 xenfb_init_shared_page(info, info->fb_info); 513 return xenfb_connect_backend(dev, info); 514} 515 516static int xenfb_remove(struct xenbus_device *dev) 517{ 518 struct xenfb_info *info = dev_get_drvdata(&dev->dev); 519 520 xenfb_disconnect_backend(info); 521 if (info->fb_info) { 522 fb_deferred_io_cleanup(info->fb_info); 523 unregister_framebuffer(info->fb_info); 524 fb_dealloc_cmap(&info->fb_info->cmap); 525 framebuffer_release(info->fb_info); 526 } 527 free_page((unsigned long)info->page); 528 vfree(info->mfns); 529 vfree(info->fb); 530 kfree(info); 531 532 return 0; 533} 534 535static unsigned long vmalloc_to_mfn(void *address) 536{ 537 return pfn_to_mfn(vmalloc_to_pfn(address)); 538} 539 540static void xenfb_init_shared_page(struct xenfb_info *info, 541 struct fb_info *fb_info) 542{ 543 int i; 544 int epd = PAGE_SIZE / sizeof(info->mfns[0]); 545 546 for (i = 0; i < info->nr_pages; i++) 547 info->mfns[i] = vmalloc_to_mfn(info->fb + i * PAGE_SIZE); 548 549 for (i = 0; i * epd < info->nr_pages; i++) 550 info->page->pd[i] = vmalloc_to_mfn(&info->mfns[i * epd]); 551 552 info->page->width = fb_info->var.xres; 553 info->page->height = fb_info->var.yres; 554 info->page->depth = fb_info->var.bits_per_pixel; 555 info->page->line_length = fb_info->fix.line_length; 556 info->page->mem_length = fb_info->fix.smem_len; 557 info->page->in_cons = info->page->in_prod = 0; 558 info->page->out_cons = info->page->out_prod = 0; 559} 560 561static int xenfb_connect_backend(struct xenbus_device *dev, 562 struct xenfb_info *info) 563{ 564 int ret, evtchn; 565 struct xenbus_transaction xbt; 566 567 ret = xenbus_alloc_evtchn(dev, &evtchn); 568 if (ret) 569 return ret; 570 ret = bind_evtchn_to_irqhandler(evtchn, xenfb_event_handler, 571 0, dev->devicetype, info); 572 if (ret < 0) { 573 xenbus_free_evtchn(dev, evtchn); 574 xenbus_dev_fatal(dev, ret, "bind_evtchn_to_irqhandler"); 575 return ret; 576 } 577 info->irq = ret; 578 579 again: 580 ret = xenbus_transaction_start(&xbt); 581 if (ret) { 582 xenbus_dev_fatal(dev, ret, "starting transaction"); 583 return ret; 584 } 585 ret = xenbus_printf(xbt, dev->nodename, "page-ref", "%lu", 586 virt_to_mfn(info->page)); 587 if (ret) 588 goto error_xenbus; 589 ret = xenbus_printf(xbt, dev->nodename, "event-channel", "%u", 590 evtchn); 591 if (ret) 592 goto error_xenbus; 593 ret = xenbus_printf(xbt, dev->nodename, "protocol", "%s", 594 XEN_IO_PROTO_ABI_NATIVE); 595 if (ret) 596 goto error_xenbus; 597 ret = xenbus_printf(xbt, dev->nodename, "feature-update", "1"); 598 if (ret) 599 goto error_xenbus; 600 ret = xenbus_transaction_end(xbt, 0); 601 if (ret) { 602 if (ret == -EAGAIN) 603 goto again; 604 xenbus_dev_fatal(dev, ret, "completing transaction"); 605 return ret; 606 } 607 608 xenbus_switch_state(dev, XenbusStateInitialised); 609 return 0; 610 611 error_xenbus: 612 xenbus_transaction_end(xbt, 1); 613 xenbus_dev_fatal(dev, ret, "writing xenstore"); 614 return ret; 615} 616 617static void xenfb_disconnect_backend(struct xenfb_info *info) 618{ 619 if (info->irq >= 0) 620 unbind_from_irqhandler(info->irq, info); 621 info->irq = -1; 622} 623 624static void xenfb_backend_changed(struct xenbus_device *dev, 625 enum xenbus_state backend_state) 626{ 627 struct xenfb_info *info = dev_get_drvdata(&dev->dev); 628 int val; 629 630 switch (backend_state) { 631 case XenbusStateInitialising: 632 case XenbusStateInitialised: 633 case XenbusStateUnknown: 634 case XenbusStateClosed: 635 break; 636 637 case XenbusStateInitWait: 638InitWait: 639 xenbus_switch_state(dev, XenbusStateConnected); 640 break; 641 642 case XenbusStateConnected: 643 /* 644 * Work around xenbus race condition: If backend goes 645 * through InitWait to Connected fast enough, we can 646 * get Connected twice here. 647 */ 648 if (dev->state != XenbusStateConnected) 649 goto InitWait; /* no InitWait seen yet, fudge it */ 650 651 if (xenbus_scanf(XBT_NIL, info->xbdev->otherend, 652 "request-update", "%d", &val) < 0) 653 val = 0; 654 if (val) 655 info->update_wanted = 1; 656 657 if (xenbus_scanf(XBT_NIL, dev->otherend, 658 "feature-resize", "%d", &val) < 0) 659 val = 0; 660 info->feature_resize = val; 661 break; 662 663 case XenbusStateClosing: 664 xenbus_frontend_closed(dev); 665 break; 666 } 667} 668 669static struct xenbus_device_id xenfb_ids[] = { 670 { "vfb" }, 671 { "" } 672}; 673 674static struct xenbus_driver xenfb_driver = { 675 .name = "vfb", 676 .owner = THIS_MODULE, 677 .ids = xenfb_ids, 678 .probe = xenfb_probe, 679 .remove = xenfb_remove, 680 .resume = xenfb_resume, 681 .otherend_changed = xenfb_backend_changed, 682}; 683 684static int __init xenfb_init(void) 685{ 686 if (!xen_domain()) 687 return -ENODEV; 688 689 /* Nothing to do if running in dom0. */ 690 if (xen_initial_domain()) 691 return -ENODEV; 692 693 return xenbus_register_frontend(&xenfb_driver); 694} 695 696static void __exit xenfb_cleanup(void) 697{ 698 xenbus_unregister_driver(&xenfb_driver); 699} 700 701module_init(xenfb_init); 702module_exit(xenfb_cleanup); 703 704MODULE_DESCRIPTION("Xen virtual framebuffer device frontend"); 705MODULE_LICENSE("GPL"); 706MODULE_ALIAS("xen:vfb"); 707