VirtualBox

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

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

bugref:8087: Additions/x11: support non-root X server: do not disable VBVA on mode changes, as this caused all our reported capability flags to be lost, and the host to think we did not support dynamic resizing. We already disable VBVA when the master is changed and that is enough. The motivation for disabling VBVA was that we did not enable it until we saw that the user-space master was willing to send us dirty rectangle information.

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