Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

drm/vmwgfx: Implement virtual crc generation

crc checksums are used to validate the output. Normally they're part
of the actual display hardware but on virtual stack there's nothing
to automatically generate them.

Implement crc generation for the vmwgfx stack. This works only on
screen targets, where it's possibly to easily make sure that the
guest side contents of the surface matches the host sides output.

Just like the vblank support, crc generation can only be enabled via:
guestinfo.vmwgfx.vkms_enable = "TRUE"
option in the vmx file.

Makes IGT's kms_pipe_crc_basic pass and allows a huge number of other
IGT tests which require CRC generation of the output to actually run
on vmwgfx. Makes it possible to actually validate a lof of the kms and
drm functionality with vmwgfx.

Signed-off-by: Zack Rusin <zack.rusin@broadcom.com>
Acked-by: Martin Krastev <martin.krastev@broadcom.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20240412025511.78553-3-zack.rusin@broadcom.com

+553 -35
+1
drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
··· 1198 1198 1199 1199 vmw_svga_disable(dev_priv); 1200 1200 1201 + vmw_vkms_cleanup(dev_priv); 1201 1202 vmw_kms_close(dev_priv); 1202 1203 vmw_overlay_close(dev_priv); 1203 1204
+2
drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
··· 616 616 uint32 *devcaps; 617 617 618 618 bool vkms_enabled; 619 + struct workqueue_struct *crc_workq; 619 620 620 621 /* 621 622 * mksGuestStat instance-descriptor and pid arrays ··· 812 811 void vmw_resource_mob_detach(struct vmw_resource *res); 813 812 void vmw_resource_dirty_update(struct vmw_resource *res, pgoff_t start, 814 813 pgoff_t end); 814 + int vmw_resource_clean(struct vmw_resource *res); 815 815 int vmw_resources_clean(struct vmw_bo *vbo, pgoff_t start, 816 816 pgoff_t end, pgoff_t *num_prefault); 817 817
+28 -3
drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
··· 40 40 41 41 void vmw_du_init(struct vmw_display_unit *du) 42 42 { 43 - hrtimer_init(&du->vkms.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); 44 - du->vkms.timer.function = &vmw_vkms_vblank_simulate; 43 + vmw_vkms_crtc_init(&du->crtc); 45 44 } 46 45 47 46 void vmw_du_cleanup(struct vmw_display_unit *du) 48 47 { 49 48 struct vmw_private *dev_priv = vmw_priv(du->primary.dev); 50 - hrtimer_cancel(&du->vkms.timer); 49 + 50 + vmw_vkms_crtc_cleanup(&du->crtc); 51 51 drm_plane_cleanup(&du->primary); 52 52 if (vmw_cmd_supported(dev_priv)) 53 53 drm_plane_cleanup(&du->cursor.base); ··· 963 963 void vmw_du_crtc_atomic_begin(struct drm_crtc *crtc, 964 964 struct drm_atomic_state *state) 965 965 { 966 + vmw_vkms_crtc_atomic_begin(crtc, state); 966 967 } 967 968 968 969 /** ··· 2030 2029 "hotplug_mode_update", 0, 1); 2031 2030 } 2032 2031 2032 + static void 2033 + vmw_atomic_commit_tail(struct drm_atomic_state *old_state) 2034 + { 2035 + struct vmw_private *vmw = vmw_priv(old_state->dev); 2036 + struct drm_crtc *crtc; 2037 + struct drm_crtc_state *old_crtc_state; 2038 + int i; 2039 + 2040 + drm_atomic_helper_commit_tail(old_state); 2041 + 2042 + if (vmw->vkms_enabled) { 2043 + for_each_old_crtc_in_state(old_state, crtc, old_crtc_state, i) { 2044 + struct vmw_display_unit *du = vmw_crtc_to_du(crtc); 2045 + (void)old_crtc_state; 2046 + flush_work(&du->vkms.crc_generator_work); 2047 + } 2048 + } 2049 + } 2050 + 2051 + static const struct drm_mode_config_helper_funcs vmw_mode_config_helpers = { 2052 + .atomic_commit_tail = vmw_atomic_commit_tail, 2053 + }; 2054 + 2033 2055 int vmw_kms_init(struct vmw_private *dev_priv) 2034 2056 { 2035 2057 struct drm_device *dev = &dev_priv->drm; ··· 2072 2048 dev->mode_config.max_width = dev_priv->texture_max_width; 2073 2049 dev->mode_config.max_height = dev_priv->texture_max_height; 2074 2050 dev->mode_config.preferred_depth = dev_priv->assume_16bpp ? 16 : 32; 2051 + dev->mode_config.helper_private = &vmw_mode_config_helpers; 2075 2052 2076 2053 drm_mode_create_suggested_offset_properties(dev); 2077 2054 vmw_kms_create_hotplug_mode_update_property(dev_priv);
+14 -1
drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
··· 378 378 int set_gui_y; 379 379 380 380 struct { 381 + struct work_struct crc_generator_work; 381 382 struct hrtimer timer; 382 383 ktime_t period_ns; 383 - struct drm_pending_vblank_event *event; 384 + 385 + /* protects concurrent access to the vblank handler */ 386 + atomic_t atomic_lock; 387 + /* protected by @atomic_lock */ 388 + bool crc_enabled; 389 + struct vmw_surface *surface; 390 + 391 + /* protects concurrent access to the crc worker */ 392 + spinlock_t crc_state_lock; 393 + /* protected by @crc_state_lock */ 394 + bool crc_pending; 395 + u64 frame_start; 396 + u64 frame_end; 384 397 } vkms; 385 398 }; 386 399
+20 -12
drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
··· 1064 1064 end << PAGE_SHIFT); 1065 1065 } 1066 1066 1067 + int vmw_resource_clean(struct vmw_resource *res) 1068 + { 1069 + int ret = 0; 1070 + 1071 + if (res->res_dirty) { 1072 + if (!res->func->clean) 1073 + return -EINVAL; 1074 + 1075 + ret = res->func->clean(res); 1076 + if (ret) 1077 + return ret; 1078 + res->res_dirty = false; 1079 + } 1080 + return ret; 1081 + } 1082 + 1067 1083 /** 1068 1084 * vmw_resources_clean - Clean resources intersecting a mob range 1069 1085 * @vbo: The mob buffer object ··· 1096 1080 unsigned long res_start = start << PAGE_SHIFT; 1097 1081 unsigned long res_end = end << PAGE_SHIFT; 1098 1082 unsigned long last_cleaned = 0; 1083 + int ret; 1099 1084 1100 1085 /* 1101 1086 * Find the resource with lowest backup_offset that intersects the ··· 1123 1106 * intersecting the range. 1124 1107 */ 1125 1108 while (found) { 1126 - if (found->res_dirty) { 1127 - int ret; 1128 - 1129 - if (!found->func->clean) 1130 - return -EINVAL; 1131 - 1132 - ret = found->func->clean(found); 1133 - if (ret) 1134 - return ret; 1135 - 1136 - found->res_dirty = false; 1137 - } 1109 + ret = vmw_resource_clean(found); 1110 + if (ret) 1111 + return ret; 1138 1112 last_cleaned = found->guest_memory_offset + found->guest_memory_size; 1139 1113 cur = rb_next(&found->mob_node); 1140 1114 if (!cur)
+15 -7
drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
··· 409 409 crtc->x, crtc->y); 410 410 } 411 411 412 - 413 - static void vmw_stdu_crtc_helper_prepare(struct drm_crtc *crtc) 414 - { 415 - } 416 - 417 412 static void vmw_stdu_crtc_atomic_disable(struct drm_crtc *crtc, 418 413 struct drm_atomic_state *state) 419 414 { ··· 778 783 .enable_vblank = vmw_vkms_enable_vblank, 779 784 .disable_vblank = vmw_vkms_disable_vblank, 780 785 .get_vblank_timestamp = vmw_vkms_get_vblank_timestamp, 786 + .get_crc_sources = vmw_vkms_get_crc_sources, 787 + .set_crc_source = vmw_vkms_set_crc_source, 788 + .verify_crc_source = vmw_vkms_verify_crc_source, 781 789 }; 782 790 783 791 ··· 1412 1414 vmw_fence_obj_unreference(&fence); 1413 1415 } 1414 1416 1417 + static void 1418 + vmw_stdu_crtc_atomic_flush(struct drm_crtc *crtc, 1419 + struct drm_atomic_state *state) 1420 + { 1421 + struct vmw_private *vmw = vmw_priv(crtc->dev); 1422 + struct vmw_screen_target_display_unit *stdu = vmw_crtc_to_stdu(crtc); 1423 + 1424 + if (vmw->vkms_enabled) 1425 + vmw_vkms_set_crc_surface(crtc, stdu->display_srf); 1426 + vmw_vkms_crtc_atomic_flush(crtc, state); 1427 + } 1415 1428 1416 1429 static const struct drm_plane_funcs vmw_stdu_plane_funcs = { 1417 1430 .update_plane = drm_atomic_helper_update_plane, ··· 1463 1454 }; 1464 1455 1465 1456 static const struct drm_crtc_helper_funcs vmw_stdu_crtc_helper_funcs = { 1466 - .prepare = vmw_stdu_crtc_helper_prepare, 1467 1457 .mode_set_nofb = vmw_stdu_crtc_mode_set_nofb, 1468 1458 .atomic_check = vmw_du_crtc_atomic_check, 1469 1459 .atomic_begin = vmw_du_crtc_atomic_begin, 1470 - .atomic_flush = vmw_vkms_crtc_atomic_flush, 1460 + .atomic_flush = vmw_stdu_crtc_atomic_flush, 1471 1461 .atomic_enable = vmw_vkms_crtc_atomic_enable, 1472 1462 .atomic_disable = vmw_stdu_crtc_atomic_disable, 1473 1463 };
+448 -9
drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c
··· 28 28 29 29 #include "vmwgfx_vkms.h" 30 30 31 + #include "vmwgfx_bo.h" 31 32 #include "vmwgfx_drv.h" 32 33 #include "vmwgfx_kms.h" 33 34 #include "vmwgfx_vkms.h" 34 35 36 + #include "vmw_surface_cache.h" 37 + 35 38 #include <drm/drm_crtc.h> 39 + #include <drm/drm_debugfs_crc.h> 36 40 #include <drm/drm_print.h> 37 41 #include <drm/drm_vblank.h> 38 42 43 + #include <linux/crc32.h> 44 + #include <linux/delay.h> 45 + 39 46 #define GUESTINFO_VBLANK "guestinfo.vmwgfx.vkms_enable" 40 47 41 - enum hrtimer_restart 48 + static int 49 + vmw_surface_sync(struct vmw_private *vmw, 50 + struct vmw_surface *surf) 51 + { 52 + int ret; 53 + struct vmw_fence_obj *fence = NULL; 54 + struct vmw_bo *bo = surf->res.guest_memory_bo; 55 + 56 + vmw_resource_clean(&surf->res); 57 + 58 + ret = ttm_bo_reserve(&bo->tbo, false, false, NULL); 59 + if (ret != 0) { 60 + drm_warn(&vmw->drm, "%s: failed reserve\n", __func__); 61 + goto done; 62 + } 63 + 64 + ret = vmw_execbuf_fence_commands(NULL, vmw, &fence, NULL); 65 + if (ret != 0) { 66 + drm_warn(&vmw->drm, "%s: failed execbuf\n", __func__); 67 + ttm_bo_unreserve(&bo->tbo); 68 + goto done; 69 + } 70 + 71 + dma_fence_wait(&fence->base, false); 72 + dma_fence_put(&fence->base); 73 + 74 + ttm_bo_unreserve(&bo->tbo); 75 + done: 76 + return ret; 77 + } 78 + 79 + static int 80 + compute_crc(struct drm_crtc *crtc, 81 + struct vmw_surface *surf, 82 + u32 *crc) 83 + { 84 + u8 *mapped_surface; 85 + struct vmw_bo *bo = surf->res.guest_memory_bo; 86 + const struct SVGA3dSurfaceDesc *desc = 87 + vmw_surface_get_desc(surf->metadata.format); 88 + u32 row_pitch_bytes; 89 + SVGA3dSize blocks; 90 + u32 y; 91 + 92 + *crc = 0; 93 + 94 + vmw_surface_get_size_in_blocks(desc, &surf->metadata.base_size, &blocks); 95 + row_pitch_bytes = blocks.width * desc->pitchBytesPerBlock; 96 + WARN_ON(!bo); 97 + mapped_surface = vmw_bo_map_and_cache(bo); 98 + 99 + for (y = 0; y < blocks.height; y++) { 100 + *crc = crc32_le(*crc, mapped_surface, row_pitch_bytes); 101 + mapped_surface += row_pitch_bytes; 102 + } 103 + 104 + vmw_bo_unmap(bo); 105 + 106 + return 0; 107 + } 108 + 109 + static void 110 + crc_generate_worker(struct work_struct *work) 111 + { 112 + struct vmw_display_unit *du = 113 + container_of(work, struct vmw_display_unit, vkms.crc_generator_work); 114 + struct drm_crtc *crtc = &du->crtc; 115 + struct vmw_private *vmw = vmw_priv(crtc->dev); 116 + bool crc_pending; 117 + u64 frame_start, frame_end; 118 + u32 crc32 = 0; 119 + struct vmw_surface *surf = 0; 120 + int ret; 121 + 122 + spin_lock_irq(&du->vkms.crc_state_lock); 123 + crc_pending = du->vkms.crc_pending; 124 + spin_unlock_irq(&du->vkms.crc_state_lock); 125 + 126 + /* 127 + * We raced with the vblank hrtimer and previous work already computed 128 + * the crc, nothing to do. 129 + */ 130 + if (!crc_pending) 131 + return; 132 + 133 + spin_lock_irq(&du->vkms.crc_state_lock); 134 + surf = du->vkms.surface; 135 + spin_unlock_irq(&du->vkms.crc_state_lock); 136 + 137 + if (vmw_surface_sync(vmw, surf)) { 138 + drm_warn(crtc->dev, "CRC worker wasn't able to sync the crc surface!\n"); 139 + return; 140 + } 141 + 142 + ret = compute_crc(crtc, surf, &crc32); 143 + if (ret) 144 + return; 145 + 146 + spin_lock_irq(&du->vkms.crc_state_lock); 147 + frame_start = du->vkms.frame_start; 148 + frame_end = du->vkms.frame_end; 149 + crc_pending = du->vkms.crc_pending; 150 + du->vkms.frame_start = 0; 151 + du->vkms.frame_end = 0; 152 + du->vkms.crc_pending = false; 153 + spin_unlock_irq(&du->vkms.crc_state_lock); 154 + 155 + /* 156 + * The worker can fall behind the vblank hrtimer, make sure we catch up. 157 + */ 158 + while (frame_start <= frame_end) 159 + drm_crtc_add_crc_entry(crtc, true, frame_start++, &crc32); 160 + } 161 + 162 + static enum hrtimer_restart 42 163 vmw_vkms_vblank_simulate(struct hrtimer *timer) 43 164 { 44 165 struct vmw_display_unit *du = container_of(timer, struct vmw_display_unit, vkms.timer); 45 166 struct drm_crtc *crtc = &du->crtc; 167 + struct vmw_private *vmw = vmw_priv(crtc->dev); 168 + struct vmw_surface *surf = NULL; 46 169 u64 ret_overrun; 47 - bool ret; 170 + bool locked, ret; 48 171 49 172 ret_overrun = hrtimer_forward_now(&du->vkms.timer, 50 173 du->vkms.period_ns); 51 174 if (ret_overrun != 1) 52 - DRM_WARN("%s: vblank timer overrun\n", __func__); 175 + drm_dbg_driver(crtc->dev, "vblank timer missed %lld frames.\n", 176 + ret_overrun - 1); 53 177 178 + locked = vmw_vkms_vblank_trylock(crtc); 54 179 ret = drm_crtc_handle_vblank(crtc); 55 - /* Don't queue timer again when vblank is disabled. */ 56 - if (!ret) 57 - return HRTIMER_NORESTART; 180 + WARN_ON(!ret); 181 + if (!locked) 182 + return HRTIMER_RESTART; 183 + surf = du->vkms.surface; 184 + vmw_vkms_unlock(crtc); 185 + 186 + if (du->vkms.crc_enabled && surf) { 187 + u64 frame = drm_crtc_accurate_vblank_count(crtc); 188 + 189 + spin_lock(&du->vkms.crc_state_lock); 190 + if (!du->vkms.crc_pending) 191 + du->vkms.frame_start = frame; 192 + else 193 + drm_dbg_driver(crtc->dev, 194 + "crc worker falling behind, frame_start: %llu, frame_end: %llu\n", 195 + du->vkms.frame_start, frame); 196 + du->vkms.frame_end = frame; 197 + du->vkms.crc_pending = true; 198 + spin_unlock(&du->vkms.crc_state_lock); 199 + 200 + ret = queue_work(vmw->crc_workq, &du->vkms.crc_generator_work); 201 + if (!ret) 202 + drm_dbg_driver(crtc->dev, "Composer worker already queued\n"); 203 + } 58 204 59 205 return HRTIMER_RESTART; 60 206 } ··· 224 78 if (!ret && vmw->vkms_enabled) { 225 79 ret = drm_vblank_init(&vmw->drm, VMWGFX_NUM_DISPLAY_UNITS); 226 80 vmw->vkms_enabled = (ret == 0); 227 - drm_info(&vmw->drm, "vkms_enabled = %d\n", vmw->vkms_enabled); 228 81 } 82 + 83 + vmw->crc_workq = alloc_ordered_workqueue("vmwgfx_crc_generator", 0); 84 + if (!vmw->crc_workq) { 85 + drm_warn(&vmw->drm, "crc workqueue allocation failed. Disabling vkms."); 86 + vmw->vkms_enabled = false; 87 + } 88 + if (vmw->vkms_enabled) 89 + drm_info(&vmw->drm, "VKMS enabled\n"); 90 + } 91 + 92 + void 93 + vmw_vkms_cleanup(struct vmw_private *vmw) 94 + { 95 + destroy_workqueue(vmw->crc_workq); 229 96 } 230 97 231 98 bool ··· 292 133 293 134 drm_calc_timestamping_constants(crtc, &crtc->mode); 294 135 136 + hrtimer_init(&du->vkms.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); 137 + du->vkms.timer.function = &vmw_vkms_vblank_simulate; 295 138 du->vkms.period_ns = ktime_set(0, vblank->framedur_ns); 296 139 hrtimer_start(&du->vkms.timer, du->vkms.period_ns, HRTIMER_MODE_REL); 297 140 ··· 309 148 if (!vmw->vkms_enabled) 310 149 return; 311 150 312 - hrtimer_try_to_cancel(&du->vkms.timer); 151 + hrtimer_cancel(&du->vkms.timer); 152 + du->vkms.surface = NULL; 153 + du->vkms.period_ns = ktime_set(0, 0); 154 + } 155 + 156 + enum vmw_vkms_lock_state { 157 + VMW_VKMS_LOCK_UNLOCKED = 0, 158 + VMW_VKMS_LOCK_MODESET = 1, 159 + VMW_VKMS_LOCK_VBLANK = 2 160 + }; 161 + 162 + void 163 + vmw_vkms_crtc_init(struct drm_crtc *crtc) 164 + { 165 + struct vmw_display_unit *du = vmw_crtc_to_du(crtc); 166 + 167 + atomic_set(&du->vkms.atomic_lock, VMW_VKMS_LOCK_UNLOCKED); 168 + spin_lock_init(&du->vkms.crc_state_lock); 169 + 170 + INIT_WORK(&du->vkms.crc_generator_work, crc_generate_worker); 171 + du->vkms.surface = NULL; 172 + } 173 + 174 + void 175 + vmw_vkms_crtc_cleanup(struct drm_crtc *crtc) 176 + { 177 + struct vmw_display_unit *du = vmw_crtc_to_du(crtc); 178 + 179 + WARN_ON(work_pending(&du->vkms.crc_generator_work)); 180 + hrtimer_cancel(&du->vkms.timer); 181 + } 182 + 183 + void 184 + vmw_vkms_crtc_atomic_begin(struct drm_crtc *crtc, 185 + struct drm_atomic_state *state) 186 + { 187 + struct vmw_private *vmw = vmw_priv(crtc->dev); 188 + 189 + if (vmw->vkms_enabled) 190 + vmw_vkms_modeset_lock(crtc); 313 191 } 314 192 315 193 void ··· 358 158 unsigned long flags; 359 159 struct vmw_private *vmw = vmw_priv(crtc->dev); 360 160 361 - if (vmw->vkms_enabled && crtc->state->event) { 161 + if (!vmw->vkms_enabled) 162 + return; 163 + 164 + if (crtc->state->event) { 362 165 spin_lock_irqsave(&crtc->dev->event_lock, flags); 363 166 364 167 if (drm_crtc_vblank_get(crtc) != 0) ··· 373 170 374 171 crtc->state->event = NULL; 375 172 } 173 + 174 + vmw_vkms_unlock(crtc); 376 175 } 377 176 378 177 void ··· 395 190 396 191 if (vmw->vkms_enabled) 397 192 drm_crtc_vblank_off(crtc); 193 + } 194 + 195 + static bool 196 + is_crc_supported(struct drm_crtc *crtc) 197 + { 198 + struct vmw_private *vmw = vmw_priv(crtc->dev); 199 + 200 + if (!vmw->vkms_enabled) 201 + return false; 202 + 203 + if (vmw->active_display_unit != vmw_du_screen_target) 204 + return false; 205 + 206 + return true; 207 + } 208 + 209 + static const char * const pipe_crc_sources[] = {"auto"}; 210 + 211 + static int 212 + crc_parse_source(const char *src_name, 213 + bool *enabled) 214 + { 215 + int ret = 0; 216 + 217 + if (!src_name) { 218 + *enabled = false; 219 + } else if (strcmp(src_name, "auto") == 0) { 220 + *enabled = true; 221 + } else { 222 + *enabled = false; 223 + ret = -EINVAL; 224 + } 225 + 226 + return ret; 227 + } 228 + 229 + const char *const * 230 + vmw_vkms_get_crc_sources(struct drm_crtc *crtc, 231 + size_t *count) 232 + { 233 + *count = 0; 234 + if (!is_crc_supported(crtc)) 235 + return NULL; 236 + 237 + *count = ARRAY_SIZE(pipe_crc_sources); 238 + return pipe_crc_sources; 239 + } 240 + 241 + int 242 + vmw_vkms_verify_crc_source(struct drm_crtc *crtc, 243 + const char *src_name, 244 + size_t *values_cnt) 245 + { 246 + bool enabled; 247 + 248 + if (!is_crc_supported(crtc)) 249 + return -EINVAL; 250 + 251 + if (crc_parse_source(src_name, &enabled) < 0) { 252 + drm_dbg_driver(crtc->dev, "unknown source '%s'\n", src_name); 253 + return -EINVAL; 254 + } 255 + 256 + *values_cnt = 1; 257 + 258 + return 0; 259 + } 260 + 261 + int 262 + vmw_vkms_set_crc_source(struct drm_crtc *crtc, 263 + const char *src_name) 264 + { 265 + struct vmw_display_unit *du = vmw_crtc_to_du(crtc); 266 + bool enabled, prev_enabled, locked; 267 + int ret; 268 + 269 + if (!is_crc_supported(crtc)) 270 + return -EINVAL; 271 + 272 + ret = crc_parse_source(src_name, &enabled); 273 + 274 + if (enabled) 275 + drm_crtc_vblank_get(crtc); 276 + 277 + locked = vmw_vkms_modeset_lock_relaxed(crtc); 278 + prev_enabled = du->vkms.crc_enabled; 279 + du->vkms.crc_enabled = enabled; 280 + if (locked) 281 + vmw_vkms_unlock(crtc); 282 + 283 + if (prev_enabled) 284 + drm_crtc_vblank_put(crtc); 285 + 286 + return ret; 287 + } 288 + 289 + void 290 + vmw_vkms_set_crc_surface(struct drm_crtc *crtc, 291 + struct vmw_surface *surf) 292 + { 293 + struct vmw_display_unit *du = vmw_crtc_to_du(crtc); 294 + struct vmw_private *vmw = vmw_priv(crtc->dev); 295 + 296 + if (vmw->vkms_enabled) { 297 + WARN_ON(atomic_read(&du->vkms.atomic_lock) != VMW_VKMS_LOCK_MODESET); 298 + du->vkms.surface = surf; 299 + } 300 + } 301 + 302 + /** 303 + * vmw_vkms_lock_max_wait_ns - Return the max wait for the vkms lock 304 + * @du: The vmw_display_unit from which to grab the vblank timings 305 + * 306 + * Returns the maximum wait time used to acquire the vkms lock. By 307 + * default uses a time of a single frame and in case where vblank 308 + * was not initialized for the display unit 1/60th of a second. 309 + */ 310 + static inline u64 311 + vmw_vkms_lock_max_wait_ns(struct vmw_display_unit *du) 312 + { 313 + s64 nsecs = ktime_to_ns(du->vkms.period_ns); 314 + 315 + return (nsecs > 0) ? nsecs : 16666666; 316 + } 317 + 318 + /** 319 + * vmw_vkms_modeset_lock - Protects access to crtc during modeset 320 + * @crtc: The crtc to lock for vkms 321 + * 322 + * This function prevents the VKMS timers/callbacks from being called 323 + * while a modeset operation is in process. We don't want the callbacks 324 + * e.g. the vblank simulator to be trying to access incomplete state 325 + * so we need to make sure they execute only when the modeset has 326 + * finished. 327 + * 328 + * Normally this would have been done with a spinlock but locking the 329 + * entire atomic modeset with vmwgfx is impossible because kms prepare 330 + * executes non-atomic ops (e.g. vmw_validation_prepare holds a mutex to 331 + * guard various bits of state). Which means that we need to synchronize 332 + * atomic context (the vblank handler) with the non-atomic entirity 333 + * of kms - so use an atomic_t to track which part of vkms has access 334 + * to the basic vkms state. 335 + */ 336 + void 337 + vmw_vkms_modeset_lock(struct drm_crtc *crtc) 338 + { 339 + struct vmw_display_unit *du = vmw_crtc_to_du(crtc); 340 + const u64 nsecs_delay = 10; 341 + const u64 MAX_NSECS_DELAY = vmw_vkms_lock_max_wait_ns(du); 342 + u64 total_delay = 0; 343 + int ret; 344 + 345 + do { 346 + ret = atomic_cmpxchg(&du->vkms.atomic_lock, 347 + VMW_VKMS_LOCK_UNLOCKED, 348 + VMW_VKMS_LOCK_MODESET); 349 + if (ret == VMW_VKMS_LOCK_UNLOCKED || total_delay >= MAX_NSECS_DELAY) 350 + break; 351 + ndelay(nsecs_delay); 352 + total_delay += nsecs_delay; 353 + } while (1); 354 + 355 + if (total_delay >= MAX_NSECS_DELAY) { 356 + drm_warn(crtc->dev, "VKMS lock expired! total_delay = %lld, ret = %d, cur = %d\n", 357 + total_delay, ret, atomic_read(&du->vkms.atomic_lock)); 358 + } 359 + } 360 + 361 + /** 362 + * vmw_vkms_modeset_lock_relaxed - Protects access to crtc during modeset 363 + * @crtc: The crtc to lock for vkms 364 + * 365 + * Much like vmw_vkms_modeset_lock except that when the crtc is currently 366 + * in a modeset it will return immediately. 367 + * 368 + * Returns true if actually locked vkms to modeset or false otherwise. 369 + */ 370 + bool 371 + vmw_vkms_modeset_lock_relaxed(struct drm_crtc *crtc) 372 + { 373 + struct vmw_display_unit *du = vmw_crtc_to_du(crtc); 374 + const u64 nsecs_delay = 10; 375 + const u64 MAX_NSECS_DELAY = vmw_vkms_lock_max_wait_ns(du); 376 + u64 total_delay = 0; 377 + int ret; 378 + 379 + do { 380 + ret = atomic_cmpxchg(&du->vkms.atomic_lock, 381 + VMW_VKMS_LOCK_UNLOCKED, 382 + VMW_VKMS_LOCK_MODESET); 383 + if (ret == VMW_VKMS_LOCK_UNLOCKED || 384 + ret == VMW_VKMS_LOCK_MODESET || 385 + total_delay >= MAX_NSECS_DELAY) 386 + break; 387 + ndelay(nsecs_delay); 388 + total_delay += nsecs_delay; 389 + } while (1); 390 + 391 + if (total_delay >= MAX_NSECS_DELAY) { 392 + drm_warn(crtc->dev, "VKMS relaxed lock expired!\n"); 393 + return false; 394 + } 395 + 396 + return ret == VMW_VKMS_LOCK_UNLOCKED; 397 + } 398 + 399 + /** 400 + * vmw_vkms_vblank_trylock - Protects access to crtc during vblank 401 + * @crtc: The crtc to lock for vkms 402 + * 403 + * Tries to lock vkms for vblank, returns immediately. 404 + * 405 + * Returns true if locked vkms to vblank or false otherwise. 406 + */ 407 + bool 408 + vmw_vkms_vblank_trylock(struct drm_crtc *crtc) 409 + { 410 + struct vmw_display_unit *du = vmw_crtc_to_du(crtc); 411 + u32 ret; 412 + 413 + ret = atomic_cmpxchg(&du->vkms.atomic_lock, 414 + VMW_VKMS_LOCK_UNLOCKED, 415 + VMW_VKMS_LOCK_VBLANK); 416 + 417 + return ret == VMW_VKMS_LOCK_UNLOCKED; 418 + } 419 + 420 + void 421 + vmw_vkms_unlock(struct drm_crtc *crtc) 422 + { 423 + struct vmw_display_unit *du = vmw_crtc_to_du(crtc); 424 + 425 + /* Release flag; mark it as unlocked. */ 426 + atomic_set(&du->vkms.atomic_lock, VMW_VKMS_LOCK_UNLOCKED); 398 427 }
+25 -3
drivers/gpu/drm/vmwgfx/vmwgfx_vkms.h
··· 32 32 #include <linux/hrtimer_types.h> 33 33 #include <linux/types.h> 34 34 35 - struct vmw_private; 36 - struct drm_crtc; 37 35 struct drm_atomic_state; 36 + struct drm_crtc; 37 + struct vmw_private; 38 + struct vmw_surface; 38 39 39 40 void vmw_vkms_init(struct vmw_private *vmw); 41 + void vmw_vkms_cleanup(struct vmw_private *vmw); 42 + 43 + void vmw_vkms_modeset_lock(struct drm_crtc *crtc); 44 + bool vmw_vkms_modeset_lock_relaxed(struct drm_crtc *crtc); 45 + bool vmw_vkms_vblank_trylock(struct drm_crtc *crtc); 46 + void vmw_vkms_unlock(struct drm_crtc *crtc); 47 + 40 48 bool vmw_vkms_get_vblank_timestamp(struct drm_crtc *crtc, 41 49 int *max_error, 42 50 ktime_t *vblank_time, 43 51 bool in_vblank_irq); 44 52 int vmw_vkms_enable_vblank(struct drm_crtc *crtc); 45 53 void vmw_vkms_disable_vblank(struct drm_crtc *crtc); 54 + 55 + void vmw_vkms_crtc_init(struct drm_crtc *crtc); 56 + void vmw_vkms_crtc_cleanup(struct drm_crtc *crtc); 57 + void vmw_vkms_crtc_atomic_begin(struct drm_crtc *crtc, 58 + struct drm_atomic_state *state); 46 59 void vmw_vkms_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_atomic_state *state); 47 - enum hrtimer_restart vmw_vkms_vblank_simulate(struct hrtimer *timer); 48 60 void vmw_vkms_crtc_atomic_enable(struct drm_crtc *crtc, 49 61 struct drm_atomic_state *state); 50 62 void vmw_vkms_crtc_atomic_disable(struct drm_crtc *crtc, 51 63 struct drm_atomic_state *state); 64 + 65 + const char *const *vmw_vkms_get_crc_sources(struct drm_crtc *crtc, 66 + size_t *count); 67 + int vmw_vkms_verify_crc_source(struct drm_crtc *crtc, 68 + const char *src_name, 69 + size_t *values_cnt); 70 + int vmw_vkms_set_crc_source(struct drm_crtc *crtc, 71 + const char *src_name); 72 + void vmw_vkms_set_crc_surface(struct drm_crtc *crtc, 73 + struct vmw_surface *surf); 52 74 53 75 #endif