aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/xen-fbfront.c
diff options
context:
space:
mode:
authorMarkus Armbruster <armbru@redhat.com>2008-04-02 13:54:07 -0400
committerIngo Molnar <mingo@elte.hu>2008-04-24 17:57:33 -0400
commit4ee36dc08e5c4d16d078f59acd6d9d536f9718dd (patch)
tree284527b0418065b0092d7cc9ea3ee6d899a5a53e /drivers/video/xen-fbfront.c
parent4f93f09b72d6ff47b2399b79ed6d1cbc7dbf991b (diff)
xen pvfb: Para-virtual framebuffer, keyboard and pointer driver
This is a pair of Xen para-virtual frontend device drivers: drivers/video/xen-fbfront.c provides a framebuffer, and drivers/input/xen-kbdfront provides keyboard and mouse. The backends run in dom0 user space. The two drivers are not in two separate patches, because the intermediate step (one driver, not the other) is somewhat problematic: the backend in dom0 needs both drivers, and will refuse to complete device initialization unless they're both present. Signed-off-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Ingo Molnar <mingo@elte.hu> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'drivers/video/xen-fbfront.c')
-rw-r--r--drivers/video/xen-fbfront.c550
1 files changed, 550 insertions, 0 deletions
diff --git a/drivers/video/xen-fbfront.c b/drivers/video/xen-fbfront.c
new file mode 100644
index 000000000000..619a6f8d65a2
--- /dev/null
+++ b/drivers/video/xen-fbfront.c
@@ -0,0 +1,550 @@
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/kernel.h>
22#include <linux/errno.h>
23#include <linux/fb.h>
24#include <linux/module.h>
25#include <linux/vmalloc.h>
26#include <linux/mm.h>
27#include <asm/xen/hypervisor.h>
28#include <xen/events.h>
29#include <xen/page.h>
30#include <xen/interface/io/fbif.h>
31#include <xen/interface/io/protocols.h>
32#include <xen/xenbus.h>
33
34struct xenfb_info {
35 unsigned char *fb;
36 struct fb_info *fb_info;
37 int x1, y1, x2, y2; /* dirty rectangle,
38 protected by dirty_lock */
39 spinlock_t dirty_lock;
40 int nr_pages;
41 int irq;
42 struct xenfb_page *page;
43 unsigned long *mfns;
44 int update_wanted; /* XENFB_TYPE_UPDATE wanted */
45
46 struct xenbus_device *xbdev;
47};
48
49static u32 xenfb_mem_len = XENFB_WIDTH * XENFB_HEIGHT * XENFB_DEPTH / 8;
50
51static int xenfb_remove(struct xenbus_device *);
52static void xenfb_init_shared_page(struct xenfb_info *);
53static int xenfb_connect_backend(struct xenbus_device *, struct xenfb_info *);
54static void xenfb_disconnect_backend(struct xenfb_info *);
55
56static void xenfb_do_update(struct xenfb_info *info,
57 int x, int y, int w, int h)
58{
59 union xenfb_out_event event;
60 u32 prod;
61
62 event.type = XENFB_TYPE_UPDATE;
63 event.update.x = x;
64 event.update.y = y;
65 event.update.width = w;
66 event.update.height = h;
67
68 prod = info->page->out_prod;
69 /* caller ensures !xenfb_queue_full() */
70 mb(); /* ensure ring space available */
71 XENFB_OUT_RING_REF(info->page, prod) = event;
72 wmb(); /* ensure ring contents visible */
73 info->page->out_prod = prod + 1;
74
75 notify_remote_via_irq(info->irq);
76}
77
78static int xenfb_queue_full(struct xenfb_info *info)
79{
80 u32 cons, prod;
81
82 prod = info->page->out_prod;
83 cons = info->page->out_cons;
84 return prod - cons == XENFB_OUT_RING_LEN;
85}
86
87static void xenfb_refresh(struct xenfb_info *info,
88 int x1, int y1, int w, int h)
89{
90 unsigned long flags;
91 int y2 = y1 + h - 1;
92 int x2 = x1 + w - 1;
93
94 if (!info->update_wanted)
95 return;
96
97 spin_lock_irqsave(&info->dirty_lock, flags);
98
99 /* Combine with dirty rectangle: */
100 if (info->y1 < y1)
101 y1 = info->y1;
102 if (info->y2 > y2)
103 y2 = info->y2;
104 if (info->x1 < x1)
105 x1 = info->x1;
106 if (info->x2 > x2)
107 x2 = info->x2;
108
109 if (xenfb_queue_full(info)) {
110 /* Can't send right now, stash it in the dirty rectangle */
111 info->x1 = x1;
112 info->x2 = x2;
113 info->y1 = y1;
114 info->y2 = y2;
115 spin_unlock_irqrestore(&info->dirty_lock, flags);
116 return;
117 }
118
119 /* Clear dirty rectangle: */
120 info->x1 = info->y1 = INT_MAX;
121 info->x2 = info->y2 = 0;
122
123 spin_unlock_irqrestore(&info->dirty_lock, flags);
124
125 if (x1 <= x2 && y1 <= y2)
126 xenfb_do_update(info, x1, y1, x2 - x1 + 1, y2 - y1 + 1);
127}
128
129static void xenfb_deferred_io(struct fb_info *fb_info,
130 struct list_head *pagelist)
131{
132 struct xenfb_info *info = fb_info->par;
133 struct page *page;
134 unsigned long beg, end;
135 int y1, y2, miny, maxy;
136
137 miny = INT_MAX;
138 maxy = 0;
139 list_for_each_entry(page, pagelist, lru) {
140 beg = page->index << PAGE_SHIFT;
141 end = beg + PAGE_SIZE - 1;
142 y1 = beg / fb_info->fix.line_length;
143 y2 = end / fb_info->fix.line_length;
144 if (y2 >= fb_info->var.yres)
145 y2 = fb_info->var.yres - 1;
146 if (miny > y1)
147 miny = y1;
148 if (maxy < y2)
149 maxy = y2;
150 }
151 xenfb_refresh(info, 0, miny, fb_info->var.xres, maxy - miny + 1);
152}
153
154static struct fb_deferred_io xenfb_defio = {
155 .delay = HZ / 20,
156 .deferred_io = xenfb_deferred_io,
157};
158
159static int xenfb_setcolreg(unsigned regno, unsigned red, unsigned green,
160 unsigned blue, unsigned transp,
161 struct fb_info *info)
162{
163 u32 v;
164
165 if (regno > info->cmap.len)
166 return 1;
167
168#define CNVT_TOHW(val, width) ((((val)<<(width))+0x7FFF-(val))>>16)
169 red = CNVT_TOHW(red, info->var.red.length);
170 green = CNVT_TOHW(green, info->var.green.length);
171 blue = CNVT_TOHW(blue, info->var.blue.length);
172 transp = CNVT_TOHW(transp, info->var.transp.length);
173#undef CNVT_TOHW
174
175 v = (red << info->var.red.offset) |
176 (green << info->var.green.offset) |
177 (blue << info->var.blue.offset);
178
179 switch (info->var.bits_per_pixel) {
180 case 16:
181 case 24:
182 case 32:
183 ((u32 *)info->pseudo_palette)[regno] = v;
184 break;
185 }
186
187 return 0;
188}
189
190static void xenfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
191{
192 struct xenfb_info *info = p->par;
193
194 sys_fillrect(p, rect);
195 xenfb_refresh(info, rect->dx, rect->dy, rect->width, rect->height);
196}
197
198static void xenfb_imageblit(struct fb_info *p, const struct fb_image *image)
199{
200 struct xenfb_info *info = p->par;
201
202 sys_imageblit(p, image);
203 xenfb_refresh(info, image->dx, image->dy, image->width, image->height);
204}
205
206static void xenfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
207{
208 struct xenfb_info *info = p->par;
209
210 sys_copyarea(p, area);
211 xenfb_refresh(info, area->dx, area->dy, area->width, area->height);
212}
213
214static ssize_t xenfb_write(struct fb_info *p, const char __user *buf,
215 size_t count, loff_t *ppos)
216{
217 struct xenfb_info *info = p->par;
218 ssize_t res;
219
220 res = fb_sys_write(p, buf, count, ppos);
221 xenfb_refresh(info, 0, 0, info->page->width, info->page->height);
222 return res;
223}
224
225static struct fb_ops xenfb_fb_ops = {
226 .owner = THIS_MODULE,
227 .fb_read = fb_sys_read,
228 .fb_write = xenfb_write,
229 .fb_setcolreg = xenfb_setcolreg,
230 .fb_fillrect = xenfb_fillrect,
231 .fb_copyarea = xenfb_copyarea,
232 .fb_imageblit = xenfb_imageblit,
233};
234
235static irqreturn_t xenfb_event_handler(int rq, void *dev_id)
236{
237 /*
238 * No in events recognized, simply ignore them all.
239 * If you need to recognize some, see xen-kbdfront's
240 * input_handler() for how to do that.
241 */
242 struct xenfb_info *info = dev_id;
243 struct xenfb_page *page = info->page;
244
245 if (page->in_cons != page->in_prod) {
246 info->page->in_cons = info->page->in_prod;
247 notify_remote_via_irq(info->irq);
248 }
249
250 /* Flush dirty rectangle: */
251 xenfb_refresh(info, INT_MAX, INT_MAX, -INT_MAX, -INT_MAX);
252
253 return IRQ_HANDLED;
254}
255
256static int __devinit xenfb_probe(struct xenbus_device *dev,
257 const struct xenbus_device_id *id)
258{
259 struct xenfb_info *info;
260 struct fb_info *fb_info;
261 int ret;
262
263 info = kzalloc(sizeof(*info), GFP_KERNEL);
264 if (info == NULL) {
265 xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure");
266 return -ENOMEM;
267 }
268 dev->dev.driver_data = info;
269 info->xbdev = dev;
270 info->irq = -1;
271 info->x1 = info->y1 = INT_MAX;
272 spin_lock_init(&info->dirty_lock);
273
274 info->fb = vmalloc(xenfb_mem_len);
275 if (info->fb == NULL)
276 goto error_nomem;
277 memset(info->fb, 0, xenfb_mem_len);
278
279 info->nr_pages = (xenfb_mem_len + PAGE_SIZE - 1) >> PAGE_SHIFT;
280
281 info->mfns = vmalloc(sizeof(unsigned long) * info->nr_pages);
282 if (!info->mfns)
283 goto error_nomem;
284
285 /* set up shared page */
286 info->page = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO);
287 if (!info->page)
288 goto error_nomem;
289
290 xenfb_init_shared_page(info);
291
292 /* abusing framebuffer_alloc() to allocate pseudo_palette */
293 fb_info = framebuffer_alloc(sizeof(u32) * 256, NULL);
294 if (fb_info == NULL)
295 goto error_nomem;
296
297 /* complete the abuse: */
298 fb_info->pseudo_palette = fb_info->par;
299 fb_info->par = info;
300
301 fb_info->screen_base = info->fb;
302
303 fb_info->fbops = &xenfb_fb_ops;
304 fb_info->var.xres_virtual = fb_info->var.xres = info->page->width;
305 fb_info->var.yres_virtual = fb_info->var.yres = info->page->height;
306 fb_info->var.bits_per_pixel = info->page->depth;
307
308 fb_info->var.red = (struct fb_bitfield){16, 8, 0};
309 fb_info->var.green = (struct fb_bitfield){8, 8, 0};
310 fb_info->var.blue = (struct fb_bitfield){0, 8, 0};
311
312 fb_info->var.activate = FB_ACTIVATE_NOW;
313 fb_info->var.height = -1;
314 fb_info->var.width = -1;
315 fb_info->var.vmode = FB_VMODE_NONINTERLACED;
316
317 fb_info->fix.visual = FB_VISUAL_TRUECOLOR;
318 fb_info->fix.line_length = info->page->line_length;
319 fb_info->fix.smem_start = 0;
320 fb_info->fix.smem_len = xenfb_mem_len;
321 strcpy(fb_info->fix.id, "xen");
322 fb_info->fix.type = FB_TYPE_PACKED_PIXELS;
323 fb_info->fix.accel = FB_ACCEL_NONE;
324
325 fb_info->flags = FBINFO_FLAG_DEFAULT;
326
327 ret = fb_alloc_cmap(&fb_info->cmap, 256, 0);
328 if (ret < 0) {
329 framebuffer_release(fb_info);
330 xenbus_dev_fatal(dev, ret, "fb_alloc_cmap");
331 goto error;
332 }
333
334 fb_info->fbdefio = &xenfb_defio;
335 fb_deferred_io_init(fb_info);
336
337 ret = register_framebuffer(fb_info);
338 if (ret) {
339 fb_deferred_io_cleanup(fb_info);
340 fb_dealloc_cmap(&fb_info->cmap);
341 framebuffer_release(fb_info);
342 xenbus_dev_fatal(dev, ret, "register_framebuffer");
343 goto error;
344 }
345 info->fb_info = fb_info;
346
347 ret = xenfb_connect_backend(dev, info);
348 if (ret < 0)
349 goto error;
350
351 return 0;
352
353 error_nomem:
354 ret = -ENOMEM;
355 xenbus_dev_fatal(dev, ret, "allocating device memory");
356 error:
357 xenfb_remove(dev);
358 return ret;
359}
360
361static int xenfb_resume(struct xenbus_device *dev)
362{
363 struct xenfb_info *info = dev->dev.driver_data;
364
365 xenfb_disconnect_backend(info);
366 xenfb_init_shared_page(info);
367 return xenfb_connect_backend(dev, info);
368}
369
370static int xenfb_remove(struct xenbus_device *dev)
371{
372 struct xenfb_info *info = dev->dev.driver_data;
373
374 xenfb_disconnect_backend(info);
375 if (info->fb_info) {
376 fb_deferred_io_cleanup(info->fb_info);
377 unregister_framebuffer(info->fb_info);
378 fb_dealloc_cmap(&info->fb_info->cmap);
379 framebuffer_release(info->fb_info);
380 }
381 free_page((unsigned long)info->page);
382 vfree(info->mfns);
383 vfree(info->fb);
384 kfree(info);
385
386 return 0;
387}
388
389static unsigned long vmalloc_to_mfn(void *address)
390{
391 return pfn_to_mfn(vmalloc_to_pfn(address));
392}
393
394static void xenfb_init_shared_page(struct xenfb_info *info)
395{
396 int i;
397
398 for (i = 0; i < info->nr_pages; i++)
399 info->mfns[i] = vmalloc_to_mfn(info->fb + i * PAGE_SIZE);
400
401 info->page->pd[0] = vmalloc_to_mfn(info->mfns);
402 info->page->pd[1] = 0;
403 info->page->width = XENFB_WIDTH;
404 info->page->height = XENFB_HEIGHT;
405 info->page->depth = XENFB_DEPTH;
406 info->page->line_length = (info->page->depth / 8) * info->page->width;
407 info->page->mem_length = xenfb_mem_len;
408 info->page->in_cons = info->page->in_prod = 0;
409 info->page->out_cons = info->page->out_prod = 0;
410}
411
412static int xenfb_connect_backend(struct xenbus_device *dev,
413 struct xenfb_info *info)
414{
415 int ret, evtchn;
416 struct xenbus_transaction xbt;
417
418 ret = xenbus_alloc_evtchn(dev, &evtchn);
419 if (ret)
420 return ret;
421 ret = bind_evtchn_to_irqhandler(evtchn, xenfb_event_handler,
422 0, dev->devicetype, info);
423 if (ret < 0) {
424 xenbus_free_evtchn(dev, evtchn);
425 xenbus_dev_fatal(dev, ret, "bind_evtchn_to_irqhandler");
426 return ret;
427 }
428 info->irq = ret;
429
430 again:
431 ret = xenbus_transaction_start(&xbt);
432 if (ret) {
433 xenbus_dev_fatal(dev, ret, "starting transaction");
434 return ret;
435 }
436 ret = xenbus_printf(xbt, dev->nodename, "page-ref", "%lu",
437 virt_to_mfn(info->page));
438 if (ret)
439 goto error_xenbus;
440 ret = xenbus_printf(xbt, dev->nodename, "event-channel", "%u",
441 evtchn);
442 if (ret)
443 goto error_xenbus;
444 ret = xenbus_printf(xbt, dev->nodename, "protocol", "%s",
445 XEN_IO_PROTO_ABI_NATIVE);
446 if (ret)
447 goto error_xenbus;
448 ret = xenbus_printf(xbt, dev->nodename, "feature-update", "1");
449 if (ret)
450 goto error_xenbus;
451 ret = xenbus_transaction_end(xbt, 0);
452 if (ret) {
453 if (ret == -EAGAIN)
454 goto again;
455 xenbus_dev_fatal(dev, ret, "completing transaction");
456 return ret;
457 }
458
459 xenbus_switch_state(dev, XenbusStateInitialised);
460 return 0;
461
462 error_xenbus:
463 xenbus_transaction_end(xbt, 1);
464 xenbus_dev_fatal(dev, ret, "writing xenstore");
465 return ret;
466}
467
468static void xenfb_disconnect_backend(struct xenfb_info *info)
469{
470 if (info->irq >= 0)
471 unbind_from_irqhandler(info->irq, info);
472 info->irq = -1;
473}
474
475static void xenfb_backend_changed(struct xenbus_device *dev,
476 enum xenbus_state backend_state)
477{
478 struct xenfb_info *info = dev->dev.driver_data;
479 int val;
480
481 switch (backend_state) {
482 case XenbusStateInitialising:
483 case XenbusStateInitialised:
484 case XenbusStateUnknown:
485 case XenbusStateClosed:
486 break;
487
488 case XenbusStateInitWait:
489InitWait:
490 xenbus_switch_state(dev, XenbusStateConnected);
491 break;
492
493 case XenbusStateConnected:
494 /*
495 * Work around xenbus race condition: If backend goes
496 * through InitWait to Connected fast enough, we can
497 * get Connected twice here.
498 */
499 if (dev->state != XenbusStateConnected)
500 goto InitWait; /* no InitWait seen yet, fudge it */
501
502 if (xenbus_scanf(XBT_NIL, info->xbdev->otherend,
503 "request-update", "%d", &val) < 0)
504 val = 0;
505 if (val)
506 info->update_wanted = 1;
507 break;
508
509 case XenbusStateClosing:
510 xenbus_frontend_closed(dev);
511 break;
512 }
513}
514
515static struct xenbus_device_id xenfb_ids[] = {
516 { "vfb" },
517 { "" }
518};
519
520static struct xenbus_driver xenfb = {
521 .name = "vfb",
522 .owner = THIS_MODULE,
523 .ids = xenfb_ids,
524 .probe = xenfb_probe,
525 .remove = xenfb_remove,
526 .resume = xenfb_resume,
527 .otherend_changed = xenfb_backend_changed,
528};
529
530static int __init xenfb_init(void)
531{
532 if (!is_running_on_xen())
533 return -ENODEV;
534
535 /* Nothing to do if running in dom0. */
536 if (is_initial_xendomain())
537 return -ENODEV;
538
539 return xenbus_register_frontend(&xenfb);
540}
541
542static void __exit xenfb_cleanup(void)
543{
544 xenbus_unregister_driver(&xenfb);
545}
546
547module_init(xenfb_init);
548module_exit(xenfb_cleanup);
549
550MODULE_LICENSE("GPL");