1 | // SPDX-License-Identifier: GPL-2.0 OR MIT |
2 | /************************************************************************** |
3 | * |
4 | * Copyright 2011-2023 VMware, Inc., Palo Alto, CA., USA |
5 | * |
6 | * Permission is hereby granted, free of charge, to any person obtaining a |
7 | * copy of this software and associated documentation files (the |
8 | * "Software"), to deal in the Software without restriction, including |
9 | * without limitation the rights to use, copy, modify, merge, publish, |
10 | * distribute, sub license, and/or sell copies of the Software, and to |
11 | * permit persons to whom the Software is furnished to do so, subject to |
12 | * the following conditions: |
13 | * |
14 | * The above copyright notice and this permission notice (including the |
15 | * next paragraph) shall be included in all copies or substantial portions |
16 | * of the Software. |
17 | * |
18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
20 | * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL |
21 | * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, |
22 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
23 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE |
24 | * USE OR OTHER DEALINGS IN THE SOFTWARE. |
25 | * |
26 | **************************************************************************/ |
27 | |
28 | #include "vmwgfx_bo.h" |
29 | #include "vmwgfx_kms.h" |
30 | |
31 | #include <drm/drm_atomic.h> |
32 | #include <drm/drm_atomic_helper.h> |
33 | #include <drm/drm_damage_helper.h> |
34 | #include <drm/drm_fourcc.h> |
35 | |
36 | #define vmw_crtc_to_sou(x) \ |
37 | container_of(x, struct vmw_screen_object_unit, base.crtc) |
38 | #define vmw_encoder_to_sou(x) \ |
39 | container_of(x, struct vmw_screen_object_unit, base.encoder) |
40 | #define vmw_connector_to_sou(x) \ |
41 | container_of(x, struct vmw_screen_object_unit, base.connector) |
42 | |
43 | /** |
44 | * struct vmw_kms_sou_surface_dirty - Closure structure for |
45 | * blit surface to screen command. |
46 | * @base: The base type we derive from. Used by vmw_kms_helper_dirty(). |
47 | * @left: Left side of bounding box. |
48 | * @right: Right side of bounding box. |
49 | * @top: Top side of bounding box. |
50 | * @bottom: Bottom side of bounding box. |
51 | * @dst_x: Difference between source clip rects and framebuffer coordinates. |
52 | * @dst_y: Difference between source clip rects and framebuffer coordinates. |
53 | * @sid: Surface id of surface to copy from. |
54 | */ |
55 | struct vmw_kms_sou_surface_dirty { |
56 | struct vmw_kms_dirty base; |
57 | s32 left, right, top, bottom; |
58 | s32 dst_x, dst_y; |
59 | u32 sid; |
60 | }; |
61 | |
62 | /* |
63 | * SVGA commands that are used by this code. Please see the device headers |
64 | * for explanation. |
65 | */ |
66 | struct vmw_kms_sou_readback_blit { |
67 | uint32 ; |
68 | SVGAFifoCmdBlitScreenToGMRFB body; |
69 | }; |
70 | |
71 | struct vmw_kms_sou_bo_blit { |
72 | uint32 ; |
73 | SVGAFifoCmdBlitGMRFBToScreen body; |
74 | }; |
75 | |
76 | struct vmw_kms_sou_dirty_cmd { |
77 | SVGA3dCmdHeader ; |
78 | SVGA3dCmdBlitSurfaceToScreen body; |
79 | }; |
80 | |
81 | struct vmw_kms_sou_define_gmrfb { |
82 | uint32_t ; |
83 | SVGAFifoCmdDefineGMRFB body; |
84 | }; |
85 | |
86 | /* |
87 | * Display unit using screen objects. |
88 | */ |
89 | struct vmw_screen_object_unit { |
90 | struct vmw_display_unit base; |
91 | |
92 | unsigned long buffer_size; /**< Size of allocated buffer */ |
93 | struct vmw_bo *buffer; /**< Backing store buffer */ |
94 | |
95 | bool defined; |
96 | }; |
97 | |
98 | static void vmw_sou_destroy(struct vmw_screen_object_unit *sou) |
99 | { |
100 | vmw_du_cleanup(du: &sou->base); |
101 | kfree(objp: sou); |
102 | } |
103 | |
104 | |
105 | /* |
106 | * Screen Object Display Unit CRTC functions |
107 | */ |
108 | |
109 | static void vmw_sou_crtc_destroy(struct drm_crtc *crtc) |
110 | { |
111 | vmw_sou_destroy(vmw_crtc_to_sou(crtc)); |
112 | } |
113 | |
114 | /* |
115 | * Send the fifo command to create a screen. |
116 | */ |
117 | static int vmw_sou_fifo_create(struct vmw_private *dev_priv, |
118 | struct vmw_screen_object_unit *sou, |
119 | int x, int y, |
120 | struct drm_display_mode *mode) |
121 | { |
122 | size_t fifo_size; |
123 | |
124 | struct { |
125 | struct { |
126 | uint32_t cmdType; |
127 | } ; |
128 | SVGAScreenObject obj; |
129 | } *cmd; |
130 | |
131 | BUG_ON(!sou->buffer); |
132 | |
133 | fifo_size = sizeof(*cmd); |
134 | cmd = VMW_CMD_RESERVE(dev_priv, fifo_size); |
135 | if (unlikely(cmd == NULL)) |
136 | return -ENOMEM; |
137 | |
138 | memset(cmd, 0, fifo_size); |
139 | cmd->header.cmdType = SVGA_CMD_DEFINE_SCREEN; |
140 | cmd->obj.structSize = sizeof(SVGAScreenObject); |
141 | cmd->obj.id = sou->base.unit; |
142 | cmd->obj.flags = SVGA_SCREEN_HAS_ROOT | |
143 | (sou->base.unit == 0 ? SVGA_SCREEN_IS_PRIMARY : 0); |
144 | cmd->obj.size.width = mode->hdisplay; |
145 | cmd->obj.size.height = mode->vdisplay; |
146 | cmd->obj.root.x = x; |
147 | cmd->obj.root.y = y; |
148 | sou->base.set_gui_x = cmd->obj.root.x; |
149 | sou->base.set_gui_y = cmd->obj.root.y; |
150 | |
151 | /* Ok to assume that buffer is pinned in vram */ |
152 | vmw_bo_get_guest_ptr(buf: &sou->buffer->tbo, ptr: &cmd->obj.backingStore.ptr); |
153 | cmd->obj.backingStore.pitch = mode->hdisplay * 4; |
154 | |
155 | vmw_cmd_commit(dev_priv, bytes: fifo_size); |
156 | |
157 | sou->defined = true; |
158 | |
159 | return 0; |
160 | } |
161 | |
162 | /* |
163 | * Send the fifo command to destroy a screen. |
164 | */ |
165 | static int vmw_sou_fifo_destroy(struct vmw_private *dev_priv, |
166 | struct vmw_screen_object_unit *sou) |
167 | { |
168 | size_t fifo_size; |
169 | int ret; |
170 | |
171 | struct { |
172 | struct { |
173 | uint32_t cmdType; |
174 | } ; |
175 | SVGAFifoCmdDestroyScreen body; |
176 | } *cmd; |
177 | |
178 | /* no need to do anything */ |
179 | if (unlikely(!sou->defined)) |
180 | return 0; |
181 | |
182 | fifo_size = sizeof(*cmd); |
183 | cmd = VMW_CMD_RESERVE(dev_priv, fifo_size); |
184 | if (unlikely(cmd == NULL)) |
185 | return -ENOMEM; |
186 | |
187 | memset(cmd, 0, fifo_size); |
188 | cmd->header.cmdType = SVGA_CMD_DESTROY_SCREEN; |
189 | cmd->body.screenId = sou->base.unit; |
190 | |
191 | vmw_cmd_commit(dev_priv, bytes: fifo_size); |
192 | |
193 | /* Force sync */ |
194 | ret = vmw_fallback_wait(dev_priv, lazy: false, fifo_idle: true, seqno: 0, interruptible: false, timeout: 3*HZ); |
195 | if (unlikely(ret != 0)) |
196 | DRM_ERROR("Failed to sync with HW" ); |
197 | else |
198 | sou->defined = false; |
199 | |
200 | return ret; |
201 | } |
202 | |
203 | /** |
204 | * vmw_sou_crtc_mode_set_nofb - Create new screen |
205 | * |
206 | * @crtc: CRTC associated with the new screen |
207 | * |
208 | * This function creates/destroys a screen. This function cannot fail, so if |
209 | * somehow we run into a failure, just do the best we can to get out. |
210 | */ |
211 | static void vmw_sou_crtc_mode_set_nofb(struct drm_crtc *crtc) |
212 | { |
213 | struct vmw_private *dev_priv; |
214 | struct vmw_screen_object_unit *sou; |
215 | struct vmw_framebuffer *vfb; |
216 | struct drm_framebuffer *fb; |
217 | struct drm_plane_state *ps; |
218 | struct vmw_plane_state *vps; |
219 | int ret; |
220 | |
221 | sou = vmw_crtc_to_sou(crtc); |
222 | dev_priv = vmw_priv(dev: crtc->dev); |
223 | ps = crtc->primary->state; |
224 | fb = ps->fb; |
225 | vps = vmw_plane_state_to_vps(ps); |
226 | |
227 | vfb = (fb) ? vmw_framebuffer_to_vfb(fb) : NULL; |
228 | |
229 | if (sou->defined) { |
230 | ret = vmw_sou_fifo_destroy(dev_priv, sou); |
231 | if (ret) { |
232 | DRM_ERROR("Failed to destroy Screen Object\n" ); |
233 | return; |
234 | } |
235 | } |
236 | |
237 | if (vfb) { |
238 | struct drm_connector_state *conn_state; |
239 | struct vmw_connector_state *vmw_conn_state; |
240 | int x, y; |
241 | |
242 | sou->buffer = vps->bo; |
243 | sou->buffer_size = vps->bo_size; |
244 | |
245 | conn_state = sou->base.connector.state; |
246 | vmw_conn_state = vmw_connector_state_to_vcs(conn_state); |
247 | |
248 | x = vmw_conn_state->gui_x; |
249 | y = vmw_conn_state->gui_y; |
250 | |
251 | ret = vmw_sou_fifo_create(dev_priv, sou, x, y, mode: &crtc->mode); |
252 | if (ret) |
253 | DRM_ERROR("Failed to define Screen Object %dx%d\n" , |
254 | crtc->x, crtc->y); |
255 | |
256 | } else { |
257 | sou->buffer = NULL; |
258 | sou->buffer_size = 0; |
259 | } |
260 | } |
261 | |
262 | /** |
263 | * vmw_sou_crtc_helper_prepare - Noop |
264 | * |
265 | * @crtc: CRTC associated with the new screen |
266 | * |
267 | * Prepares the CRTC for a mode set, but we don't need to do anything here. |
268 | */ |
269 | static void vmw_sou_crtc_helper_prepare(struct drm_crtc *crtc) |
270 | { |
271 | } |
272 | |
273 | /** |
274 | * vmw_sou_crtc_atomic_enable - Noop |
275 | * |
276 | * @crtc: CRTC associated with the new screen |
277 | * @state: Unused |
278 | * |
279 | * This is called after a mode set has been completed. |
280 | */ |
281 | static void vmw_sou_crtc_atomic_enable(struct drm_crtc *crtc, |
282 | struct drm_atomic_state *state) |
283 | { |
284 | } |
285 | |
286 | /** |
287 | * vmw_sou_crtc_atomic_disable - Turns off CRTC |
288 | * |
289 | * @crtc: CRTC to be turned off |
290 | * @state: Unused |
291 | */ |
292 | static void vmw_sou_crtc_atomic_disable(struct drm_crtc *crtc, |
293 | struct drm_atomic_state *state) |
294 | { |
295 | struct vmw_private *dev_priv; |
296 | struct vmw_screen_object_unit *sou; |
297 | int ret; |
298 | |
299 | |
300 | if (!crtc) { |
301 | DRM_ERROR("CRTC is NULL\n" ); |
302 | return; |
303 | } |
304 | |
305 | sou = vmw_crtc_to_sou(crtc); |
306 | dev_priv = vmw_priv(dev: crtc->dev); |
307 | |
308 | if (sou->defined) { |
309 | ret = vmw_sou_fifo_destroy(dev_priv, sou); |
310 | if (ret) |
311 | DRM_ERROR("Failed to destroy Screen Object\n" ); |
312 | } |
313 | } |
314 | |
315 | static const struct drm_crtc_funcs vmw_screen_object_crtc_funcs = { |
316 | .gamma_set = vmw_du_crtc_gamma_set, |
317 | .destroy = vmw_sou_crtc_destroy, |
318 | .reset = vmw_du_crtc_reset, |
319 | .atomic_duplicate_state = vmw_du_crtc_duplicate_state, |
320 | .atomic_destroy_state = vmw_du_crtc_destroy_state, |
321 | .set_config = drm_atomic_helper_set_config, |
322 | .page_flip = drm_atomic_helper_page_flip, |
323 | }; |
324 | |
325 | /* |
326 | * Screen Object Display Unit encoder functions |
327 | */ |
328 | |
329 | static void vmw_sou_encoder_destroy(struct drm_encoder *encoder) |
330 | { |
331 | vmw_sou_destroy(vmw_encoder_to_sou(encoder)); |
332 | } |
333 | |
334 | static const struct drm_encoder_funcs vmw_screen_object_encoder_funcs = { |
335 | .destroy = vmw_sou_encoder_destroy, |
336 | }; |
337 | |
338 | /* |
339 | * Screen Object Display Unit connector functions |
340 | */ |
341 | |
342 | static void vmw_sou_connector_destroy(struct drm_connector *connector) |
343 | { |
344 | vmw_sou_destroy(vmw_connector_to_sou(connector)); |
345 | } |
346 | |
347 | static const struct drm_connector_funcs vmw_sou_connector_funcs = { |
348 | .dpms = vmw_du_connector_dpms, |
349 | .detect = vmw_du_connector_detect, |
350 | .fill_modes = drm_helper_probe_single_connector_modes, |
351 | .destroy = vmw_sou_connector_destroy, |
352 | .reset = vmw_du_connector_reset, |
353 | .atomic_duplicate_state = vmw_du_connector_duplicate_state, |
354 | .atomic_destroy_state = vmw_du_connector_destroy_state, |
355 | }; |
356 | |
357 | |
358 | static const struct |
359 | drm_connector_helper_funcs vmw_sou_connector_helper_funcs = { |
360 | .get_modes = vmw_connector_get_modes, |
361 | .mode_valid = vmw_connector_mode_valid |
362 | }; |
363 | |
364 | |
365 | |
366 | /* |
367 | * Screen Object Display Plane Functions |
368 | */ |
369 | |
370 | /** |
371 | * vmw_sou_primary_plane_cleanup_fb - Frees sou backing buffer |
372 | * |
373 | * @plane: display plane |
374 | * @old_state: Contains the FB to clean up |
375 | * |
376 | * Unpins the display surface |
377 | * |
378 | * Returns 0 on success |
379 | */ |
380 | static void |
381 | vmw_sou_primary_plane_cleanup_fb(struct drm_plane *plane, |
382 | struct drm_plane_state *old_state) |
383 | { |
384 | struct vmw_plane_state *vps = vmw_plane_state_to_vps(old_state); |
385 | struct drm_crtc *crtc = plane->state->crtc ? |
386 | plane->state->crtc : old_state->crtc; |
387 | |
388 | if (vps->bo) |
389 | vmw_bo_unpin(vmw_priv: vmw_priv(dev: crtc->dev), bo: vps->bo, interruptible: false); |
390 | vmw_bo_unreference(buf: &vps->bo); |
391 | vps->bo_size = 0; |
392 | |
393 | vmw_du_plane_cleanup_fb(plane, old_state); |
394 | } |
395 | |
396 | |
397 | /** |
398 | * vmw_sou_primary_plane_prepare_fb - allocate backing buffer |
399 | * |
400 | * @plane: display plane |
401 | * @new_state: info on the new plane state, including the FB |
402 | * |
403 | * The SOU backing buffer is our equivalent of the display plane. |
404 | * |
405 | * Returns 0 on success |
406 | */ |
407 | static int |
408 | vmw_sou_primary_plane_prepare_fb(struct drm_plane *plane, |
409 | struct drm_plane_state *new_state) |
410 | { |
411 | struct drm_framebuffer *new_fb = new_state->fb; |
412 | struct drm_crtc *crtc = plane->state->crtc ?: new_state->crtc; |
413 | struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state); |
414 | struct vmw_private *dev_priv; |
415 | int ret; |
416 | struct vmw_bo_params bo_params = { |
417 | .domain = VMW_BO_DOMAIN_VRAM, |
418 | .busy_domain = VMW_BO_DOMAIN_VRAM, |
419 | .bo_type = ttm_bo_type_device, |
420 | .pin = true |
421 | }; |
422 | |
423 | if (!new_fb) { |
424 | vmw_bo_unreference(buf: &vps->bo); |
425 | vps->bo_size = 0; |
426 | |
427 | return 0; |
428 | } |
429 | |
430 | bo_params.size = new_state->crtc_w * new_state->crtc_h * 4; |
431 | dev_priv = vmw_priv(dev: crtc->dev); |
432 | |
433 | if (vps->bo) { |
434 | if (vps->bo_size == bo_params.size) { |
435 | /* |
436 | * Note that this might temporarily up the pin-count |
437 | * to 2, until cleanup_fb() is called. |
438 | */ |
439 | return vmw_bo_pin_in_vram(dev_priv, buf: vps->bo, |
440 | interruptible: true); |
441 | } |
442 | |
443 | vmw_bo_unreference(buf: &vps->bo); |
444 | vps->bo_size = 0; |
445 | } |
446 | |
447 | vmw_svga_enable(dev_priv); |
448 | |
449 | /* After we have alloced the backing store might not be able to |
450 | * resume the overlays, this is preferred to failing to alloc. |
451 | */ |
452 | vmw_overlay_pause_all(dev_priv); |
453 | ret = vmw_bo_create(dev_priv, params: &bo_params, p_bo: &vps->bo); |
454 | vmw_overlay_resume_all(dev_priv); |
455 | if (ret) |
456 | return ret; |
457 | |
458 | vps->bo_size = bo_params.size; |
459 | |
460 | /* |
461 | * TTM already thinks the buffer is pinned, but make sure the |
462 | * pin_count is upped. |
463 | */ |
464 | return vmw_bo_pin_in_vram(dev_priv, buf: vps->bo, interruptible: true); |
465 | } |
466 | |
467 | static uint32_t vmw_sou_bo_fifo_size(struct vmw_du_update_plane *update, |
468 | uint32_t num_hits) |
469 | { |
470 | return sizeof(struct vmw_kms_sou_define_gmrfb) + |
471 | sizeof(struct vmw_kms_sou_bo_blit) * num_hits; |
472 | } |
473 | |
474 | static uint32_t vmw_sou_bo_define_gmrfb(struct vmw_du_update_plane *update, |
475 | void *cmd) |
476 | { |
477 | struct vmw_framebuffer_bo *vfbbo = |
478 | container_of(update->vfb, typeof(*vfbbo), base); |
479 | struct vmw_kms_sou_define_gmrfb *gmr = cmd; |
480 | int depth = update->vfb->base.format->depth; |
481 | |
482 | /* Emulate RGBA support, contrary to svga_reg.h this is not |
483 | * supported by hosts. This is only a problem if we are reading |
484 | * this value later and expecting what we uploaded back. |
485 | */ |
486 | if (depth == 32) |
487 | depth = 24; |
488 | |
489 | gmr->header = SVGA_CMD_DEFINE_GMRFB; |
490 | |
491 | gmr->body.format.bitsPerPixel = update->vfb->base.format->cpp[0] * 8; |
492 | gmr->body.format.colorDepth = depth; |
493 | gmr->body.format.reserved = 0; |
494 | gmr->body.bytesPerLine = update->vfb->base.pitches[0]; |
495 | vmw_bo_get_guest_ptr(buf: &vfbbo->buffer->tbo, ptr: &gmr->body.ptr); |
496 | |
497 | return sizeof(*gmr); |
498 | } |
499 | |
500 | static uint32_t vmw_sou_bo_populate_clip(struct vmw_du_update_plane *update, |
501 | void *cmd, struct drm_rect *clip, |
502 | uint32_t fb_x, uint32_t fb_y) |
503 | { |
504 | struct vmw_kms_sou_bo_blit *blit = cmd; |
505 | |
506 | blit->header = SVGA_CMD_BLIT_GMRFB_TO_SCREEN; |
507 | blit->body.destScreenId = update->du->unit; |
508 | blit->body.srcOrigin.x = fb_x; |
509 | blit->body.srcOrigin.y = fb_y; |
510 | blit->body.destRect.left = clip->x1; |
511 | blit->body.destRect.top = clip->y1; |
512 | blit->body.destRect.right = clip->x2; |
513 | blit->body.destRect.bottom = clip->y2; |
514 | |
515 | return sizeof(*blit); |
516 | } |
517 | |
518 | static uint32_t vmw_stud_bo_post_clip(struct vmw_du_update_plane *update, |
519 | void *cmd, struct drm_rect *bb) |
520 | { |
521 | return 0; |
522 | } |
523 | |
524 | /** |
525 | * vmw_sou_plane_update_bo - Update display unit for bo backed fb. |
526 | * @dev_priv: Device private. |
527 | * @plane: Plane state. |
528 | * @old_state: Old plane state. |
529 | * @vfb: Framebuffer which is blitted to display unit. |
530 | * @out_fence: If non-NULL, will return a ref-counted pointer to vmw_fence_obj. |
531 | * The returned fence pointer may be NULL in which case the device |
532 | * has already synchronized. |
533 | * |
534 | * Return: 0 on success or a negative error code on failure. |
535 | */ |
536 | static int vmw_sou_plane_update_bo(struct vmw_private *dev_priv, |
537 | struct drm_plane *plane, |
538 | struct drm_plane_state *old_state, |
539 | struct vmw_framebuffer *vfb, |
540 | struct vmw_fence_obj **out_fence) |
541 | { |
542 | struct vmw_du_update_plane_buffer bo_update; |
543 | |
544 | memset(&bo_update, 0, sizeof(struct vmw_du_update_plane_buffer)); |
545 | bo_update.base.plane = plane; |
546 | bo_update.base.old_state = old_state; |
547 | bo_update.base.dev_priv = dev_priv; |
548 | bo_update.base.du = vmw_crtc_to_du(plane->state->crtc); |
549 | bo_update.base.vfb = vfb; |
550 | bo_update.base.out_fence = out_fence; |
551 | bo_update.base.mutex = NULL; |
552 | bo_update.base.intr = true; |
553 | |
554 | bo_update.base.calc_fifo_size = vmw_sou_bo_fifo_size; |
555 | bo_update.base.post_prepare = vmw_sou_bo_define_gmrfb; |
556 | bo_update.base.clip = vmw_sou_bo_populate_clip; |
557 | bo_update.base.post_clip = vmw_stud_bo_post_clip; |
558 | |
559 | return vmw_du_helper_plane_update(update: &bo_update.base); |
560 | } |
561 | |
562 | static uint32_t vmw_sou_surface_fifo_size(struct vmw_du_update_plane *update, |
563 | uint32_t num_hits) |
564 | { |
565 | return sizeof(struct vmw_kms_sou_dirty_cmd) + sizeof(SVGASignedRect) * |
566 | num_hits; |
567 | } |
568 | |
569 | static uint32_t vmw_sou_surface_post_prepare(struct vmw_du_update_plane *update, |
570 | void *cmd) |
571 | { |
572 | struct vmw_du_update_plane_surface *srf_update; |
573 | |
574 | srf_update = container_of(update, typeof(*srf_update), base); |
575 | |
576 | /* |
577 | * SOU SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN is special in the sense that |
578 | * its bounding box is filled before iterating over all the clips. So |
579 | * store the FIFO start address and revisit to fill the details. |
580 | */ |
581 | srf_update->cmd_start = cmd; |
582 | |
583 | return 0; |
584 | } |
585 | |
586 | static uint32_t vmw_sou_surface_pre_clip(struct vmw_du_update_plane *update, |
587 | void *cmd, uint32_t num_hits) |
588 | { |
589 | struct vmw_kms_sou_dirty_cmd *blit = cmd; |
590 | struct vmw_framebuffer_surface *vfbs; |
591 | |
592 | vfbs = container_of(update->vfb, typeof(*vfbs), base); |
593 | |
594 | blit->header.id = SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN; |
595 | blit->header.size = sizeof(blit->body) + sizeof(SVGASignedRect) * |
596 | num_hits; |
597 | |
598 | blit->body.srcImage.sid = vfbs->surface->res.id; |
599 | blit->body.destScreenId = update->du->unit; |
600 | |
601 | /* Update the source and destination bounding box later in post_clip */ |
602 | blit->body.srcRect.left = 0; |
603 | blit->body.srcRect.top = 0; |
604 | blit->body.srcRect.right = 0; |
605 | blit->body.srcRect.bottom = 0; |
606 | |
607 | blit->body.destRect.left = 0; |
608 | blit->body.destRect.top = 0; |
609 | blit->body.destRect.right = 0; |
610 | blit->body.destRect.bottom = 0; |
611 | |
612 | return sizeof(*blit); |
613 | } |
614 | |
615 | static uint32_t vmw_sou_surface_clip_rect(struct vmw_du_update_plane *update, |
616 | void *cmd, struct drm_rect *clip, |
617 | uint32_t src_x, uint32_t src_y) |
618 | { |
619 | SVGASignedRect *rect = cmd; |
620 | |
621 | /* |
622 | * rects are relative to dest bounding box rect on screen object, so |
623 | * translate to it later in post_clip |
624 | */ |
625 | rect->left = clip->x1; |
626 | rect->top = clip->y1; |
627 | rect->right = clip->x2; |
628 | rect->bottom = clip->y2; |
629 | |
630 | return sizeof(*rect); |
631 | } |
632 | |
633 | static uint32_t vmw_sou_surface_post_clip(struct vmw_du_update_plane *update, |
634 | void *cmd, struct drm_rect *bb) |
635 | { |
636 | struct vmw_du_update_plane_surface *srf_update; |
637 | struct drm_plane_state *state = update->plane->state; |
638 | struct drm_rect src_bb; |
639 | struct vmw_kms_sou_dirty_cmd *blit; |
640 | SVGASignedRect *rect; |
641 | uint32_t num_hits; |
642 | int translate_src_x; |
643 | int translate_src_y; |
644 | int i; |
645 | |
646 | srf_update = container_of(update, typeof(*srf_update), base); |
647 | |
648 | blit = srf_update->cmd_start; |
649 | rect = (SVGASignedRect *)&blit[1]; |
650 | |
651 | num_hits = (blit->header.size - sizeof(blit->body))/ |
652 | sizeof(SVGASignedRect); |
653 | |
654 | src_bb = *bb; |
655 | |
656 | /* To translate bb back to fb src coord */ |
657 | translate_src_x = (state->src_x >> 16) - state->crtc_x; |
658 | translate_src_y = (state->src_y >> 16) - state->crtc_y; |
659 | |
660 | drm_rect_translate(r: &src_bb, dx: translate_src_x, dy: translate_src_y); |
661 | |
662 | blit->body.srcRect.left = src_bb.x1; |
663 | blit->body.srcRect.top = src_bb.y1; |
664 | blit->body.srcRect.right = src_bb.x2; |
665 | blit->body.srcRect.bottom = src_bb.y2; |
666 | |
667 | blit->body.destRect.left = bb->x1; |
668 | blit->body.destRect.top = bb->y1; |
669 | blit->body.destRect.right = bb->x2; |
670 | blit->body.destRect.bottom = bb->y2; |
671 | |
672 | /* rects are relative to dest bb rect */ |
673 | for (i = 0; i < num_hits; i++) { |
674 | rect->left -= bb->x1; |
675 | rect->top -= bb->y1; |
676 | rect->right -= bb->x1; |
677 | rect->bottom -= bb->y1; |
678 | rect++; |
679 | } |
680 | |
681 | return 0; |
682 | } |
683 | |
684 | /** |
685 | * vmw_sou_plane_update_surface - Update display unit for surface backed fb. |
686 | * @dev_priv: Device private. |
687 | * @plane: Plane state. |
688 | * @old_state: Old plane state. |
689 | * @vfb: Framebuffer which is blitted to display unit |
690 | * @out_fence: If non-NULL, will return a ref-counted pointer to vmw_fence_obj. |
691 | * The returned fence pointer may be NULL in which case the device |
692 | * has already synchronized. |
693 | * |
694 | * Return: 0 on success or a negative error code on failure. |
695 | */ |
696 | static int vmw_sou_plane_update_surface(struct vmw_private *dev_priv, |
697 | struct drm_plane *plane, |
698 | struct drm_plane_state *old_state, |
699 | struct vmw_framebuffer *vfb, |
700 | struct vmw_fence_obj **out_fence) |
701 | { |
702 | struct vmw_du_update_plane_surface srf_update; |
703 | |
704 | memset(&srf_update, 0, sizeof(struct vmw_du_update_plane_surface)); |
705 | srf_update.base.plane = plane; |
706 | srf_update.base.old_state = old_state; |
707 | srf_update.base.dev_priv = dev_priv; |
708 | srf_update.base.du = vmw_crtc_to_du(plane->state->crtc); |
709 | srf_update.base.vfb = vfb; |
710 | srf_update.base.out_fence = out_fence; |
711 | srf_update.base.mutex = &dev_priv->cmdbuf_mutex; |
712 | srf_update.base.intr = true; |
713 | |
714 | srf_update.base.calc_fifo_size = vmw_sou_surface_fifo_size; |
715 | srf_update.base.post_prepare = vmw_sou_surface_post_prepare; |
716 | srf_update.base.pre_clip = vmw_sou_surface_pre_clip; |
717 | srf_update.base.clip = vmw_sou_surface_clip_rect; |
718 | srf_update.base.post_clip = vmw_sou_surface_post_clip; |
719 | |
720 | return vmw_du_helper_plane_update(update: &srf_update.base); |
721 | } |
722 | |
723 | static void |
724 | vmw_sou_primary_plane_atomic_update(struct drm_plane *plane, |
725 | struct drm_atomic_state *state) |
726 | { |
727 | struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane); |
728 | struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane); |
729 | struct drm_crtc *crtc = new_state->crtc; |
730 | struct vmw_fence_obj *fence = NULL; |
731 | int ret; |
732 | |
733 | /* In case of device error, maintain consistent atomic state */ |
734 | if (crtc && new_state->fb) { |
735 | struct vmw_private *dev_priv = vmw_priv(dev: crtc->dev); |
736 | struct vmw_framebuffer *vfb = |
737 | vmw_framebuffer_to_vfb(new_state->fb); |
738 | |
739 | if (vfb->bo) |
740 | ret = vmw_sou_plane_update_bo(dev_priv, plane, |
741 | old_state, vfb, out_fence: &fence); |
742 | else |
743 | ret = vmw_sou_plane_update_surface(dev_priv, plane, |
744 | old_state, vfb, |
745 | out_fence: &fence); |
746 | if (ret != 0) |
747 | DRM_ERROR("Failed to update screen.\n" ); |
748 | } else { |
749 | /* Do nothing when fb and crtc is NULL (blank crtc) */ |
750 | return; |
751 | } |
752 | |
753 | if (fence) |
754 | vmw_fence_obj_unreference(fence_p: &fence); |
755 | } |
756 | |
757 | |
758 | static const struct drm_plane_funcs vmw_sou_plane_funcs = { |
759 | .update_plane = drm_atomic_helper_update_plane, |
760 | .disable_plane = drm_atomic_helper_disable_plane, |
761 | .destroy = vmw_du_primary_plane_destroy, |
762 | .reset = vmw_du_plane_reset, |
763 | .atomic_duplicate_state = vmw_du_plane_duplicate_state, |
764 | .atomic_destroy_state = vmw_du_plane_destroy_state, |
765 | }; |
766 | |
767 | static const struct drm_plane_funcs vmw_sou_cursor_funcs = { |
768 | .update_plane = drm_atomic_helper_update_plane, |
769 | .disable_plane = drm_atomic_helper_disable_plane, |
770 | .destroy = vmw_du_cursor_plane_destroy, |
771 | .reset = vmw_du_plane_reset, |
772 | .atomic_duplicate_state = vmw_du_plane_duplicate_state, |
773 | .atomic_destroy_state = vmw_du_plane_destroy_state, |
774 | }; |
775 | |
776 | /* |
777 | * Atomic Helpers |
778 | */ |
779 | static const struct |
780 | drm_plane_helper_funcs vmw_sou_cursor_plane_helper_funcs = { |
781 | .atomic_check = vmw_du_cursor_plane_atomic_check, |
782 | .atomic_update = vmw_du_cursor_plane_atomic_update, |
783 | .prepare_fb = vmw_du_cursor_plane_prepare_fb, |
784 | .cleanup_fb = vmw_du_cursor_plane_cleanup_fb, |
785 | }; |
786 | |
787 | static const struct |
788 | drm_plane_helper_funcs vmw_sou_primary_plane_helper_funcs = { |
789 | .atomic_check = vmw_du_primary_plane_atomic_check, |
790 | .atomic_update = vmw_sou_primary_plane_atomic_update, |
791 | .prepare_fb = vmw_sou_primary_plane_prepare_fb, |
792 | .cleanup_fb = vmw_sou_primary_plane_cleanup_fb, |
793 | }; |
794 | |
795 | static const struct drm_crtc_helper_funcs vmw_sou_crtc_helper_funcs = { |
796 | .prepare = vmw_sou_crtc_helper_prepare, |
797 | .mode_set_nofb = vmw_sou_crtc_mode_set_nofb, |
798 | .atomic_check = vmw_du_crtc_atomic_check, |
799 | .atomic_begin = vmw_du_crtc_atomic_begin, |
800 | .atomic_flush = vmw_du_crtc_atomic_flush, |
801 | .atomic_enable = vmw_sou_crtc_atomic_enable, |
802 | .atomic_disable = vmw_sou_crtc_atomic_disable, |
803 | }; |
804 | |
805 | |
806 | static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit) |
807 | { |
808 | struct vmw_screen_object_unit *sou; |
809 | struct drm_device *dev = &dev_priv->drm; |
810 | struct drm_connector *connector; |
811 | struct drm_encoder *encoder; |
812 | struct drm_plane *primary; |
813 | struct vmw_cursor_plane *cursor; |
814 | struct drm_crtc *crtc; |
815 | int ret; |
816 | |
817 | sou = kzalloc(size: sizeof(*sou), GFP_KERNEL); |
818 | if (!sou) |
819 | return -ENOMEM; |
820 | |
821 | sou->base.unit = unit; |
822 | crtc = &sou->base.crtc; |
823 | encoder = &sou->base.encoder; |
824 | connector = &sou->base.connector; |
825 | primary = &sou->base.primary; |
826 | cursor = &sou->base.cursor; |
827 | |
828 | sou->base.pref_active = (unit == 0); |
829 | sou->base.pref_width = dev_priv->initial_width; |
830 | sou->base.pref_height = dev_priv->initial_height; |
831 | |
832 | /* |
833 | * Remove this after enabling atomic because property values can |
834 | * only exist in a state object |
835 | */ |
836 | sou->base.is_implicit = false; |
837 | |
838 | /* Initialize primary plane */ |
839 | ret = drm_universal_plane_init(dev, plane: primary, |
840 | possible_crtcs: 0, funcs: &vmw_sou_plane_funcs, |
841 | formats: vmw_primary_plane_formats, |
842 | ARRAY_SIZE(vmw_primary_plane_formats), |
843 | NULL, type: DRM_PLANE_TYPE_PRIMARY, NULL); |
844 | if (ret) { |
845 | DRM_ERROR("Failed to initialize primary plane" ); |
846 | goto err_free; |
847 | } |
848 | |
849 | drm_plane_helper_add(plane: primary, funcs: &vmw_sou_primary_plane_helper_funcs); |
850 | drm_plane_enable_fb_damage_clips(plane: primary); |
851 | |
852 | /* Initialize cursor plane */ |
853 | ret = drm_universal_plane_init(dev, plane: &cursor->base, |
854 | possible_crtcs: 0, funcs: &vmw_sou_cursor_funcs, |
855 | formats: vmw_cursor_plane_formats, |
856 | ARRAY_SIZE(vmw_cursor_plane_formats), |
857 | NULL, type: DRM_PLANE_TYPE_CURSOR, NULL); |
858 | if (ret) { |
859 | DRM_ERROR("Failed to initialize cursor plane" ); |
860 | drm_plane_cleanup(plane: &sou->base.primary); |
861 | goto err_free; |
862 | } |
863 | |
864 | drm_plane_helper_add(plane: &cursor->base, funcs: &vmw_sou_cursor_plane_helper_funcs); |
865 | |
866 | ret = drm_connector_init(dev, connector, funcs: &vmw_sou_connector_funcs, |
867 | DRM_MODE_CONNECTOR_VIRTUAL); |
868 | if (ret) { |
869 | DRM_ERROR("Failed to initialize connector\n" ); |
870 | goto err_free; |
871 | } |
872 | |
873 | drm_connector_helper_add(connector, funcs: &vmw_sou_connector_helper_funcs); |
874 | connector->status = vmw_du_connector_detect(connector, force: true); |
875 | |
876 | ret = drm_encoder_init(dev, encoder, funcs: &vmw_screen_object_encoder_funcs, |
877 | DRM_MODE_ENCODER_VIRTUAL, NULL); |
878 | if (ret) { |
879 | DRM_ERROR("Failed to initialize encoder\n" ); |
880 | goto err_free_connector; |
881 | } |
882 | |
883 | (void) drm_connector_attach_encoder(connector, encoder); |
884 | encoder->possible_crtcs = (1 << unit); |
885 | encoder->possible_clones = 0; |
886 | |
887 | ret = drm_connector_register(connector); |
888 | if (ret) { |
889 | DRM_ERROR("Failed to register connector\n" ); |
890 | goto err_free_encoder; |
891 | } |
892 | |
893 | ret = drm_crtc_init_with_planes(dev, crtc, primary, |
894 | cursor: &cursor->base, |
895 | funcs: &vmw_screen_object_crtc_funcs, NULL); |
896 | if (ret) { |
897 | DRM_ERROR("Failed to initialize CRTC\n" ); |
898 | goto err_free_unregister; |
899 | } |
900 | |
901 | drm_crtc_helper_add(crtc, funcs: &vmw_sou_crtc_helper_funcs); |
902 | |
903 | drm_mode_crtc_set_gamma_size(crtc, gamma_size: 256); |
904 | |
905 | drm_object_attach_property(obj: &connector->base, |
906 | property: dev_priv->hotplug_mode_update_property, init_val: 1); |
907 | drm_object_attach_property(obj: &connector->base, |
908 | property: dev->mode_config.suggested_x_property, init_val: 0); |
909 | drm_object_attach_property(obj: &connector->base, |
910 | property: dev->mode_config.suggested_y_property, init_val: 0); |
911 | return 0; |
912 | |
913 | err_free_unregister: |
914 | drm_connector_unregister(connector); |
915 | err_free_encoder: |
916 | drm_encoder_cleanup(encoder); |
917 | err_free_connector: |
918 | drm_connector_cleanup(connector); |
919 | err_free: |
920 | kfree(objp: sou); |
921 | return ret; |
922 | } |
923 | |
924 | int vmw_kms_sou_init_display(struct vmw_private *dev_priv) |
925 | { |
926 | struct drm_device *dev = &dev_priv->drm; |
927 | int i; |
928 | |
929 | /* Screen objects won't work if GMR's aren't available */ |
930 | if (!dev_priv->has_gmr) |
931 | return -ENOSYS; |
932 | |
933 | if (!(dev_priv->capabilities & SVGA_CAP_SCREEN_OBJECT_2)) { |
934 | return -ENOSYS; |
935 | } |
936 | |
937 | for (i = 0; i < VMWGFX_NUM_DISPLAY_UNITS; ++i) |
938 | vmw_sou_init(dev_priv, unit: i); |
939 | |
940 | dev_priv->active_display_unit = vmw_du_screen_object; |
941 | |
942 | drm_mode_config_reset(dev); |
943 | |
944 | return 0; |
945 | } |
946 | |
947 | static int do_bo_define_gmrfb(struct vmw_private *dev_priv, |
948 | struct vmw_framebuffer *framebuffer) |
949 | { |
950 | struct vmw_bo *buf = |
951 | container_of(framebuffer, struct vmw_framebuffer_bo, |
952 | base)->buffer; |
953 | int depth = framebuffer->base.format->depth; |
954 | struct { |
955 | uint32_t ; |
956 | SVGAFifoCmdDefineGMRFB body; |
957 | } *cmd; |
958 | |
959 | /* Emulate RGBA support, contrary to svga_reg.h this is not |
960 | * supported by hosts. This is only a problem if we are reading |
961 | * this value later and expecting what we uploaded back. |
962 | */ |
963 | if (depth == 32) |
964 | depth = 24; |
965 | |
966 | cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd)); |
967 | if (!cmd) |
968 | return -ENOMEM; |
969 | |
970 | cmd->header = SVGA_CMD_DEFINE_GMRFB; |
971 | cmd->body.format.bitsPerPixel = framebuffer->base.format->cpp[0] * 8; |
972 | cmd->body.format.colorDepth = depth; |
973 | cmd->body.format.reserved = 0; |
974 | cmd->body.bytesPerLine = framebuffer->base.pitches[0]; |
975 | /* Buffer is reserved in vram or GMR */ |
976 | vmw_bo_get_guest_ptr(buf: &buf->tbo, ptr: &cmd->body.ptr); |
977 | vmw_cmd_commit(dev_priv, bytes: sizeof(*cmd)); |
978 | |
979 | return 0; |
980 | } |
981 | |
982 | /** |
983 | * vmw_sou_surface_fifo_commit - Callback to fill in and submit a |
984 | * blit surface to screen command. |
985 | * |
986 | * @dirty: The closure structure. |
987 | * |
988 | * Fills in the missing fields in the command, and translates the cliprects |
989 | * to match the destination bounding box encoded. |
990 | */ |
991 | static void vmw_sou_surface_fifo_commit(struct vmw_kms_dirty *dirty) |
992 | { |
993 | struct vmw_kms_sou_surface_dirty *sdirty = |
994 | container_of(dirty, typeof(*sdirty), base); |
995 | struct vmw_kms_sou_dirty_cmd *cmd = dirty->cmd; |
996 | s32 trans_x = dirty->unit->crtc.x - sdirty->dst_x; |
997 | s32 trans_y = dirty->unit->crtc.y - sdirty->dst_y; |
998 | size_t region_size = dirty->num_hits * sizeof(SVGASignedRect); |
999 | SVGASignedRect *blit = (SVGASignedRect *) &cmd[1]; |
1000 | int i; |
1001 | |
1002 | if (!dirty->num_hits) { |
1003 | vmw_cmd_commit(dev_priv: dirty->dev_priv, bytes: 0); |
1004 | return; |
1005 | } |
1006 | |
1007 | cmd->header.id = SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN; |
1008 | cmd->header.size = sizeof(cmd->body) + region_size; |
1009 | |
1010 | /* |
1011 | * Use the destination bounding box to specify destination - and |
1012 | * source bounding regions. |
1013 | */ |
1014 | cmd->body.destRect.left = sdirty->left; |
1015 | cmd->body.destRect.right = sdirty->right; |
1016 | cmd->body.destRect.top = sdirty->top; |
1017 | cmd->body.destRect.bottom = sdirty->bottom; |
1018 | |
1019 | cmd->body.srcRect.left = sdirty->left + trans_x; |
1020 | cmd->body.srcRect.right = sdirty->right + trans_x; |
1021 | cmd->body.srcRect.top = sdirty->top + trans_y; |
1022 | cmd->body.srcRect.bottom = sdirty->bottom + trans_y; |
1023 | |
1024 | cmd->body.srcImage.sid = sdirty->sid; |
1025 | cmd->body.destScreenId = dirty->unit->unit; |
1026 | |
1027 | /* Blits are relative to the destination rect. Translate. */ |
1028 | for (i = 0; i < dirty->num_hits; ++i, ++blit) { |
1029 | blit->left -= sdirty->left; |
1030 | blit->right -= sdirty->left; |
1031 | blit->top -= sdirty->top; |
1032 | blit->bottom -= sdirty->top; |
1033 | } |
1034 | |
1035 | vmw_cmd_commit(dev_priv: dirty->dev_priv, bytes: region_size + sizeof(*cmd)); |
1036 | |
1037 | sdirty->left = sdirty->top = S32_MAX; |
1038 | sdirty->right = sdirty->bottom = S32_MIN; |
1039 | } |
1040 | |
1041 | /** |
1042 | * vmw_sou_surface_clip - Callback to encode a blit surface to screen cliprect. |
1043 | * |
1044 | * @dirty: The closure structure |
1045 | * |
1046 | * Encodes a SVGASignedRect cliprect and updates the bounding box of the |
1047 | * BLIT_SURFACE_TO_SCREEN command. |
1048 | */ |
1049 | static void vmw_sou_surface_clip(struct vmw_kms_dirty *dirty) |
1050 | { |
1051 | struct vmw_kms_sou_surface_dirty *sdirty = |
1052 | container_of(dirty, typeof(*sdirty), base); |
1053 | struct vmw_kms_sou_dirty_cmd *cmd = dirty->cmd; |
1054 | SVGASignedRect *blit = (SVGASignedRect *) &cmd[1]; |
1055 | |
1056 | /* Destination rect. */ |
1057 | blit += dirty->num_hits; |
1058 | blit->left = dirty->unit_x1; |
1059 | blit->top = dirty->unit_y1; |
1060 | blit->right = dirty->unit_x2; |
1061 | blit->bottom = dirty->unit_y2; |
1062 | |
1063 | /* Destination bounding box */ |
1064 | sdirty->left = min_t(s32, sdirty->left, dirty->unit_x1); |
1065 | sdirty->top = min_t(s32, sdirty->top, dirty->unit_y1); |
1066 | sdirty->right = max_t(s32, sdirty->right, dirty->unit_x2); |
1067 | sdirty->bottom = max_t(s32, sdirty->bottom, dirty->unit_y2); |
1068 | |
1069 | dirty->num_hits++; |
1070 | } |
1071 | |
1072 | /** |
1073 | * vmw_kms_sou_do_surface_dirty - Dirty part of a surface backed framebuffer |
1074 | * |
1075 | * @dev_priv: Pointer to the device private structure. |
1076 | * @framebuffer: Pointer to the surface-buffer backed framebuffer. |
1077 | * @clips: Array of clip rects. Either @clips or @vclips must be NULL. |
1078 | * @vclips: Alternate array of clip rects. Either @clips or @vclips must |
1079 | * be NULL. |
1080 | * @srf: Pointer to surface to blit from. If NULL, the surface attached |
1081 | * to @framebuffer will be used. |
1082 | * @dest_x: X coordinate offset to align @srf with framebuffer coordinates. |
1083 | * @dest_y: Y coordinate offset to align @srf with framebuffer coordinates. |
1084 | * @num_clips: Number of clip rects in @clips. |
1085 | * @inc: Increment to use when looping over @clips. |
1086 | * @out_fence: If non-NULL, will return a ref-counted pointer to a |
1087 | * struct vmw_fence_obj. The returned fence pointer may be NULL in which |
1088 | * case the device has already synchronized. |
1089 | * @crtc: If crtc is passed, perform surface dirty on that crtc only. |
1090 | * |
1091 | * Returns 0 on success, negative error code on failure. -ERESTARTSYS if |
1092 | * interrupted. |
1093 | */ |
1094 | int vmw_kms_sou_do_surface_dirty(struct vmw_private *dev_priv, |
1095 | struct vmw_framebuffer *framebuffer, |
1096 | struct drm_clip_rect *clips, |
1097 | struct drm_vmw_rect *vclips, |
1098 | struct vmw_resource *srf, |
1099 | s32 dest_x, |
1100 | s32 dest_y, |
1101 | unsigned num_clips, int inc, |
1102 | struct vmw_fence_obj **out_fence, |
1103 | struct drm_crtc *crtc) |
1104 | { |
1105 | struct vmw_framebuffer_surface *vfbs = |
1106 | container_of(framebuffer, typeof(*vfbs), base); |
1107 | struct vmw_kms_sou_surface_dirty sdirty; |
1108 | DECLARE_VAL_CONTEXT(val_ctx, NULL, 0); |
1109 | int ret; |
1110 | |
1111 | if (!srf) |
1112 | srf = &vfbs->surface->res; |
1113 | |
1114 | ret = vmw_validation_add_resource(ctx: &val_ctx, res: srf, priv_size: 0, VMW_RES_DIRTY_NONE, |
1115 | NULL, NULL); |
1116 | if (ret) |
1117 | return ret; |
1118 | |
1119 | ret = vmw_validation_prepare(ctx: &val_ctx, mutex: &dev_priv->cmdbuf_mutex, intr: true); |
1120 | if (ret) |
1121 | goto out_unref; |
1122 | |
1123 | sdirty.base.fifo_commit = vmw_sou_surface_fifo_commit; |
1124 | sdirty.base.clip = vmw_sou_surface_clip; |
1125 | sdirty.base.dev_priv = dev_priv; |
1126 | sdirty.base.fifo_reserve_size = sizeof(struct vmw_kms_sou_dirty_cmd) + |
1127 | sizeof(SVGASignedRect) * num_clips; |
1128 | sdirty.base.crtc = crtc; |
1129 | |
1130 | sdirty.sid = srf->id; |
1131 | sdirty.left = sdirty.top = S32_MAX; |
1132 | sdirty.right = sdirty.bottom = S32_MIN; |
1133 | sdirty.dst_x = dest_x; |
1134 | sdirty.dst_y = dest_y; |
1135 | |
1136 | ret = vmw_kms_helper_dirty(dev_priv, framebuffer, clips, vclips, |
1137 | dest_x, dest_y, num_clips, increment: inc, |
1138 | dirty: &sdirty.base); |
1139 | vmw_kms_helper_validation_finish(dev_priv, NULL, ctx: &val_ctx, out_fence, |
1140 | NULL); |
1141 | |
1142 | return ret; |
1143 | |
1144 | out_unref: |
1145 | vmw_validation_unref_lists(ctx: &val_ctx); |
1146 | return ret; |
1147 | } |
1148 | |
1149 | /** |
1150 | * vmw_sou_bo_fifo_commit - Callback to submit a set of readback clips. |
1151 | * |
1152 | * @dirty: The closure structure. |
1153 | * |
1154 | * Commits a previously built command buffer of readback clips. |
1155 | */ |
1156 | static void vmw_sou_bo_fifo_commit(struct vmw_kms_dirty *dirty) |
1157 | { |
1158 | if (!dirty->num_hits) { |
1159 | vmw_cmd_commit(dev_priv: dirty->dev_priv, bytes: 0); |
1160 | return; |
1161 | } |
1162 | |
1163 | vmw_cmd_commit(dev_priv: dirty->dev_priv, |
1164 | bytes: sizeof(struct vmw_kms_sou_bo_blit) * |
1165 | dirty->num_hits); |
1166 | } |
1167 | |
1168 | /** |
1169 | * vmw_sou_bo_clip - Callback to encode a readback cliprect. |
1170 | * |
1171 | * @dirty: The closure structure |
1172 | * |
1173 | * Encodes a BLIT_GMRFB_TO_SCREEN cliprect. |
1174 | */ |
1175 | static void vmw_sou_bo_clip(struct vmw_kms_dirty *dirty) |
1176 | { |
1177 | struct vmw_kms_sou_bo_blit *blit = dirty->cmd; |
1178 | |
1179 | blit += dirty->num_hits; |
1180 | blit->header = SVGA_CMD_BLIT_GMRFB_TO_SCREEN; |
1181 | blit->body.destScreenId = dirty->unit->unit; |
1182 | blit->body.srcOrigin.x = dirty->fb_x; |
1183 | blit->body.srcOrigin.y = dirty->fb_y; |
1184 | blit->body.destRect.left = dirty->unit_x1; |
1185 | blit->body.destRect.top = dirty->unit_y1; |
1186 | blit->body.destRect.right = dirty->unit_x2; |
1187 | blit->body.destRect.bottom = dirty->unit_y2; |
1188 | dirty->num_hits++; |
1189 | } |
1190 | |
1191 | /** |
1192 | * vmw_kms_sou_do_bo_dirty - Dirty part of a buffer-object backed framebuffer |
1193 | * |
1194 | * @dev_priv: Pointer to the device private structure. |
1195 | * @framebuffer: Pointer to the buffer-object backed framebuffer. |
1196 | * @clips: Array of clip rects. |
1197 | * @vclips: Alternate array of clip rects. Either @clips or @vclips must |
1198 | * be NULL. |
1199 | * @num_clips: Number of clip rects in @clips. |
1200 | * @increment: Increment to use when looping over @clips. |
1201 | * @interruptible: Whether to perform waits interruptible if possible. |
1202 | * @out_fence: If non-NULL, will return a ref-counted pointer to a |
1203 | * struct vmw_fence_obj. The returned fence pointer may be NULL in which |
1204 | * case the device has already synchronized. |
1205 | * @crtc: If crtc is passed, perform bo dirty on that crtc only. |
1206 | * |
1207 | * Returns 0 on success, negative error code on failure. -ERESTARTSYS if |
1208 | * interrupted. |
1209 | */ |
1210 | int vmw_kms_sou_do_bo_dirty(struct vmw_private *dev_priv, |
1211 | struct vmw_framebuffer *framebuffer, |
1212 | struct drm_clip_rect *clips, |
1213 | struct drm_vmw_rect *vclips, |
1214 | unsigned num_clips, int increment, |
1215 | bool interruptible, |
1216 | struct vmw_fence_obj **out_fence, |
1217 | struct drm_crtc *crtc) |
1218 | { |
1219 | struct vmw_bo *buf = |
1220 | container_of(framebuffer, struct vmw_framebuffer_bo, |
1221 | base)->buffer; |
1222 | struct vmw_kms_dirty dirty; |
1223 | DECLARE_VAL_CONTEXT(val_ctx, NULL, 0); |
1224 | int ret; |
1225 | |
1226 | vmw_bo_placement_set(bo: buf, domain: VMW_BO_DOMAIN_GMR | VMW_BO_DOMAIN_VRAM, |
1227 | busy_domain: VMW_BO_DOMAIN_GMR | VMW_BO_DOMAIN_VRAM); |
1228 | ret = vmw_validation_add_bo(ctx: &val_ctx, vbo: buf); |
1229 | if (ret) |
1230 | return ret; |
1231 | |
1232 | ret = vmw_validation_prepare(ctx: &val_ctx, NULL, intr: interruptible); |
1233 | if (ret) |
1234 | goto out_unref; |
1235 | |
1236 | ret = do_bo_define_gmrfb(dev_priv, framebuffer); |
1237 | if (unlikely(ret != 0)) |
1238 | goto out_revert; |
1239 | |
1240 | dirty.crtc = crtc; |
1241 | dirty.fifo_commit = vmw_sou_bo_fifo_commit; |
1242 | dirty.clip = vmw_sou_bo_clip; |
1243 | dirty.fifo_reserve_size = sizeof(struct vmw_kms_sou_bo_blit) * |
1244 | num_clips; |
1245 | ret = vmw_kms_helper_dirty(dev_priv, framebuffer, clips, vclips, |
1246 | dest_x: 0, dest_y: 0, num_clips, increment, dirty: &dirty); |
1247 | vmw_kms_helper_validation_finish(dev_priv, NULL, ctx: &val_ctx, out_fence, |
1248 | NULL); |
1249 | |
1250 | return ret; |
1251 | |
1252 | out_revert: |
1253 | vmw_validation_revert(ctx: &val_ctx); |
1254 | out_unref: |
1255 | vmw_validation_unref_lists(ctx: &val_ctx); |
1256 | |
1257 | return ret; |
1258 | } |
1259 | |
1260 | |
1261 | /** |
1262 | * vmw_sou_readback_fifo_commit - Callback to submit a set of readback clips. |
1263 | * |
1264 | * @dirty: The closure structure. |
1265 | * |
1266 | * Commits a previously built command buffer of readback clips. |
1267 | */ |
1268 | static void vmw_sou_readback_fifo_commit(struct vmw_kms_dirty *dirty) |
1269 | { |
1270 | if (!dirty->num_hits) { |
1271 | vmw_cmd_commit(dev_priv: dirty->dev_priv, bytes: 0); |
1272 | return; |
1273 | } |
1274 | |
1275 | vmw_cmd_commit(dev_priv: dirty->dev_priv, |
1276 | bytes: sizeof(struct vmw_kms_sou_readback_blit) * |
1277 | dirty->num_hits); |
1278 | } |
1279 | |
1280 | /** |
1281 | * vmw_sou_readback_clip - Callback to encode a readback cliprect. |
1282 | * |
1283 | * @dirty: The closure structure |
1284 | * |
1285 | * Encodes a BLIT_SCREEN_TO_GMRFB cliprect. |
1286 | */ |
1287 | static void vmw_sou_readback_clip(struct vmw_kms_dirty *dirty) |
1288 | { |
1289 | struct vmw_kms_sou_readback_blit *blit = dirty->cmd; |
1290 | |
1291 | blit += dirty->num_hits; |
1292 | blit->header = SVGA_CMD_BLIT_SCREEN_TO_GMRFB; |
1293 | blit->body.srcScreenId = dirty->unit->unit; |
1294 | blit->body.destOrigin.x = dirty->fb_x; |
1295 | blit->body.destOrigin.y = dirty->fb_y; |
1296 | blit->body.srcRect.left = dirty->unit_x1; |
1297 | blit->body.srcRect.top = dirty->unit_y1; |
1298 | blit->body.srcRect.right = dirty->unit_x2; |
1299 | blit->body.srcRect.bottom = dirty->unit_y2; |
1300 | dirty->num_hits++; |
1301 | } |
1302 | |
1303 | /** |
1304 | * vmw_kms_sou_readback - Perform a readback from the screen object system to |
1305 | * a buffer-object backed framebuffer. |
1306 | * |
1307 | * @dev_priv: Pointer to the device private structure. |
1308 | * @file_priv: Pointer to a struct drm_file identifying the caller. |
1309 | * Must be set to NULL if @user_fence_rep is NULL. |
1310 | * @vfb: Pointer to the buffer-object backed framebuffer. |
1311 | * @user_fence_rep: User-space provided structure for fence information. |
1312 | * Must be set to non-NULL if @file_priv is non-NULL. |
1313 | * @vclips: Array of clip rects. |
1314 | * @num_clips: Number of clip rects in @vclips. |
1315 | * @crtc: If crtc is passed, readback on that crtc only. |
1316 | * |
1317 | * Returns 0 on success, negative error code on failure. -ERESTARTSYS if |
1318 | * interrupted. |
1319 | */ |
1320 | int vmw_kms_sou_readback(struct vmw_private *dev_priv, |
1321 | struct drm_file *file_priv, |
1322 | struct vmw_framebuffer *vfb, |
1323 | struct drm_vmw_fence_rep __user *user_fence_rep, |
1324 | struct drm_vmw_rect *vclips, |
1325 | uint32_t num_clips, |
1326 | struct drm_crtc *crtc) |
1327 | { |
1328 | struct vmw_bo *buf = |
1329 | container_of(vfb, struct vmw_framebuffer_bo, base)->buffer; |
1330 | struct vmw_kms_dirty dirty; |
1331 | DECLARE_VAL_CONTEXT(val_ctx, NULL, 0); |
1332 | int ret; |
1333 | |
1334 | vmw_bo_placement_set(bo: buf, domain: VMW_BO_DOMAIN_GMR | VMW_BO_DOMAIN_VRAM, |
1335 | busy_domain: VMW_BO_DOMAIN_GMR | VMW_BO_DOMAIN_VRAM); |
1336 | ret = vmw_validation_add_bo(ctx: &val_ctx, vbo: buf); |
1337 | if (ret) |
1338 | return ret; |
1339 | |
1340 | ret = vmw_validation_prepare(ctx: &val_ctx, NULL, intr: true); |
1341 | if (ret) |
1342 | goto out_unref; |
1343 | |
1344 | ret = do_bo_define_gmrfb(dev_priv, framebuffer: vfb); |
1345 | if (unlikely(ret != 0)) |
1346 | goto out_revert; |
1347 | |
1348 | dirty.crtc = crtc; |
1349 | dirty.fifo_commit = vmw_sou_readback_fifo_commit; |
1350 | dirty.clip = vmw_sou_readback_clip; |
1351 | dirty.fifo_reserve_size = sizeof(struct vmw_kms_sou_readback_blit) * |
1352 | num_clips; |
1353 | ret = vmw_kms_helper_dirty(dev_priv, framebuffer: vfb, NULL, vclips, |
1354 | dest_x: 0, dest_y: 0, num_clips, increment: 1, dirty: &dirty); |
1355 | vmw_kms_helper_validation_finish(dev_priv, file_priv, ctx: &val_ctx, NULL, |
1356 | user_fence_rep); |
1357 | |
1358 | return ret; |
1359 | |
1360 | out_revert: |
1361 | vmw_validation_revert(ctx: &val_ctx); |
1362 | out_unref: |
1363 | vmw_validation_unref_lists(ctx: &val_ctx); |
1364 | |
1365 | return ret; |
1366 | } |
1367 | |