// SPDX-License-Identifier: MIT /* * Copyright © 2020 Intel Corporation */ #include #include "i915_drv.h" #include "intel_atomic.h" #include "intel_display_types.h" #include "intel_global_state.h" static void __intel_atomic_global_state_free(struct kref *kref) { struct intel_global_state *obj_state = container_of(kref, struct intel_global_state, ref); struct intel_global_obj *obj = obj_state->obj; obj->funcs->atomic_destroy_state(obj, obj_state); } static void intel_atomic_global_state_put(struct intel_global_state *obj_state) { kref_put(&obj_state->ref, __intel_atomic_global_state_free); } static struct intel_global_state * intel_atomic_global_state_get(struct intel_global_state *obj_state) { kref_get(&obj_state->ref); return obj_state; } void intel_atomic_global_obj_init(struct drm_i915_private *dev_priv, struct intel_global_obj *obj, struct intel_global_state *state, const struct intel_global_state_funcs *funcs) { memset(obj, 0, sizeof(*obj)); state->obj = obj; kref_init(&state->ref); obj->state = state; obj->funcs = funcs; list_add_tail(&obj->head, &dev_priv->global_obj_list); } void intel_atomic_global_obj_cleanup(struct drm_i915_private *dev_priv) { struct intel_global_obj *obj, *next; list_for_each_entry_safe(obj, next, &dev_priv->global_obj_list, head) { list_del(&obj->head); drm_WARN_ON(&dev_priv->drm, kref_read(&obj->state->ref) != 1); intel_atomic_global_state_put(obj->state); } } static void assert_global_state_write_locked(struct drm_i915_private *dev_priv) { struct intel_crtc *crtc; for_each_intel_crtc(&dev_priv->drm, crtc) drm_modeset_lock_assert_held(&crtc->base.mutex); } static bool modeset_lock_is_held(struct drm_modeset_acquire_ctx *ctx, struct drm_modeset_lock *lock) { struct drm_modeset_lock *l; list_for_each_entry(l, &ctx->locked, head) { if (lock == l) return true; } return false; } static void assert_global_state_read_locked(struct intel_atomic_state *state) { struct drm_modeset_acquire_ctx *ctx = state->base.acquire_ctx; struct drm_i915_private *dev_priv = to_i915(state->base.dev); struct intel_crtc *crtc; for_each_intel_crtc(&dev_priv->drm, crtc) { if (modeset_lock_is_held(ctx, &crtc->base.mutex)) return; } drm_WARN(&dev_priv->drm, 1, "Global state not read locked\n"); } struct intel_global_state * intel_atomic_get_global_obj_state(struct intel_atomic_state *state, struct intel_global_obj *obj) { struct drm_i915_private *i915 = to_i915(state->base.dev); int index, num_objs, i; size_t size; struct __intel_global_objs_state *arr; struct intel_global_state *obj_state; for (i = 0; i < state->num_global_objs; i++) if (obj == state->global_objs[i].ptr) return state->global_objs[i].state; assert_global_state_read_locked(state); num_objs = state->num_global_objs + 1; size = sizeof(*state->global_objs) * num_objs; arr = krealloc(state->global_objs, size, GFP_KERNEL); if (!arr) return ERR_PTR(-ENOMEM); state->global_objs = arr; index = state->num_global_objs; memset(&state->global_objs[index], 0, sizeof(*state->global_objs)); obj_state = obj->funcs->atomic_duplicate_state(obj); if (!obj_state) return ERR_PTR(-ENOMEM); obj_state->obj = obj; obj_state->changed = false; kref_init(&obj_state->ref); state->global_objs[index].state = obj_state; state->global_objs[index].old_state = intel_atomic_global_state_get(obj->state); state->global_objs[index].new_state = obj_state; state->global_objs[index].ptr = obj; obj_state->state = state; state->num_global_objs = num_objs; drm_dbg_atomic(&i915->drm, "Added new global object %p state %p to %p\n", obj, obj_state, state); return obj_state; } struct intel_global_state * intel_atomic_get_old_global_obj_state(struct intel_atomic_state *state, struct intel_global_obj *obj) { int i; for (i = 0; i < state->num_global_objs; i++) if (obj == state->global_objs[i].ptr) return state->global_objs[i].old_state; return NULL; } struct intel_global_state * intel_atomic_get_new_global_obj_state(struct intel_atomic_state *state, struct intel_global_obj *obj) { int i; for (i = 0; i < state->num_global_objs; i++) if (obj == state->global_objs[i].ptr) return state->global_objs[i].new_state; return NULL; } void intel_atomic_swap_global_state(struct intel_atomic_state *state) { struct drm_i915_private *dev_priv = to_i915(state->base.dev); struct intel_global_state *old_obj_state, *new_obj_state; struct intel_global_obj *obj; int i; for_each_oldnew_global_obj_in_state(state, obj, old_obj_state, new_obj_state, i) { drm_WARN_ON(&dev_priv->drm, obj->state != old_obj_state); /* * If the new state wasn't modified (and properly * locked for write access) we throw it away. */ if (!new_obj_state->changed) continue; assert_global_state_write_locked(dev_priv); old_obj_state->state = state; new_obj_state->state = NULL; state->global_objs[i].state = old_obj_state; intel_atomic_global_state_put(obj->state); obj->state = intel_atomic_global_state_get(new_obj_state); } } void intel_atomic_clear_global_state(struct intel_atomic_state *state) { int i; for (i = 0; i < state->num_global_objs; i++) { intel_atomic_global_state_put(state->global_objs[i].old_state); intel_atomic_global_state_put(state->global_objs[i].new_state); state->global_objs[i].ptr = NULL; state->global_objs[i].state = NULL; state->global_objs[i].old_state = NULL; state->global_objs[i].new_state = NULL; } state->num_global_objs = 0; } int intel_atomic_lock_global_state(struct intel_global_state *obj_state) { struct intel_atomic_state *state = obj_state->state; struct drm_i915_private *dev_priv = to_i915(state->base.dev); struct intel_crtc *crtc; for_each_intel_crtc(&dev_priv->drm, crtc) { int ret; ret = drm_modeset_lock(&crtc->base.mutex, state->base.acquire_ctx); if (ret) return ret; } obj_state->changed = true; return 0; } int intel_atomic_serialize_global_state(struct intel_global_state *obj_state) { struct intel_atomic_state *state = obj_state->state; struct drm_i915_private *dev_priv = to_i915(state->base.dev); struct intel_crtc *crtc; for_each_intel_crtc(&dev_priv->drm, crtc) { struct intel_crtc_state *crtc_state; crtc_state = intel_atomic_get_crtc_state(&state->base, crtc); if (IS_ERR(crtc_state)) return PTR_ERR(crtc_state); } obj_state->changed = true; return 0; }