VirtualBox

source: vbox/trunk/src/VBox/Additions/linux/drm/vbox_fb.c@ 66891

Last change on this file since 66891 was 66544, checked in by vboxsync, 8 years ago

bugref:8524: Additions/linux: play nicely with distribution-installed Additions
Change header of files which are expected to end up in the Linux kernel to the MIT licence to simplify life for people wanting to port vboxvideo to other kernels and to simplify synchronising changes back to VirtualBox. Update author information in files which have it, but do not add it to files which do not.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.9 KB
Line 
1/* $Id: vbox_fb.c 66544 2017-04-12 17:02:30Z vboxsync $ */
2/** @file
3 * VirtualBox Additions Linux kernel video driver
4 */
5
6/*
7 * Copyright (C) 2013-2017 Oracle Corporation
8 * This file is based on ast_fb.c
9 * Copyright 2012 Red Hat Inc.
10 *
11 * Permission is hereby granted, free of charge, to any person obtaining a
12 * copy of this software and associated documentation files (the
13 * "Software"), to deal in the Software without restriction, including
14 * without limitation the rights to use, copy, modify, merge, publish,
15 * distribute, sub license, and/or sell copies of the Software, and to
16 * permit persons to whom the Software is furnished to do so, subject to
17 * the following conditions:
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
22 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
23 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
24 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
25 * USE OR OTHER DEALINGS IN THE SOFTWARE.
26 *
27 * The above copyright notice and this permission notice (including the
28 * next paragraph) shall be included in all copies or substantial portions
29 * of the Software.
30 *
31 * Authors: Dave Airlie <[email protected]>
32 * Michael Thayer <[email protected],
33 */
34/* Include from most specific to most general to be able to override things. */
35#include "vbox_drv.h"
36#include <VBoxVideo.h>
37
38#include <linux/module.h>
39#include <linux/kernel.h>
40#include <linux/errno.h>
41#include <linux/string.h>
42#include <linux/mm.h>
43#include <linux/tty.h>
44#include <linux/sysrq.h>
45#include <linux/delay.h>
46#include <linux/fb.h>
47#include <linux/init.h>
48
49
50#include <drm/drmP.h>
51#include <drm/drm_crtc.h>
52#include <drm/drm_fb_helper.h>
53#include <drm/drm_crtc_helper.h>
54#include "vbox_drv.h"
55
56#define VBOX_DIRTY_DELAY (HZ / 30)
57/**
58 * Tell the host about dirty rectangles to update.
59 */
60static void vbox_dirty_update(struct vbox_fbdev *fbdev,
61 int x, int y, int width, int height)
62{
63 struct drm_gem_object *obj;
64 struct vbox_bo *bo;
65 int ret = -EBUSY;
66 bool store_for_later = false;
67 int x2, y2;
68 unsigned long flags;
69 struct drm_clip_rect rect;
70
71 obj = fbdev->afb.obj;
72 bo = gem_to_vbox_bo(obj);
73
74 /*
75 * try and reserve the BO, if we fail with busy
76 * then the BO is being moved and we should
77 * store up the damage until later.
78 */
79 if (drm_can_sleep())
80 ret = vbox_bo_reserve(bo, true);
81 if (ret) {
82 if (ret != -EBUSY)
83 return;
84
85 store_for_later = true;
86 }
87
88 x2 = x + width - 1;
89 y2 = y + height - 1;
90 spin_lock_irqsave(&fbdev->dirty_lock, flags);
91
92 if (fbdev->y1 < y)
93 y = fbdev->y1;
94 if (fbdev->y2 > y2)
95 y2 = fbdev->y2;
96 if (fbdev->x1 < x)
97 x = fbdev->x1;
98 if (fbdev->x2 > x2)
99 x2 = fbdev->x2;
100
101 if (store_for_later) {
102 fbdev->x1 = x;
103 fbdev->x2 = x2;
104 fbdev->y1 = y;
105 fbdev->y2 = y2;
106 spin_unlock_irqrestore(&fbdev->dirty_lock, flags);
107 return;
108 }
109
110 fbdev->x1 = fbdev->y1 = INT_MAX;
111 fbdev->x2 = fbdev->y2 = 0;
112 spin_unlock_irqrestore(&fbdev->dirty_lock, flags);
113
114 /* Not sure why the original code subtracted 1 here, but I will keep it that
115 * way to avoid unnecessary differences. */
116 rect.x1 = x;
117 rect.x2 = x2 + 1;
118 rect.y1 = y;
119 rect.y2 = y2 + 1;
120 vbox_framebuffer_dirty_rectangles(&fbdev->afb.base, &rect, 1);
121
122 vbox_bo_unreserve(bo);
123}
124
125#ifdef CONFIG_FB_DEFERRED_IO
126static void vbox_deferred_io(struct fb_info *info,
127 struct list_head *pagelist)
128{
129 struct vbox_fbdev *fbdev = info->par;
130 unsigned long start, end, min, max;
131 struct page *page;
132 int y1, y2;
133
134 min = ULONG_MAX;
135 max = 0;
136 list_for_each_entry(page, pagelist, lru) {
137 start = page->index << PAGE_SHIFT;
138 end = start + PAGE_SIZE - 1;
139 min = min(min, start);
140 max = max(max, end);
141 }
142
143 if (min < max) {
144 y1 = min / info->fix.line_length;
145 y2 = (max / info->fix.line_length) + 1;
146 printk(KERN_INFO "%s: Calling dirty update: 0, %d, %d, %d\n",
147 __func__, y1, info->var.xres, y2 - y1 - 1);
148 vbox_dirty_update(fbdev, 0, y1, info->var.xres, y2 - y1 - 1);
149 }
150}
151
152static struct fb_deferred_io vbox_defio =
153{
154 .delay = VBOX_DIRTY_DELAY,
155 .deferred_io = vbox_deferred_io,
156};
157#endif
158
159static void vbox_fillrect(struct fb_info *info,
160 const struct fb_fillrect *rect)
161{
162 struct vbox_fbdev *fbdev = info->par;
163 sys_fillrect(info, rect);
164 vbox_dirty_update(fbdev, rect->dx, rect->dy, rect->width,
165 rect->height);
166}
167
168static void vbox_copyarea(struct fb_info *info,
169 const struct fb_copyarea *area)
170{
171 struct vbox_fbdev *fbdev = info->par;
172 sys_copyarea(info, area);
173 vbox_dirty_update(fbdev, area->dx, area->dy, area->width,
174 area->height);
175}
176
177static void vbox_imageblit(struct fb_info *info,
178 const struct fb_image *image)
179{
180 struct vbox_fbdev *fbdev = info->par;
181 sys_imageblit(info, image);
182 vbox_dirty_update(fbdev, image->dx, image->dy, image->width,
183 image->height);
184}
185
186static struct fb_ops vboxfb_ops = {
187 .owner = THIS_MODULE,
188 .fb_check_var = drm_fb_helper_check_var,
189 .fb_set_par = drm_fb_helper_set_par,
190 .fb_fillrect = vbox_fillrect,
191 .fb_copyarea = vbox_copyarea,
192 .fb_imageblit = vbox_imageblit,
193 .fb_pan_display = drm_fb_helper_pan_display,
194 .fb_blank = drm_fb_helper_blank,
195 .fb_setcmap = drm_fb_helper_setcmap,
196 .fb_debug_enter = drm_fb_helper_debug_enter,
197 .fb_debug_leave = drm_fb_helper_debug_leave,
198};
199
200static int vboxfb_create_object(struct vbox_fbdev *fbdev,
201 struct DRM_MODE_FB_CMD *mode_cmd,
202 struct drm_gem_object **gobj_p)
203{
204 struct drm_device *dev = fbdev->helper.dev;
205 u32 size;
206 struct drm_gem_object *gobj;
207#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
208 __u32 pitch = mode_cmd->pitch;
209#else
210 __u32 pitch = mode_cmd->pitches[0];
211#endif
212
213 int ret = 0;
214
215 size = pitch * mode_cmd->height;
216 ret = vbox_gem_create(dev, size, true, &gobj);
217 if (ret)
218 return ret;
219
220 *gobj_p = gobj;
221 return ret;
222}
223
224static int vboxfb_create(struct drm_fb_helper *helper,
225 struct drm_fb_helper_surface_size *sizes)
226{
227 struct vbox_fbdev *fbdev =
228 container_of(helper, struct vbox_fbdev, helper);
229 struct drm_device *dev = fbdev->helper.dev;
230 struct DRM_MODE_FB_CMD mode_cmd;
231 struct drm_framebuffer *fb;
232 struct fb_info *info;
233 __u32 pitch;
234 int size, ret;
235 struct device *device = &dev->pdev->dev;
236 struct drm_gem_object *gobj = NULL;
237 struct vbox_bo *bo = NULL;
238 mode_cmd.width = sizes->surface_width;
239 mode_cmd.height = sizes->surface_height;
240 pitch = mode_cmd.width * ((sizes->surface_bpp + 7) / 8);
241#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
242 mode_cmd.bpp = sizes->surface_bpp;
243 mode_cmd.depth = sizes->surface_depth;
244 mode_cmd.pitch = pitch;
245#else
246 mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
247 sizes->surface_depth);
248 mode_cmd.pitches[0] = pitch;
249#endif
250
251 size = pitch * mode_cmd.height;
252
253 ret = vboxfb_create_object(fbdev, &mode_cmd, &gobj);
254 if (ret) {
255 DRM_ERROR("failed to create fbcon backing object %d\n", ret);
256 return ret;
257 }
258
259 ret = vbox_framebuffer_init(dev, &fbdev->afb, &mode_cmd, gobj);
260 if (ret)
261 return ret;
262
263 bo = gem_to_vbox_bo(gobj);
264
265 ret = vbox_bo_reserve(bo, false);
266 if (ret)
267 return ret;
268
269 ret = vbox_bo_pin(bo, TTM_PL_FLAG_VRAM, NULL);
270 if (ret) {
271 vbox_bo_unreserve(bo);
272 return ret;
273 }
274
275 ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap);
276 vbox_bo_unreserve(bo);
277 if (ret) {
278 DRM_ERROR("failed to kmap fbcon\n");
279 return ret;
280 }
281
282 info = framebuffer_alloc(0, device);
283 if (!info)
284 return -ENOMEM;
285 info->par = fbdev;
286
287 fbdev->size = size;
288
289 fb = &fbdev->afb.base;
290 fbdev->helper.fb = fb;
291 fbdev->helper.fbdev = info;
292
293 strcpy(info->fix.id, "vboxdrmfb");
294
295 /* The last flag forces a mode set on VT switches even if the kernel does
296 * not think it is needed. */
297 info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT
298 | FBINFO_MISC_ALWAYS_SETPAR;
299 info->fbops = &vboxfb_ops;
300
301 ret = fb_alloc_cmap(&info->cmap, 256, 0);
302 if (ret)
303 return -ENOMEM;
304
305 /* This seems to be done for safety checking that the framebuffer is not
306 * registered twice by different drivers. */
307 info->apertures = alloc_apertures(1);
308 if (!info->apertures)
309 return -ENOMEM;
310 info->apertures->ranges[0].base = pci_resource_start(dev->pdev, 0);
311 info->apertures->ranges[0].size = pci_resource_len(dev->pdev, 0);
312
313#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
314 drm_fb_helper_fill_fix(info, fb->pitches[0], fb->format->depth);
315#else
316 drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
317#endif
318 drm_fb_helper_fill_var(info, &fbdev->helper, sizes->fb_width, sizes->fb_height);
319
320 info->screen_base = bo->kmap.virtual;
321 info->screen_size = size;
322
323#ifdef CONFIG_FB_DEFERRED_IO
324 info->fbdefio = &vbox_defio;
325 fb_deferred_io_init(info);
326#endif
327
328 info->pixmap.flags = FB_PIXMAP_SYSTEM;
329
330 DRM_DEBUG_KMS("allocated %dx%d\n",
331 fb->width, fb->height);
332
333 return 0;
334}
335
336static void vbox_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
337 u16 blue, int regno)
338{
339
340}
341
342static void vbox_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
343 u16 *blue, int regno)
344{
345 *red = regno;
346 *green = regno;
347 *blue = regno;
348}
349
350static struct drm_fb_helper_funcs vbox_fb_helper_funcs = {
351 .gamma_set = vbox_fb_gamma_set,
352 .gamma_get = vbox_fb_gamma_get,
353 .fb_probe = vboxfb_create,
354};
355
356static void vbox_fbdev_destroy(struct drm_device *dev,
357 struct vbox_fbdev *fbdev)
358{
359 struct fb_info *info;
360 struct vbox_framebuffer *afb = &fbdev->afb;
361 if (fbdev->helper.fbdev) {
362 info = fbdev->helper.fbdev;
363 unregister_framebuffer(info);
364 if (info->cmap.len)
365 fb_dealloc_cmap(&info->cmap);
366 framebuffer_release(info);
367 }
368
369 if (afb->obj) {
370 struct vbox_bo *bo = gem_to_vbox_bo(afb->obj);
371 if (!vbox_bo_reserve(bo, false)) {
372 if (bo->kmap.virtual)
373 ttm_bo_kunmap(&bo->kmap);
374 /* QXL does this, but is it really needed before freeing? */
375 if (bo->pin_count)
376 vbox_bo_unpin(bo);
377 vbox_bo_unreserve(bo);
378 }
379 drm_gem_object_unreference_unlocked(afb->obj);
380 afb->obj = NULL;
381 }
382 drm_fb_helper_fini(&fbdev->helper);
383
384#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
385 drm_framebuffer_unregister_private(&afb->base);
386#endif
387 drm_framebuffer_cleanup(&afb->base);
388}
389
390int vbox_fbdev_init(struct drm_device *dev)
391{
392 struct vbox_private *vbox = dev->dev_private;
393 struct vbox_fbdev *fbdev;
394 int ret;
395
396 fbdev = kzalloc(sizeof(struct vbox_fbdev), GFP_KERNEL);
397 if (!fbdev)
398 return -ENOMEM;
399
400 vbox->fbdev = fbdev;
401 spin_lock_init(&fbdev->dirty_lock);
402
403#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
404 fbdev->helper.funcs = &vbox_fb_helper_funcs;
405#else
406 drm_fb_helper_prepare(dev, &fbdev->helper, &vbox_fb_helper_funcs);
407#endif
408#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
409 ret = drm_fb_helper_init(dev, &fbdev->helper, vbox->num_crtcs);
410#else
411 ret = drm_fb_helper_init(dev, &fbdev->helper, vbox->num_crtcs, vbox->num_crtcs);
412#endif
413 if (ret)
414 goto free;
415
416 ret = drm_fb_helper_single_add_all_connectors(&fbdev->helper);
417 if (ret)
418 goto fini;
419
420 /* disable all the possible outputs/crtcs before entering KMS mode */
421 drm_helper_disable_unused_functions(dev);
422
423 ret = drm_fb_helper_initial_config(&fbdev->helper, 32);
424 if (ret)
425 goto fini;
426
427 return 0;
428fini:
429 drm_fb_helper_fini(&fbdev->helper);
430free:
431 kfree(fbdev);
432 vbox->fbdev = NULL;
433 return ret;
434}
435
436void vbox_fbdev_fini(struct drm_device *dev)
437{
438 struct vbox_private *vbox = dev->dev_private;
439
440 if (!vbox->fbdev)
441 return;
442
443 vbox_fbdev_destroy(dev, vbox->fbdev);
444 kfree(vbox->fbdev);
445 vbox->fbdev = NULL;
446}
447
448void vbox_fbdev_set_suspend(struct drm_device *dev, int state)
449{
450 struct vbox_private *vbox = dev->dev_private;
451
452 if (!vbox->fbdev)
453 return;
454
455 fb_set_suspend(vbox->fbdev->helper.fbdev, state);
456}
457
458void vbox_fbdev_set_base(struct vbox_private *vbox, unsigned long gpu_addr)
459{
460 vbox->fbdev->helper.fbdev->fix.smem_start =
461 vbox->fbdev->helper.fbdev->apertures->ranges[0].base +
462 gpu_addr;
463 vbox->fbdev->helper.fbdev->fix.smem_len = vbox->available_vram_size - gpu_addr;
464}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette