VirtualBox

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

Last change on this file since 74773 was 74773, checked in by vboxsync, 6 years ago

Additions/linux/drm: add clean-ups made when adding driver to Linux staging.
bugref:4567: Linux kernel driver maintenance
Based on work done by Hans de Goede. Main changes:

  • Lots of cosmetic and white space changes to reduce unnecessary differences. Replaced some if (RT_FAILURE(ret)) checks by if (ret).
  • Use additional helpers instead of hard-coding.
  • Use devm_* managed allocation functions in a number of places.
  • Linux-style resource clean-up in a number of functions (goto clean-ups, collapsing nested if-else with breaks and returns, etc).
  • Fixed fbdev fix base setting.
  • Added vbox_check_supported() to vbox_main.c and use instead of VBoxHGSMIIsSupported().
  • Added vbox_bo_move().
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.0 KB
Line 
1/* $Id: vbox_fb.c 74773 2018-10-11 15:19:06Z 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 <airlied@redhat.com>
32 * Michael Thayer <michael.thayer@oracle.com,
33 */
34#include <linux/module.h>
35#include <linux/kernel.h>
36#include <linux/errno.h>
37#include <linux/string.h>
38#include <linux/mm.h>
39#include <linux/tty.h>
40#include <linux/sysrq.h>
41#include <linux/delay.h>
42#include <linux/fb.h>
43#include <linux/init.h>
44
45#include <drm/drmP.h>
46#include <drm/drm_crtc.h>
47#include <drm/drm_fb_helper.h>
48#include <drm/drm_crtc_helper.h>
49
50#include "vbox_drv.h"
51#include <VBoxVideo.h>
52
53#define VBOX_DIRTY_DELAY (HZ / 30)
54/**
55 * Tell the host about dirty rectangles to update.
56 */
57static void vbox_dirty_update(struct vbox_fbdev *fbdev,
58 int x, int y, int width, int height)
59{
60 struct drm_gem_object *obj;
61 struct vbox_bo *bo;
62 int ret = -EBUSY;
63 bool store_for_later = false;
64 int x2, y2;
65 unsigned long flags;
66 struct drm_clip_rect rect;
67
68 obj = fbdev->afb.obj;
69 bo = gem_to_vbox_bo(obj);
70
71 /*
72 * try and reserve the BO, if we fail with busy
73 * then the BO is being moved and we should
74 * store up the damage until later.
75 */
76 if (drm_can_sleep())
77 ret = vbox_bo_reserve(bo, true);
78 if (ret) {
79 if (ret != -EBUSY)
80 return;
81
82 store_for_later = true;
83 }
84
85 x2 = x + width - 1;
86 y2 = y + height - 1;
87 spin_lock_irqsave(&fbdev->dirty_lock, flags);
88
89 if (fbdev->y1 < y)
90 y = fbdev->y1;
91 if (fbdev->y2 > y2)
92 y2 = fbdev->y2;
93 if (fbdev->x1 < x)
94 x = fbdev->x1;
95 if (fbdev->x2 > x2)
96 x2 = fbdev->x2;
97
98 if (store_for_later) {
99 fbdev->x1 = x;
100 fbdev->x2 = x2;
101 fbdev->y1 = y;
102 fbdev->y2 = y2;
103 spin_unlock_irqrestore(&fbdev->dirty_lock, flags);
104 return;
105 }
106
107 fbdev->x1 = INT_MAX;
108 fbdev->y1 = INT_MAX;
109 fbdev->x2 = 0;
110 fbdev->y2 = 0;
111
112 spin_unlock_irqrestore(&fbdev->dirty_lock, flags);
113
114 /*
115 * Not sure why the original code subtracted 1 here, but I will keep
116 * it that way to avoid unnecessary differences.
117 */
118 rect.x1 = x;
119 rect.x2 = x2 + 1;
120 rect.y1 = y;
121 rect.y2 = y2 + 1;
122 vbox_framebuffer_dirty_rectangles(&fbdev->afb.base, &rect, 1);
123
124 vbox_bo_unreserve(bo);
125}
126
127#ifdef CONFIG_FB_DEFERRED_IO
128static void vbox_deferred_io(struct fb_info *info, struct list_head *pagelist)
129{
130 struct vbox_fbdev *fbdev = info->par;
131 unsigned long start, end, min, max;
132 struct page *page;
133 int y1, y2;
134
135 min = ULONG_MAX;
136 max = 0;
137 list_for_each_entry(page, pagelist, lru) {
138 start = page->index << PAGE_SHIFT;
139 end = start + PAGE_SIZE - 1;
140 min = min(min, start);
141 max = max(max, end);
142 }
143
144 if (min < max) {
145 y1 = min / info->fix.line_length;
146 y2 = (max / info->fix.line_length) + 1;
147 DRM_INFO("%s: Calling dirty update: 0, %d, %d, %d\n",
148 __func__, y1, info->var.xres, y2 - y1 - 1);
149 vbox_dirty_update(fbdev, 0, y1, info->var.xres, y2 - y1 - 1);
150 }
151}
152
153static struct fb_deferred_io vbox_defio = {
154 .delay = VBOX_DIRTY_DELAY,
155 .deferred_io = vbox_deferred_io,
156};
157#endif
158
159static void vbox_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
160{
161 struct vbox_fbdev *fbdev = info->par;
162
163 sys_fillrect(info, rect);
164 vbox_dirty_update(fbdev, rect->dx, rect->dy, rect->width, rect->height);
165}
166
167static void vbox_copyarea(struct fb_info *info, const struct fb_copyarea *area)
168{
169 struct vbox_fbdev *fbdev = info->par;
170
171 sys_copyarea(info, area);
172 vbox_dirty_update(fbdev, area->dx, area->dy, area->width, area->height);
173}
174
175static void vbox_imageblit(struct fb_info *info, const struct fb_image *image)
176{
177 struct vbox_fbdev *fbdev = info->par;
178
179 sys_imageblit(info, image);
180 vbox_dirty_update(fbdev, image->dx, image->dy, image->width,
181 image->height);
182}
183
184static struct fb_ops vboxfb_ops = {
185 .owner = THIS_MODULE,
186 .fb_check_var = drm_fb_helper_check_var,
187 .fb_set_par = drm_fb_helper_set_par,
188 .fb_fillrect = vbox_fillrect,
189 .fb_copyarea = vbox_copyarea,
190 .fb_imageblit = vbox_imageblit,
191 .fb_pan_display = drm_fb_helper_pan_display,
192 .fb_blank = drm_fb_helper_blank,
193 .fb_setcmap = drm_fb_helper_setcmap,
194 .fb_debug_enter = drm_fb_helper_debug_enter,
195 .fb_debug_leave = drm_fb_helper_debug_leave,
196};
197
198static int vboxfb_create_object(struct vbox_fbdev *fbdev,
199 struct DRM_MODE_FB_CMD *mode_cmd,
200 struct drm_gem_object **gobj_p)
201{
202 struct drm_device *dev = fbdev->helper.dev;
203 u32 size;
204 struct drm_gem_object *gobj;
205#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
206 u32 pitch = mode_cmd->pitch;
207#else
208 u32 pitch = mode_cmd->pitches[0];
209#endif
210
211 int ret;
212
213 size = pitch * mode_cmd->height;
214 ret = vbox_gem_create(dev, size, true, &gobj);
215 if (ret)
216 return ret;
217
218 *gobj_p = gobj;
219
220 return 0;
221}
222
223static int vboxfb_create(struct drm_fb_helper *helper,
224 struct drm_fb_helper_surface_size *sizes)
225{
226 struct vbox_fbdev *fbdev =
227 container_of(helper, struct vbox_fbdev, helper);
228 struct drm_device *dev = fbdev->helper.dev;
229 struct DRM_MODE_FB_CMD mode_cmd;
230 struct drm_framebuffer *fb;
231 struct fb_info *info;
232 struct device *device = &dev->pdev->dev;
233 struct drm_gem_object *gobj;
234 struct vbox_bo *bo;
235 int size, ret;
236 u32 pitch;
237
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 /*
296 * The last flag forces a mode set on VT switches even if the kernel
297 * does not think it is needed.
298 */
299 info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT |
300 FBINFO_MISC_ALWAYS_SETPAR;
301 info->fbops = &vboxfb_ops;
302
303 ret = fb_alloc_cmap(&info->cmap, 256, 0);
304 if (ret)
305 return -ENOMEM;
306
307 /*
308 * This seems to be done for safety checking that the framebuffer
309 * is not registered twice by different drivers.
310 */
311 info->apertures = alloc_apertures(1);
312 if (!info->apertures)
313 return -ENOMEM;
314 info->apertures->ranges[0].base = pci_resource_start(dev->pdev, 0);
315 info->apertures->ranges[0].size = pci_resource_len(dev->pdev, 0);
316
317#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) || defined(RHEL_75)
318 drm_fb_helper_fill_fix(info, fb->pitches[0], fb->format->depth);
319#else
320 drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
321#endif
322 drm_fb_helper_fill_var(info, &fbdev->helper, sizes->fb_width,
323 sizes->fb_height);
324
325 info->screen_base = bo->kmap.virtual;
326 info->screen_size = size;
327
328#ifdef CONFIG_FB_DEFERRED_IO
329 info->fbdefio = &vbox_defio;
330 fb_deferred_io_init(info);
331#endif
332
333 info->pixmap.flags = FB_PIXMAP_SYSTEM;
334
335 DRM_DEBUG_KMS("allocated %dx%d\n", fb->width, fb->height);
336
337 return 0;
338}
339
340static struct drm_fb_helper_funcs vbox_fb_helper_funcs = {
341 .fb_probe = vboxfb_create,
342};
343
344#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0) && !defined(RHEL_73)
345static void drm_fb_helper_unregister_fbi(struct drm_fb_helper *fb_helper)
346{
347 if (fb_helper && fb_helper->fbdev)
348 unregister_framebuffer(fb_helper->fbdev);
349}
350#endif
351
352void vbox_fbdev_fini(struct drm_device *dev)
353{
354 struct vbox_private *vbox = dev->dev_private;
355 struct vbox_fbdev *fbdev = vbox->fbdev;
356 struct vbox_framebuffer *afb = &fbdev->afb;
357
358 drm_fb_helper_unregister_fbi(&fbdev->helper);
359
360 if (afb->obj) {
361 struct vbox_bo *bo = gem_to_vbox_bo(afb->obj);
362
363 if (!vbox_bo_reserve(bo, false)) {
364 if (bo->kmap.virtual)
365 ttm_bo_kunmap(&bo->kmap);
366 /*
367 * QXL does this, but is it really needed before
368 * freeing?
369 */
370 if (bo->pin_count)
371 vbox_bo_unpin(bo);
372 vbox_bo_unreserve(bo);
373 }
374 drm_gem_object_unreference_unlocked(afb->obj);
375 afb->obj = NULL;
376 }
377 drm_fb_helper_fini(&fbdev->helper);
378
379#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
380 drm_framebuffer_unregister_private(&afb->base);
381#endif
382 drm_framebuffer_cleanup(&afb->base);
383}
384
385int vbox_fbdev_init(struct drm_device *dev)
386{
387 struct vbox_private *vbox = dev->dev_private;
388 struct vbox_fbdev *fbdev;
389 int ret;
390
391 fbdev = devm_kzalloc(dev->dev, sizeof(*fbdev), GFP_KERNEL);
392 if (!fbdev)
393 return -ENOMEM;
394
395 vbox->fbdev = fbdev;
396 spin_lock_init(&fbdev->dirty_lock);
397
398#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0) && !defined(RHEL_73)
399 fbdev->helper.funcs = &vbox_fb_helper_funcs;
400#else
401 drm_fb_helper_prepare(dev, &fbdev->helper, &vbox_fb_helper_funcs);
402#endif
403#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) || defined(RHEL_75)
404 ret = drm_fb_helper_init(dev, &fbdev->helper, vbox->num_crtcs);
405#else
406 ret =
407 drm_fb_helper_init(dev, &fbdev->helper, vbox->num_crtcs,
408 vbox->num_crtcs);
409#endif
410 if (ret)
411 return ret;
412
413 ret = drm_fb_helper_single_add_all_connectors(&fbdev->helper);
414 if (ret)
415 goto err_fini;
416
417 /* disable all the possible outputs/crtcs before entering KMS mode */
418 drm_helper_disable_unused_functions(dev);
419
420 ret = drm_fb_helper_initial_config(&fbdev->helper, 32);
421 if (ret)
422 goto err_fini;
423
424 return 0;
425
426err_fini:
427 drm_fb_helper_fini(&fbdev->helper);
428 return ret;
429}
430
431void vbox_fbdev_set_base(struct vbox_private *vbox, unsigned long gpu_addr)
432{
433 struct fb_info *fbdev = vbox->fbdev->helper.fbdev;
434
435 fbdev->fix.smem_start = fbdev->apertures->ranges[0].base + gpu_addr;
436 fbdev->fix.smem_len = vbox->available_vram_size - gpu_addr;
437}
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