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