VirtualBox

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

Last change on this file since 62557 was 62557, checked in by vboxsync, 9 years ago

bugref:8087: Additions/x11: support non-root X server: only touch VBVA under the lock, as functions which touch VBVA can be called on several threads at once. Also, do not disable VBVA when someone acquires master, as the frame buffer can still send rectangles after that, which re-enables it.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 28.6 KB
Line 
1/* $Id: vbox_mode.c 62557 2016-07-26 10:23:12Z 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 <airlied@redhat.com>
49 */
50#include "vbox_drv.h"
51
52#include <VBox/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
77 LogFunc(("vboxvideo: %d: vbox_crtc=%p, CRTC_FB(crtc)=%p\n", __LINE__,
78 vbox_crtc, CRTC_FB(crtc)));
79 vbox = crtc->dev->dev_private;
80 width = mode->hdisplay ? mode->hdisplay : 640;
81 height = mode->vdisplay ? mode->vdisplay : 480;
82 crtc_id = vbox_crtc->crtc_id;
83 bpp = crtc->enabled ? CRTC_FB(crtc)->bits_per_pixel : 32;
84#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
85 pitch = crtc->enabled ? CRTC_FB(crtc)->pitch : width * bpp / 8;
86#else
87 pitch = crtc->enabled ? CRTC_FB(crtc)->pitches[0] : width * bpp / 8;
88#endif
89 /* This is the old way of setting graphics modes. It assumed one screen
90 * and a frame-buffer at the start of video RAM. On older versions of
91 * VirtualBox, certain parts of the code still assume that the first
92 * screen is programmed this way, so try to fake it. */
93 if ( vbox_crtc->crtc_id == 0
94 && crtc->enabled
95 && vbox_crtc->fb_offset / pitch < 0xffff - crtc->y
96 && vbox_crtc->fb_offset % (bpp / 8) == 0)
97 VBoxVideoSetModeRegisters(width, height, pitch * 8 / bpp,
98 CRTC_FB(crtc)->bits_per_pixel, 0,
99 vbox_crtc->fb_offset % pitch / bpp * 8 + crtc->x,
100 vbox_crtc->fb_offset / pitch + crtc->y);
101 flags = VBVA_SCREEN_F_ACTIVE;
102 flags |= (crtc->enabled && !vbox_crtc->blanked ? 0 : VBVA_SCREEN_F_BLANK);
103 flags |= (vbox_crtc->disconnected ? VBVA_SCREEN_F_DISABLED : 0);
104 VBoxHGSMIProcessDisplayInfo(&vbox->submit_info, vbox_crtc->crtc_id,
105 crtc->x, crtc->y,
106 crtc->x * bpp / 8 + crtc->y * pitch,
107 pitch, width, height,
108 vbox_crtc->blanked ? 0 : bpp, flags);
109 VBoxHGSMIReportFlagsLocation(&vbox->submit_info, vbox->host_flags_offset);
110 LogFunc(("vboxvideo: %d\n", __LINE__));
111}
112
113static int vbox_set_view(struct drm_crtc *crtc)
114{
115 struct vbox_crtc *vbox_crtc = to_vbox_crtc(crtc);
116 struct vbox_private *vbox = crtc->dev->dev_private;
117 void *p;
118
119 LogFunc(("vboxvideo: %d: vbox_crtc=%p\n", __LINE__, vbox_crtc));
120 /* Tell the host about the view. This design originally targeted the
121 * Windows XP driver architecture and assumed that each screen would have
122 * a dedicated frame buffer with the command buffer following it, the whole
123 * being a "view". The host works out which screen a command buffer belongs
124 * to by checking whether it is in the first view, then whether it is in the
125 * second and so on. The first match wins. We cheat around this by making
126 * the first view be the managed memory plus the first command buffer, the
127 * second the same plus the second buffer and so on. */
128 p = VBoxHGSMIBufferAlloc(&vbox->submit_info, sizeof(VBVAINFOVIEW), HGSMI_CH_VBVA,
129 VBVA_INFO_VIEW);
130 if (p)
131 {
132 VBVAINFOVIEW *pInfo = (VBVAINFOVIEW *)p;
133 pInfo->u32ViewIndex = vbox_crtc->crtc_id;
134 pInfo->u32ViewOffset = vbox_crtc->fb_offset;
135 pInfo->u32ViewSize = vbox->available_vram_size - vbox_crtc->fb_offset
136 + vbox_crtc->crtc_id * VBVA_MIN_BUFFER_SIZE;
137 pInfo->u32MaxScreenSize = vbox->available_vram_size - vbox_crtc->fb_offset;
138 VBoxHGSMIBufferSubmit(&vbox->submit_info, p);
139 VBoxHGSMIBufferFree(&vbox->submit_info, p);
140 }
141 else
142 return -ENOMEM;
143 LogFunc(("vboxvideo: %d: p=%p\n", __LINE__, p));
144 return 0;
145}
146
147static void vbox_crtc_load_lut(struct drm_crtc *crtc)
148{
149
150}
151
152static void vbox_crtc_dpms(struct drm_crtc *crtc, int mode)
153{
154 struct vbox_crtc *vbox_crtc = to_vbox_crtc(crtc);
155 struct vbox_private *vbox = crtc->dev->dev_private;
156
157 LogFunc(("vboxvideo: %d: vbox_crtc=%p, mode=%d\n", __LINE__, vbox_crtc,
158 mode));
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 mutex_lock(&vbox->hw_mutex);
170 vbox_do_modeset(crtc, &crtc->hwmode);
171 mutex_unlock(&vbox->hw_mutex);
172 LogFunc(("vboxvideo: %d\n", __LINE__));
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/* We move buffers which are not in active use out of VRAM to save memory. */
183static int vbox_crtc_do_set_base(struct drm_crtc *crtc,
184 struct drm_framebuffer *fb,
185 int x, int y, int atomic)
186{
187 struct vbox_private *vbox = crtc->dev->dev_private;
188 struct vbox_crtc *vbox_crtc = to_vbox_crtc(crtc);
189 struct drm_gem_object *obj;
190 struct vbox_framebuffer *vbox_fb;
191 struct vbox_bo *bo;
192 int ret;
193 u64 gpu_addr;
194
195 LogFunc(("vboxvideo: %d: fb=%p, vbox_crtc=%p\n", __LINE__, fb, vbox_crtc));
196 /* push the previous fb to system ram */
197 if (!atomic && fb) {
198 vbox_fb = to_vbox_framebuffer(fb);
199 obj = vbox_fb->obj;
200 bo = gem_to_vbox_bo(obj);
201 ret = vbox_bo_reserve(bo, false);
202 if (ret)
203 return ret;
204 vbox_bo_push_sysram(bo);
205 vbox_bo_unreserve(bo);
206 }
207
208 vbox_fb = to_vbox_framebuffer(CRTC_FB(crtc));
209 obj = vbox_fb->obj;
210 bo = gem_to_vbox_bo(obj);
211
212 ret = vbox_bo_reserve(bo, false);
213 if (ret)
214 return ret;
215
216 ret = vbox_bo_pin(bo, TTM_PL_FLAG_VRAM, &gpu_addr);
217 if (ret) {
218 vbox_bo_unreserve(bo);
219 return ret;
220 }
221
222 if (&vbox->fbdev->afb == vbox_fb) {
223 /* if pushing console in kmap it */
224 ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap);
225 if (ret)
226 DRM_ERROR("failed to kmap fbcon\n");
227 else
228 vbox_fbdev_set_base(vbox, gpu_addr);
229 }
230 vbox_bo_unreserve(bo);
231
232 /* vbox_set_start_address_crt1(crtc, (u32)gpu_addr); */
233 vbox_crtc->fb_offset = gpu_addr;
234 if (vbox_crtc->crtc_id == 0) {
235 vbox->input_mapping_width = CRTC_FB(crtc)->width;
236 vbox->input_mapping_height = CRTC_FB(crtc)->height;
237 }
238 LogFunc(("vboxvideo: %d: vbox_fb=%p, obj=%p, bo=%p, gpu_addr=%u\n",
239 __LINE__, vbox_fb, obj, bo, (unsigned)gpu_addr));
240 return 0;
241}
242
243static int vbox_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
244 struct drm_framebuffer *old_fb)
245{
246 LogFunc(("vboxvideo: %d\n", __LINE__));
247 return vbox_crtc_do_set_base(crtc, old_fb, x, y, 0);
248}
249
250static int vbox_crtc_mode_set(struct drm_crtc *crtc,
251 struct drm_display_mode *mode,
252 struct drm_display_mode *adjusted_mode,
253 int x, int y,
254 struct drm_framebuffer *old_fb)
255{
256 struct vbox_private *vbox = crtc->dev->dev_private;
257 int rc = 0;
258
259 LogFunc(("vboxvideo: %d: vbox=%p\n", __LINE__, vbox));
260 vbox_crtc_mode_set_base(crtc, x, y, old_fb);
261 mutex_lock(&vbox->hw_mutex);
262 /* Disable VBVA when someone sets a new mode until they send us dirty
263 * rectangles, which proves that they can. A single screen can work
264 * without VBVA. */
265 vbox_disable_accel(vbox);
266 rc = vbox_set_view(crtc);
267 if (!rc)
268 vbox_do_modeset(crtc, mode);
269 /* Note that the input mapping is always relative to the first screen. */
270 VBoxHGSMIUpdateInputMapping(&vbox->submit_info, 0, 0,
271 vbox->input_mapping_width,
272 vbox->input_mapping_height);
273 mutex_unlock(&vbox->hw_mutex);
274 LogFunc(("vboxvideo: %d\n", __LINE__));
275 return rc;
276}
277
278static void vbox_crtc_disable(struct drm_crtc *crtc)
279{
280
281}
282
283static void vbox_crtc_prepare(struct drm_crtc *crtc)
284{
285
286}
287
288static void vbox_crtc_commit(struct drm_crtc *crtc)
289{
290
291}
292
293
294static const struct drm_crtc_helper_funcs vbox_crtc_helper_funcs = {
295 .dpms = vbox_crtc_dpms,
296 .mode_fixup = vbox_crtc_mode_fixup,
297 .mode_set = vbox_crtc_mode_set,
298 /* .mode_set_base = vbox_crtc_mode_set_base, */
299 .disable = vbox_crtc_disable,
300 .load_lut = vbox_crtc_load_lut,
301 .prepare = vbox_crtc_prepare,
302 .commit = vbox_crtc_commit,
303
304};
305
306static void vbox_crtc_reset(struct drm_crtc *crtc)
307{
308
309}
310
311
312static void vbox_crtc_destroy(struct drm_crtc *crtc)
313{
314 drm_crtc_cleanup(crtc);
315 kfree(crtc);
316}
317
318static const struct drm_crtc_funcs vbox_crtc_funcs = {
319 .cursor_move = vbox_cursor_move,
320 .cursor_set2 = vbox_cursor_set2,
321 .reset = vbox_crtc_reset,
322 .set_config = drm_crtc_helper_set_config,
323 /* .gamma_set = vbox_crtc_gamma_set, */
324 .destroy = vbox_crtc_destroy,
325};
326
327static struct vbox_crtc *vbox_crtc_init(struct drm_device *dev, unsigned i)
328{
329 struct vbox_crtc *vbox_crtc;
330
331 LogFunc(("vboxvideo: %d\n", __LINE__));
332 vbox_crtc = kzalloc(sizeof(struct vbox_crtc), GFP_KERNEL);
333 if (!vbox_crtc)
334 return NULL;
335 vbox_crtc->crtc_id = i;
336
337 drm_crtc_init(dev, &vbox_crtc->base, &vbox_crtc_funcs);
338 drm_mode_crtc_set_gamma_size(&vbox_crtc->base, 256);
339 drm_crtc_helper_add(&vbox_crtc->base, &vbox_crtc_helper_funcs);
340 LogFunc(("vboxvideo: %d: crtc=%p\n", __LINE__, vbox_crtc));
341
342 return vbox_crtc;
343}
344
345static void vbox_encoder_destroy(struct drm_encoder *encoder)
346{
347 LogFunc(("vboxvideo: %d: encoder=%p\n", __LINE__, encoder));
348 drm_encoder_cleanup(encoder);
349 kfree(encoder);
350}
351
352#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
353static struct drm_encoder *drm_encoder_find(struct drm_device *dev, uint32_t id)
354{
355 struct drm_mode_object *mo;
356 mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_ENCODER);
357 return mo ? obj_to_encoder(mo) : NULL;
358}
359#endif
360
361static struct drm_encoder *vbox_best_single_encoder(struct drm_connector *connector)
362{
363 int enc_id = connector->encoder_ids[0];
364
365 LogFunc(("vboxvideo: %d: connector=%p\n", __LINE__, connector));
366 /* pick the encoder ids */
367 if (enc_id)
368 return drm_encoder_find(connector->dev, enc_id);
369 LogFunc(("vboxvideo: %d\n", __LINE__));
370 return NULL;
371}
372
373
374static const struct drm_encoder_funcs vbox_enc_funcs = {
375 .destroy = vbox_encoder_destroy,
376};
377
378static void vbox_encoder_dpms(struct drm_encoder *encoder, int mode)
379{
380
381}
382
383static bool vbox_mode_fixup(struct drm_encoder *encoder,
384 const struct drm_display_mode *mode,
385 struct drm_display_mode *adjusted_mode)
386{
387 return true;
388}
389
390static void vbox_encoder_mode_set(struct drm_encoder *encoder,
391 struct drm_display_mode *mode,
392 struct drm_display_mode *adjusted_mode)
393{
394}
395
396static void vbox_encoder_prepare(struct drm_encoder *encoder)
397{
398
399}
400
401static void vbox_encoder_commit(struct drm_encoder *encoder)
402{
403
404}
405
406
407static const struct drm_encoder_helper_funcs vbox_enc_helper_funcs = {
408 .dpms = vbox_encoder_dpms,
409 .mode_fixup = vbox_mode_fixup,
410 .prepare = vbox_encoder_prepare,
411 .commit = vbox_encoder_commit,
412 .mode_set = vbox_encoder_mode_set,
413};
414
415static struct drm_encoder *vbox_encoder_init(struct drm_device *dev, unsigned i)
416{
417 struct vbox_encoder *vbox_encoder;
418
419 LogFunc(("vboxvideo: %d: dev=%d\n", __LINE__));
420 vbox_encoder = kzalloc(sizeof(struct vbox_encoder), GFP_KERNEL);
421 if (!vbox_encoder)
422 return NULL;
423
424 drm_encoder_init(dev, &vbox_encoder->base, &vbox_enc_funcs,
425 DRM_MODE_ENCODER_DAC
426#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0)
427 , NULL
428#endif
429 );
430 drm_encoder_helper_add(&vbox_encoder->base, &vbox_enc_helper_funcs);
431
432 vbox_encoder->base.possible_crtcs = 1 << i;
433 LogFunc(("vboxvideo: %d: vbox_encoder=%p\n", __LINE__, vbox_encoder));
434 return &vbox_encoder->base;
435}
436
437/** Generate EDID data with a mode-unique serial number for the virtual
438 * monitor to try to persuade Unity that different modes correspond to
439 * different monitors and it should not try to force the same resolution on
440 * them. */
441static void vbox_set_edid(struct drm_connector *connector, int width,
442 int height)
443{
444 enum { EDID_SIZE = 128 };
445 unsigned char edid[EDID_SIZE] = {
446 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, /* header */
447 0x58, 0x58, /* manufacturer (VBX) */
448 0x00, 0x00, /* product code */
449 0x00, 0x00,0x00, 0x00, /* serial number goes here */
450 0x01, /* week of manufacture */
451 0x00, /* year of manufacture */
452 0x01, 0x03, /* EDID version */
453 0x80, /* capabilities - digital */
454 0x00, /* horiz. res in cm, zero for projectors */
455 0x00, /* vert. res in cm */
456 0x78, /* display gamma (120 == 2.2). */
457 0xEE, /* features (standby, suspend, off, RGB, standard colour space,
458 * preferred timing mode) */
459 0xEE, 0x91, 0xA3, 0x54, 0x4C, 0x99, 0x26, 0x0F, 0x50, 0x54,
460 /* chromaticity for standard colour space. */
461 0x00, 0x00, 0x00, /* no default timings */
462 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
463 0x01, 0x01, 0x01, 0x01, /* no standard timings */
464 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x02, 0x02, 0x02, 0x02,
465 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* descriptor block 1 goes here */
466 0x00, 0x00, 0x00, 0xFD, 0x00, /* descriptor block 2, monitor ranges */
467 0x00, 0xC8, 0x00, 0xC8, 0x64, 0x00, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20,
468 0x20, /* 0-200Hz vertical, 0-200KHz horizontal, 1000MHz pixel clock */
469 0x00, 0x00, 0x00, 0xFC, 0x00, /* descriptor block 3, monitor name */
470 'V', 'B', 'O', 'X', ' ', 'm', 'o', 'n', 'i', 't', 'o', 'r', '\n',
471 0x00, 0x00, 0x00, 0x10, 0x00, /* descriptor block 4: dummy data */
472 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
473 0x20,
474 0x00, /* number of extensions */
475 0x00 /* checksum goes here */
476 };
477 int clock = (width + 6) * (height + 6) * 60 / 10000;
478 unsigned i;
479 unsigned sum = 0;
480
481 edid[12] = width & 0xff;
482 edid[13] = width >> 8;
483 edid[14] = height & 0xff;
484 edid[15] = height >> 8;
485 edid[54] = clock & 0xff;
486 edid[55] = clock >> 8;
487 edid[56] = width & 0xff;
488 edid[58] = (width >> 4) & 0xf0;
489 edid[59] = height & 0xff;
490 edid[61] = (height >> 4) & 0xf0;
491 for (i = 0; i < EDID_SIZE - 1; ++i)
492 sum += edid[i];
493 edid[EDID_SIZE - 1] = (0x100 - (sum & 0xFF)) & 0xFF;
494 drm_mode_connector_update_edid_property(connector, (struct edid *)edid);
495}
496
497static int vbox_get_modes(struct drm_connector *connector)
498{
499 struct vbox_connector *vbox_connector = NULL;
500 struct drm_display_mode *mode = NULL;
501 struct vbox_private *vbox = NULL;
502 unsigned num_modes = 0;
503 int preferred_width, preferred_height;
504
505 LogFunc(("vboxvideo: %d: connector=%p\n", __LINE__, connector));
506 vbox_connector = to_vbox_connector(connector);
507 vbox = connector->dev->dev_private;
508 if (vbox_connector->vbox_crtc->crtc_id == 0)
509 vbox_enable_caps(vbox);
510 if (!vbox->initial_mode_queried) {
511 if (vbox_connector->vbox_crtc->crtc_id == 0) {
512 vbox->initial_mode_queried = true;
513 vbox_report_hotplug(vbox);
514 }
515 return drm_add_modes_noedid(connector, 800, 600);
516 }
517 num_modes = drm_add_modes_noedid(connector, 2560, 1600);
518 preferred_width = vbox_connector->mode_hint.width ? vbox_connector->mode_hint.width : 1024;
519 preferred_height = vbox_connector->mode_hint.height ? vbox_connector->mode_hint.height : 768;
520 mode = drm_cvt_mode(connector->dev, preferred_width, preferred_height, 60, false,
521 false, false);
522 if (mode)
523 {
524 mode->type |= DRM_MODE_TYPE_PREFERRED;
525 drm_mode_probed_add(connector, mode);
526 ++num_modes;
527 }
528 vbox_set_edid(connector, preferred_width, preferred_height);
529 return num_modes;
530}
531
532static int vbox_mode_valid(struct drm_connector *connector,
533 struct drm_display_mode *mode)
534{
535 return MODE_OK;
536}
537
538static void vbox_connector_destroy(struct drm_connector *connector)
539{
540 struct vbox_connector *vbox_connector = NULL;
541
542 LogFunc(("vboxvideo: %d: connector=%p\n", __LINE__, connector));
543 vbox_connector = to_vbox_connector(connector);
544#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
545 drm_sysfs_connector_remove(connector);
546#else
547 drm_connector_unregister(connector);
548#endif
549 drm_connector_cleanup(connector);
550 kfree(connector);
551}
552
553static enum drm_connector_status
554vbox_connector_detect(struct drm_connector *connector, bool force)
555{
556 struct vbox_connector *vbox_connector = NULL;
557
558 (void) force;
559 LogFunc(("vboxvideo: %d: connector=%p\n", __LINE__, connector));
560 vbox_connector = to_vbox_connector(connector);
561 return vbox_connector->mode_hint.disconnected ?
562 connector_status_disconnected : connector_status_connected;
563}
564
565static int vbox_fill_modes(struct drm_connector *connector, uint32_t max_x, uint32_t max_y)
566{
567 struct vbox_connector *vbox_connector;
568 struct drm_device *dev;
569 struct drm_display_mode *mode, *iterator;
570
571 LogFunc(("vboxvideo: %d: connector=%p, max_x=%lu, max_y = %lu\n", __LINE__,
572 connector, (unsigned long)max_x, (unsigned long)max_y));
573 vbox_connector = to_vbox_connector(connector);
574 dev = vbox_connector->base.dev;
575 list_for_each_entry_safe(mode, iterator, &connector->modes, head)
576 {
577 list_del(&mode->head);
578 drm_mode_destroy(dev, mode);
579 }
580 return drm_helper_probe_single_connector_modes(connector, max_x, max_y);
581}
582
583static const struct drm_connector_helper_funcs vbox_connector_helper_funcs = {
584 .mode_valid = vbox_mode_valid,
585 .get_modes = vbox_get_modes,
586 .best_encoder = vbox_best_single_encoder,
587};
588
589static const struct drm_connector_funcs vbox_connector_funcs = {
590 .dpms = drm_helper_connector_dpms,
591 .detect = vbox_connector_detect,
592 .fill_modes = vbox_fill_modes,
593 .destroy = vbox_connector_destroy,
594};
595
596static int vbox_connector_init(struct drm_device *dev,
597 struct vbox_crtc *vbox_crtc,
598 struct drm_encoder *encoder)
599{
600 struct vbox_connector *vbox_connector;
601 struct drm_connector *connector;
602
603 LogFunc(("vboxvideo: %d: dev=%p, encoder=%p\n", __LINE__, dev,
604 encoder));
605 vbox_connector = kzalloc(sizeof(struct vbox_connector), GFP_KERNEL);
606 if (!vbox_connector)
607 return -ENOMEM;
608
609 connector = &vbox_connector->base;
610 vbox_connector->vbox_crtc = vbox_crtc;
611
612 drm_connector_init(dev, connector, &vbox_connector_funcs,
613 DRM_MODE_CONNECTOR_VGA);
614 drm_connector_helper_add(connector, &vbox_connector_helper_funcs);
615
616 connector->interlace_allowed = 0;
617 connector->doublescan_allowed = 0;
618
619#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
620 drm_mode_create_suggested_offset_properties(dev);
621 drm_object_attach_property(&connector->base,
622 dev->mode_config.suggested_x_property, 0);
623 drm_object_attach_property(&connector->base,
624 dev->mode_config.suggested_y_property, 0);
625#endif
626#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
627 drm_sysfs_connector_add(connector);
628#else
629 drm_connector_register(connector);
630#endif
631
632 drm_mode_connector_attach_encoder(connector, encoder);
633
634 LogFunc(("vboxvideo: %d: connector=%p\n", __LINE__, connector));
635 return 0;
636}
637
638int vbox_mode_init(struct drm_device *dev)
639{
640 struct vbox_private *vbox = dev->dev_private;
641 struct drm_encoder *encoder;
642 struct vbox_crtc *vbox_crtc;
643 unsigned i;
644 /* vbox_cursor_init(dev); */
645 LogFunc(("vboxvideo: %d: dev=%p\n", __LINE__, dev));
646 for (i = 0; i < vbox->num_crtcs; ++i)
647 {
648 vbox_crtc = vbox_crtc_init(dev, i);
649 if (!vbox_crtc)
650 return -ENOMEM;
651 encoder = vbox_encoder_init(dev, i);
652 if (!encoder)
653 return -ENOMEM;
654 vbox_connector_init(dev, vbox_crtc, encoder);
655 }
656 return 0;
657}
658
659void vbox_mode_fini(struct drm_device *dev)
660{
661 /* vbox_cursor_fini(dev); */
662}
663
664
665/** Copy the ARGB image and generate the mask, which is needed in case the host
666 * does not support ARGB cursors. The mask is a 1BPP bitmap with the bit set
667 * if the corresponding alpha value in the ARGB image is greater than 0xF0. */
668static void copy_cursor_image(u8 *src, u8 *dst, int width, int height,
669 size_t mask_size)
670{
671 unsigned i, j;
672 size_t line_size = (width + 7) / 8;
673
674 memcpy(dst + mask_size, src, width * height * 4);
675 for (i = 0; i < height; ++i)
676 for (j = 0; j < width; ++j)
677 if (((uint32_t *)src)[i * width + j] > 0xf0000000)
678 dst[i * line_size + j / 8] |= (0x80 >> (j % 8));
679}
680
681static int vbox_cursor_set2(struct drm_crtc *crtc, struct drm_file *file_priv,
682 uint32_t handle, uint32_t width, uint32_t height,
683 int32_t hot_x, int32_t hot_y)
684{
685 struct vbox_private *vbox = crtc->dev->dev_private;
686 struct vbox_crtc *vbox_crtc = to_vbox_crtc(crtc);
687 struct drm_gem_object *obj;
688 struct vbox_bo *bo;
689 int ret, rc;
690 struct ttm_bo_kmap_obj uobj_map;
691 u8 *src;
692 u8 *dst = NULL;
693 u32 caps = 0;
694 size_t data_size, mask_size;
695 bool src_isiomem;
696
697 /* Re-set this regularly as in 5.0.20 and earlier the information was lost
698 * on save and restore. */
699 VBoxHGSMIUpdateInputMapping(&vbox->submit_info, 0, 0,
700 vbox->input_mapping_width,
701 vbox->input_mapping_height);
702 if (!handle) {
703 bool cursor_enabled = false;
704 struct drm_crtc *crtci;
705
706 /* Hide cursor. */
707 vbox_crtc->cursor_enabled = false;
708 list_for_each_entry(crtci, &vbox->dev->mode_config.crtc_list, head)
709 if (to_vbox_crtc(crtci)->cursor_enabled)
710 cursor_enabled = true;
711 if (!cursor_enabled)
712 VBoxHGSMIUpdatePointerShape(&vbox->submit_info, 0, 0, 0, 0, 0, NULL, 0);
713 return 0;
714 }
715 vbox_crtc->cursor_enabled = true;
716 if ( width > VBOX_MAX_CURSOR_WIDTH || height > VBOX_MAX_CURSOR_HEIGHT
717 || width == 0 || height == 0)
718 return -EINVAL;
719 rc = VBoxQueryConfHGSMI(&vbox->submit_info,
720 VBOX_VBVA_CONF32_CURSOR_CAPABILITIES, &caps);
721 ret = -RTErrConvertToErrno(rc);
722 if (ret)
723 return ret;
724 if ( caps & VMMDEV_MOUSE_HOST_CANNOT_HWPOINTER
725 || !(caps & VMMDEV_MOUSE_HOST_WANTS_ABSOLUTE))
726 return -EINVAL;
727
728#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
729 obj = drm_gem_object_lookup(file_priv, handle);
730#else
731 obj = drm_gem_object_lookup(crtc->dev, file_priv, handle);
732#endif
733 if (obj)
734 {
735 bo = gem_to_vbox_bo(obj);
736 ret = vbox_bo_reserve(bo, false);
737 if (!ret)
738 {
739 /* The mask must be calculated based on the alpha channel, one bit
740 * per ARGB word, and must be 32-bit padded. */
741 mask_size = ((width + 7) / 8 * height + 3) & ~3;
742 data_size = width * height * 4 + mask_size;
743 vbox->cursor_hot_x = min((uint32_t)max(hot_x, 0), width);
744 vbox->cursor_hot_y = min((uint32_t)max(hot_y, 0), height);
745 vbox->cursor_width = width;
746 vbox->cursor_height = height;
747 vbox->cursor_data_size = data_size;
748 dst = vbox->cursor_data;
749 ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &uobj_map);
750 if (!ret)
751 {
752 src = ttm_kmap_obj_virtual(&uobj_map, &src_isiomem);
753 if (!src_isiomem)
754 {
755 uint32_t flags = VBOX_MOUSE_POINTER_VISIBLE
756 | VBOX_MOUSE_POINTER_SHAPE
757 | VBOX_MOUSE_POINTER_ALPHA;
758 copy_cursor_image(src, dst, width, height, mask_size);
759 rc = VBoxHGSMIUpdatePointerShape(&vbox->submit_info, flags,
760 vbox->cursor_hot_x,
761 vbox->cursor_hot_y,
762 width, height, dst,
763 data_size);
764 ret = -RTErrConvertToErrno(rc);
765 }
766 else
767 DRM_ERROR("src cursor bo should be in main memory\n");
768 ttm_bo_kunmap(&uobj_map);
769 }
770 else
771 vbox->cursor_data_size = 0;
772 vbox_bo_unreserve(bo);
773 }
774 drm_gem_object_unreference_unlocked(obj);
775 }
776 else
777 {
778 DRM_ERROR("Cannot find cursor object %x for crtc\n", handle);
779 ret = -ENOENT;
780 }
781 return ret;
782}
783
784static int vbox_cursor_move(struct drm_crtc *crtc,
785 int x, int y)
786{
787 struct vbox_private *vbox = crtc->dev->dev_private;
788 uint32_t flags = VBOX_MOUSE_POINTER_VISIBLE
789 | VBOX_MOUSE_POINTER_SHAPE
790 | VBOX_MOUSE_POINTER_ALPHA;
791 uint32_t host_x, host_y;
792 uint32_t hot_x = 0;
793 uint32_t hot_y = 0;
794 int rc;
795
796 /* We compare these to unsigned later and don't need to handle negative. */
797 if (x + crtc->x < 0 || y + crtc->y < 0 || vbox->cursor_data_size == 0)
798 return 0;
799 rc = VBoxHGSMICursorPosition(&vbox->submit_info, true, x + crtc->x,
800 y + crtc->y, &host_x, &host_y);
801 /* Work around a bug after save and restore in 5.0.20 and earlier. */
802 if (RT_FAILURE(rc) || (host_x == 0 && host_y == 0))
803 return -RTErrConvertToErrno(rc);
804 if (x + crtc->x < host_x)
805 hot_x = min(host_x - x - crtc->x, vbox->cursor_width);
806 if (y + crtc->y < host_y)
807 hot_y = min(host_y - y - crtc->y, vbox->cursor_height);
808 if (hot_x == vbox->cursor_hot_x && hot_y == vbox->cursor_hot_y)
809 return 0;
810 vbox->cursor_hot_x = hot_x;
811 vbox->cursor_hot_y = hot_y;
812 rc = VBoxHGSMIUpdatePointerShape(&vbox->submit_info, flags, hot_x, hot_y,
813 vbox->cursor_width, vbox->cursor_height,
814 vbox->cursor_data,
815 vbox->cursor_data_size);
816 return -RTErrConvertToErrno(rc);
817}
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