VirtualBox

source: vbox/trunk/src/VBox/Additions/linux/drm/vbox_main.c@ 55808

Last change on this file since 55808 was 55808, checked in by vboxsync, 10 years ago

Additions/linux/drm: fix warnings.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.1 KB
Line 
1/** @file $Id: vbox_main.c 55808 2015-05-11 18:31:56Z vboxsync $
2 *
3 * VirtualBox Additions Linux kernel video driver
4 */
5
6/*
7 * Copyright (C) 2013 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_main.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 "vbox_drv.h"
49
50#include <VBox/VBoxVideoGuest.h>
51#include <VBox/VBoxVideo.h>
52
53#include <drm/drm_fb_helper.h>
54#include <drm/drm_crtc_helper.h>
55
56static void vbox_user_framebuffer_destroy(struct drm_framebuffer *fb)
57{
58 struct vbox_framebuffer *vbox_fb = to_vbox_framebuffer(fb);
59 if (vbox_fb->obj)
60 drm_gem_object_unreference_unlocked(vbox_fb->obj);
61
62 LogFunc(("vboxvideo: %d: vbox_fb=%p, vbox_fb->obj=%p\n", __LINE__,
63 vbox_fb, vbox_fb->obj));
64 drm_framebuffer_cleanup(fb);
65 kfree(fb);
66}
67
68static int vbox_user_framebuffer_create_handle(struct drm_framebuffer *fb,
69 struct drm_file *file,
70 unsigned int *handle)
71{
72 return -EINVAL;
73}
74
75/** Send information about dirty rectangles to VBVA. If necessary we enable
76 * VBVA first, as this is normally disabled after a mode set in case a user
77 * takes over the console that is not aware of VBVA (i.e. the VESA BIOS). */
78void vbox_framebuffer_dirty_rectangles(struct drm_framebuffer *fb,
79 struct drm_clip_rect *pRects,
80 unsigned cRects)
81{
82 struct vbox_private *vbox = fb->dev->dev_private;
83 unsigned i;
84 unsigned long flags;
85
86 LogFunc(("vboxvideo: %d: fb=%p, cRects=%u, vbox=%p\n", __LINE__, fb,
87 cRects, vbox));
88 spin_lock_irqsave(&vbox->dev_lock, flags);
89 for (i = 0; i < cRects; ++i)
90 {
91 struct drm_crtc *crtc;
92 list_for_each_entry(crtc, &fb->dev->mode_config.crtc_list, head)
93 {
94 unsigned iCrtc = to_vbox_crtc(crtc)->crtc_id;
95 struct VBVABUFFER *pVBVA = vbox->paVBVACtx[iCrtc].pVBVA;
96 VBVACMDHDR cmdHdr;
97
98 if (!pVBVA)
99 {
100 pVBVA = (struct VBVABUFFER *) ( ((uint8_t *)vbox->vram)
101 + vbox->vram_size
102 + iCrtc * VBVA_MIN_BUFFER_SIZE);
103 if (!VBoxVBVAEnable(&vbox->paVBVACtx[iCrtc], &vbox->Ctx, pVBVA, iCrtc))
104 AssertReleaseMsgFailed(("VBoxVBVAEnable failed - heap allocation error, very old host or driver error.\n"));
105 }
106 if ( CRTC_FB(crtc) != fb
107 || pRects[i].x1 > crtc->x
108 + crtc->hwmode.hdisplay
109 || pRects[i].y1 > crtc->y
110 + crtc->hwmode.vdisplay
111 || pRects[i].x2 < crtc->x
112 || pRects[i].y2 < crtc->y)
113 continue;
114 cmdHdr.x = (int16_t)pRects[i].x1;
115 cmdHdr.y = (int16_t)pRects[i].y1;
116 cmdHdr.w = (uint16_t)pRects[i].x2 - pRects[i].x1;
117 cmdHdr.h = (uint16_t)pRects[i].y2 - pRects[i].y1;
118 if (VBoxVBVABufferBeginUpdate(&vbox->paVBVACtx[iCrtc],
119 &vbox->Ctx))
120 {
121 VBoxVBVAWrite(&vbox->paVBVACtx[iCrtc], &vbox->Ctx, &cmdHdr,
122 sizeof(cmdHdr));
123 VBoxVBVABufferEndUpdate(&vbox->paVBVACtx[iCrtc]);
124 }
125 }
126 }
127 spin_unlock_irqrestore(&vbox->dev_lock, flags);
128 LogFunc(("vboxvideo: %d\n", __LINE__));
129}
130
131static int vbox_user_framebuffer_dirty(struct drm_framebuffer *fb,
132 struct drm_file *file_priv,
133 unsigned flags, unsigned color,
134 struct drm_clip_rect *pRects,
135 unsigned cRects)
136{
137 LogFunc(("vboxvideo: %d, flags=%u\n", __LINE__, flags));
138 vbox_framebuffer_dirty_rectangles(fb, pRects, cRects);
139 return 0;
140}
141
142static const struct drm_framebuffer_funcs vbox_fb_funcs =
143{
144 .destroy = vbox_user_framebuffer_destroy,
145 .create_handle = vbox_user_framebuffer_create_handle,
146 .dirty = vbox_user_framebuffer_dirty,
147};
148
149
150int vbox_framebuffer_init(struct drm_device *dev,
151 struct vbox_framebuffer *vbox_fb,
152 struct DRM_MODE_FB_CMD *mode_cmd,
153 struct drm_gem_object *obj)
154{
155 int ret;
156
157 LogFunc(("vboxvideo: %d: dev=%p, vbox_fb=%p, obj=%p\n", __LINE__, dev,
158 vbox_fb, obj));
159 drm_helper_mode_fill_fb_struct(&vbox_fb->base, mode_cmd);
160 vbox_fb->obj = obj;
161 ret = drm_framebuffer_init(dev, &vbox_fb->base, &vbox_fb_funcs);
162 if (ret)
163 {
164 DRM_ERROR("framebuffer init failed %d\n", ret);
165 LogFunc(("vboxvideo: %d\n", __LINE__));
166 return ret;
167 }
168 LogFunc(("vboxvideo: %d\n", __LINE__));
169 return 0;
170}
171
172static struct drm_framebuffer *
173vbox_user_framebuffer_create(struct drm_device *dev,
174 struct drm_file *filp,
175 struct drm_mode_fb_cmd2 *mode_cmd)
176{
177 struct drm_gem_object *obj;
178 struct vbox_framebuffer *vbox_fb;
179 int ret;
180
181 LogFunc(("vboxvideo: %d\n", __LINE__));
182 obj = drm_gem_object_lookup(dev, filp, mode_cmd->handles[0]);
183 if (obj == NULL)
184 return ERR_PTR(-ENOENT);
185
186 vbox_fb = kzalloc(sizeof(*vbox_fb), GFP_KERNEL);
187 if (!vbox_fb)
188 {
189 drm_gem_object_unreference_unlocked(obj);
190 return ERR_PTR(-ENOMEM);
191 }
192
193 ret = vbox_framebuffer_init(dev, vbox_fb, mode_cmd, obj);
194 if (ret)
195 {
196 drm_gem_object_unreference_unlocked(obj);
197 kfree(vbox_fb);
198 return ERR_PTR(ret);
199 }
200 LogFunc(("vboxvideo: %d\n", __LINE__));
201 return &vbox_fb->base;
202}
203
204static const struct drm_mode_config_funcs vbox_mode_funcs =
205{
206 .fb_create = vbox_user_framebuffer_create,
207};
208
209static void disableVBVA(struct vbox_private *pVBox)
210{
211 unsigned i;
212
213 if (pVBox->paVBVACtx)
214 {
215 for (i = 0; i < pVBox->cCrtcs; ++i)
216 VBoxVBVADisable(&pVBox->paVBVACtx[i], &pVBox->Ctx, i);
217 kfree(pVBox->paVBVACtx);
218 pVBox->paVBVACtx = NULL;
219 }
220}
221
222static int vbox_vbva_init(struct vbox_private *vbox)
223{
224 unsigned i;
225 bool fRC = true;
226 LogFunc(("vboxvideo: %d: vbox=%p, vbox->cCrtcs=%u, vbox->paVBVACtx=%p\n",
227 __LINE__, vbox, (unsigned)vbox->cCrtcs, vbox->paVBVACtx));
228 if (!vbox->paVBVACtx)
229 {
230 vbox->paVBVACtx = kzalloc( sizeof(struct VBVABUFFERCONTEXT)
231 * vbox->cCrtcs,
232 GFP_KERNEL);
233 if (!vbox->paVBVACtx)
234 return -ENOMEM;
235 }
236 /* Take a command buffer for each screen from the end of usable VRAM. */
237 vbox->vram_size -= vbox->cCrtcs * VBVA_MIN_BUFFER_SIZE;
238 for (i = 0; i < vbox->cCrtcs; ++i)
239 VBoxVBVASetupBufferContext(&vbox->paVBVACtx[i],
240 vbox->vram_size + i * VBVA_MIN_BUFFER_SIZE,
241 VBVA_MIN_BUFFER_SIZE);
242 LogFunc(("vboxvideo: %d: vbox->paVBVACtx=%p, vbox->vram_size=%u\n",
243 __LINE__, vbox->paVBVACtx, (unsigned)vbox->vram_size));
244 return 0;
245}
246
247
248/** Allocation function for the HGSMI heap and data. */
249static DECLCALLBACK(void *) hgsmiEnvAlloc(void *pvEnv, HGSMISIZE cb)
250{
251 NOREF(pvEnv);
252 return kmalloc(cb, GFP_KERNEL);
253}
254
255
256/** Free function for the HGSMI heap and data. */
257static DECLCALLBACK(void) hgsmiEnvFree(void *pvEnv, void *pv)
258{
259 NOREF(pvEnv);
260 kfree(pv);
261}
262
263
264/** Pointers to the HGSMI heap and data manipulation functions. */
265static HGSMIENV g_hgsmiEnv =
266{
267 NULL,
268 hgsmiEnvAlloc,
269 hgsmiEnvFree
270};
271
272
273/** Set up our heaps and data exchange buffers in VRAM before handing the rest
274 * to the memory manager. */
275static int setupAcceleration(struct vbox_private *pVBox, uint32_t *poffBase)
276{
277 uint32_t offBase, offGuestHeap, cbGuestHeap;
278 void *pvGuestHeap;
279
280 VBoxHGSMIGetBaseMappingInfo(pVBox->full_vram_size, &offBase, NULL,
281 &offGuestHeap, &cbGuestHeap, NULL);
282 if (poffBase)
283 *poffBase = offBase;
284 pvGuestHeap = ((uint8_t *)pVBox->vram) + offBase + offGuestHeap;
285 if (RT_FAILURE(VBoxHGSMISetupGuestContext(&pVBox->Ctx, pvGuestHeap,
286 cbGuestHeap,
287 offBase + offGuestHeap,
288 &g_hgsmiEnv)))
289 return -ENOMEM;
290 /* Reduce available VRAM size to reflect the guest heap. */
291 pVBox->vram_size = offBase;
292 /* Linux drm represents monitors as a 32-bit array. */
293 pVBox->cCrtcs = RT_MIN(VBoxHGSMIGetMonitorCount(&pVBox->Ctx), 32);
294 return vbox_vbva_init(pVBox);
295}
296
297
298int vbox_driver_load(struct drm_device *dev, unsigned long flags)
299{
300 struct vbox_private *vbox;
301 int ret = 0;
302 uint32_t offBase;
303
304 LogFunc(("vboxvideo: %d: dev=%p\n", __LINE__, dev));
305 if (!VBoxHGSMIIsSupported())
306 return -ENODEV;
307 vbox = kzalloc(sizeof(struct vbox_private), GFP_KERNEL);
308 if (!vbox)
309 return -ENOMEM;
310
311 dev->dev_private = vbox;
312 vbox->dev = dev;
313
314 spin_lock_init(&vbox->dev_lock);
315 /* I hope this won't interfere with the memory manager. */
316 vbox->vram = pci_iomap(dev->pdev, 0, 0);
317 if (!vbox->vram)
318 {
319 ret = -EIO;
320 goto out_free;
321 }
322 vbox->full_vram_size = VBoxVideoGetVRAMSize();
323 vbox->fAnyX = VBoxVideoAnyWidthAllowed();
324 DRM_INFO("VRAM %08x\n", vbox->full_vram_size);
325
326 ret = setupAcceleration(vbox, &offBase);
327 if (ret)
328 goto out_free;
329
330 ret = vbox_mm_init(vbox);
331 if (ret)
332 goto out_free;
333
334 drm_mode_config_init(dev);
335
336 dev->mode_config.funcs = (void *)&vbox_mode_funcs;
337 dev->mode_config.min_width = 64;
338 dev->mode_config.min_height = 64;
339 dev->mode_config.preferred_depth = 24;
340 dev->mode_config.max_width = VBE_DISPI_MAX_XRES;
341 dev->mode_config.max_height = VBE_DISPI_MAX_YRES;
342
343 ret = vbox_mode_init(dev);
344 if (ret)
345 goto out_free;
346
347 ret = vbox_fbdev_init(dev);
348 if (ret)
349 goto out_free;
350 LogFunc(("vboxvideo: %d: vbox=%p, vbox->vram=%p, vbox->full_vram_size=%u\n",
351 __LINE__, vbox, vbox->vram, (unsigned)vbox->full_vram_size));
352 return 0;
353out_free:
354 if (vbox->vram)
355 pci_iounmap(dev->pdev, vbox->vram);
356 kfree(vbox);
357 dev->dev_private = NULL;
358 LogFunc(("vboxvideo: %d: ret=%d\n", __LINE__, ret));
359 return ret;
360}
361
362int vbox_driver_unload(struct drm_device *dev)
363{
364 struct vbox_private *vbox = dev->dev_private;
365
366 LogFunc(("vboxvideo: %d\n", __LINE__));
367 vbox_mode_fini(dev);
368 vbox_fbdev_fini(dev);
369 drm_mode_config_cleanup(dev);
370
371 disableVBVA(vbox);
372 vbox_mm_fini(vbox);
373 pci_iounmap(dev->pdev, vbox->vram);
374 kfree(vbox);
375 LogFunc(("vboxvideo: %d\n", __LINE__));
376 return 0;
377}
378
379
380#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0)
381static bool drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper
382 *pHelper)
383{
384 bool rc;
385
386 drm_modeset_lock_all(pHelper->dev);
387 rc = drm_fb_helper_restore_fbdev_mode(pHelper);
388 drm_modeset_unlock_all(pHelper->dev);
389 return rc;
390}
391#endif
392
393
394void vbox_driver_lastclose(struct drm_device *pDev)
395{
396 struct vbox_private *pVBox = pDev->dev_private;
397
398 if (pVBox->fbdev)
399 drm_fb_helper_restore_fbdev_mode_unlocked(&pVBox->fbdev->helper);
400}
401
402
403int vbox_gem_create(struct drm_device *dev,
404 u32 size, bool iskernel,
405 struct drm_gem_object **obj)
406{
407 struct vbox_bo *vboxbo;
408 int ret;
409
410 LogFunc(("vboxvideo: %d: dev=%p, size=%u, iskernel=%u\n", __LINE__,
411 dev, (unsigned)size, (unsigned)iskernel));
412 *obj = NULL;
413
414 size = roundup(size, PAGE_SIZE);
415 if (size == 0)
416 return -EINVAL;
417
418 ret = vbox_bo_create(dev, size, 0, 0, &vboxbo);
419 if (ret)
420 {
421 if (ret != -ERESTARTSYS)
422 DRM_ERROR("failed to allocate GEM object\n");
423 return ret;
424 }
425 *obj = &vboxbo->gem;
426 LogFunc(("vboxvideo: %d: obj=%p\n", __LINE__, obj));
427 return 0;
428}
429
430int vbox_dumb_create(struct drm_file *file,
431 struct drm_device *dev,
432 struct drm_mode_create_dumb *args)
433{
434 int ret;
435 struct drm_gem_object *gobj;
436 u32 handle;
437
438 LogFunc(("vboxvideo: %d: args->width=%u, args->height=%u, args->bpp=%u\n",
439 __LINE__, (unsigned)args->width, (unsigned)args->height,
440 (unsigned)args->bpp));
441 args->pitch = args->width * ((args->bpp + 7) / 8);
442 args->size = args->pitch * args->height;
443
444 ret = vbox_gem_create(dev, args->size, false,
445 &gobj);
446 if (ret)
447 return ret;
448
449 ret = drm_gem_handle_create(file, gobj, &handle);
450 drm_gem_object_unreference_unlocked(gobj);
451 if (ret)
452 return ret;
453
454 args->handle = handle;
455 LogFunc(("vboxvideo: %d: args->handle=%u\n", __LINE__,
456 (unsigned)args->handle));
457 return 0;
458}
459
460int vbox_dumb_destroy(struct drm_file *file,
461 struct drm_device *dev,
462 uint32_t handle)
463{
464 LogFunc(("vboxvideo: %d: dev=%p, handle=%u\n", __LINE__, dev,
465 (unsigned)handle));
466 return drm_gem_handle_delete(file, handle);
467}
468
469void vbox_bo_unref(struct vbox_bo **bo)
470{
471 struct ttm_buffer_object *tbo;
472
473 if ((*bo) == NULL)
474 return;
475
476 LogFunc(("vboxvideo: %d: bo=%p\n", __LINE__, bo));
477 tbo = &((*bo)->bo);
478 ttm_bo_unref(&tbo);
479 if (tbo == NULL)
480 *bo = NULL;
481
482}
483void vbox_gem_free_object(struct drm_gem_object *obj)
484{
485 struct vbox_bo *vbox_bo = gem_to_vbox_bo(obj);
486
487 LogFunc(("vboxvideo: %d: vbox_bo=%p\n", __LINE__, vbox_bo));
488 if (!vbox_bo)
489 return;
490 vbox_bo_unref(&vbox_bo);
491}
492
493
494static inline u64 vbox_bo_mmap_offset(struct vbox_bo *bo)
495{
496#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 12, 0)
497 return bo->bo.addr_space_offset;
498#else
499 return drm_vma_node_offset_addr(&bo->bo.vma_node);
500#endif
501}
502int
503vbox_dumb_mmap_offset(struct drm_file *file,
504 struct drm_device *dev,
505 uint32_t handle,
506 uint64_t *offset)
507{
508 struct drm_gem_object *obj;
509 int ret;
510 struct vbox_bo *bo = NULL;
511
512 LogFunc(("vboxvideo: %d: dev=%p, handle=%u\n", __LINE__,
513 dev, (unsigned)handle));
514 mutex_lock(&dev->struct_mutex);
515 obj = drm_gem_object_lookup(dev, file, handle);
516 if (obj == NULL)
517 {
518 ret = -ENOENT;
519 goto out_unlock;
520 }
521
522 bo = gem_to_vbox_bo(obj);
523 *offset = vbox_bo_mmap_offset(bo);
524
525 drm_gem_object_unreference(obj);
526 ret = 0;
527out_unlock:
528 mutex_unlock(&dev->struct_mutex);
529 LogFunc(("vboxvideo: %d: bo=%p, offset=%llu\n", __LINE__,
530 bo, (unsigned long long)*offset));
531 return ret;
532
533}
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