VirtualBox

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

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

bugref:8834: Makeself installers/Linux: indicate problems better in the exit codes
[PATCH] additions/linux/drm: Cleanup vram mapping and offset
calculating code

Trying to put everything in one pci_iomap_range mapped area leads to
hard to read code and has no advantages. It actually causes us to map
more memory then we need, resulting in part of the memory being intended
for generic use also being mapped.

This commit splits the mapping up in to 2 pci_iomap_range calls, one
for the guest_heap + host-flags and another one for the vbva buffers
which is not done until we actually know how much buffers we need.

Signed-off-by: Hans de Goede <hdegoede@…>
Minor adjustment: GUEST_HEAP_OFFSET -> GUEST_HEAP_OFFSET(vbox)

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