1 | // SPDX-License-Identifier: MIT |
2 | /* |
3 | * Copyright (C) 2013-2017 Oracle Corporation |
4 | * This file is based on ast_mode.c |
5 | * Copyright 2012 Red Hat Inc. |
6 | * Parts based on xf86-video-ast |
7 | * Copyright (c) 2005 ASPEED Technology Inc. |
8 | * Authors: Dave Airlie <airlied@redhat.com> |
9 | * Michael Thayer <michael.thayer@oracle.com, |
10 | * Hans de Goede <hdegoede@redhat.com> |
11 | */ |
12 | |
13 | #include <linux/iosys-map.h> |
14 | #include <linux/export.h> |
15 | |
16 | #include <drm/drm_atomic.h> |
17 | #include <drm/drm_atomic_helper.h> |
18 | #include <drm/drm_edid.h> |
19 | #include <drm/drm_fb_helper.h> |
20 | #include <drm/drm_fourcc.h> |
21 | #include <drm/drm_framebuffer.h> |
22 | #include <drm/drm_gem_atomic_helper.h> |
23 | #include <drm/drm_gem_framebuffer_helper.h> |
24 | #include <drm/drm_plane_helper.h> |
25 | #include <drm/drm_probe_helper.h> |
26 | |
27 | #include "hgsmi_channels.h" |
28 | #include "vbox_drv.h" |
29 | #include "vboxvideo.h" |
30 | |
31 | /* |
32 | * Set a graphics mode. Poke any required values into registers, do an HGSMI |
33 | * mode set and tell the host we support advanced graphics functions. |
34 | */ |
35 | static void vbox_do_modeset(struct drm_crtc *crtc) |
36 | { |
37 | struct drm_framebuffer *fb = crtc->primary->state->fb; |
38 | struct vbox_crtc *vbox_crtc = to_vbox_crtc(crtc); |
39 | struct vbox_private *vbox; |
40 | int width, height, bpp, pitch; |
41 | u16 flags; |
42 | s32 x_offset, y_offset; |
43 | |
44 | vbox = to_vbox_dev(crtc->dev); |
45 | width = vbox_crtc->width ? vbox_crtc->width : 640; |
46 | height = vbox_crtc->height ? vbox_crtc->height : 480; |
47 | bpp = fb ? fb->format->cpp[0] * 8 : 32; |
48 | pitch = fb ? fb->pitches[0] : width * bpp / 8; |
49 | x_offset = vbox->single_framebuffer ? vbox_crtc->x : vbox_crtc->x_hint; |
50 | y_offset = vbox->single_framebuffer ? vbox_crtc->y : vbox_crtc->y_hint; |
51 | |
52 | /* |
53 | * This is the old way of setting graphics modes. It assumed one screen |
54 | * and a frame-buffer at the start of video RAM. On older versions of |
55 | * VirtualBox, certain parts of the code still assume that the first |
56 | * screen is programmed this way, so try to fake it. |
57 | */ |
58 | if (vbox_crtc->crtc_id == 0 && fb && |
59 | vbox_crtc->fb_offset / pitch < 0xffff - crtc->y && |
60 | vbox_crtc->fb_offset % (bpp / 8) == 0) { |
61 | vbox_write_ioport(VBE_DISPI_INDEX_XRES, data: width); |
62 | vbox_write_ioport(VBE_DISPI_INDEX_YRES, data: height); |
63 | vbox_write_ioport(VBE_DISPI_INDEX_VIRT_WIDTH, data: pitch * 8 / bpp); |
64 | vbox_write_ioport(VBE_DISPI_INDEX_BPP, data: bpp); |
65 | vbox_write_ioport(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED); |
66 | vbox_write_ioport(VBE_DISPI_INDEX_X_OFFSET, |
67 | data: vbox_crtc->fb_offset % pitch / bpp * 8 + vbox_crtc->x); |
68 | vbox_write_ioport(VBE_DISPI_INDEX_Y_OFFSET, |
69 | data: vbox_crtc->fb_offset / pitch + vbox_crtc->y); |
70 | } |
71 | |
72 | flags = VBVA_SCREEN_F_ACTIVE; |
73 | flags |= (fb && crtc->state->enable) ? 0 : VBVA_SCREEN_F_BLANK; |
74 | flags |= vbox_crtc->disconnected ? VBVA_SCREEN_F_DISABLED : 0; |
75 | hgsmi_process_display_info(ctx: vbox->guest_pool, display: vbox_crtc->crtc_id, |
76 | origin_x: x_offset, origin_y: y_offset, |
77 | start_offset: vbox_crtc->x * bpp / 8 + |
78 | vbox_crtc->y * pitch, |
79 | pitch, width, height, bpp, flags); |
80 | } |
81 | |
82 | static int vbox_set_view(struct drm_crtc *crtc) |
83 | { |
84 | struct vbox_crtc *vbox_crtc = to_vbox_crtc(crtc); |
85 | struct vbox_private *vbox = to_vbox_dev(crtc->dev); |
86 | struct vbva_infoview *p; |
87 | |
88 | /* |
89 | * Tell the host about the view. This design originally targeted the |
90 | * Windows XP driver architecture and assumed that each screen would |
91 | * have a dedicated frame buffer with the command buffer following it, |
92 | * the whole being a "view". The host works out which screen a command |
93 | * buffer belongs to by checking whether it is in the first view, then |
94 | * whether it is in the second and so on. The first match wins. We |
95 | * cheat around this by making the first view be the managed memory |
96 | * plus the first command buffer, the second the same plus the second |
97 | * buffer and so on. |
98 | */ |
99 | p = hgsmi_buffer_alloc(guest_pool: vbox->guest_pool, size: sizeof(*p), |
100 | HGSMI_CH_VBVA, VBVA_INFO_VIEW); |
101 | if (!p) |
102 | return -ENOMEM; |
103 | |
104 | p->view_index = vbox_crtc->crtc_id; |
105 | p->view_offset = vbox_crtc->fb_offset; |
106 | p->view_size = vbox->available_vram_size - vbox_crtc->fb_offset + |
107 | vbox_crtc->crtc_id * VBVA_MIN_BUFFER_SIZE; |
108 | p->max_screen_size = vbox->available_vram_size - vbox_crtc->fb_offset; |
109 | |
110 | hgsmi_buffer_submit(guest_pool: vbox->guest_pool, buf: p); |
111 | hgsmi_buffer_free(guest_pool: vbox->guest_pool, buf: p); |
112 | |
113 | return 0; |
114 | } |
115 | |
116 | /* |
117 | * Try to map the layout of virtual screens to the range of the input device. |
118 | * Return true if we need to re-set the crtc modes due to screen offset |
119 | * changes. |
120 | */ |
121 | static bool vbox_set_up_input_mapping(struct vbox_private *vbox) |
122 | { |
123 | struct drm_crtc *crtci; |
124 | struct drm_connector *connectori; |
125 | struct drm_framebuffer *fb, *fb1 = NULL; |
126 | bool single_framebuffer = true; |
127 | bool old_single_framebuffer = vbox->single_framebuffer; |
128 | u16 width = 0, height = 0; |
129 | |
130 | /* |
131 | * Are we using an X.Org-style single large frame-buffer for all crtcs? |
132 | * If so then screen layout can be deduced from the crtc offsets. |
133 | * Same fall-back if this is the fbdev frame-buffer. |
134 | */ |
135 | list_for_each_entry(crtci, &vbox->ddev.mode_config.crtc_list, head) { |
136 | fb = crtci->primary->state->fb; |
137 | if (!fb) |
138 | continue; |
139 | |
140 | if (!fb1) { |
141 | fb1 = fb; |
142 | if (fb1 == vbox->ddev.fb_helper->fb) |
143 | break; |
144 | } else if (fb != fb1) { |
145 | single_framebuffer = false; |
146 | } |
147 | } |
148 | if (!fb1) |
149 | return false; |
150 | |
151 | if (single_framebuffer) { |
152 | vbox->single_framebuffer = true; |
153 | vbox->input_mapping_width = fb1->width; |
154 | vbox->input_mapping_height = fb1->height; |
155 | return old_single_framebuffer != vbox->single_framebuffer; |
156 | } |
157 | /* Otherwise calculate the total span of all screens. */ |
158 | list_for_each_entry(connectori, &vbox->ddev.mode_config.connector_list, |
159 | head) { |
160 | struct vbox_connector *vbox_connector = |
161 | to_vbox_connector(connectori); |
162 | struct vbox_crtc *vbox_crtc = vbox_connector->vbox_crtc; |
163 | |
164 | width = max_t(u16, width, vbox_crtc->x_hint + |
165 | vbox_connector->mode_hint.width); |
166 | height = max_t(u16, height, vbox_crtc->y_hint + |
167 | vbox_connector->mode_hint.height); |
168 | } |
169 | |
170 | vbox->single_framebuffer = false; |
171 | vbox->input_mapping_width = width; |
172 | vbox->input_mapping_height = height; |
173 | |
174 | return old_single_framebuffer != vbox->single_framebuffer; |
175 | } |
176 | |
177 | static void vbox_crtc_set_base_and_mode(struct drm_crtc *crtc, |
178 | struct drm_framebuffer *fb, |
179 | int x, int y) |
180 | { |
181 | struct drm_gem_vram_object *gbo = drm_gem_vram_of_gem(gem: fb->obj[0]); |
182 | struct vbox_private *vbox = to_vbox_dev(crtc->dev); |
183 | struct vbox_crtc *vbox_crtc = to_vbox_crtc(crtc); |
184 | bool needs_modeset = drm_atomic_crtc_needs_modeset(state: crtc->state); |
185 | |
186 | mutex_lock(&vbox->hw_mutex); |
187 | |
188 | if (crtc->state->enable) { |
189 | vbox_crtc->width = crtc->state->mode.hdisplay; |
190 | vbox_crtc->height = crtc->state->mode.vdisplay; |
191 | } |
192 | |
193 | vbox_crtc->x = x; |
194 | vbox_crtc->y = y; |
195 | vbox_crtc->fb_offset = drm_gem_vram_offset(gbo); |
196 | |
197 | /* vbox_do_modeset() checks vbox->single_framebuffer so update it now */ |
198 | if (needs_modeset && vbox_set_up_input_mapping(vbox)) { |
199 | struct drm_crtc *crtci; |
200 | |
201 | list_for_each_entry(crtci, &vbox->ddev.mode_config.crtc_list, |
202 | head) { |
203 | if (crtci == crtc) |
204 | continue; |
205 | vbox_do_modeset(crtc: crtci); |
206 | } |
207 | } |
208 | |
209 | vbox_set_view(crtc); |
210 | vbox_do_modeset(crtc); |
211 | |
212 | if (needs_modeset) |
213 | hgsmi_update_input_mapping(ctx: vbox->guest_pool, origin_x: 0, origin_y: 0, |
214 | width: vbox->input_mapping_width, |
215 | height: vbox->input_mapping_height); |
216 | |
217 | mutex_unlock(lock: &vbox->hw_mutex); |
218 | } |
219 | |
220 | static void vbox_crtc_atomic_enable(struct drm_crtc *crtc, |
221 | struct drm_atomic_state *state) |
222 | { |
223 | } |
224 | |
225 | static void vbox_crtc_atomic_disable(struct drm_crtc *crtc, |
226 | struct drm_atomic_state *state) |
227 | { |
228 | } |
229 | |
230 | static void vbox_crtc_atomic_flush(struct drm_crtc *crtc, |
231 | struct drm_atomic_state *state) |
232 | { |
233 | } |
234 | |
235 | static const struct drm_crtc_helper_funcs vbox_crtc_helper_funcs = { |
236 | .atomic_enable = vbox_crtc_atomic_enable, |
237 | .atomic_disable = vbox_crtc_atomic_disable, |
238 | .atomic_flush = vbox_crtc_atomic_flush, |
239 | }; |
240 | |
241 | static void vbox_crtc_destroy(struct drm_crtc *crtc) |
242 | { |
243 | drm_crtc_cleanup(crtc); |
244 | kfree(objp: crtc); |
245 | } |
246 | |
247 | static const struct drm_crtc_funcs vbox_crtc_funcs = { |
248 | .set_config = drm_atomic_helper_set_config, |
249 | .page_flip = drm_atomic_helper_page_flip, |
250 | /* .gamma_set = vbox_crtc_gamma_set, */ |
251 | .destroy = vbox_crtc_destroy, |
252 | .reset = drm_atomic_helper_crtc_reset, |
253 | .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, |
254 | .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, |
255 | }; |
256 | |
257 | static int vbox_primary_atomic_check(struct drm_plane *plane, |
258 | struct drm_atomic_state *state) |
259 | { |
260 | struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, |
261 | plane); |
262 | struct drm_crtc_state *crtc_state = NULL; |
263 | |
264 | if (new_state->crtc) { |
265 | crtc_state = drm_atomic_get_existing_crtc_state(state, |
266 | crtc: new_state->crtc); |
267 | if (WARN_ON(!crtc_state)) |
268 | return -EINVAL; |
269 | } |
270 | |
271 | return drm_atomic_helper_check_plane_state(plane_state: new_state, crtc_state, |
272 | DRM_PLANE_NO_SCALING, |
273 | DRM_PLANE_NO_SCALING, |
274 | can_position: false, can_update_disabled: true); |
275 | } |
276 | |
277 | static void vbox_primary_atomic_update(struct drm_plane *plane, |
278 | struct drm_atomic_state *state) |
279 | { |
280 | struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, |
281 | plane); |
282 | struct drm_crtc *crtc = new_state->crtc; |
283 | struct drm_framebuffer *fb = new_state->fb; |
284 | struct vbox_private *vbox = to_vbox_dev(fb->dev); |
285 | struct drm_mode_rect *clips; |
286 | uint32_t num_clips, i; |
287 | |
288 | vbox_crtc_set_base_and_mode(crtc, fb, |
289 | x: new_state->src_x >> 16, |
290 | y: new_state->src_y >> 16); |
291 | |
292 | /* Send information about dirty rectangles to VBVA. */ |
293 | |
294 | clips = drm_plane_get_damage_clips(state: new_state); |
295 | num_clips = drm_plane_get_damage_clips_count(state: new_state); |
296 | |
297 | if (!num_clips) |
298 | return; |
299 | |
300 | mutex_lock(&vbox->hw_mutex); |
301 | |
302 | for (i = 0; i < num_clips; ++i, ++clips) { |
303 | struct vbva_cmd_hdr cmd_hdr; |
304 | unsigned int crtc_id = to_vbox_crtc(crtc)->crtc_id; |
305 | |
306 | cmd_hdr.x = (s16)clips->x1; |
307 | cmd_hdr.y = (s16)clips->y1; |
308 | cmd_hdr.w = (u16)clips->x2 - clips->x1; |
309 | cmd_hdr.h = (u16)clips->y2 - clips->y1; |
310 | |
311 | if (!vbva_buffer_begin_update(vbva_ctx: &vbox->vbva_info[crtc_id], |
312 | ctx: vbox->guest_pool)) |
313 | continue; |
314 | |
315 | vbva_write(vbva_ctx: &vbox->vbva_info[crtc_id], ctx: vbox->guest_pool, |
316 | p: &cmd_hdr, len: sizeof(cmd_hdr)); |
317 | vbva_buffer_end_update(vbva_ctx: &vbox->vbva_info[crtc_id]); |
318 | } |
319 | |
320 | mutex_unlock(lock: &vbox->hw_mutex); |
321 | } |
322 | |
323 | static void vbox_primary_atomic_disable(struct drm_plane *plane, |
324 | struct drm_atomic_state *state) |
325 | { |
326 | struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, |
327 | plane); |
328 | struct drm_crtc *crtc = old_state->crtc; |
329 | |
330 | /* vbox_do_modeset checks plane->state->fb and will disable if NULL */ |
331 | vbox_crtc_set_base_and_mode(crtc, fb: old_state->fb, |
332 | x: old_state->src_x >> 16, |
333 | y: old_state->src_y >> 16); |
334 | } |
335 | |
336 | static int vbox_cursor_atomic_check(struct drm_plane *plane, |
337 | struct drm_atomic_state *state) |
338 | { |
339 | struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, |
340 | plane); |
341 | struct drm_crtc_state *crtc_state = NULL; |
342 | u32 width = new_state->crtc_w; |
343 | u32 height = new_state->crtc_h; |
344 | int ret; |
345 | |
346 | if (new_state->crtc) { |
347 | crtc_state = drm_atomic_get_existing_crtc_state(state, |
348 | crtc: new_state->crtc); |
349 | if (WARN_ON(!crtc_state)) |
350 | return -EINVAL; |
351 | } |
352 | |
353 | ret = drm_atomic_helper_check_plane_state(plane_state: new_state, crtc_state, |
354 | DRM_PLANE_NO_SCALING, |
355 | DRM_PLANE_NO_SCALING, |
356 | can_position: true, can_update_disabled: true); |
357 | if (ret) |
358 | return ret; |
359 | |
360 | if (!new_state->fb) |
361 | return 0; |
362 | |
363 | if (width > VBOX_MAX_CURSOR_WIDTH || height > VBOX_MAX_CURSOR_HEIGHT || |
364 | width == 0 || height == 0) |
365 | return -EINVAL; |
366 | |
367 | return 0; |
368 | } |
369 | |
370 | /* |
371 | * Copy the ARGB image and generate the mask, which is needed in case the host |
372 | * does not support ARGB cursors. The mask is a 1BPP bitmap with the bit set |
373 | * if the corresponding alpha value in the ARGB image is greater than 0xF0. |
374 | */ |
375 | static void copy_cursor_image(u8 *src, u8 *dst, u32 width, u32 height, |
376 | size_t mask_size) |
377 | { |
378 | size_t line_size = (width + 7) / 8; |
379 | u32 i, j; |
380 | |
381 | memcpy(dst + mask_size, src, width * height * 4); |
382 | for (i = 0; i < height; ++i) |
383 | for (j = 0; j < width; ++j) |
384 | if (((u32 *)src)[i * width + j] > 0xf0000000) |
385 | dst[i * line_size + j / 8] |= (0x80 >> (j % 8)); |
386 | } |
387 | |
388 | static void vbox_cursor_atomic_update(struct drm_plane *plane, |
389 | struct drm_atomic_state *state) |
390 | { |
391 | struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, |
392 | plane); |
393 | struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, |
394 | plane); |
395 | struct vbox_private *vbox = |
396 | container_of(plane->dev, struct vbox_private, ddev); |
397 | struct vbox_crtc *vbox_crtc = to_vbox_crtc(new_state->crtc); |
398 | struct drm_framebuffer *fb = new_state->fb; |
399 | u32 width = new_state->crtc_w; |
400 | u32 height = new_state->crtc_h; |
401 | struct drm_shadow_plane_state *shadow_plane_state = |
402 | to_drm_shadow_plane_state(state: new_state); |
403 | struct iosys_map map = shadow_plane_state->data[0]; |
404 | u8 *src = map.vaddr; /* TODO: Use mapping abstraction properly */ |
405 | size_t data_size, mask_size; |
406 | u32 flags; |
407 | |
408 | /* |
409 | * VirtualBox uses the host windowing system to draw the cursor so |
410 | * moves are a no-op, we only need to upload new cursor sprites. |
411 | */ |
412 | if (fb == old_state->fb) |
413 | return; |
414 | |
415 | mutex_lock(&vbox->hw_mutex); |
416 | |
417 | vbox_crtc->cursor_enabled = true; |
418 | |
419 | /* |
420 | * The mask must be calculated based on the alpha |
421 | * channel, one bit per ARGB word, and must be 32-bit |
422 | * padded. |
423 | */ |
424 | mask_size = ((width + 7) / 8 * height + 3) & ~3; |
425 | data_size = width * height * 4 + mask_size; |
426 | |
427 | copy_cursor_image(src, dst: vbox->cursor_data, width, height, mask_size); |
428 | |
429 | flags = VBOX_MOUSE_POINTER_VISIBLE | VBOX_MOUSE_POINTER_SHAPE | |
430 | VBOX_MOUSE_POINTER_ALPHA; |
431 | hgsmi_update_pointer_shape(ctx: vbox->guest_pool, flags, |
432 | min_t(u32, max(fb->hot_x, 0), width), |
433 | min_t(u32, max(fb->hot_y, 0), height), |
434 | width, height, pixels: vbox->cursor_data, len: data_size); |
435 | |
436 | mutex_unlock(lock: &vbox->hw_mutex); |
437 | } |
438 | |
439 | static void vbox_cursor_atomic_disable(struct drm_plane *plane, |
440 | struct drm_atomic_state *state) |
441 | { |
442 | struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, |
443 | plane); |
444 | struct vbox_private *vbox = |
445 | container_of(plane->dev, struct vbox_private, ddev); |
446 | struct vbox_crtc *vbox_crtc = to_vbox_crtc(old_state->crtc); |
447 | bool cursor_enabled = false; |
448 | struct drm_crtc *crtci; |
449 | |
450 | mutex_lock(&vbox->hw_mutex); |
451 | |
452 | vbox_crtc->cursor_enabled = false; |
453 | |
454 | list_for_each_entry(crtci, &vbox->ddev.mode_config.crtc_list, head) { |
455 | if (to_vbox_crtc(crtci)->cursor_enabled) |
456 | cursor_enabled = true; |
457 | } |
458 | |
459 | if (!cursor_enabled) |
460 | hgsmi_update_pointer_shape(ctx: vbox->guest_pool, flags: 0, hot_x: 0, hot_y: 0, |
461 | width: 0, height: 0, NULL, len: 0); |
462 | |
463 | mutex_unlock(lock: &vbox->hw_mutex); |
464 | } |
465 | |
466 | static const u32 vbox_cursor_plane_formats[] = { |
467 | DRM_FORMAT_ARGB8888, |
468 | }; |
469 | |
470 | static const struct drm_plane_helper_funcs vbox_cursor_helper_funcs = { |
471 | .atomic_check = vbox_cursor_atomic_check, |
472 | .atomic_update = vbox_cursor_atomic_update, |
473 | .atomic_disable = vbox_cursor_atomic_disable, |
474 | DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, |
475 | }; |
476 | |
477 | static const struct drm_plane_funcs vbox_cursor_plane_funcs = { |
478 | .update_plane = drm_atomic_helper_update_plane, |
479 | .disable_plane = drm_atomic_helper_disable_plane, |
480 | .destroy = drm_plane_helper_destroy, |
481 | DRM_GEM_SHADOW_PLANE_FUNCS, |
482 | }; |
483 | |
484 | static const u32 vbox_primary_plane_formats[] = { |
485 | DRM_FORMAT_XRGB8888, |
486 | DRM_FORMAT_ARGB8888, |
487 | }; |
488 | |
489 | static const struct drm_plane_helper_funcs vbox_primary_helper_funcs = { |
490 | .atomic_check = vbox_primary_atomic_check, |
491 | .atomic_update = vbox_primary_atomic_update, |
492 | .atomic_disable = vbox_primary_atomic_disable, |
493 | DRM_GEM_VRAM_PLANE_HELPER_FUNCS, |
494 | }; |
495 | |
496 | static const struct drm_plane_funcs vbox_primary_plane_funcs = { |
497 | .update_plane = drm_atomic_helper_update_plane, |
498 | .disable_plane = drm_atomic_helper_disable_plane, |
499 | .destroy = drm_plane_helper_destroy, |
500 | .reset = drm_atomic_helper_plane_reset, |
501 | .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, |
502 | .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, |
503 | }; |
504 | |
505 | static struct drm_plane *vbox_create_plane(struct vbox_private *vbox, |
506 | unsigned int possible_crtcs, |
507 | enum drm_plane_type type) |
508 | { |
509 | const struct drm_plane_helper_funcs *helper_funcs = NULL; |
510 | const struct drm_plane_funcs *funcs; |
511 | struct drm_plane *plane; |
512 | const u32 *formats; |
513 | int num_formats; |
514 | int err; |
515 | |
516 | if (type == DRM_PLANE_TYPE_PRIMARY) { |
517 | funcs = &vbox_primary_plane_funcs; |
518 | formats = vbox_primary_plane_formats; |
519 | helper_funcs = &vbox_primary_helper_funcs; |
520 | num_formats = ARRAY_SIZE(vbox_primary_plane_formats); |
521 | } else if (type == DRM_PLANE_TYPE_CURSOR) { |
522 | funcs = &vbox_cursor_plane_funcs; |
523 | formats = vbox_cursor_plane_formats; |
524 | helper_funcs = &vbox_cursor_helper_funcs; |
525 | num_formats = ARRAY_SIZE(vbox_cursor_plane_formats); |
526 | } else { |
527 | return ERR_PTR(error: -EINVAL); |
528 | } |
529 | |
530 | plane = kzalloc(size: sizeof(*plane), GFP_KERNEL); |
531 | if (!plane) |
532 | return ERR_PTR(error: -ENOMEM); |
533 | |
534 | err = drm_universal_plane_init(dev: &vbox->ddev, plane, possible_crtcs, |
535 | funcs, formats, format_count: num_formats, |
536 | NULL, type, NULL); |
537 | if (err) |
538 | goto free_plane; |
539 | |
540 | drm_plane_helper_add(plane, funcs: helper_funcs); |
541 | |
542 | return plane; |
543 | |
544 | free_plane: |
545 | kfree(objp: plane); |
546 | return ERR_PTR(error: -EINVAL); |
547 | } |
548 | |
549 | static struct vbox_crtc *vbox_crtc_init(struct drm_device *dev, unsigned int i) |
550 | { |
551 | struct vbox_private *vbox = |
552 | container_of(dev, struct vbox_private, ddev); |
553 | struct drm_plane *cursor = NULL; |
554 | struct vbox_crtc *vbox_crtc; |
555 | struct drm_plane *primary; |
556 | u32 caps = 0; |
557 | int ret; |
558 | |
559 | ret = hgsmi_query_conf(ctx: vbox->guest_pool, |
560 | VBOX_VBVA_CONF32_CURSOR_CAPABILITIES, value_ret: &caps); |
561 | if (ret) |
562 | return ERR_PTR(error: ret); |
563 | |
564 | vbox_crtc = kzalloc(size: sizeof(*vbox_crtc), GFP_KERNEL); |
565 | if (!vbox_crtc) |
566 | return ERR_PTR(error: -ENOMEM); |
567 | |
568 | primary = vbox_create_plane(vbox, possible_crtcs: 1 << i, type: DRM_PLANE_TYPE_PRIMARY); |
569 | if (IS_ERR(ptr: primary)) { |
570 | ret = PTR_ERR(ptr: primary); |
571 | goto free_mem; |
572 | } |
573 | |
574 | if ((caps & VBOX_VBVA_CURSOR_CAPABILITY_HARDWARE)) { |
575 | cursor = vbox_create_plane(vbox, possible_crtcs: 1 << i, type: DRM_PLANE_TYPE_CURSOR); |
576 | if (IS_ERR(ptr: cursor)) { |
577 | ret = PTR_ERR(ptr: cursor); |
578 | goto clean_primary; |
579 | } |
580 | } else { |
581 | DRM_WARN("VirtualBox host is too old, no cursor support\n" ); |
582 | } |
583 | |
584 | vbox_crtc->crtc_id = i; |
585 | |
586 | ret = drm_crtc_init_with_planes(dev, crtc: &vbox_crtc->base, primary, cursor, |
587 | funcs: &vbox_crtc_funcs, NULL); |
588 | if (ret) |
589 | goto clean_cursor; |
590 | |
591 | drm_mode_crtc_set_gamma_size(crtc: &vbox_crtc->base, gamma_size: 256); |
592 | drm_crtc_helper_add(crtc: &vbox_crtc->base, funcs: &vbox_crtc_helper_funcs); |
593 | |
594 | return vbox_crtc; |
595 | |
596 | clean_cursor: |
597 | if (cursor) { |
598 | drm_plane_cleanup(plane: cursor); |
599 | kfree(objp: cursor); |
600 | } |
601 | clean_primary: |
602 | drm_plane_cleanup(plane: primary); |
603 | kfree(objp: primary); |
604 | free_mem: |
605 | kfree(objp: vbox_crtc); |
606 | return ERR_PTR(error: ret); |
607 | } |
608 | |
609 | static void vbox_encoder_destroy(struct drm_encoder *encoder) |
610 | { |
611 | drm_encoder_cleanup(encoder); |
612 | kfree(objp: encoder); |
613 | } |
614 | |
615 | static const struct drm_encoder_funcs vbox_enc_funcs = { |
616 | .destroy = vbox_encoder_destroy, |
617 | }; |
618 | |
619 | static struct drm_encoder *vbox_encoder_init(struct drm_device *dev, |
620 | unsigned int i) |
621 | { |
622 | struct vbox_encoder *vbox_encoder; |
623 | |
624 | vbox_encoder = kzalloc(size: sizeof(*vbox_encoder), GFP_KERNEL); |
625 | if (!vbox_encoder) |
626 | return NULL; |
627 | |
628 | drm_encoder_init(dev, encoder: &vbox_encoder->base, funcs: &vbox_enc_funcs, |
629 | DRM_MODE_ENCODER_DAC, NULL); |
630 | |
631 | vbox_encoder->base.possible_crtcs = 1 << i; |
632 | return &vbox_encoder->base; |
633 | } |
634 | |
635 | /* |
636 | * Generate EDID data with a mode-unique serial number for the virtual |
637 | * monitor to try to persuade Unity that different modes correspond to |
638 | * different monitors and it should not try to force the same resolution on |
639 | * them. |
640 | */ |
641 | static void vbox_set_edid(struct drm_connector *connector, int width, |
642 | int height) |
643 | { |
644 | enum { EDID_SIZE = 128 }; |
645 | unsigned char edid[EDID_SIZE] = { |
646 | 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, /* header */ |
647 | 0x58, 0x58, /* manufacturer (VBX) */ |
648 | 0x00, 0x00, /* product code */ |
649 | 0x00, 0x00, 0x00, 0x00, /* serial number goes here */ |
650 | 0x01, /* week of manufacture */ |
651 | 0x00, /* year of manufacture */ |
652 | 0x01, 0x03, /* EDID version */ |
653 | 0x80, /* capabilities - digital */ |
654 | 0x00, /* horiz. res in cm, zero for projectors */ |
655 | 0x00, /* vert. res in cm */ |
656 | 0x78, /* display gamma (120 == 2.2). */ |
657 | 0xEE, /* features (standby, suspend, off, RGB, std */ |
658 | /* colour space, preferred timing mode) */ |
659 | 0xEE, 0x91, 0xA3, 0x54, 0x4C, 0x99, 0x26, 0x0F, 0x50, 0x54, |
660 | /* chromaticity for standard colour space. */ |
661 | 0x00, 0x00, 0x00, /* no default timings */ |
662 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, |
663 | 0x01, 0x01, |
664 | 0x01, 0x01, 0x01, 0x01, /* no standard timings */ |
665 | 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x02, 0x02, |
666 | 0x02, 0x02, |
667 | /* descriptor block 1 goes below */ |
668 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
669 | /* descriptor block 2, monitor ranges */ |
670 | 0x00, 0x00, 0x00, 0xFD, 0x00, |
671 | 0x00, 0xC8, 0x00, 0xC8, 0x64, 0x00, 0x0A, 0x20, 0x20, 0x20, |
672 | 0x20, 0x20, |
673 | /* 0-200Hz vertical, 0-200KHz horizontal, 1000MHz pixel clock */ |
674 | 0x20, |
675 | /* descriptor block 3, monitor name */ |
676 | 0x00, 0x00, 0x00, 0xFC, 0x00, |
677 | 'V', 'B', 'O', 'X', ' ', 'm', 'o', 'n', 'i', 't', 'o', 'r', |
678 | '\n', |
679 | /* descriptor block 4: dummy data */ |
680 | 0x00, 0x00, 0x00, 0x10, 0x00, |
681 | 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, |
682 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, |
683 | 0x20, |
684 | 0x00, /* number of extensions */ |
685 | 0x00 /* checksum goes here */ |
686 | }; |
687 | int clock = (width + 6) * (height + 6) * 60 / 10000; |
688 | unsigned int i, sum = 0; |
689 | |
690 | edid[12] = width & 0xff; |
691 | edid[13] = width >> 8; |
692 | edid[14] = height & 0xff; |
693 | edid[15] = height >> 8; |
694 | edid[54] = clock & 0xff; |
695 | edid[55] = clock >> 8; |
696 | edid[56] = width & 0xff; |
697 | edid[58] = (width >> 4) & 0xf0; |
698 | edid[59] = height & 0xff; |
699 | edid[61] = (height >> 4) & 0xf0; |
700 | for (i = 0; i < EDID_SIZE - 1; ++i) |
701 | sum += edid[i]; |
702 | edid[EDID_SIZE - 1] = (0x100 - (sum & 0xFF)) & 0xFF; |
703 | drm_connector_update_edid_property(connector, edid: (struct edid *)edid); |
704 | } |
705 | |
706 | static int vbox_get_modes(struct drm_connector *connector) |
707 | { |
708 | struct vbox_connector *vbox_connector = NULL; |
709 | struct drm_display_mode *mode = NULL; |
710 | struct vbox_private *vbox = NULL; |
711 | unsigned int num_modes = 0; |
712 | int preferred_width, preferred_height; |
713 | |
714 | vbox_connector = to_vbox_connector(connector); |
715 | vbox = to_vbox_dev(connector->dev); |
716 | |
717 | hgsmi_report_flags_location(ctx: vbox->guest_pool, GUEST_HEAP_OFFSET(vbox) + |
718 | HOST_FLAGS_OFFSET); |
719 | if (vbox_connector->vbox_crtc->crtc_id == 0) |
720 | vbox_report_caps(vbox); |
721 | |
722 | num_modes = drm_add_modes_noedid(connector, hdisplay: 2560, vdisplay: 1600); |
723 | preferred_width = vbox_connector->mode_hint.width ? |
724 | vbox_connector->mode_hint.width : 1024; |
725 | preferred_height = vbox_connector->mode_hint.height ? |
726 | vbox_connector->mode_hint.height : 768; |
727 | mode = drm_cvt_mode(dev: connector->dev, hdisplay: preferred_width, vdisplay: preferred_height, |
728 | vrefresh: 60, reduced: false, interlaced: false, margins: false); |
729 | if (mode) { |
730 | mode->type |= DRM_MODE_TYPE_PREFERRED; |
731 | drm_mode_probed_add(connector, mode); |
732 | ++num_modes; |
733 | } |
734 | vbox_set_edid(connector, width: preferred_width, height: preferred_height); |
735 | |
736 | if (vbox_connector->vbox_crtc->x_hint != -1) |
737 | drm_object_property_set_value(obj: &connector->base, |
738 | property: vbox->ddev.mode_config.suggested_x_property, |
739 | val: vbox_connector->vbox_crtc->x_hint); |
740 | else |
741 | drm_object_property_set_value(obj: &connector->base, |
742 | property: vbox->ddev.mode_config.suggested_x_property, val: 0); |
743 | |
744 | if (vbox_connector->vbox_crtc->y_hint != -1) |
745 | drm_object_property_set_value(obj: &connector->base, |
746 | property: vbox->ddev.mode_config.suggested_y_property, |
747 | val: vbox_connector->vbox_crtc->y_hint); |
748 | else |
749 | drm_object_property_set_value(obj: &connector->base, |
750 | property: vbox->ddev.mode_config.suggested_y_property, val: 0); |
751 | |
752 | return num_modes; |
753 | } |
754 | |
755 | static void vbox_connector_destroy(struct drm_connector *connector) |
756 | { |
757 | drm_connector_unregister(connector); |
758 | drm_connector_cleanup(connector); |
759 | kfree(objp: connector); |
760 | } |
761 | |
762 | static enum drm_connector_status |
763 | vbox_connector_detect(struct drm_connector *connector, bool force) |
764 | { |
765 | struct vbox_connector *vbox_connector; |
766 | |
767 | vbox_connector = to_vbox_connector(connector); |
768 | |
769 | return vbox_connector->mode_hint.disconnected ? |
770 | connector_status_disconnected : connector_status_connected; |
771 | } |
772 | |
773 | static int vbox_fill_modes(struct drm_connector *connector, u32 max_x, |
774 | u32 max_y) |
775 | { |
776 | struct vbox_connector *vbox_connector; |
777 | struct drm_device *dev; |
778 | struct drm_display_mode *mode, *iterator; |
779 | |
780 | vbox_connector = to_vbox_connector(connector); |
781 | dev = vbox_connector->base.dev; |
782 | list_for_each_entry_safe(mode, iterator, &connector->modes, head) { |
783 | list_del(entry: &mode->head); |
784 | drm_mode_destroy(dev, mode); |
785 | } |
786 | |
787 | return drm_helper_probe_single_connector_modes(connector, maxX: max_x, maxY: max_y); |
788 | } |
789 | |
790 | static const struct drm_connector_helper_funcs vbox_connector_helper_funcs = { |
791 | .get_modes = vbox_get_modes, |
792 | }; |
793 | |
794 | static const struct drm_connector_funcs vbox_connector_funcs = { |
795 | .detect = vbox_connector_detect, |
796 | .fill_modes = vbox_fill_modes, |
797 | .destroy = vbox_connector_destroy, |
798 | .reset = drm_atomic_helper_connector_reset, |
799 | .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, |
800 | .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, |
801 | }; |
802 | |
803 | static int vbox_connector_init(struct drm_device *dev, |
804 | struct vbox_crtc *vbox_crtc, |
805 | struct drm_encoder *encoder) |
806 | { |
807 | struct vbox_connector *vbox_connector; |
808 | struct drm_connector *connector; |
809 | |
810 | vbox_connector = kzalloc(size: sizeof(*vbox_connector), GFP_KERNEL); |
811 | if (!vbox_connector) |
812 | return -ENOMEM; |
813 | |
814 | connector = &vbox_connector->base; |
815 | vbox_connector->vbox_crtc = vbox_crtc; |
816 | |
817 | drm_connector_init(dev, connector, funcs: &vbox_connector_funcs, |
818 | DRM_MODE_CONNECTOR_VGA); |
819 | drm_connector_helper_add(connector, funcs: &vbox_connector_helper_funcs); |
820 | |
821 | connector->interlace_allowed = 0; |
822 | connector->doublescan_allowed = 0; |
823 | |
824 | drm_mode_create_suggested_offset_properties(dev); |
825 | drm_object_attach_property(obj: &connector->base, |
826 | property: dev->mode_config.suggested_x_property, init_val: 0); |
827 | drm_object_attach_property(obj: &connector->base, |
828 | property: dev->mode_config.suggested_y_property, init_val: 0); |
829 | |
830 | drm_connector_attach_encoder(connector, encoder); |
831 | |
832 | return 0; |
833 | } |
834 | |
835 | static const struct drm_mode_config_funcs vbox_mode_funcs = { |
836 | .fb_create = drm_gem_fb_create_with_dirty, |
837 | .mode_valid = drm_vram_helper_mode_valid, |
838 | .atomic_check = drm_atomic_helper_check, |
839 | .atomic_commit = drm_atomic_helper_commit, |
840 | }; |
841 | |
842 | int vbox_mode_init(struct vbox_private *vbox) |
843 | { |
844 | struct drm_device *dev = &vbox->ddev; |
845 | struct drm_encoder *encoder; |
846 | struct vbox_crtc *vbox_crtc; |
847 | unsigned int i; |
848 | int ret; |
849 | |
850 | drm_mode_config_init(dev); |
851 | |
852 | dev->mode_config.funcs = (void *)&vbox_mode_funcs; |
853 | dev->mode_config.min_width = 0; |
854 | dev->mode_config.min_height = 0; |
855 | dev->mode_config.preferred_depth = 24; |
856 | dev->mode_config.max_width = VBE_DISPI_MAX_XRES; |
857 | dev->mode_config.max_height = VBE_DISPI_MAX_YRES; |
858 | |
859 | for (i = 0; i < vbox->num_crtcs; ++i) { |
860 | vbox_crtc = vbox_crtc_init(dev, i); |
861 | if (IS_ERR(ptr: vbox_crtc)) { |
862 | ret = PTR_ERR(ptr: vbox_crtc); |
863 | goto err_drm_mode_cleanup; |
864 | } |
865 | encoder = vbox_encoder_init(dev, i); |
866 | if (!encoder) { |
867 | ret = -ENOMEM; |
868 | goto err_drm_mode_cleanup; |
869 | } |
870 | ret = vbox_connector_init(dev, vbox_crtc, encoder); |
871 | if (ret) |
872 | goto err_drm_mode_cleanup; |
873 | } |
874 | |
875 | drm_mode_config_reset(dev); |
876 | return 0; |
877 | |
878 | err_drm_mode_cleanup: |
879 | drm_mode_config_cleanup(dev); |
880 | return ret; |
881 | } |
882 | |
883 | void vbox_mode_fini(struct vbox_private *vbox) |
884 | { |
885 | drm_mode_config_cleanup(dev: &vbox->ddev); |
886 | } |
887 | |