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