diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index 3c88d7d8c76498b72fed804fdb97780eb950114c..787ffe6698107bb28f67a1ec48e6c70b989811dd 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -190,6 +190,7 @@ i915-y += \ display/intel_fbc.o \ display/intel_fifo_underrun.o \ display/intel_frontbuffer.o \ + display/intel_global_state.o \ display/intel_hdcp.o \ display/intel_hotplug.o \ display/intel_lpe_audio.o \ diff --git a/drivers/gpu/drm/i915/display/intel_atomic.c b/drivers/gpu/drm/i915/display/intel_atomic.c index 110cc58346f5946c4daec19e9267635036bcf9f7..476d0271de0a4b3bc39febc01b39a65e1d6ed1a2 100644 --- a/drivers/gpu/drm/i915/display/intel_atomic.c +++ b/drivers/gpu/drm/i915/display/intel_atomic.c @@ -37,6 +37,7 @@ #include "intel_atomic.h" #include "intel_cdclk.h" #include "intel_display_types.h" +#include "intel_global_state.h" #include "intel_hdcp.h" #include "intel_psr.h" #include "intel_sprite.h" @@ -502,6 +503,7 @@ void intel_atomic_state_free(struct drm_atomic_state *_state) struct intel_atomic_state *state = to_intel_atomic_state(_state); drm_atomic_state_default_release(&state->base); + kfree(state->global_objs); i915_sw_fence_fini(&state->commit_ready); @@ -513,6 +515,7 @@ void intel_atomic_state_clear(struct drm_atomic_state *s) struct intel_atomic_state *state = to_intel_atomic_state(s); drm_atomic_state_default_clear(&state->base); + intel_atomic_clear_global_state(state); state->dpll_set = state->modeset = false; state->global_state_changed = false; @@ -532,7 +535,7 @@ intel_atomic_get_crtc_state(struct drm_atomic_state *state, return to_intel_crtc_state(crtc_state); } -int intel_atomic_lock_global_state(struct intel_atomic_state *state) +int _intel_atomic_lock_global_state(struct intel_atomic_state *state) { struct drm_i915_private *dev_priv = to_i915(state->base.dev); struct intel_crtc *crtc; @@ -551,7 +554,7 @@ int intel_atomic_lock_global_state(struct intel_atomic_state *state) return 0; } -int intel_atomic_serialize_global_state(struct intel_atomic_state *state) +int _intel_atomic_serialize_global_state(struct intel_atomic_state *state) { struct drm_i915_private *dev_priv = to_i915(state->base.dev); struct intel_crtc *crtc; diff --git a/drivers/gpu/drm/i915/display/intel_atomic.h b/drivers/gpu/drm/i915/display/intel_atomic.h index 88133eea0a171b71353bb91e6a30289082d4584e..11146292b06f528391c7b40749b6f5ed97034aba 100644 --- a/drivers/gpu/drm/i915/display/intel_atomic.h +++ b/drivers/gpu/drm/i915/display/intel_atomic.h @@ -56,8 +56,8 @@ int intel_atomic_setup_scalers(struct drm_i915_private *dev_priv, struct intel_crtc *intel_crtc, struct intel_crtc_state *crtc_state); -int intel_atomic_lock_global_state(struct intel_atomic_state *state); +int _intel_atomic_lock_global_state(struct intel_atomic_state *state); -int intel_atomic_serialize_global_state(struct intel_atomic_state *state); +int _intel_atomic_serialize_global_state(struct intel_atomic_state *state); #endif /* __INTEL_ATOMIC_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_audio.c b/drivers/gpu/drm/i915/display/intel_audio.c index fef292c915d404686d1ccc6856fb397b6b2c97ce..93bc403c3fa5ef3a9be1914be2b488dbdd7c2b45 100644 --- a/drivers/gpu/drm/i915/display/intel_audio.c +++ b/drivers/gpu/drm/i915/display/intel_audio.c @@ -829,7 +829,7 @@ static void glk_force_audio_cdclk(struct drm_i915_private *dev_priv, enable ? 2 * 96000 : 0; /* Protects dev_priv->cdclk.force_min_cdclk */ - ret = intel_atomic_lock_global_state(to_intel_atomic_state(state)); + ret = _intel_atomic_lock_global_state(to_intel_atomic_state(state)); if (!ret) ret = drm_atomic_commit(state); diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index 3e0379db9b2bdca7d3f310f837ddb8bed18e0454..6368c0ef1e0f669093af9e9a9a39f696ccc85111 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -2080,7 +2080,7 @@ static int intel_compute_min_cdclk(struct intel_atomic_state *state) cdclk_state->min_cdclk[i] = min_cdclk; - ret = intel_atomic_lock_global_state(state); + ret = _intel_atomic_lock_global_state(state); if (ret) return ret; } @@ -2128,7 +2128,7 @@ static int bxt_compute_min_voltage_level(struct intel_atomic_state *state) cdclk_state->min_voltage_level[i] = min_voltage_level; - ret = intel_atomic_lock_global_state(state); + ret = _intel_atomic_lock_global_state(state); if (ret) return ret; } @@ -2403,12 +2403,12 @@ int intel_modeset_calc_cdclk(struct intel_atomic_state *state) * Also serialize commits across all crtcs * if the actual hw needs to be poked. */ - ret = intel_atomic_serialize_global_state(state); + ret = _intel_atomic_serialize_global_state(state); if (ret) return ret; } else if (intel_cdclk_changed(&old_cdclk_state->logical, &new_cdclk_state->logical)) { - ret = intel_atomic_lock_global_state(state); + ret = _intel_atomic_lock_global_state(state); if (ret) return ret; } else { diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index cced6b9b8ee2909b6c851872d28b098023a8f2aa..63a3e96cbaf5a9c7830f66a4b6cf49e4453cc366 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -14562,7 +14562,7 @@ static int intel_modeset_checks(struct intel_atomic_state *state) } if (state->active_pipe_changes) { - ret = intel_atomic_lock_global_state(state); + ret = _intel_atomic_lock_global_state(state); if (ret) return ret; } @@ -15842,6 +15842,8 @@ static int intel_atomic_commit(struct drm_device *dev, ret = drm_atomic_helper_setup_commit(&state->base, nonblock); if (!ret) ret = drm_atomic_helper_swap_state(&state->base, true); + if (!ret) + intel_atomic_swap_global_state(state); if (ret) { i915_sw_fence_commit(&state->commit_ready); @@ -17759,6 +17761,7 @@ static void intel_mode_config_init(struct drm_i915_private *i915) struct drm_mode_config *mode_config = &i915->drm.mode_config; drm_mode_config_init(&i915->drm); + INIT_LIST_HEAD(&i915->global_obj_list); mode_config->min_width = 0; mode_config->min_height = 0; @@ -17800,6 +17803,12 @@ static void intel_mode_config_init(struct drm_i915_private *i915) } } +static void intel_mode_config_cleanup(struct drm_i915_private *i915) +{ + intel_atomic_global_obj_cleanup(i915); + drm_mode_config_cleanup(&i915->drm); +} + int intel_modeset_init(struct drm_i915_private *i915) { struct drm_device *dev = &i915->drm; @@ -17839,7 +17848,7 @@ int intel_modeset_init(struct drm_i915_private *i915) for_each_pipe(i915, pipe) { ret = intel_crtc_init(i915, pipe); if (ret) { - drm_mode_config_cleanup(dev); + intel_mode_config_cleanup(i915); return ret; } } @@ -18807,7 +18816,7 @@ void intel_modeset_driver_remove(struct drm_i915_private *i915) intel_hdcp_component_fini(i915); - drm_mode_config_cleanup(&i915->drm); + intel_mode_config_cleanup(i915); intel_overlay_cleanup(i915); diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index aa2e212f5db2e8344a16ff85b282b02af2a420a2..efeda019ba79a8f38f8507f13fe65cfd232d42a2 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -47,6 +47,7 @@ #include "intel_de.h" struct drm_printer; +struct __intel_global_objs_state; /* * Display related stuff @@ -462,6 +463,9 @@ struct intel_atomic_state { intel_wakeref_t wakeref; + struct __intel_global_objs_state *global_objs; + int num_global_objs; + struct intel_cdclk_state cdclk_state; bool dpll_set, modeset; diff --git a/drivers/gpu/drm/i915/display/intel_global_state.c b/drivers/gpu/drm/i915/display/intel_global_state.c new file mode 100644 index 0000000000000000000000000000000000000000..a0cc894c386814d4a776085457aa78a47ba94589 --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_global_state.c @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2020 Intel Corporation + */ + +#include <linux/string.h> + +#include "i915_drv.h" +#include "intel_atomic.h" +#include "intel_display_types.h" +#include "intel_global_state.h" + +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)); + + 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); + obj->funcs->atomic_destroy_state(obj, 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; + } + + WARN(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) +{ + 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->changed = false; + + state->global_objs[index].state = obj_state; + state->global_objs[index].old_state = 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_DEBUG_ATOMIC("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) { + WARN_ON(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; + obj->state = 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++) { + struct intel_global_obj *obj = state->global_objs[i].ptr; + + obj->funcs->atomic_destroy_state(obj, + state->global_objs[i].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; +} diff --git a/drivers/gpu/drm/i915/display/intel_global_state.h b/drivers/gpu/drm/i915/display/intel_global_state.h new file mode 100644 index 0000000000000000000000000000000000000000..e6163a4690296678f9bc425a9deb8bb46b60fb04 --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_global_state.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2020 Intel Corporation + */ + +#ifndef __INTEL_GLOBAL_STATE_H__ +#define __INTEL_GLOBAL_STATE_H__ + +#include <linux/list.h> + +struct drm_i915_private; +struct intel_atomic_state; +struct intel_global_obj; +struct intel_global_state; + +struct intel_global_state_funcs { + struct intel_global_state *(*atomic_duplicate_state)(struct intel_global_obj *obj); + void (*atomic_destroy_state)(struct intel_global_obj *obj, + struct intel_global_state *state); +}; + +struct intel_global_obj { + struct list_head head; + struct intel_global_state *state; + const struct intel_global_state_funcs *funcs; +}; + +#define intel_for_each_global_obj(obj, dev_priv) \ + list_for_each_entry(obj, &(dev_priv)->global_obj_list, head) + +#define for_each_new_global_obj_in_state(__state, obj, new_obj_state, __i) \ + for ((__i) = 0; \ + (__i) < (__state)->num_global_objs && \ + ((obj) = (__state)->global_objs[__i].ptr, \ + (new_obj_state) = (__state)->global_objs[__i].new_state, 1); \ + (__i)++) \ + for_each_if(obj) + +#define for_each_old_global_obj_in_state(__state, obj, new_obj_state, __i) \ + for ((__i) = 0; \ + (__i) < (__state)->num_global_objs && \ + ((obj) = (__state)->global_objs[__i].ptr, \ + (new_obj_state) = (__state)->global_objs[__i].old_state, 1); \ + (__i)++) \ + for_each_if(obj) + +#define for_each_oldnew_global_obj_in_state(__state, obj, old_obj_state, new_obj_state, __i) \ + for ((__i) = 0; \ + (__i) < (__state)->num_global_objs && \ + ((obj) = (__state)->global_objs[__i].ptr, \ + (old_obj_state) = (__state)->global_objs[__i].old_state, \ + (new_obj_state) = (__state)->global_objs[__i].new_state, 1); \ + (__i)++) \ + for_each_if(obj) + +struct intel_global_state { + struct intel_atomic_state *state; + bool changed; +}; + +struct __intel_global_objs_state { + struct intel_global_obj *ptr; + struct intel_global_state *state, *old_state, *new_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); +void intel_atomic_global_obj_cleanup(struct drm_i915_private *dev_priv); + +struct intel_global_state * +intel_atomic_get_global_obj_state(struct intel_atomic_state *state, + struct intel_global_obj *obj); +struct intel_global_state * +intel_atomic_get_old_global_obj_state(struct intel_atomic_state *state, + struct intel_global_obj *obj); +struct intel_global_state * +intel_atomic_get_new_global_obj_state(struct intel_atomic_state *state, + struct intel_global_obj *obj); + +void intel_atomic_swap_global_state(struct intel_atomic_state *state); +void intel_atomic_clear_global_state(struct intel_atomic_state *state); +int intel_atomic_lock_global_state(struct intel_global_state *obj_state); +int intel_atomic_serialize_global_state(struct intel_global_state *obj_state); + +#endif diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 1ab8238e09fe2d5dc70cbef9ac2cc0e56ca2f420..3ed48371f3dac3c3847997f9ee6239ae3fc0c918 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -71,6 +71,7 @@ #include "display/intel_dpll_mgr.h" #include "display/intel_dsb.h" #include "display/intel_frontbuffer.h" +#include "display/intel_global_state.h" #include "display/intel_gmbus.h" #include "display/intel_opregion.h" @@ -1098,6 +1099,8 @@ struct drm_i915_private { */ struct mutex dpll_lock; + struct list_head global_obj_list; + /* * For reading active_pipes, cdclk_state holding any crtc * lock is sufficient, for writing must hold all of them.