VirtualBox

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

最後變更 在這個檔案從90577是 90577,由 vboxsync 提交於 3 年 前

Linux Host and Guest drivers: another attempt to introduce initial support for RHEL 8.5 kernels, bugref:4567.

CentOS kernel 4.18.0-326.el8 (RHEL 8,5) has backported commits from vanilla kernel 5.10+.
Fedora kernel 5.9.0-36.eln104 (RHEL 8,99) does not have some of these changes yet.
This commit attempts to move relevant code into RHEL 8,5 specific section. For some
parts of DRM code, changes are only affect RHEL 8,5 (8,99 is excluded) due to missing
commits (on 8,99) from newer kernels.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 12.7 KB
 
1/* $Id: vbox_fb.c 90577 2021-08-09 09:57:00Z vboxsync $ */
2/** @file
3 * VirtualBox Additions Linux kernel video driver
4 */
5
6/*
7 * Copyright (C) 2013-2020 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 <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 "vbox_drv.h"
46
47#include <drm/drm_crtc.h>
48#include <drm/drm_fb_helper.h>
49#include <drm/drm_crtc_helper.h>
50
51#include <VBoxVideo.h>
52
53#if RTLNX_VER_MAX(4,7,0) && !RTLNX_RHEL_MAJ_PREREQ(7,4)
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#endif /* RTLNX_VER_MAX(4,7,0) && !RTLNX_RHEL_MAJ_PREREQ(7,4) */
127
128#ifdef CONFIG_FB_DEFERRED_IO
129# if RTLNX_VER_MAX(4,7,0) && !RTLNX_RHEL_MAJ_PREREQ(7,4)
130static void drm_fb_helper_deferred_io(struct fb_info *info, struct list_head *pagelist)
131{
132 struct vbox_fbdev *fbdev = info->par;
133 unsigned long start, end, min, max;
134 struct page *page;
135 int y1, y2;
136
137 min = ULONG_MAX;
138 max = 0;
139 list_for_each_entry(page, pagelist, lru) {
140 start = page->index << PAGE_SHIFT;
141 end = start + PAGE_SIZE - 1;
142 min = min(min, start);
143 max = max(max, end);
144 }
145
146 if (min < max) {
147 y1 = min / info->fix.line_length;
148 y2 = (max / info->fix.line_length) + 1;
149 DRM_INFO("%s: Calling dirty update: 0, %d, %d, %d\n",
150 __func__, y1, info->var.xres, y2 - y1 - 1);
151 vbox_dirty_update(fbdev, 0, y1, info->var.xres, y2 - y1 - 1);
152 }
153}
154# endif
155
156static struct fb_deferred_io vbox_defio = {
157 .delay = HZ / 30,
158 .deferred_io = drm_fb_helper_deferred_io,
159};
160#endif /* CONFIG_FB_DEFERRED_IO */
161
162#if RTLNX_VER_MAX(4,3,0) && !RTLNX_RHEL_MAJ_PREREQ(7,3)
163static void drm_fb_helper_sys_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
164{
165 struct vbox_fbdev *fbdev = info->par;
166
167 sys_fillrect(info, rect);
168 vbox_dirty_update(fbdev, rect->dx, rect->dy, rect->width, rect->height);
169}
170
171static void drm_fb_helper_sys_copyarea(struct fb_info *info, const struct fb_copyarea *area)
172{
173 struct vbox_fbdev *fbdev = info->par;
174
175 sys_copyarea(info, area);
176 vbox_dirty_update(fbdev, area->dx, area->dy, area->width, area->height);
177}
178
179static void drm_fb_helper_sys_imageblit(struct fb_info *info, const struct fb_image *image)
180{
181 struct vbox_fbdev *fbdev = info->par;
182
183 sys_imageblit(info, image);
184 vbox_dirty_update(fbdev, image->dx, image->dy, image->width,
185 image->height);
186}
187#endif /* RTLNX_VER_MAX(4,3,0) && !RTLNX_RHEL_MAJ_PREREQ(7,3) */
188
189static struct fb_ops vboxfb_ops = {
190 .owner = THIS_MODULE,
191 .fb_check_var = drm_fb_helper_check_var,
192 .fb_set_par = drm_fb_helper_set_par,
193 .fb_fillrect = drm_fb_helper_sys_fillrect,
194 .fb_copyarea = drm_fb_helper_sys_copyarea,
195 .fb_imageblit = drm_fb_helper_sys_imageblit,
196 .fb_pan_display = drm_fb_helper_pan_display,
197 .fb_blank = drm_fb_helper_blank,
198 .fb_setcmap = drm_fb_helper_setcmap,
199 .fb_debug_enter = drm_fb_helper_debug_enter,
200 .fb_debug_leave = drm_fb_helper_debug_leave,
201};
202
203static int vboxfb_create_object(struct vbox_fbdev *fbdev,
204 struct DRM_MODE_FB_CMD *mode_cmd,
205 struct drm_gem_object **gobj_p)
206{
207 struct drm_device *dev = fbdev->helper.dev;
208 u32 size;
209 struct drm_gem_object *gobj;
210#if RTLNX_VER_MAX(3,3,0)
211 u32 pitch = mode_cmd->pitch;
212#else
213 u32 pitch = mode_cmd->pitches[0];
214#endif
215
216 int ret;
217
218 size = pitch * mode_cmd->height;
219 ret = vbox_gem_create(dev, size, true, &gobj);
220 if (ret)
221 return ret;
222
223 *gobj_p = gobj;
224
225 return 0;
226}
227
228#if RTLNX_VER_MAX(4,3,0) && !RTLNX_RHEL_MAJ_PREREQ(7,3)
229static struct fb_info *drm_fb_helper_alloc_fbi(struct drm_fb_helper *helper)
230{
231 struct fb_info *info;
232 struct vbox_fbdev *fbdev =
233 container_of(helper, struct vbox_fbdev, helper);
234 struct drm_device *dev = fbdev->helper.dev;
235 struct device *device = &dev->pdev->dev;
236
237 info = framebuffer_alloc(0, device);
238 if (!info)
239 return ERR_PTR(-ENOMEM);
240 fbdev->helper.fbdev = info;
241
242 if (fb_alloc_cmap(&info->cmap, 256, 0))
243 return ERR_PTR(-ENOMEM);
244
245 info->apertures = alloc_apertures(1);
246 if (!info->apertures)
247 return ERR_PTR(-ENOMEM);
248
249 return info;
250}
251#endif
252
253static int vboxfb_create(struct drm_fb_helper *helper,
254 struct drm_fb_helper_surface_size *sizes)
255{
256 struct vbox_fbdev *fbdev =
257 container_of(helper, struct vbox_fbdev, helper);
258 struct drm_device *dev = fbdev->helper.dev;
259 struct DRM_MODE_FB_CMD mode_cmd;
260 struct drm_framebuffer *fb;
261 struct fb_info *info;
262 struct drm_gem_object *gobj;
263 struct vbox_bo *bo;
264 int size, ret;
265 u32 pitch;
266
267 mode_cmd.width = sizes->surface_width;
268 mode_cmd.height = sizes->surface_height;
269 pitch = mode_cmd.width * ((sizes->surface_bpp + 7) / 8);
270#if RTLNX_VER_MAX(3,3,0)
271 mode_cmd.bpp = sizes->surface_bpp;
272 mode_cmd.depth = sizes->surface_depth;
273 mode_cmd.pitch = pitch;
274#else
275 mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
276 sizes->surface_depth);
277 mode_cmd.pitches[0] = pitch;
278#endif
279
280 size = pitch * mode_cmd.height;
281
282 ret = vboxfb_create_object(fbdev, &mode_cmd, &gobj);
283 if (ret) {
284 DRM_ERROR("failed to create fbcon backing object %d\n", ret);
285 return ret;
286 }
287
288 ret = vbox_framebuffer_init(dev, &fbdev->afb, &mode_cmd, gobj);
289 if (ret)
290 return ret;
291
292 bo = gem_to_vbox_bo(gobj);
293
294 ret = vbox_bo_reserve(bo, false);
295 if (ret)
296 return ret;
297
298 ret = vbox_bo_pin(bo, VBOX_MEM_TYPE_VRAM, NULL);
299 if (ret) {
300 vbox_bo_unreserve(bo);
301 return ret;
302 }
303
304#if RTLNX_VER_MIN(5,14,0)
305 ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.resource->num_pages, &bo->kmap);
306#elif RTLNX_VER_MIN(5,12,0) || RTLNX_RHEL_MAJ_PREREQ(8,5)
307 ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.mem.num_pages, &bo->kmap);
308#else
309 ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap);
310#endif
311
312 vbox_bo_unreserve(bo);
313 if (ret) {
314 DRM_ERROR("failed to kmap fbcon\n");
315 return ret;
316 }
317
318 info = drm_fb_helper_alloc_fbi(helper);
319 if (IS_ERR(info))
320 return -PTR_ERR(info);
321
322 info->par = fbdev;
323
324 fbdev->size = size;
325
326 fb = &fbdev->afb.base;
327 fbdev->helper.fb = fb;
328
329 strcpy(info->fix.id, "vboxdrmfb");
330
331 /*
332 * The last flag forces a mode set on VT switches even if the kernel
333 * does not think it is needed.
334 */
335 info->flags = FBINFO_DEFAULT | FBINFO_MISC_ALWAYS_SETPAR;
336 info->fbops = &vboxfb_ops;
337
338 /*
339 * This seems to be done for safety checking that the framebuffer
340 * is not registered twice by different drivers.
341 */
342 info->apertures->ranges[0].base = pci_resource_start(VBOX_DRM_TO_PCI_DEV(dev), 0);
343 info->apertures->ranges[0].size = pci_resource_len(VBOX_DRM_TO_PCI_DEV(dev), 0);
344
345#if RTLNX_VER_MIN(5,2,0) || RTLNX_RHEL_MAJ_PREREQ(8,2)
346 /*
347 * The corresponding 5.2-rc1 Linux DRM kernel changes have been
348 * also backported to older RedHat based 4.18.0 Linux kernels.
349 */
350 drm_fb_helper_fill_info(info, &fbdev->helper, sizes);
351#elif RTLNX_VER_MIN(4,11,0) || RTLNX_RHEL_MAJ_PREREQ(7, 5)
352 drm_fb_helper_fill_fix(info, fb->pitches[0], fb->format->depth);
353#else
354 drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
355#endif
356#if RTLNX_VER_MAX(5,2,0) && !RTLNX_RHEL_MAJ_PREREQ(8,2)
357 drm_fb_helper_fill_var(info, &fbdev->helper, sizes->fb_width,
358 sizes->fb_height);
359#endif
360
361 info->screen_base = (char __iomem *)bo->kmap.virtual;
362 info->screen_size = size;
363
364#ifdef CONFIG_FB_DEFERRED_IO
365 info->fbdefio = &vbox_defio;
366 fb_deferred_io_init(info);
367#endif
368
369 info->pixmap.flags = FB_PIXMAP_SYSTEM;
370
371 DRM_DEBUG_KMS("allocated %dx%d\n", fb->width, fb->height);
372
373 return 0;
374}
375
376static struct drm_fb_helper_funcs vbox_fb_helper_funcs = {
377 .fb_probe = vboxfb_create,
378};
379
380#if RTLNX_VER_MAX(4,3,0) && !RTLNX_RHEL_MAJ_PREREQ(7,3)
381static void drm_fb_helper_unregister_fbi(struct drm_fb_helper *fb_helper)
382{
383 if (fb_helper && fb_helper->fbdev)
384 unregister_framebuffer(fb_helper->fbdev);
385}
386#endif
387
388void vbox_fbdev_fini(struct drm_device *dev)
389{
390 struct vbox_private *vbox = dev->dev_private;
391 struct vbox_fbdev *fbdev = vbox->fbdev;
392 struct vbox_framebuffer *afb = &fbdev->afb;
393
394#ifdef CONFIG_FB_DEFERRED_IO
395 if (fbdev->helper.fbdev && fbdev->helper.fbdev->fbdefio)
396 fb_deferred_io_cleanup(fbdev->helper.fbdev);
397#endif
398
399 drm_fb_helper_unregister_fbi(&fbdev->helper);
400
401 if (afb->obj) {
402 struct vbox_bo *bo = gem_to_vbox_bo(afb->obj);
403
404 if (!vbox_bo_reserve(bo, false)) {
405 if (bo->kmap.virtual)
406 ttm_bo_kunmap(&bo->kmap);
407 /*
408 * QXL does this, but is it really needed before
409 * freeing?
410 */
411 if (bo->pin_count)
412 vbox_bo_unpin(bo);
413 vbox_bo_unreserve(bo);
414 }
415#if RTLNX_VER_MIN(5,9,0) || RTLNX_RHEL_MIN(8,4) || RTLNX_SUSE_MAJ_PREREQ(15,3)
416 drm_gem_object_put(afb->obj);
417#else
418 drm_gem_object_put_unlocked(afb->obj);
419#endif
420 afb->obj = NULL;
421 }
422 drm_fb_helper_fini(&fbdev->helper);
423
424#if RTLNX_VER_MIN(3,9,0)
425 drm_framebuffer_unregister_private(&afb->base);
426#endif
427 drm_framebuffer_cleanup(&afb->base);
428}
429
430int vbox_fbdev_init(struct drm_device *dev)
431{
432 struct vbox_private *vbox = dev->dev_private;
433 struct vbox_fbdev *fbdev;
434 int ret = 0;
435
436 fbdev = devm_kzalloc(dev->dev, sizeof(*fbdev), GFP_KERNEL);
437 if (!fbdev)
438 return -ENOMEM;
439
440 vbox->fbdev = fbdev;
441 spin_lock_init(&fbdev->dirty_lock);
442
443#if RTLNX_VER_MAX(3,17,0) && !RTLNX_RHEL_MAJ_PREREQ(7,2)
444 fbdev->helper.funcs = &vbox_fb_helper_funcs;
445#else
446 drm_fb_helper_prepare(dev, &fbdev->helper, &vbox_fb_helper_funcs);
447#endif
448#if RTLNX_VER_MIN(5,7,0) || RTLNX_RHEL_MIN(8,4) || RTLNX_SUSE_MAJ_PREREQ(15,3)
449 ret = drm_fb_helper_init(dev, &fbdev->helper);
450#elif RTLNX_VER_MIN(4,11,0) || RTLNX_RHEL_MAJ_PREREQ(7,5)
451 ret = drm_fb_helper_init(dev, &fbdev->helper, vbox->num_crtcs);
452#else /* < 4.11.0 */
453 ret =
454 drm_fb_helper_init(dev, &fbdev->helper, vbox->num_crtcs,
455 vbox->num_crtcs);
456#endif
457 if (ret)
458 return ret;
459
460#if RTLNX_VER_MAX(5,7,0) && !RTLNX_RHEL_MAJ_PREREQ(8,4) && !RTLNX_SUSE_MAJ_PREREQ(15,3)
461 ret = drm_fb_helper_single_add_all_connectors(&fbdev->helper);
462 if (ret)
463 goto err_fini;
464#endif
465
466 /* disable all the possible outputs/crtcs before entering KMS mode */
467 drm_helper_disable_unused_functions(dev);
468
469 ret = drm_fb_helper_initial_config(&fbdev->helper, 32);
470 if (ret)
471 goto err_fini;
472
473 return 0;
474
475err_fini:
476 drm_fb_helper_fini(&fbdev->helper);
477 return ret;
478}
479
480void vbox_fbdev_set_base(struct vbox_private *vbox, unsigned long gpu_addr)
481{
482 struct fb_info *fbdev = vbox->fbdev->helper.fbdev;
483
484 fbdev->fix.smem_start = fbdev->apertures->ranges[0].base + gpu_addr;
485 fbdev->fix.smem_len = vbox->available_vram_size - gpu_addr;
486}
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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