VirtualBox

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

Last change on this file since 65193 was 64959, checked in by vboxsync, 8 years ago

Additions/linux/drm: fix breakage with recent kernels by removing an unneeded use of a drm function which was removed from the kernel altogether. It was inherited with other code from the AST kernel driver and never did anything useful in our driver (nor in AST for that matter).

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