1 | /* |
2 | * Copyright (C) 2015 Red Hat, Inc. |
3 | * All Rights Reserved. |
4 | * |
5 | * Permission is hereby granted, free of charge, to any person obtaining |
6 | * a copy of this software and associated documentation files (the |
7 | * "Software"), to deal in the Software without restriction, including |
8 | * without limitation the rights to use, copy, modify, merge, publish, |
9 | * distribute, sublicense, and/or sell copies of the Software, and to |
10 | * permit persons to whom the Software is furnished to do so, subject to |
11 | * the following conditions: |
12 | * |
13 | * The above copyright notice and this permission notice (including the |
14 | * next paragraph) shall be included in all copies or substantial |
15 | * portions of the Software. |
16 | * |
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
20 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE |
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
24 | */ |
25 | |
26 | #include <drm/drm_atomic_helper.h> |
27 | #include <drm/drm_damage_helper.h> |
28 | #include <drm/drm_fourcc.h> |
29 | |
30 | #include "virtgpu_drv.h" |
31 | |
32 | static const uint32_t virtio_gpu_formats[] = { |
33 | DRM_FORMAT_HOST_XRGB8888, |
34 | }; |
35 | |
36 | static const uint32_t virtio_gpu_cursor_formats[] = { |
37 | DRM_FORMAT_HOST_ARGB8888, |
38 | }; |
39 | |
40 | uint32_t virtio_gpu_translate_format(uint32_t drm_fourcc) |
41 | { |
42 | uint32_t format; |
43 | |
44 | switch (drm_fourcc) { |
45 | case DRM_FORMAT_XRGB8888: |
46 | format = VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM; |
47 | break; |
48 | case DRM_FORMAT_ARGB8888: |
49 | format = VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM; |
50 | break; |
51 | case DRM_FORMAT_BGRX8888: |
52 | format = VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM; |
53 | break; |
54 | case DRM_FORMAT_BGRA8888: |
55 | format = VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM; |
56 | break; |
57 | default: |
58 | /* |
59 | * This should not happen, we handle everything listed |
60 | * in virtio_gpu_formats[]. |
61 | */ |
62 | format = 0; |
63 | break; |
64 | } |
65 | WARN_ON(format == 0); |
66 | return format; |
67 | } |
68 | |
69 | static const struct drm_plane_funcs virtio_gpu_plane_funcs = { |
70 | .update_plane = drm_atomic_helper_update_plane, |
71 | .disable_plane = drm_atomic_helper_disable_plane, |
72 | .reset = drm_atomic_helper_plane_reset, |
73 | .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, |
74 | .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, |
75 | }; |
76 | |
77 | static int virtio_gpu_plane_atomic_check(struct drm_plane *plane, |
78 | struct drm_atomic_state *state) |
79 | { |
80 | struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, |
81 | plane); |
82 | bool is_cursor = plane->type == DRM_PLANE_TYPE_CURSOR; |
83 | struct drm_crtc_state *crtc_state; |
84 | int ret; |
85 | |
86 | if (!new_plane_state->fb || WARN_ON(!new_plane_state->crtc)) |
87 | return 0; |
88 | |
89 | crtc_state = drm_atomic_get_crtc_state(state, |
90 | crtc: new_plane_state->crtc); |
91 | if (IS_ERR(ptr: crtc_state)) |
92 | return PTR_ERR(ptr: crtc_state); |
93 | |
94 | ret = drm_atomic_helper_check_plane_state(plane_state: new_plane_state, crtc_state, |
95 | DRM_PLANE_NO_SCALING, |
96 | DRM_PLANE_NO_SCALING, |
97 | can_position: is_cursor, can_update_disabled: true); |
98 | return ret; |
99 | } |
100 | |
101 | static void virtio_gpu_update_dumb_bo(struct virtio_gpu_device *vgdev, |
102 | struct drm_plane_state *state, |
103 | struct drm_rect *rect) |
104 | { |
105 | struct virtio_gpu_object *bo = |
106 | gem_to_virtio_gpu_obj(state->fb->obj[0]); |
107 | struct virtio_gpu_object_array *objs; |
108 | uint32_t w = rect->x2 - rect->x1; |
109 | uint32_t h = rect->y2 - rect->y1; |
110 | uint32_t x = rect->x1; |
111 | uint32_t y = rect->y1; |
112 | uint32_t off = x * state->fb->format->cpp[0] + |
113 | y * state->fb->pitches[0]; |
114 | |
115 | objs = virtio_gpu_array_alloc(nents: 1); |
116 | if (!objs) |
117 | return; |
118 | virtio_gpu_array_add_obj(objs, obj: &bo->base.base); |
119 | |
120 | virtio_gpu_cmd_transfer_to_host_2d(vgdev, offset: off, width: w, height: h, x, y, |
121 | objs, NULL); |
122 | } |
123 | |
124 | static void virtio_gpu_resource_flush(struct drm_plane *plane, |
125 | uint32_t x, uint32_t y, |
126 | uint32_t width, uint32_t height) |
127 | { |
128 | struct drm_device *dev = plane->dev; |
129 | struct virtio_gpu_device *vgdev = dev->dev_private; |
130 | struct virtio_gpu_framebuffer *vgfb; |
131 | struct virtio_gpu_object *bo; |
132 | |
133 | vgfb = to_virtio_gpu_framebuffer(plane->state->fb); |
134 | bo = gem_to_virtio_gpu_obj(vgfb->base.obj[0]); |
135 | if (vgfb->fence) { |
136 | struct virtio_gpu_object_array *objs; |
137 | |
138 | objs = virtio_gpu_array_alloc(nents: 1); |
139 | if (!objs) |
140 | return; |
141 | virtio_gpu_array_add_obj(objs, obj: vgfb->base.obj[0]); |
142 | virtio_gpu_array_lock_resv(objs); |
143 | virtio_gpu_cmd_resource_flush(vgdev, resource_id: bo->hw_res_handle, x, y, |
144 | width, height, objs, fence: vgfb->fence); |
145 | virtio_gpu_notify(vgdev); |
146 | |
147 | dma_fence_wait_timeout(&vgfb->fence->f, intr: true, |
148 | timeout: msecs_to_jiffies(m: 50)); |
149 | dma_fence_put(fence: &vgfb->fence->f); |
150 | vgfb->fence = NULL; |
151 | } else { |
152 | virtio_gpu_cmd_resource_flush(vgdev, resource_id: bo->hw_res_handle, x, y, |
153 | width, height, NULL, NULL); |
154 | virtio_gpu_notify(vgdev); |
155 | } |
156 | } |
157 | |
158 | static void virtio_gpu_primary_plane_update(struct drm_plane *plane, |
159 | struct drm_atomic_state *state) |
160 | { |
161 | struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, |
162 | plane); |
163 | struct drm_device *dev = plane->dev; |
164 | struct virtio_gpu_device *vgdev = dev->dev_private; |
165 | struct virtio_gpu_output *output = NULL; |
166 | struct virtio_gpu_object *bo; |
167 | struct drm_rect rect; |
168 | |
169 | if (plane->state->crtc) |
170 | output = drm_crtc_to_virtio_gpu_output(plane->state->crtc); |
171 | if (old_state->crtc) |
172 | output = drm_crtc_to_virtio_gpu_output(old_state->crtc); |
173 | if (WARN_ON(!output)) |
174 | return; |
175 | |
176 | if (!plane->state->fb || !output->crtc.state->active) { |
177 | DRM_DEBUG("nofb\n" ); |
178 | virtio_gpu_cmd_set_scanout(vgdev, scanout_id: output->index, resource_id: 0, |
179 | width: plane->state->src_w >> 16, |
180 | height: plane->state->src_h >> 16, |
181 | x: 0, y: 0); |
182 | virtio_gpu_notify(vgdev); |
183 | return; |
184 | } |
185 | |
186 | if (!drm_atomic_helper_damage_merged(old_state, state: plane->state, rect: &rect)) |
187 | return; |
188 | |
189 | bo = gem_to_virtio_gpu_obj(plane->state->fb->obj[0]); |
190 | if (bo->dumb) |
191 | virtio_gpu_update_dumb_bo(vgdev, state: plane->state, rect: &rect); |
192 | |
193 | if (plane->state->fb != old_state->fb || |
194 | plane->state->src_w != old_state->src_w || |
195 | plane->state->src_h != old_state->src_h || |
196 | plane->state->src_x != old_state->src_x || |
197 | plane->state->src_y != old_state->src_y || |
198 | output->needs_modeset) { |
199 | output->needs_modeset = false; |
200 | DRM_DEBUG("handle 0x%x, crtc %dx%d+%d+%d, src %dx%d+%d+%d\n" , |
201 | bo->hw_res_handle, |
202 | plane->state->crtc_w, plane->state->crtc_h, |
203 | plane->state->crtc_x, plane->state->crtc_y, |
204 | plane->state->src_w >> 16, |
205 | plane->state->src_h >> 16, |
206 | plane->state->src_x >> 16, |
207 | plane->state->src_y >> 16); |
208 | |
209 | if (bo->host3d_blob || bo->guest_blob) { |
210 | virtio_gpu_cmd_set_scanout_blob |
211 | (vgdev, scanout_id: output->index, bo, |
212 | fb: plane->state->fb, |
213 | width: plane->state->src_w >> 16, |
214 | height: plane->state->src_h >> 16, |
215 | x: plane->state->src_x >> 16, |
216 | y: plane->state->src_y >> 16); |
217 | } else { |
218 | virtio_gpu_cmd_set_scanout(vgdev, scanout_id: output->index, |
219 | resource_id: bo->hw_res_handle, |
220 | width: plane->state->src_w >> 16, |
221 | height: plane->state->src_h >> 16, |
222 | x: plane->state->src_x >> 16, |
223 | y: plane->state->src_y >> 16); |
224 | } |
225 | } |
226 | |
227 | virtio_gpu_resource_flush(plane, |
228 | x: rect.x1, |
229 | y: rect.y1, |
230 | width: rect.x2 - rect.x1, |
231 | height: rect.y2 - rect.y1); |
232 | } |
233 | |
234 | static int virtio_gpu_plane_prepare_fb(struct drm_plane *plane, |
235 | struct drm_plane_state *new_state) |
236 | { |
237 | struct drm_device *dev = plane->dev; |
238 | struct virtio_gpu_device *vgdev = dev->dev_private; |
239 | struct virtio_gpu_framebuffer *vgfb; |
240 | struct virtio_gpu_object *bo; |
241 | |
242 | if (!new_state->fb) |
243 | return 0; |
244 | |
245 | vgfb = to_virtio_gpu_framebuffer(new_state->fb); |
246 | bo = gem_to_virtio_gpu_obj(vgfb->base.obj[0]); |
247 | if (!bo || (plane->type == DRM_PLANE_TYPE_PRIMARY && !bo->guest_blob)) |
248 | return 0; |
249 | |
250 | if (bo->dumb && (plane->state->fb != new_state->fb)) { |
251 | vgfb->fence = virtio_gpu_fence_alloc(vgdev, base_fence_ctx: vgdev->fence_drv.context, |
252 | ring_idx: 0); |
253 | if (!vgfb->fence) |
254 | return -ENOMEM; |
255 | } |
256 | |
257 | return 0; |
258 | } |
259 | |
260 | static void virtio_gpu_plane_cleanup_fb(struct drm_plane *plane, |
261 | struct drm_plane_state *state) |
262 | { |
263 | struct virtio_gpu_framebuffer *vgfb; |
264 | |
265 | if (!state->fb) |
266 | return; |
267 | |
268 | vgfb = to_virtio_gpu_framebuffer(state->fb); |
269 | if (vgfb->fence) { |
270 | dma_fence_put(fence: &vgfb->fence->f); |
271 | vgfb->fence = NULL; |
272 | } |
273 | } |
274 | |
275 | static void virtio_gpu_cursor_plane_update(struct drm_plane *plane, |
276 | struct drm_atomic_state *state) |
277 | { |
278 | struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, |
279 | plane); |
280 | struct drm_device *dev = plane->dev; |
281 | struct virtio_gpu_device *vgdev = dev->dev_private; |
282 | struct virtio_gpu_output *output = NULL; |
283 | struct virtio_gpu_framebuffer *vgfb; |
284 | struct virtio_gpu_object *bo = NULL; |
285 | uint32_t handle; |
286 | |
287 | if (plane->state->crtc) |
288 | output = drm_crtc_to_virtio_gpu_output(plane->state->crtc); |
289 | if (old_state->crtc) |
290 | output = drm_crtc_to_virtio_gpu_output(old_state->crtc); |
291 | if (WARN_ON(!output)) |
292 | return; |
293 | |
294 | if (plane->state->fb) { |
295 | vgfb = to_virtio_gpu_framebuffer(plane->state->fb); |
296 | bo = gem_to_virtio_gpu_obj(vgfb->base.obj[0]); |
297 | handle = bo->hw_res_handle; |
298 | } else { |
299 | handle = 0; |
300 | } |
301 | |
302 | if (bo && bo->dumb && (plane->state->fb != old_state->fb)) { |
303 | /* new cursor -- update & wait */ |
304 | struct virtio_gpu_object_array *objs; |
305 | |
306 | objs = virtio_gpu_array_alloc(nents: 1); |
307 | if (!objs) |
308 | return; |
309 | virtio_gpu_array_add_obj(objs, obj: vgfb->base.obj[0]); |
310 | virtio_gpu_array_lock_resv(objs); |
311 | virtio_gpu_cmd_transfer_to_host_2d |
312 | (vgdev, offset: 0, |
313 | width: plane->state->crtc_w, |
314 | height: plane->state->crtc_h, |
315 | x: 0, y: 0, objs, fence: vgfb->fence); |
316 | virtio_gpu_notify(vgdev); |
317 | dma_fence_wait(fence: &vgfb->fence->f, intr: true); |
318 | dma_fence_put(fence: &vgfb->fence->f); |
319 | vgfb->fence = NULL; |
320 | } |
321 | |
322 | if (plane->state->fb != old_state->fb) { |
323 | DRM_DEBUG("update, handle %d, pos +%d+%d, hot %d,%d\n" , handle, |
324 | plane->state->crtc_x, |
325 | plane->state->crtc_y, |
326 | plane->state->fb ? plane->state->fb->hot_x : 0, |
327 | plane->state->fb ? plane->state->fb->hot_y : 0); |
328 | output->cursor.hdr.type = |
329 | cpu_to_le32(VIRTIO_GPU_CMD_UPDATE_CURSOR); |
330 | output->cursor.resource_id = cpu_to_le32(handle); |
331 | if (plane->state->fb) { |
332 | output->cursor.hot_x = |
333 | cpu_to_le32(plane->state->fb->hot_x); |
334 | output->cursor.hot_y = |
335 | cpu_to_le32(plane->state->fb->hot_y); |
336 | } else { |
337 | output->cursor.hot_x = cpu_to_le32(0); |
338 | output->cursor.hot_y = cpu_to_le32(0); |
339 | } |
340 | } else { |
341 | DRM_DEBUG("move +%d+%d\n" , |
342 | plane->state->crtc_x, |
343 | plane->state->crtc_y); |
344 | output->cursor.hdr.type = |
345 | cpu_to_le32(VIRTIO_GPU_CMD_MOVE_CURSOR); |
346 | } |
347 | output->cursor.pos.x = cpu_to_le32(plane->state->crtc_x); |
348 | output->cursor.pos.y = cpu_to_le32(plane->state->crtc_y); |
349 | virtio_gpu_cursor_ping(vgdev, output); |
350 | } |
351 | |
352 | static const struct drm_plane_helper_funcs virtio_gpu_primary_helper_funcs = { |
353 | .prepare_fb = virtio_gpu_plane_prepare_fb, |
354 | .cleanup_fb = virtio_gpu_plane_cleanup_fb, |
355 | .atomic_check = virtio_gpu_plane_atomic_check, |
356 | .atomic_update = virtio_gpu_primary_plane_update, |
357 | }; |
358 | |
359 | static const struct drm_plane_helper_funcs virtio_gpu_cursor_helper_funcs = { |
360 | .prepare_fb = virtio_gpu_plane_prepare_fb, |
361 | .cleanup_fb = virtio_gpu_plane_cleanup_fb, |
362 | .atomic_check = virtio_gpu_plane_atomic_check, |
363 | .atomic_update = virtio_gpu_cursor_plane_update, |
364 | }; |
365 | |
366 | struct drm_plane *virtio_gpu_plane_init(struct virtio_gpu_device *vgdev, |
367 | enum drm_plane_type type, |
368 | int index) |
369 | { |
370 | struct drm_device *dev = vgdev->ddev; |
371 | const struct drm_plane_helper_funcs *funcs; |
372 | struct drm_plane *plane; |
373 | const uint32_t *formats; |
374 | int nformats; |
375 | |
376 | if (type == DRM_PLANE_TYPE_CURSOR) { |
377 | formats = virtio_gpu_cursor_formats; |
378 | nformats = ARRAY_SIZE(virtio_gpu_cursor_formats); |
379 | funcs = &virtio_gpu_cursor_helper_funcs; |
380 | } else { |
381 | formats = virtio_gpu_formats; |
382 | nformats = ARRAY_SIZE(virtio_gpu_formats); |
383 | funcs = &virtio_gpu_primary_helper_funcs; |
384 | } |
385 | |
386 | plane = drmm_universal_plane_alloc(dev, struct drm_plane, dev, |
387 | 1 << index, &virtio_gpu_plane_funcs, |
388 | formats, nformats, NULL, type, NULL); |
389 | if (IS_ERR(ptr: plane)) |
390 | return plane; |
391 | |
392 | drm_plane_helper_add(plane, funcs); |
393 | |
394 | if (type == DRM_PLANE_TYPE_PRIMARY) |
395 | drm_plane_enable_fb_damage_clips(plane); |
396 | |
397 | return plane; |
398 | } |
399 | |