VirtualBox

source: vbox/trunk/src/VBox/Additions/linux/drm/vbox_mode.c@ 74172

Last change on this file since 74172 was 74172, checked in by vboxsync, 7 years ago

Additions/drm: do not unpin an old frame buffer before the new one is in place.
bugref:9240: Additions/linux/vboxvideo: GPU memory management bug
We have seen sporadic cases of [unpin bad] messages in guest dmesg, and were
able to connect some of them to unpinning an old frame buffer when pinning a
new one before the new pin had succeeded. If the new frame buffer was too
big to be pinned in the available video memory the pin operation failed, but
the old frame buffer was already unpinned. As this did not get tracked in
the error path a subsequent unpin operation on it failed with the message
above.
This change fixes that by only unpinning the old frame buffer after the new
one has been successfully pinned. While this potentially increases memory
pressure further, it avoids the case where we unpin the old one, fail to pin
the new one and fail to re-pin the old one, as well as the case of the old one
getting re-pinned at a different address.
Thank you Hans de Goede for review and comments/suggestions.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 26.1 KB
Line 
1/* $Id: vbox_mode.c 74172 2018-09-10 08:17:28Z 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_mode.c
9 * Copyright 2012 Red Hat Inc.
10 * Parts based on xf86-video-ast
11 * Copyright (c) 2005 ASPEED Technology Inc.
12 *
13 * Permission is hereby granted, free of charge, to any person obtaining a
14 * copy of this software and associated documentation files (the
15 * "Software"), to deal in the Software without restriction, including
16 * without limitation the rights to use, copy, modify, merge, publish,
17 * distribute, sub license, and/or sell copies of the Software, and to
18 * permit persons to whom the Software is furnished to do so, subject to
19 * the following conditions:
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
24 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
25 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
26 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
27 * USE OR OTHER DEALINGS IN THE SOFTWARE.
28 *
29 * The above copyright notice and this permission notice (including the
30 * next paragraph) shall be included in all copies or substantial portions
31 * of the Software.
32 *
33 */
34/*
35 * Authors: Dave Airlie <[email protected]>
36 * Michael Thayer <[email protected],
37 * Hans de Goede <[email protected]>
38 */
39#include "vbox_drv.h"
40#include <linux/export.h>
41#include <drm/drm_crtc_helper.h>
42#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) || defined(RHEL_73)
43#include <drm/drm_plane_helper.h>
44#endif
45
46#include "VBoxVideo.h"
47#include "hgsmi_channels.h"
48
49static int vbox_cursor_set2(struct drm_crtc *crtc, struct drm_file *file_priv,
50 u32 handle, u32 width, u32 height,
51 s32 hot_x, s32 hot_y);
52static int vbox_cursor_move(struct drm_crtc *crtc, int x, int y);
53
54/**
55 * Set a graphics mode. Poke any required values into registers, do an HGSMI
56 * mode set and tell the host we support advanced graphics functions.
57 */
58static void vbox_do_modeset(struct drm_crtc *crtc,
59 const struct drm_display_mode *mode)
60{
61 struct vbox_crtc *vbox_crtc = to_vbox_crtc(crtc);
62 struct vbox_private *vbox;
63 int width, height, bpp, pitch;
64 unsigned int crtc_id;
65 u16 flags;
66 s32 x_offset, y_offset;
67
68 vbox = crtc->dev->dev_private;
69 width = mode->hdisplay ? mode->hdisplay : 640;
70 height = mode->vdisplay ? mode->vdisplay : 480;
71 crtc_id = vbox_crtc->crtc_id;
72#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) || defined(RHEL_75)
73 bpp = crtc->enabled ? CRTC_FB(crtc)->format->cpp[0] * 8 : 32;
74 pitch = crtc->enabled ? CRTC_FB(crtc)->pitches[0] : width * bpp / 8;
75#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0)
76 bpp = crtc->enabled ? CRTC_FB(crtc)->bits_per_pixel : 32;
77 pitch = crtc->enabled ? CRTC_FB(crtc)->pitches[0] : width * bpp / 8;
78#else
79 bpp = crtc->enabled ? CRTC_FB(crtc)->bits_per_pixel : 32;
80 pitch = crtc->enabled ? CRTC_FB(crtc)->pitch : width * bpp / 8;
81#endif
82 x_offset = vbox->single_framebuffer ? crtc->x : vbox_crtc->x_hint;
83 y_offset = vbox->single_framebuffer ? crtc->y : vbox_crtc->y_hint;
84
85 /*
86 * This is the old way of setting graphics modes. It assumed one screen
87 * and a frame-buffer at the start of video RAM. On older versions of
88 * VirtualBox, certain parts of the code still assume that the first
89 * screen is programmed this way, so try to fake it.
90 */
91 if (vbox_crtc->crtc_id == 0 && crtc->enabled &&
92 vbox_crtc->fb_offset / pitch < 0xffff - crtc->y &&
93 vbox_crtc->fb_offset % (bpp / 8) == 0)
94 VBoxVideoSetModeRegisters(
95 width, height, pitch * 8 / bpp,
96#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) || defined(RHEL_75)
97 CRTC_FB(crtc)->format->cpp[0] * 8,
98#else
99 CRTC_FB(crtc)->bits_per_pixel,
100#endif
101 0,
102 vbox_crtc->fb_offset % pitch / bpp * 8 + crtc->x,
103 vbox_crtc->fb_offset / pitch + crtc->y);
104
105 flags = VBVA_SCREEN_F_ACTIVE;
106 flags |= (crtc->enabled && !vbox_crtc->blanked) ?
107 0 : VBVA_SCREEN_F_BLANK;
108 flags |= vbox_crtc->disconnected ? VBVA_SCREEN_F_DISABLED : 0;
109 VBoxHGSMIProcessDisplayInfo(vbox->guest_pool, vbox_crtc->crtc_id,
110 x_offset, y_offset,
111 crtc->x * bpp / 8 + crtc->y * pitch,
112 pitch, width, height,
113 vbox_crtc->blanked ? 0 : bpp, flags);
114}
115
116static int vbox_set_view(struct drm_crtc *crtc)
117{
118 struct vbox_crtc *vbox_crtc = to_vbox_crtc(crtc);
119 struct vbox_private *vbox = crtc->dev->dev_private;
120 void *p;
121
122 /*
123 * Tell the host about the view. This design originally targeted the
124 * Windows XP driver architecture and assumed that each screen would
125 * have a dedicated frame buffer with the command buffer following it,
126 * the whole being a "view". The host works out which screen a command
127 * buffer belongs to by checking whether it is in the first view, then
128 * whether it is in the second and so on. The first match wins. We
129 * cheat around this by making the first view be the managed memory
130 * plus the first command buffer, the second the same plus the second
131 * buffer and so on.
132 */
133 p = VBoxHGSMIBufferAlloc(vbox->guest_pool, sizeof(VBVAINFOVIEW),
134 HGSMI_CH_VBVA, VBVA_INFO_VIEW);
135 if (p) {
136 VBVAINFOVIEW *pInfo = (VBVAINFOVIEW *) p;
137
138 pInfo->u32ViewIndex = vbox_crtc->crtc_id;
139 pInfo->u32ViewOffset = vbox_crtc->fb_offset;
140 pInfo->u32ViewSize =
141 vbox->available_vram_size - vbox_crtc->fb_offset +
142 vbox_crtc->crtc_id * VBVA_MIN_BUFFER_SIZE;
143 pInfo->u32MaxScreenSize =
144 vbox->available_vram_size - vbox_crtc->fb_offset;
145 VBoxHGSMIBufferSubmit(vbox->guest_pool, p);
146 VBoxHGSMIBufferFree(vbox->guest_pool, p);
147 } else {
148 return -ENOMEM;
149 }
150
151 return 0;
152}
153
154static void vbox_crtc_dpms(struct drm_crtc *crtc, int mode)
155{
156 struct vbox_crtc *vbox_crtc = to_vbox_crtc(crtc);
157 struct vbox_private *vbox = crtc->dev->dev_private;
158
159 switch (mode) {
160 case DRM_MODE_DPMS_ON:
161 vbox_crtc->blanked = false;
162 break;
163 case DRM_MODE_DPMS_STANDBY:
164 case DRM_MODE_DPMS_SUSPEND:
165 case DRM_MODE_DPMS_OFF:
166 vbox_crtc->blanked = true;
167 break;
168 }
169
170 mutex_lock(&vbox->hw_mutex);
171 vbox_do_modeset(crtc, &crtc->hwmode);
172 mutex_unlock(&vbox->hw_mutex);
173}
174
175static bool vbox_crtc_mode_fixup(struct drm_crtc *crtc,
176 const struct drm_display_mode *mode,
177 struct drm_display_mode *adjusted_mode)
178{
179 return true;
180}
181
182/*
183 * Try to map the layout of virtual screens to the range of the input device.
184 * Return true if we need to re-set the crtc modes due to screen offset
185 * changes.
186 */
187static bool vbox_set_up_input_mapping(struct vbox_private *vbox)
188{
189 struct drm_crtc *crtci;
190 struct drm_connector *connectori;
191 struct drm_framebuffer *fb1 = NULL;
192 bool single_framebuffer = true;
193 bool old_single_framebuffer = vbox->single_framebuffer;
194 u16 width = 0, height = 0;
195
196 /*
197 * Are we using an X.Org-style single large frame-buffer for all crtcs?
198 * If so then screen layout can be deduced from the crtc offsets.
199 * Same fall-back if this is the fbdev frame-buffer.
200 */
201 list_for_each_entry(crtci, &vbox->dev->mode_config.crtc_list, head) {
202 if (!fb1) {
203 fb1 = CRTC_FB(crtci);
204 if (to_vbox_framebuffer(fb1) == &vbox->fbdev->afb)
205 break;
206 } else if (CRTC_FB(crtci) && fb1 != CRTC_FB(crtci)) {
207 single_framebuffer = false;
208 }
209 }
210 if (single_framebuffer) {
211 list_for_each_entry(crtci, &vbox->dev->mode_config.crtc_list,
212 head) {
213 if (to_vbox_crtc(crtci)->crtc_id == 0) {
214 vbox->single_framebuffer = true;
215 vbox->input_mapping_width =
216 CRTC_FB(crtci)->width;
217 vbox->input_mapping_height =
218 CRTC_FB(crtci)->height;
219 return old_single_framebuffer !=
220 vbox->single_framebuffer;
221 }
222 }
223 }
224 /* Otherwise calculate the total span of all screens. */
225 list_for_each_entry(connectori, &vbox->dev->mode_config.connector_list,
226 head) {
227 struct vbox_connector *vbox_connector =
228 to_vbox_connector(connectori);
229 struct vbox_crtc *vbox_crtc = vbox_connector->vbox_crtc;
230
231 width = max_t(u16, width, vbox_crtc->x_hint +
232 vbox_connector->mode_hint.width);
233 height = max_t(u16, height, vbox_crtc->y_hint +
234 vbox_connector->mode_hint.height);
235 }
236
237 vbox->single_framebuffer = false;
238 vbox->input_mapping_width = width;
239 vbox->input_mapping_height = height;
240
241 return old_single_framebuffer != vbox->single_framebuffer;
242}
243
244static int vbox_crtc_set_base(struct drm_crtc *crtc,
245 struct drm_framebuffer *old_fb, int x, int y)
246{
247 struct vbox_private *vbox = crtc->dev->dev_private;
248 struct vbox_crtc *vbox_crtc = to_vbox_crtc(crtc);
249 struct drm_gem_object *obj;
250 struct vbox_framebuffer *vbox_fb;
251 struct vbox_bo *bo;
252 int ret;
253 u64 gpu_addr;
254
255 vbox_fb = to_vbox_framebuffer(CRTC_FB(crtc));
256 obj = vbox_fb->obj;
257 bo = gem_to_vbox_bo(obj);
258
259 ret = vbox_bo_reserve(bo, false);
260 if (ret)
261 return ret;
262
263 ret = vbox_bo_pin(bo, TTM_PL_FLAG_VRAM, &gpu_addr);
264 vbox_bo_unreserve(bo);
265 if (ret)
266 return ret;
267
268 /* Unpin the previous fb. Do this after the new one has been pinned rather
269 * than before and re-pinning it on failure in case that fails too. */
270 if (old_fb) {
271 vbox_fb = to_vbox_framebuffer(old_fb);
272 obj = vbox_fb->obj;
273 bo = gem_to_vbox_bo(obj);
274 ret = vbox_bo_reserve(bo, false);
275 /* This should never fail, as no one else should be accessing it and we
276 * should be running under the modeset locks. */
277 if (!ret) {
278 vbox_bo_unpin(bo);
279 vbox_bo_unreserve(bo);
280 }
281 }
282
283 vbox_crtc->fb_offset = gpu_addr;
284 if (vbox_set_up_input_mapping(vbox)) {
285 struct drm_crtc *crtci;
286
287 list_for_each_entry(crtci, &vbox->dev->mode_config.crtc_list,
288 head) {
289 vbox_set_view(crtc);
290 vbox_do_modeset(crtci, &crtci->mode);
291 }
292 }
293
294 return 0;
295}
296
297static int vbox_crtc_mode_set(struct drm_crtc *crtc,
298 struct drm_display_mode *mode,
299 struct drm_display_mode *adjusted_mode,
300 int x, int y, struct drm_framebuffer *old_fb)
301{
302 struct vbox_private *vbox = crtc->dev->dev_private;
303 int rc = vbox_crtc_set_base(crtc, old_fb, x, y);
304 if (rc)
305 return rc;
306 mutex_lock(&vbox->hw_mutex);
307 rc = vbox_set_view(crtc);
308 if (!rc)
309 vbox_do_modeset(crtc, mode);
310 VBoxHGSMIUpdateInputMapping(vbox->guest_pool, 0, 0,
311 vbox->input_mapping_width,
312 vbox->input_mapping_height);
313 mutex_unlock(&vbox->hw_mutex);
314
315 return rc;
316}
317
318static void vbox_crtc_disable(struct drm_crtc *crtc)
319{
320}
321
322static void vbox_crtc_prepare(struct drm_crtc *crtc)
323{
324}
325
326static void vbox_crtc_commit(struct drm_crtc *crtc)
327{
328}
329
330static const struct drm_crtc_helper_funcs vbox_crtc_helper_funcs = {
331 .dpms = vbox_crtc_dpms,
332 .mode_fixup = vbox_crtc_mode_fixup,
333 .mode_set = vbox_crtc_mode_set,
334 .disable = vbox_crtc_disable,
335 .prepare = vbox_crtc_prepare,
336 .commit = vbox_crtc_commit,
337};
338
339static void vbox_crtc_reset(struct drm_crtc *crtc)
340{
341}
342
343static void vbox_crtc_destroy(struct drm_crtc *crtc)
344{
345 drm_crtc_cleanup(crtc);
346 kfree(crtc);
347}
348
349static const struct drm_crtc_funcs vbox_crtc_funcs = {
350 .cursor_move = vbox_cursor_move,
351 .cursor_set2 = vbox_cursor_set2,
352 .reset = vbox_crtc_reset,
353 .set_config = drm_crtc_helper_set_config,
354 /* .gamma_set = vbox_crtc_gamma_set, */
355 .destroy = vbox_crtc_destroy,
356};
357
358static struct vbox_crtc *vbox_crtc_init(struct drm_device *dev, unsigned int i)
359{
360 struct vbox_crtc *vbox_crtc;
361
362 vbox_crtc = kzalloc(sizeof(*vbox_crtc), GFP_KERNEL);
363 if (!vbox_crtc)
364 return NULL;
365
366 vbox_crtc->crtc_id = i;
367
368 drm_crtc_init(dev, &vbox_crtc->base, &vbox_crtc_funcs);
369 drm_mode_crtc_set_gamma_size(&vbox_crtc->base, 256);
370 drm_crtc_helper_add(&vbox_crtc->base, &vbox_crtc_helper_funcs);
371
372 return vbox_crtc;
373}
374
375static void vbox_encoder_destroy(struct drm_encoder *encoder)
376{
377 drm_encoder_cleanup(encoder);
378 kfree(encoder);
379}
380
381#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) && !defined(RHEL_73)
382static struct drm_encoder *drm_encoder_find(struct drm_device *dev, u32 id)
383{
384 struct drm_mode_object *mo;
385
386 mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_ENCODER);
387 return mo ? obj_to_encoder(mo) : NULL;
388}
389#endif
390
391static struct drm_encoder *vbox_best_single_encoder(struct drm_connector
392 *connector)
393{
394 int enc_id = connector->encoder_ids[0];
395
396 /* pick the encoder ids */
397 if (enc_id)
398#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
399 return drm_encoder_find(connector->dev, NULL, enc_id);
400#else
401 return drm_encoder_find(connector->dev, enc_id);
402#endif
403
404 return NULL;
405}
406
407static const struct drm_encoder_funcs vbox_enc_funcs = {
408 .destroy = vbox_encoder_destroy,
409};
410
411static void vbox_encoder_dpms(struct drm_encoder *encoder, int mode)
412{
413}
414
415static bool vbox_mode_fixup(struct drm_encoder *encoder,
416 const struct drm_display_mode *mode,
417 struct drm_display_mode *adjusted_mode)
418{
419 return true;
420}
421
422static void vbox_encoder_mode_set(struct drm_encoder *encoder,
423 struct drm_display_mode *mode,
424 struct drm_display_mode *adjusted_mode)
425{
426}
427
428static void vbox_encoder_prepare(struct drm_encoder *encoder)
429{
430}
431
432static void vbox_encoder_commit(struct drm_encoder *encoder)
433{
434}
435
436static const struct drm_encoder_helper_funcs vbox_enc_helper_funcs = {
437 .dpms = vbox_encoder_dpms,
438 .mode_fixup = vbox_mode_fixup,
439 .prepare = vbox_encoder_prepare,
440 .commit = vbox_encoder_commit,
441 .mode_set = vbox_encoder_mode_set,
442};
443
444static struct drm_encoder *vbox_encoder_init(struct drm_device *dev,
445 unsigned int i)
446{
447 struct vbox_encoder *vbox_encoder;
448
449 vbox_encoder = kzalloc(sizeof(*vbox_encoder), GFP_KERNEL);
450 if (!vbox_encoder)
451 return NULL;
452
453 drm_encoder_init(dev, &vbox_encoder->base, &vbox_enc_funcs,
454 DRM_MODE_ENCODER_DAC
455#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) || defined(RHEL_73)
456 , NULL
457#endif
458 );
459 drm_encoder_helper_add(&vbox_encoder->base, &vbox_enc_helper_funcs);
460
461 vbox_encoder->base.possible_crtcs = 1 << i;
462 return &vbox_encoder->base;
463}
464
465/**
466 * Generate EDID data with a mode-unique serial number for the virtual
467 * monitor to try to persuade Unity that different modes correspond to
468 * different monitors and it should not try to force the same resolution on
469 * them.
470 */
471static void vbox_set_edid(struct drm_connector *connector, int width,
472 int height)
473{
474 enum { EDID_SIZE = 128 };
475 unsigned char edid[EDID_SIZE] = {
476 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, /* header */
477 0x58, 0x58, /* manufacturer (VBX) */
478 0x00, 0x00, /* product code */
479 0x00, 0x00, 0x00, 0x00, /* serial number goes here */
480 0x01, /* week of manufacture */
481 0x00, /* year of manufacture */
482 0x01, 0x03, /* EDID version */
483 0x80, /* capabilities - digital */
484 0x00, /* horiz. res in cm, zero for projectors */
485 0x00, /* vert. res in cm */
486 0x78, /* display gamma (120 == 2.2). */
487 0xEE, /* features (standby, suspend, off, RGB, std */
488 /* colour space, preferred timing mode) */
489 0xEE, 0x91, 0xA3, 0x54, 0x4C, 0x99, 0x26, 0x0F, 0x50, 0x54,
490 /* chromaticity for standard colour space. */
491 0x00, 0x00, 0x00, /* no default timings */
492 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
493 0x01, 0x01,
494 0x01, 0x01, 0x01, 0x01, /* no standard timings */
495 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x02, 0x02,
496 0x02, 0x02,
497 /* descriptor block 1 goes below */
498 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
499 /* descriptor block 2, monitor ranges */
500 0x00, 0x00, 0x00, 0xFD, 0x00,
501 0x00, 0xC8, 0x00, 0xC8, 0x64, 0x00, 0x0A, 0x20, 0x20, 0x20,
502 0x20, 0x20,
503 /* 0-200Hz vertical, 0-200KHz horizontal, 1000MHz pixel clock */
504 0x20,
505 /* descriptor block 3, monitor name */
506 0x00, 0x00, 0x00, 0xFC, 0x00,
507 'V', 'B', 'O', 'X', ' ', 'm', 'o', 'n', 'i', 't', 'o', 'r',
508 '\n',
509 /* descriptor block 4: dummy data */
510 0x00, 0x00, 0x00, 0x10, 0x00,
511 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20,
512 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
513 0x20,
514 0x00, /* number of extensions */
515 0x00 /* checksum goes here */
516 };
517 int clock = (width + 6) * (height + 6) * 60 / 10000;
518 unsigned int i, sum = 0;
519
520 edid[12] = width & 0xff;
521 edid[13] = width >> 8;
522 edid[14] = height & 0xff;
523 edid[15] = height >> 8;
524 edid[54] = clock & 0xff;
525 edid[55] = clock >> 8;
526 edid[56] = width & 0xff;
527 edid[58] = (width >> 4) & 0xf0;
528 edid[59] = height & 0xff;
529 edid[61] = (height >> 4) & 0xf0;
530 for (i = 0; i < EDID_SIZE - 1; ++i)
531 sum += edid[i];
532 edid[EDID_SIZE - 1] = (0x100 - (sum & 0xFF)) & 0xFF;
533 drm_mode_connector_update_edid_property(connector, (struct edid *)edid);
534}
535
536static int vbox_get_modes(struct drm_connector *connector)
537{
538 struct vbox_connector *vbox_connector = NULL;
539 struct drm_display_mode *mode = NULL;
540 struct vbox_private *vbox = NULL;
541 unsigned int num_modes = 0;
542 int preferred_width, preferred_height;
543
544 vbox_connector = to_vbox_connector(connector);
545 vbox = connector->dev->dev_private;
546 /*
547 * Heuristic: we do not want to tell the host that we support dynamic
548 * resizing unless we feel confident that the user space client using
549 * the video driver can handle hot-plug events. So the first time modes
550 * are queried after a "master" switch we tell the host that we do not,
551 * and immediately after we send the client a hot-plug notification as
552 * a test to see if they will respond and query again.
553 * That is also the reason why capabilities are reported to the host at
554 * this place in the code rather than elsewhere.
555 * We need to report the flags location before reporting the IRQ
556 * capability.
557 */
558 VBoxHGSMIReportFlagsLocation(vbox->guest_pool, GUEST_HEAP_OFFSET(vbox) +
559 HOST_FLAGS_OFFSET);
560 if (vbox_connector->vbox_crtc->crtc_id == 0)
561 vbox_report_caps(vbox);
562 if (!vbox->initial_mode_queried) {
563 if (vbox_connector->vbox_crtc->crtc_id == 0) {
564 vbox->initial_mode_queried = true;
565 vbox_report_hotplug(vbox);
566 }
567 return drm_add_modes_noedid(connector, 800, 600);
568 }
569 num_modes = drm_add_modes_noedid(connector, 2560, 1600);
570 preferred_width = vbox_connector->mode_hint.width ?
571 vbox_connector->mode_hint.width : 1024;
572 preferred_height = vbox_connector->mode_hint.height ?
573 vbox_connector->mode_hint.height : 768;
574 mode = drm_cvt_mode(connector->dev, preferred_width, preferred_height,
575 60, false, false, false);
576 if (mode) {
577 mode->type |= DRM_MODE_TYPE_PREFERRED;
578 drm_mode_probed_add(connector, mode);
579 ++num_modes;
580 }
581 vbox_set_edid(connector, preferred_width, preferred_height);
582
583#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) || defined(RHEL_73)
584 if (vbox_connector->vbox_crtc->x_hint != -1)
585 drm_object_property_set_value(&connector->base,
586 vbox->dev->mode_config.suggested_x_property,
587 vbox_connector->vbox_crtc->x_hint);
588 else
589 drm_object_property_set_value(&connector->base,
590 vbox->dev->mode_config.suggested_x_property, 0);
591
592 if (vbox_connector->vbox_crtc->y_hint != -1)
593 drm_object_property_set_value(&connector->base,
594 vbox->dev->mode_config.suggested_y_property,
595 vbox_connector->vbox_crtc->y_hint);
596 else
597 drm_object_property_set_value(&connector->base,
598 vbox->dev->mode_config.suggested_y_property, 0);
599#endif
600
601 return num_modes;
602}
603
604static int vbox_mode_valid(struct drm_connector *connector,
605 struct drm_display_mode *mode)
606{
607 return MODE_OK;
608}
609
610static void vbox_connector_destroy(struct drm_connector *connector)
611{
612 struct vbox_connector *vbox_connector = NULL;
613
614 vbox_connector = to_vbox_connector(connector);
615#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0) && !defined(RHEL_73)
616 drm_sysfs_connector_remove(connector);
617#else
618 drm_connector_unregister(connector);
619#endif
620 drm_connector_cleanup(connector);
621 kfree(connector);
622}
623
624static enum drm_connector_status
625vbox_connector_detect(struct drm_connector *connector, bool force)
626{
627 struct vbox_connector *vbox_connector = NULL;
628
629 (void)force;
630 vbox_connector = to_vbox_connector(connector);
631
632 return vbox_connector->mode_hint.disconnected ?
633 connector_status_disconnected : connector_status_connected;
634}
635
636static int vbox_fill_modes(struct drm_connector *connector, u32 max_x,
637 u32 max_y)
638{
639 struct vbox_connector *vbox_connector;
640 struct drm_device *dev;
641 struct drm_display_mode *mode, *iterator;
642
643 vbox_connector = to_vbox_connector(connector);
644 dev = vbox_connector->base.dev;
645 list_for_each_entry_safe(mode, iterator, &connector->modes, head) {
646 list_del(&mode->head);
647 drm_mode_destroy(dev, mode);
648 }
649
650 return drm_helper_probe_single_connector_modes(connector, max_x, max_y);
651}
652
653static const struct drm_connector_helper_funcs vbox_connector_helper_funcs = {
654 .mode_valid = vbox_mode_valid,
655 .get_modes = vbox_get_modes,
656 .best_encoder = vbox_best_single_encoder,
657};
658
659static const struct drm_connector_funcs vbox_connector_funcs = {
660 .dpms = drm_helper_connector_dpms,
661 .detect = vbox_connector_detect,
662 .fill_modes = vbox_fill_modes,
663 .destroy = vbox_connector_destroy,
664};
665
666static int vbox_connector_init(struct drm_device *dev,
667 struct vbox_crtc *vbox_crtc,
668 struct drm_encoder *encoder)
669{
670 struct vbox_connector *vbox_connector;
671 struct drm_connector *connector;
672
673 vbox_connector = kzalloc(sizeof(*vbox_connector), GFP_KERNEL);
674 if (!vbox_connector)
675 return -ENOMEM;
676
677 connector = &vbox_connector->base;
678 vbox_connector->vbox_crtc = vbox_crtc;
679
680 drm_connector_init(dev, connector, &vbox_connector_funcs,
681 DRM_MODE_CONNECTOR_VGA);
682 drm_connector_helper_add(connector, &vbox_connector_helper_funcs);
683
684 connector->interlace_allowed = 0;
685 connector->doublescan_allowed = 0;
686
687#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) || defined(RHEL_73)
688 drm_mode_create_suggested_offset_properties(dev);
689 drm_object_attach_property(&connector->base,
690 dev->mode_config.suggested_x_property, 0);
691 drm_object_attach_property(&connector->base,
692 dev->mode_config.suggested_y_property, 0);
693#endif
694#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0) && !defined(RHEL_73)
695 drm_sysfs_connector_add(connector);
696#else
697 drm_connector_register(connector);
698#endif
699
700 drm_mode_connector_attach_encoder(connector, encoder);
701
702 return 0;
703}
704
705int vbox_mode_init(struct drm_device *dev)
706{
707 struct vbox_private *vbox = dev->dev_private;
708 struct drm_encoder *encoder;
709 struct vbox_crtc *vbox_crtc;
710 unsigned int i;
711
712 /* vbox_cursor_init(dev); */
713 for (i = 0; i < vbox->num_crtcs; ++i) {
714 vbox_crtc = vbox_crtc_init(dev, i);
715 if (!vbox_crtc)
716 return -ENOMEM;
717 encoder = vbox_encoder_init(dev, i);
718 if (!encoder)
719 return -ENOMEM;
720 vbox_connector_init(dev, vbox_crtc, encoder);
721 }
722
723 return 0;
724}
725
726void vbox_mode_fini(struct drm_device *dev)
727{
728 /* vbox_cursor_fini(dev); */
729}
730
731/**
732 * Copy the ARGB image and generate the mask, which is needed in case the host
733 * does not support ARGB cursors. The mask is a 1BPP bitmap with the bit set
734 * if the corresponding alpha value in the ARGB image is greater than 0xF0.
735 */
736static void copy_cursor_image(u8 *src, u8 *dst, u32 width, u32 height,
737 size_t mask_size)
738{
739 size_t line_size = (width + 7) / 8;
740 u32 i, j;
741
742 memcpy(dst + mask_size, src, width * height * 4);
743 for (i = 0; i < height; ++i)
744 for (j = 0; j < width; ++j)
745 if (((u32 *)src)[i * width + j] > 0xf0000000)
746 dst[i * line_size + j / 8] |= (0x80 >> (j % 8));
747}
748
749static int vbox_cursor_set2(struct drm_crtc *crtc, struct drm_file *file_priv,
750 u32 handle, u32 width, u32 height,
751 s32 hot_x, s32 hot_y)
752{
753 struct vbox_private *vbox = crtc->dev->dev_private;
754 struct vbox_crtc *vbox_crtc = to_vbox_crtc(crtc);
755 struct drm_gem_object *obj;
756 struct vbox_bo *bo;
757 int ret, rc;
758 struct ttm_bo_kmap_obj uobj_map;
759 u8 *src;
760 u8 *dst = NULL;
761 u32 caps = 0;
762 size_t data_size, mask_size;
763 bool src_isiomem;
764
765 if (!handle) {
766 bool cursor_enabled = false;
767 struct drm_crtc *crtci;
768
769 /* Hide cursor. */
770 vbox_crtc->cursor_enabled = false;
771 list_for_each_entry(crtci, &vbox->dev->mode_config.crtc_list,
772 head)
773 if (to_vbox_crtc(crtci)->cursor_enabled)
774 cursor_enabled = true;
775
776 if (!cursor_enabled)
777 VBoxHGSMIUpdatePointerShape(vbox->guest_pool, 0, 0, 0,
778 0, 0, NULL, 0);
779 return 0;
780 }
781 vbox_crtc->cursor_enabled = true;
782 if (width > VBOX_MAX_CURSOR_WIDTH || height > VBOX_MAX_CURSOR_HEIGHT ||
783 width == 0 || height == 0)
784 return -EINVAL;
785 rc = VBoxQueryConfHGSMI(vbox->guest_pool,
786 VBOX_VBVA_CONF32_CURSOR_CAPABILITIES, &caps);
787 ret = rc == VINF_SUCCESS ? 0 : rc == VERR_NO_MEMORY ? -ENOMEM : -EINVAL;
788 if (ret)
789 return ret;
790
791 if (!(caps & VBOX_VBVA_CURSOR_CAPABILITY_HARDWARE))
792 /*
793 * -EINVAL means cursor_set2() not supported, -EAGAIN means
794 * retry at once.
795 */
796 return -EBUSY;
797
798#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) || defined(RHEL_74)
799 obj = drm_gem_object_lookup(file_priv, handle);
800#else
801 obj = drm_gem_object_lookup(crtc->dev, file_priv, handle);
802#endif
803 if (obj) {
804 bo = gem_to_vbox_bo(obj);
805 ret = vbox_bo_reserve(bo, false);
806 if (!ret) {
807 /*
808 * The mask must be calculated based on the alpha
809 * channel, one bit per ARGB word, and must be 32-bit
810 * padded.
811 */
812 mask_size = ((width + 7) / 8 * height + 3) & ~3;
813 data_size = width * height * 4 + mask_size;
814 vbox->cursor_hot_x = hot_x;
815 vbox->cursor_hot_y = hot_y;
816 vbox->cursor_width = width;
817 vbox->cursor_height = height;
818 vbox->cursor_data_size = data_size;
819 dst = vbox->cursor_data;
820 ret =
821 ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages,
822 &uobj_map);
823 if (!ret) {
824 src =
825 ttm_kmap_obj_virtual(&uobj_map,
826 &src_isiomem);
827 if (!src_isiomem) {
828 u32 flags =
829 VBOX_MOUSE_POINTER_VISIBLE |
830 VBOX_MOUSE_POINTER_SHAPE |
831 VBOX_MOUSE_POINTER_ALPHA;
832 copy_cursor_image(src, dst, width,
833 height, mask_size);
834 rc = VBoxHGSMIUpdatePointerShape(
835 vbox->guest_pool, flags,
836 vbox->cursor_hot_x,
837 vbox->cursor_hot_y,
838 width, height, dst, data_size);
839 ret =
840 rc == VINF_SUCCESS ? 0 : rc ==
841 VERR_NO_MEMORY ? -ENOMEM : rc ==
842 VERR_NOT_SUPPORTED ? -EBUSY :
843 -EINVAL;
844 } else {
845 DRM_ERROR("src cursor bo should be in main memory\n");
846 }
847 ttm_bo_kunmap(&uobj_map);
848 } else {
849 vbox->cursor_data_size = 0;
850 }
851 vbox_bo_unreserve(bo);
852 }
853 drm_gem_object_unreference_unlocked(obj);
854 } else {
855 DRM_ERROR("Cannot find cursor object %x for crtc\n", handle);
856 ret = -ENOENT;
857 }
858
859 return ret;
860}
861
862static int vbox_cursor_move(struct drm_crtc *crtc, int x, int y)
863{
864 struct vbox_private *vbox = crtc->dev->dev_private;
865 s32 crtc_x =
866 vbox->single_framebuffer ? crtc->x : to_vbox_crtc(crtc)->x_hint;
867 s32 crtc_y =
868 vbox->single_framebuffer ? crtc->y : to_vbox_crtc(crtc)->y_hint;
869 int rc;
870
871 x += vbox->cursor_hot_x;
872 y += vbox->cursor_hot_y;
873 if (x + crtc_x < 0 || y + crtc_y < 0 ||
874 x + crtc_x >= vbox->input_mapping_width ||
875 y + crtc_y >= vbox->input_mapping_width ||
876 vbox->cursor_data_size == 0)
877 return 0;
878 rc = VBoxHGSMICursorPosition(vbox->guest_pool, true, x + crtc_x,
879 y + crtc_y, NULL, NULL);
880 return rc == VINF_SUCCESS ? 0 : rc == VERR_NO_MEMORY ? -ENOMEM : rc ==
881 VERR_NOT_SUPPORTED ? -EBUSY : -EINVAL;
882}
Note: See TracBrowser for help on using the repository browser.

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