]> Git Repo - linux.git/blob - drivers/gpu/drm/virtio/virtgpu_plane.c
Merge tag 'apparmor-pr-2024-07-25' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux.git] / drivers / gpu / drm / virtio / virtgpu_plane.c
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         struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state,
83                                                                                  plane);
84         bool is_cursor = plane->type == DRM_PLANE_TYPE_CURSOR;
85         struct drm_crtc_state *crtc_state;
86         int ret;
87
88         if (!new_plane_state->fb || WARN_ON(!new_plane_state->crtc))
89                 return 0;
90
91         /*
92          * Ignore damage clips if the framebuffer attached to the plane's state
93          * has changed since the last plane update (page-flip). In this case, a
94          * full plane update should happen because uploads are done per-buffer.
95          */
96         if (old_plane_state->fb != new_plane_state->fb)
97                 new_plane_state->ignore_damage_clips = true;
98
99         crtc_state = drm_atomic_get_crtc_state(state,
100                                                new_plane_state->crtc);
101         if (IS_ERR(crtc_state))
102                 return PTR_ERR(crtc_state);
103
104         ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
105                                                   DRM_PLANE_NO_SCALING,
106                                                   DRM_PLANE_NO_SCALING,
107                                                   is_cursor, true);
108         return ret;
109 }
110
111 static void virtio_gpu_update_dumb_bo(struct virtio_gpu_device *vgdev,
112                                       struct drm_plane_state *state,
113                                       struct drm_rect *rect)
114 {
115         struct virtio_gpu_object *bo =
116                 gem_to_virtio_gpu_obj(state->fb->obj[0]);
117         struct virtio_gpu_object_array *objs;
118         uint32_t w = rect->x2 - rect->x1;
119         uint32_t h = rect->y2 - rect->y1;
120         uint32_t x = rect->x1;
121         uint32_t y = rect->y1;
122         uint32_t off = x * state->fb->format->cpp[0] +
123                 y * state->fb->pitches[0];
124
125         objs = virtio_gpu_array_alloc(1);
126         if (!objs)
127                 return;
128         virtio_gpu_array_add_obj(objs, &bo->base.base);
129
130         virtio_gpu_cmd_transfer_to_host_2d(vgdev, off, w, h, x, y,
131                                            objs, NULL);
132 }
133
134 static void virtio_gpu_resource_flush(struct drm_plane *plane,
135                                       uint32_t x, uint32_t y,
136                                       uint32_t width, uint32_t height)
137 {
138         struct drm_device *dev = plane->dev;
139         struct virtio_gpu_device *vgdev = dev->dev_private;
140         struct virtio_gpu_framebuffer *vgfb;
141         struct virtio_gpu_object *bo;
142
143         vgfb = to_virtio_gpu_framebuffer(plane->state->fb);
144         bo = gem_to_virtio_gpu_obj(vgfb->base.obj[0]);
145         if (vgfb->fence) {
146                 struct virtio_gpu_object_array *objs;
147
148                 objs = virtio_gpu_array_alloc(1);
149                 if (!objs)
150                         return;
151                 virtio_gpu_array_add_obj(objs, vgfb->base.obj[0]);
152                 virtio_gpu_array_lock_resv(objs);
153                 virtio_gpu_cmd_resource_flush(vgdev, bo->hw_res_handle, x, y,
154                                               width, height, objs, vgfb->fence);
155                 virtio_gpu_notify(vgdev);
156
157                 dma_fence_wait_timeout(&vgfb->fence->f, true,
158                                        msecs_to_jiffies(50));
159                 dma_fence_put(&vgfb->fence->f);
160                 vgfb->fence = NULL;
161         } else {
162                 virtio_gpu_cmd_resource_flush(vgdev, bo->hw_res_handle, x, y,
163                                               width, height, NULL, NULL);
164                 virtio_gpu_notify(vgdev);
165         }
166 }
167
168 static void virtio_gpu_primary_plane_update(struct drm_plane *plane,
169                                             struct drm_atomic_state *state)
170 {
171         struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
172                                                                            plane);
173         struct drm_device *dev = plane->dev;
174         struct virtio_gpu_device *vgdev = dev->dev_private;
175         struct virtio_gpu_output *output = NULL;
176         struct virtio_gpu_object *bo;
177         struct drm_rect rect;
178
179         if (plane->state->crtc)
180                 output = drm_crtc_to_virtio_gpu_output(plane->state->crtc);
181         if (old_state->crtc)
182                 output = drm_crtc_to_virtio_gpu_output(old_state->crtc);
183         if (WARN_ON(!output))
184                 return;
185
186         if (!plane->state->fb || !output->crtc.state->active) {
187                 DRM_DEBUG("nofb\n");
188                 virtio_gpu_cmd_set_scanout(vgdev, output->index, 0,
189                                            plane->state->src_w >> 16,
190                                            plane->state->src_h >> 16,
191                                            0, 0);
192                 virtio_gpu_notify(vgdev);
193                 return;
194         }
195
196         if (!drm_atomic_helper_damage_merged(old_state, plane->state, &rect))
197                 return;
198
199         bo = gem_to_virtio_gpu_obj(plane->state->fb->obj[0]);
200         if (bo->dumb)
201                 virtio_gpu_update_dumb_bo(vgdev, plane->state, &rect);
202
203         if (plane->state->fb != old_state->fb ||
204             plane->state->src_w != old_state->src_w ||
205             plane->state->src_h != old_state->src_h ||
206             plane->state->src_x != old_state->src_x ||
207             plane->state->src_y != old_state->src_y ||
208             output->needs_modeset) {
209                 output->needs_modeset = false;
210                 DRM_DEBUG("handle 0x%x, crtc %dx%d+%d+%d, src %dx%d+%d+%d\n",
211                           bo->hw_res_handle,
212                           plane->state->crtc_w, plane->state->crtc_h,
213                           plane->state->crtc_x, plane->state->crtc_y,
214                           plane->state->src_w >> 16,
215                           plane->state->src_h >> 16,
216                           plane->state->src_x >> 16,
217                           plane->state->src_y >> 16);
218
219                 if (bo->host3d_blob || bo->guest_blob) {
220                         virtio_gpu_cmd_set_scanout_blob
221                                                 (vgdev, output->index, bo,
222                                                  plane->state->fb,
223                                                  plane->state->src_w >> 16,
224                                                  plane->state->src_h >> 16,
225                                                  plane->state->src_x >> 16,
226                                                  plane->state->src_y >> 16);
227                 } else {
228                         virtio_gpu_cmd_set_scanout(vgdev, output->index,
229                                                    bo->hw_res_handle,
230                                                    plane->state->src_w >> 16,
231                                                    plane->state->src_h >> 16,
232                                                    plane->state->src_x >> 16,
233                                                    plane->state->src_y >> 16);
234                 }
235         }
236
237         virtio_gpu_resource_flush(plane,
238                                   rect.x1,
239                                   rect.y1,
240                                   rect.x2 - rect.x1,
241                                   rect.y2 - rect.y1);
242 }
243
244 static int virtio_gpu_plane_prepare_fb(struct drm_plane *plane,
245                                        struct drm_plane_state *new_state)
246 {
247         struct drm_device *dev = plane->dev;
248         struct virtio_gpu_device *vgdev = dev->dev_private;
249         struct virtio_gpu_framebuffer *vgfb;
250         struct virtio_gpu_object *bo;
251
252         if (!new_state->fb)
253                 return 0;
254
255         vgfb = to_virtio_gpu_framebuffer(new_state->fb);
256         bo = gem_to_virtio_gpu_obj(vgfb->base.obj[0]);
257         if (!bo || (plane->type == DRM_PLANE_TYPE_PRIMARY && !bo->guest_blob))
258                 return 0;
259
260         if (bo->dumb && (plane->state->fb != new_state->fb)) {
261                 vgfb->fence = virtio_gpu_fence_alloc(vgdev, vgdev->fence_drv.context,
262                                                      0);
263                 if (!vgfb->fence)
264                         return -ENOMEM;
265         }
266
267         return 0;
268 }
269
270 static void virtio_gpu_plane_cleanup_fb(struct drm_plane *plane,
271                                         struct drm_plane_state *state)
272 {
273         struct virtio_gpu_framebuffer *vgfb;
274
275         if (!state->fb)
276                 return;
277
278         vgfb = to_virtio_gpu_framebuffer(state->fb);
279         if (vgfb->fence) {
280                 dma_fence_put(&vgfb->fence->f);
281                 vgfb->fence = NULL;
282         }
283 }
284
285 static void virtio_gpu_cursor_plane_update(struct drm_plane *plane,
286                                            struct drm_atomic_state *state)
287 {
288         struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
289                                                                            plane);
290         struct drm_device *dev = plane->dev;
291         struct virtio_gpu_device *vgdev = dev->dev_private;
292         struct virtio_gpu_output *output = NULL;
293         struct virtio_gpu_framebuffer *vgfb;
294         struct virtio_gpu_object *bo = NULL;
295         uint32_t handle;
296
297         if (plane->state->crtc)
298                 output = drm_crtc_to_virtio_gpu_output(plane->state->crtc);
299         if (old_state->crtc)
300                 output = drm_crtc_to_virtio_gpu_output(old_state->crtc);
301         if (WARN_ON(!output))
302                 return;
303
304         if (plane->state->fb) {
305                 vgfb = to_virtio_gpu_framebuffer(plane->state->fb);
306                 bo = gem_to_virtio_gpu_obj(vgfb->base.obj[0]);
307                 handle = bo->hw_res_handle;
308         } else {
309                 handle = 0;
310         }
311
312         if (bo && bo->dumb && (plane->state->fb != old_state->fb)) {
313                 /* new cursor -- update & wait */
314                 struct virtio_gpu_object_array *objs;
315
316                 objs = virtio_gpu_array_alloc(1);
317                 if (!objs)
318                         return;
319                 virtio_gpu_array_add_obj(objs, vgfb->base.obj[0]);
320                 virtio_gpu_array_lock_resv(objs);
321                 virtio_gpu_cmd_transfer_to_host_2d
322                         (vgdev, 0,
323                          plane->state->crtc_w,
324                          plane->state->crtc_h,
325                          0, 0, objs, vgfb->fence);
326                 virtio_gpu_notify(vgdev);
327                 dma_fence_wait(&vgfb->fence->f, true);
328                 dma_fence_put(&vgfb->fence->f);
329                 vgfb->fence = NULL;
330         }
331
332         if (plane->state->fb != old_state->fb) {
333                 DRM_DEBUG("update, handle %d, pos +%d+%d, hot %d,%d\n", handle,
334                           plane->state->crtc_x,
335                           plane->state->crtc_y,
336                           plane->state->hotspot_x,
337                           plane->state->hotspot_y);
338                 output->cursor.hdr.type =
339                         cpu_to_le32(VIRTIO_GPU_CMD_UPDATE_CURSOR);
340                 output->cursor.resource_id = cpu_to_le32(handle);
341                 if (plane->state->fb) {
342                         output->cursor.hot_x =
343                                 cpu_to_le32(plane->state->hotspot_x);
344                         output->cursor.hot_y =
345                                 cpu_to_le32(plane->state->hotspot_y);
346                 } else {
347                         output->cursor.hot_x = cpu_to_le32(0);
348                         output->cursor.hot_y = cpu_to_le32(0);
349                 }
350         } else {
351                 DRM_DEBUG("move +%d+%d\n",
352                           plane->state->crtc_x,
353                           plane->state->crtc_y);
354                 output->cursor.hdr.type =
355                         cpu_to_le32(VIRTIO_GPU_CMD_MOVE_CURSOR);
356         }
357         output->cursor.pos.x = cpu_to_le32(plane->state->crtc_x);
358         output->cursor.pos.y = cpu_to_le32(plane->state->crtc_y);
359         virtio_gpu_cursor_ping(vgdev, output);
360 }
361
362 static const struct drm_plane_helper_funcs virtio_gpu_primary_helper_funcs = {
363         .prepare_fb             = virtio_gpu_plane_prepare_fb,
364         .cleanup_fb             = virtio_gpu_plane_cleanup_fb,
365         .atomic_check           = virtio_gpu_plane_atomic_check,
366         .atomic_update          = virtio_gpu_primary_plane_update,
367 };
368
369 static const struct drm_plane_helper_funcs virtio_gpu_cursor_helper_funcs = {
370         .prepare_fb             = virtio_gpu_plane_prepare_fb,
371         .cleanup_fb             = virtio_gpu_plane_cleanup_fb,
372         .atomic_check           = virtio_gpu_plane_atomic_check,
373         .atomic_update          = virtio_gpu_cursor_plane_update,
374 };
375
376 struct drm_plane *virtio_gpu_plane_init(struct virtio_gpu_device *vgdev,
377                                         enum drm_plane_type type,
378                                         int index)
379 {
380         struct drm_device *dev = vgdev->ddev;
381         const struct drm_plane_helper_funcs *funcs;
382         struct drm_plane *plane;
383         const uint32_t *formats;
384         int nformats;
385
386         if (type == DRM_PLANE_TYPE_CURSOR) {
387                 formats = virtio_gpu_cursor_formats;
388                 nformats = ARRAY_SIZE(virtio_gpu_cursor_formats);
389                 funcs = &virtio_gpu_cursor_helper_funcs;
390         } else {
391                 formats = virtio_gpu_formats;
392                 nformats = ARRAY_SIZE(virtio_gpu_formats);
393                 funcs = &virtio_gpu_primary_helper_funcs;
394         }
395
396         plane = drmm_universal_plane_alloc(dev, struct drm_plane, dev,
397                                            1 << index, &virtio_gpu_plane_funcs,
398                                            formats, nformats, NULL, type, NULL);
399         if (IS_ERR(plane))
400                 return plane;
401
402         drm_plane_helper_add(plane, funcs);
403
404         if (type == DRM_PLANE_TYPE_PRIMARY)
405                 drm_plane_enable_fb_damage_clips(plane);
406
407         return plane;
408 }
This page took 0.066993 seconds and 4 git commands to generate.