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

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.29-rc2 703 lines 18 kB view raw
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#include <asm/xen/hypervisor.h> 29#include <xen/events.h> 30#include <xen/page.h> 31#include <xen/interface/io/fbif.h> 32#include <xen/interface/io/protocols.h> 33#include <xen/xenbus.h> 34 35struct xenfb_info { 36 unsigned char *fb; 37 struct fb_info *fb_info; 38 int x1, y1, x2, y2; /* dirty rectangle, 39 protected by dirty_lock */ 40 spinlock_t dirty_lock; 41 int nr_pages; 42 int irq; 43 struct xenfb_page *page; 44 unsigned long *mfns; 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; 50 51 struct xenbus_device *xbdev; 52}; 53 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)"); 61 62static void xenfb_make_preferred_console(void); 63static int xenfb_remove(struct xenbus_device *); 64static void xenfb_init_shared_page(struct xenfb_info *, struct fb_info *); 65static int xenfb_connect_backend(struct xenbus_device *, struct xenfb_info *); 66static void xenfb_disconnect_backend(struct xenfb_info *); 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 83static void xenfb_do_update(struct xenfb_info *info, 84 int x, int y, int w, int h) 85{ 86 union xenfb_out_event event; 87 88 memset(&event, 0, sizeof(event)); 89 event.type = XENFB_TYPE_UPDATE; 90 event.update.x = x; 91 event.update.y = y; 92 event.update.width = w; 93 event.update.height = h; 94 95 /* caller ensures !xenfb_queue_full() */ 96 xenfb_send_event(info, &event); 97} 98 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); 108} 109 110static int xenfb_queue_full(struct xenfb_info *info) 111{ 112 u32 cons, prod; 113 114 prod = info->page->out_prod; 115 cons = info->page->out_cons; 116 return prod - cons == XENFB_OUT_RING_LEN; 117} 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 133static void xenfb_refresh(struct xenfb_info *info, 134 int x1, int y1, int w, int h) 135{ 136 unsigned long flags; 137 int x2 = x1 + w - 1; 138 int y2 = y1 + h - 1; 139 140 xenfb_handle_resize_dpy(info); 141 142 if (!info->update_wanted) 143 return; 144 145 spin_lock_irqsave(&info->dirty_lock, flags); 146 147 /* Combine with dirty rectangle: */ 148 if (info->y1 < y1) 149 y1 = info->y1; 150 if (info->y2 > y2) 151 y2 = info->y2; 152 if (info->x1 < x1) 153 x1 = info->x1; 154 if (info->x2 > x2) 155 x2 = info->x2; 156 157 if (xenfb_queue_full(info)) { 158 /* Can't send right now, stash it in the dirty rectangle */ 159 info->x1 = x1; 160 info->x2 = x2; 161 info->y1 = y1; 162 info->y2 = y2; 163 spin_unlock_irqrestore(&info->dirty_lock, flags); 164 return; 165 } 166 167 /* Clear dirty rectangle: */ 168 info->x1 = info->y1 = INT_MAX; 169 info->x2 = info->y2 = 0; 170 171 spin_unlock_irqrestore(&info->dirty_lock, flags); 172 173 if (x1 <= x2 && y1 <= y2) 174 xenfb_do_update(info, x1, y1, x2 - x1 + 1, y2 - y1 + 1); 175} 176 177static void xenfb_deferred_io(struct fb_info *fb_info, 178 struct list_head *pagelist) 179{ 180 struct xenfb_info *info = fb_info->par; 181 struct page *page; 182 unsigned long beg, end; 183 int y1, y2, miny, maxy; 184 185 miny = INT_MAX; 186 maxy = 0; 187 list_for_each_entry(page, pagelist, lru) { 188 beg = page->index << PAGE_SHIFT; 189 end = beg + PAGE_SIZE - 1; 190 y1 = beg / fb_info->fix.line_length; 191 y2 = end / fb_info->fix.line_length; 192 if (y2 >= fb_info->var.yres) 193 y2 = fb_info->var.yres - 1; 194 if (miny > y1) 195 miny = y1; 196 if (maxy < y2) 197 maxy = y2; 198 } 199 xenfb_refresh(info, 0, miny, fb_info->var.xres, maxy - miny + 1); 200} 201 202static struct fb_deferred_io xenfb_defio = { 203 .delay = HZ / 20, 204 .deferred_io = xenfb_deferred_io, 205}; 206 207static int xenfb_setcolreg(unsigned regno, unsigned red, unsigned green, 208 unsigned blue, unsigned transp, 209 struct fb_info *info) 210{ 211 u32 v; 212 213 if (regno > info->cmap.len) 214 return 1; 215 216#define CNVT_TOHW(val, width) ((((val)<<(width))+0x7FFF-(val))>>16) 217 red = CNVT_TOHW(red, info->var.red.length); 218 green = CNVT_TOHW(green, info->var.green.length); 219 blue = CNVT_TOHW(blue, info->var.blue.length); 220 transp = CNVT_TOHW(transp, info->var.transp.length); 221#undef CNVT_TOHW 222 223 v = (red << info->var.red.offset) | 224 (green << info->var.green.offset) | 225 (blue << info->var.blue.offset); 226 227 switch (info->var.bits_per_pixel) { 228 case 16: 229 case 24: 230 case 32: 231 ((u32 *)info->pseudo_palette)[regno] = v; 232 break; 233 } 234 235 return 0; 236} 237 238static void xenfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect) 239{ 240 struct xenfb_info *info = p->par; 241 242 sys_fillrect(p, rect); 243 xenfb_refresh(info, rect->dx, rect->dy, rect->width, rect->height); 244} 245 246static void xenfb_imageblit(struct fb_info *p, const struct fb_image *image) 247{ 248 struct xenfb_info *info = p->par; 249 250 sys_imageblit(p, image); 251 xenfb_refresh(info, image->dx, image->dy, image->width, image->height); 252} 253 254static void xenfb_copyarea(struct fb_info *p, const struct fb_copyarea *area) 255{ 256 struct xenfb_info *info = p->par; 257 258 sys_copyarea(p, area); 259 xenfb_refresh(info, area->dx, area->dy, area->width, area->height); 260} 261 262static ssize_t xenfb_write(struct fb_info *p, const char __user *buf, 263 size_t count, loff_t *ppos) 264{ 265 struct xenfb_info *info = p->par; 266 ssize_t res; 267 268 res = fb_sys_write(p, buf, count, ppos); 269 xenfb_refresh(info, 0, 0, info->page->width, info->page->height); 270 return res; 271} 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 324static struct fb_ops xenfb_fb_ops = { 325 .owner = THIS_MODULE, 326 .fb_read = fb_sys_read, 327 .fb_write = xenfb_write, 328 .fb_setcolreg = xenfb_setcolreg, 329 .fb_fillrect = xenfb_fillrect, 330 .fb_copyarea = xenfb_copyarea, 331 .fb_imageblit = xenfb_imageblit, 332 .fb_check_var = xenfb_check_var, 333 .fb_set_par = xenfb_set_par, 334}; 335 336static irqreturn_t xenfb_event_handler(int rq, void *dev_id) 337{ 338 /* 339 * No in events recognized, simply ignore them all. 340 * If you need to recognize some, see xen-kbdfront's 341 * input_handler() for how to do that. 342 */ 343 struct xenfb_info *info = dev_id; 344 struct xenfb_page *page = info->page; 345 346 if (page->in_cons != page->in_prod) { 347 info->page->in_cons = info->page->in_prod; 348 notify_remote_via_irq(info->irq); 349 } 350 351 /* Flush dirty rectangle: */ 352 xenfb_refresh(info, INT_MAX, INT_MAX, -INT_MAX, -INT_MAX); 353 354 return IRQ_HANDLED; 355} 356 357static int __devinit xenfb_probe(struct xenbus_device *dev, 358 const struct xenbus_device_id *id) 359{ 360 struct xenfb_info *info; 361 struct fb_info *fb_info; 362 int fb_size; 363 int val; 364 int ret; 365 366 info = kzalloc(sizeof(*info), GFP_KERNEL); 367 if (info == NULL) { 368 xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure"); 369 return -ENOMEM; 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 387 dev->dev.driver_data = info; 388 info->xbdev = dev; 389 info->irq = -1; 390 info->x1 = info->y1 = INT_MAX; 391 spin_lock_init(&info->dirty_lock); 392 spin_lock_init(&info->resize_lock); 393 394 info->fb = vmalloc(fb_size); 395 if (info->fb == NULL) 396 goto error_nomem; 397 memset(info->fb, 0, fb_size); 398 399 info->nr_pages = (fb_size + PAGE_SIZE - 1) >> PAGE_SHIFT; 400 401 info->mfns = vmalloc(sizeof(unsigned long) * info->nr_pages); 402 if (!info->mfns) 403 goto error_nomem; 404 405 /* set up shared page */ 406 info->page = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO); 407 if (!info->page) 408 goto error_nomem; 409 410 /* abusing framebuffer_alloc() to allocate pseudo_palette */ 411 fb_info = framebuffer_alloc(sizeof(u32) * 256, NULL); 412 if (fb_info == NULL) 413 goto error_nomem; 414 415 /* complete the abuse: */ 416 fb_info->pseudo_palette = fb_info->par; 417 fb_info->par = info; 418 419 fb_info->screen_base = info->fb; 420 421 fb_info->fbops = &xenfb_fb_ops; 422 fb_info->var.xres_virtual = fb_info->var.xres = video[KPARAM_WIDTH]; 423 fb_info->var.yres_virtual = fb_info->var.yres = video[KPARAM_HEIGHT]; 424 fb_info->var.bits_per_pixel = XENFB_DEPTH; 425 426 fb_info->var.red = (struct fb_bitfield){16, 8, 0}; 427 fb_info->var.green = (struct fb_bitfield){8, 8, 0}; 428 fb_info->var.blue = (struct fb_bitfield){0, 8, 0}; 429 430 fb_info->var.activate = FB_ACTIVATE_NOW; 431 fb_info->var.height = -1; 432 fb_info->var.width = -1; 433 fb_info->var.vmode = FB_VMODE_NONINTERLACED; 434 435 fb_info->fix.visual = FB_VISUAL_TRUECOLOR; 436 fb_info->fix.line_length = fb_info->var.xres * XENFB_DEPTH / 8; 437 fb_info->fix.smem_start = 0; 438 fb_info->fix.smem_len = fb_size; 439 strcpy(fb_info->fix.id, "xen"); 440 fb_info->fix.type = FB_TYPE_PACKED_PIXELS; 441 fb_info->fix.accel = FB_ACCEL_NONE; 442 443 fb_info->flags = FBINFO_FLAG_DEFAULT; 444 445 ret = fb_alloc_cmap(&fb_info->cmap, 256, 0); 446 if (ret < 0) { 447 framebuffer_release(fb_info); 448 xenbus_dev_fatal(dev, ret, "fb_alloc_cmap"); 449 goto error; 450 } 451 452 fb_info->fbdefio = &xenfb_defio; 453 fb_deferred_io_init(fb_info); 454 455 xenfb_init_shared_page(info, fb_info); 456 457 ret = register_framebuffer(fb_info); 458 if (ret) { 459 fb_deferred_io_cleanup(fb_info); 460 fb_dealloc_cmap(&fb_info->cmap); 461 framebuffer_release(fb_info); 462 xenbus_dev_fatal(dev, ret, "register_framebuffer"); 463 goto error; 464 } 465 info->fb_info = fb_info; 466 467 ret = xenfb_connect_backend(dev, info); 468 if (ret < 0) 469 goto error; 470 471 xenfb_make_preferred_console(); 472 return 0; 473 474 error_nomem: 475 ret = -ENOMEM; 476 xenbus_dev_fatal(dev, ret, "allocating device memory"); 477 error: 478 xenfb_remove(dev); 479 return ret; 480} 481 482static __devinit void 483xenfb_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 504static int xenfb_resume(struct xenbus_device *dev) 505{ 506 struct xenfb_info *info = dev->dev.driver_data; 507 508 xenfb_disconnect_backend(info); 509 xenfb_init_shared_page(info, info->fb_info); 510 return xenfb_connect_backend(dev, info); 511} 512 513static int xenfb_remove(struct xenbus_device *dev) 514{ 515 struct xenfb_info *info = dev->dev.driver_data; 516 517 xenfb_disconnect_backend(info); 518 if (info->fb_info) { 519 fb_deferred_io_cleanup(info->fb_info); 520 unregister_framebuffer(info->fb_info); 521 fb_dealloc_cmap(&info->fb_info->cmap); 522 framebuffer_release(info->fb_info); 523 } 524 free_page((unsigned long)info->page); 525 vfree(info->mfns); 526 vfree(info->fb); 527 kfree(info); 528 529 return 0; 530} 531 532static unsigned long vmalloc_to_mfn(void *address) 533{ 534 return pfn_to_mfn(vmalloc_to_pfn(address)); 535} 536 537static void xenfb_init_shared_page(struct xenfb_info *info, 538 struct fb_info *fb_info) 539{ 540 int i; 541 int epd = PAGE_SIZE / sizeof(info->mfns[0]); 542 543 for (i = 0; i < info->nr_pages; i++) 544 info->mfns[i] = vmalloc_to_mfn(info->fb + i * PAGE_SIZE); 545 546 for (i = 0; i * epd < info->nr_pages; i++) 547 info->page->pd[i] = vmalloc_to_mfn(&info->mfns[i * epd]); 548 549 info->page->width = fb_info->var.xres; 550 info->page->height = fb_info->var.yres; 551 info->page->depth = fb_info->var.bits_per_pixel; 552 info->page->line_length = fb_info->fix.line_length; 553 info->page->mem_length = fb_info->fix.smem_len; 554 info->page->in_cons = info->page->in_prod = 0; 555 info->page->out_cons = info->page->out_prod = 0; 556} 557 558static int xenfb_connect_backend(struct xenbus_device *dev, 559 struct xenfb_info *info) 560{ 561 int ret, evtchn; 562 struct xenbus_transaction xbt; 563 564 ret = xenbus_alloc_evtchn(dev, &evtchn); 565 if (ret) 566 return ret; 567 ret = bind_evtchn_to_irqhandler(evtchn, xenfb_event_handler, 568 0, dev->devicetype, info); 569 if (ret < 0) { 570 xenbus_free_evtchn(dev, evtchn); 571 xenbus_dev_fatal(dev, ret, "bind_evtchn_to_irqhandler"); 572 return ret; 573 } 574 info->irq = ret; 575 576 again: 577 ret = xenbus_transaction_start(&xbt); 578 if (ret) { 579 xenbus_dev_fatal(dev, ret, "starting transaction"); 580 return ret; 581 } 582 ret = xenbus_printf(xbt, dev->nodename, "page-ref", "%lu", 583 virt_to_mfn(info->page)); 584 if (ret) 585 goto error_xenbus; 586 ret = xenbus_printf(xbt, dev->nodename, "event-channel", "%u", 587 evtchn); 588 if (ret) 589 goto error_xenbus; 590 ret = xenbus_printf(xbt, dev->nodename, "protocol", "%s", 591 XEN_IO_PROTO_ABI_NATIVE); 592 if (ret) 593 goto error_xenbus; 594 ret = xenbus_printf(xbt, dev->nodename, "feature-update", "1"); 595 if (ret) 596 goto error_xenbus; 597 ret = xenbus_transaction_end(xbt, 0); 598 if (ret) { 599 if (ret == -EAGAIN) 600 goto again; 601 xenbus_dev_fatal(dev, ret, "completing transaction"); 602 return ret; 603 } 604 605 xenbus_switch_state(dev, XenbusStateInitialised); 606 return 0; 607 608 error_xenbus: 609 xenbus_transaction_end(xbt, 1); 610 xenbus_dev_fatal(dev, ret, "writing xenstore"); 611 return ret; 612} 613 614static void xenfb_disconnect_backend(struct xenfb_info *info) 615{ 616 if (info->irq >= 0) 617 unbind_from_irqhandler(info->irq, info); 618 info->irq = -1; 619} 620 621static void xenfb_backend_changed(struct xenbus_device *dev, 622 enum xenbus_state backend_state) 623{ 624 struct xenfb_info *info = dev->dev.driver_data; 625 int val; 626 627 switch (backend_state) { 628 case XenbusStateInitialising: 629 case XenbusStateInitialised: 630 case XenbusStateUnknown: 631 case XenbusStateClosed: 632 break; 633 634 case XenbusStateInitWait: 635InitWait: 636 xenbus_switch_state(dev, XenbusStateConnected); 637 break; 638 639 case XenbusStateConnected: 640 /* 641 * Work around xenbus race condition: If backend goes 642 * through InitWait to Connected fast enough, we can 643 * get Connected twice here. 644 */ 645 if (dev->state != XenbusStateConnected) 646 goto InitWait; /* no InitWait seen yet, fudge it */ 647 648 if (xenbus_scanf(XBT_NIL, info->xbdev->otherend, 649 "request-update", "%d", &val) < 0) 650 val = 0; 651 if (val) 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; 658 break; 659 660 case XenbusStateClosing: 661 xenbus_frontend_closed(dev); 662 break; 663 } 664} 665 666static struct xenbus_device_id xenfb_ids[] = { 667 { "vfb" }, 668 { "" } 669}; 670 671static struct xenbus_driver xenfb_driver = { 672 .name = "vfb", 673 .owner = THIS_MODULE, 674 .ids = xenfb_ids, 675 .probe = xenfb_probe, 676 .remove = xenfb_remove, 677 .resume = xenfb_resume, 678 .otherend_changed = xenfb_backend_changed, 679}; 680 681static int __init xenfb_init(void) 682{ 683 if (!xen_domain()) 684 return -ENODEV; 685 686 /* Nothing to do if running in dom0. */ 687 if (xen_initial_domain()) 688 return -ENODEV; 689 690 return xenbus_register_frontend(&xenfb_driver); 691} 692 693static void __exit xenfb_cleanup(void) 694{ 695 xenbus_unregister_driver(&xenfb_driver); 696} 697 698module_init(xenfb_init); 699module_exit(xenfb_cleanup); 700 701MODULE_DESCRIPTION("Xen virtual framebuffer device frontend"); 702MODULE_LICENSE("GPL"); 703MODULE_ALIAS("xen:vfb");