aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLyude Paul <lyude@redhat.com>2019-01-10 19:53:41 -0500
committerLyude Paul <lyude@redhat.com>2019-01-10 20:42:13 -0500
commiteceae147246749c6dbaeefda802b30f804a3c54c (patch)
treefb2bde69c9d46e9a14f1164b9c0451ecca008d00
parentbea5c38f1eb66982d9506ece9d7a3941f339ac10 (diff)
drm/dp_mst: Start tracking per-port VCPI allocations
There has been a TODO waiting for quite a long time in drm_dp_mst_topology.c: /* We cannot rely on port->vcpi.num_slots to update * topology_state->avail_slots as the port may not exist if the parent * branch device was unplugged. This should be fixed by tracking * per-port slot allocation in drm_dp_mst_topology_state instead of * depending on the caller to tell us how many slots to release. */ That's not the only reason we should fix this: forcing the driver to track the VCPI allocations throughout a state's atomic check is error prone, because it means that extra care has to be taken with the order that drm_dp_atomic_find_vcpi_slots() and drm_dp_atomic_release_vcpi_slots() are called in in order to ensure idempotency. Currently the only driver actually using these helpers, i915, doesn't even do this correctly: multiple ->best_encoder() checks with i915's current implementation would not be idempotent and would over-allocate VCPI slots, something I learned trying to implement fallback retraining in MST. So: simplify this whole mess, and teach drm_dp_atomic_find_vcpi_slots() and drm_dp_atomic_release_vcpi_slots() to track the VCPI allocations for each port. This allows us to ensure idempotency without having to rely on the driver as much. Additionally: the driver doesn't need to do any kind of VCPI slot tracking anymore if it doesn't need it for it's own internal state. Additionally; this adds a new drm_dp_mst_atomic_check() helper which must be used by atomic drivers to perform validity checks for the new VCPI allocations incurred by a state. Also: update the documentation and make it more obvious that these /must/ be called by /all/ atomic drivers supporting MST. Changes since v9: * Add some missing changes that were requested by danvet that I forgot about after I redid all of the kref stuff: * Remove unnecessary state changes in intel_dp_mst_atomic_check * Cleanup atomic check logic for VCPI allocations - all we need to check in compute_config is whether or not this state disables a CRTC, then free VCPI based off that Changes since v8: * Fix compile errors, whoops! Changes since v7: - Don't check for mixed stale/valid VCPI allocations, just rely on connector registration to stop such erroneous modesets Changes since v6: - Keep a kref to all of the ports we have allocations on. This required a good bit of changing to when we call drm_dp_find_vcpi_slots(), mainly that we need to ensure that we only redo VCPI allocations on actual mode or CRTC changes, not crtc_state->active changes. Additionally, we no longer take the registration of the DRM connector for each port into account because so long as we have a kref to the port in the new or previous atomic state, the connector will stay registered. - Use the small changes to drm_dp_put_port() to add even more error checking to make misusage of the helpers more obvious. I added this after having to chase down various use-after-free conditions that started popping up from the new helpers so no one else has to troubleshoot that. - Move some accidental DRM_DEBUG_KMS() calls to DRM_DEBUG_ATOMIC() - Update documentation again, note that find/release() should both not be called on the same port in a single atomic check phase (but multiple calls to one or the other is OK) Changes since v4: - Don't skip the atomic checks for VCPI allocations if no new VCPI allocations happen in a state. This makes the next change I'm about to list here a lot easier to implement. - Don't ignore VCPI allocations on destroyed ports, instead ensure that when ports are destroyed and still have VCPI allocations in the topology state, the only state changes allowed are releasing said ports' VCPI. This prevents a state with a mix of VCPI allocations from destroyed ports, and allocations from valid ports. Changes since v3: - Don't release VCPI allocations in the topology state immediately in drm_dp_atomic_release_vcpi_slots(), instead mark them as 0 and skip over them in drm_dp_mst_duplicate_state(). This makes it so drm_dp_atomic_release_vcpi_slots() is still idempotent while also throwing warnings if the driver messes up it's book keeping and tries to release VCPI slots on a port that doesn't have any pre-existing VCPI allocation - danvet - Change mst_state/state in some debugging messages to "mst state" Changes since v2: - Use kmemdup() for duplicating MST state - danvet - Move port validation out of duplicate state callback - danvet - Handle looping through MST topology states in drm_dp_mst_atomic_check() so the driver doesn't have to do it - Fix documentation in drm_dp_atomic_find_vcpi_slots() - Move the atomic check for each individual topology state into it's own function, reduces indenting - Don't consider "stale" MST ports when calculating the bandwidth requirements. This is needed because originally we relied on the state duplication functions to prune any stale ports from the new state, which would prevent us from incorrectly considering their bandwidth requirements alongside legitimate new payloads. - Add function references in drm_dp_atomic_release_vcpi_slots() - danvet - Annotate atomic VCPI and atomic check functions with __must_check - danvet Changes since v1: - Don't use the now-removed ->atomic_check() for private objects hook, just give drivers a function to call themselves Signed-off-by: Lyude Paul <lyude@redhat.com> Reviewed-by: Daniel Vetter <daniel@ffwll.ch> Cc: David Airlie <airlied@redhat.com> Cc: Jerry Zuo <Jerry.Zuo@amd.com> Cc: Harry Wentland <harry.wentland@amd.com> Cc: Juston Li <juston.li@intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/20190111005343.17443-19-lyude@redhat.com
-rw-r--r--drivers/gpu/drm/drm_dp_mst_topology.c247
-rw-r--r--drivers/gpu/drm/i915/intel_display.c4
-rw-r--r--drivers/gpu/drm/i915/intel_dp_mst.c54
-rw-r--r--include/drm/drm_dp_mst_helper.h23
4 files changed, 266 insertions, 62 deletions
diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index e3497bc49494..88db6d7e1a36 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -3011,21 +3011,42 @@ static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr,
3011} 3011}
3012 3012
3013/** 3013/**
3014 * drm_dp_atomic_find_vcpi_slots() - Find and add vcpi slots to the state 3014 * drm_dp_atomic_find_vcpi_slots() - Find and add VCPI slots to the state
3015 * @state: global atomic state 3015 * @state: global atomic state
3016 * @mgr: MST topology manager for the port 3016 * @mgr: MST topology manager for the port
3017 * @port: port to find vcpi slots for 3017 * @port: port to find vcpi slots for
3018 * @pbn: bandwidth required for the mode in PBN 3018 * @pbn: bandwidth required for the mode in PBN
3019 * 3019 *
3020 * RETURNS: 3020 * Allocates VCPI slots to @port, replacing any previous VCPI allocations it
3021 * Total slots in the atomic state assigned for this port or error 3021 * may have had. Any atomic drivers which support MST must call this function
3022 * in their &drm_encoder_helper_funcs.atomic_check() callback to change the
3023 * current VCPI allocation for the new state, but only when
3024 * &drm_crtc_state.mode_changed or &drm_crtc_state.connectors_changed is set
3025 * to ensure compatibility with userspace applications that still use the
3026 * legacy modesetting UAPI.
3027 *
3028 * Allocations set by this function are not checked against the bandwidth
3029 * restraints of @mgr until the driver calls drm_dp_mst_atomic_check().
3030 *
3031 * Additionally, it is OK to call this function multiple times on the same
3032 * @port as needed. It is not OK however, to call this function and
3033 * drm_dp_atomic_release_vcpi_slots() in the same atomic check phase.
3034 *
3035 * See also:
3036 * drm_dp_atomic_release_vcpi_slots()
3037 * drm_dp_mst_atomic_check()
3038 *
3039 * Returns:
3040 * Total slots in the atomic state assigned for this port, or a negative error
3041 * code if the port no longer exists
3022 */ 3042 */
3023int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state, 3043int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state,
3024 struct drm_dp_mst_topology_mgr *mgr, 3044 struct drm_dp_mst_topology_mgr *mgr,
3025 struct drm_dp_mst_port *port, int pbn) 3045 struct drm_dp_mst_port *port, int pbn)
3026{ 3046{
3027 struct drm_dp_mst_topology_state *topology_state; 3047 struct drm_dp_mst_topology_state *topology_state;
3028 int req_slots; 3048 struct drm_dp_vcpi_allocation *pos, *vcpi = NULL;
3049 int prev_slots, req_slots, ret;
3029 3050
3030 topology_state = drm_atomic_get_mst_topology_state(state, mgr); 3051 topology_state = drm_atomic_get_mst_topology_state(state, mgr);
3031 if (IS_ERR(topology_state)) 3052 if (IS_ERR(topology_state))
@@ -3034,20 +3055,54 @@ int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state,
3034 port = drm_dp_mst_topology_get_port_validated(mgr, port); 3055 port = drm_dp_mst_topology_get_port_validated(mgr, port);
3035 if (port == NULL) 3056 if (port == NULL)
3036 return -EINVAL; 3057 return -EINVAL;
3037 req_slots = DIV_ROUND_UP(pbn, mgr->pbn_div);
3038 DRM_DEBUG_KMS("vcpi slots req=%d, avail=%d\n",
3039 req_slots, topology_state->avail_slots);
3040 3058
3041 if (req_slots > topology_state->avail_slots) { 3059 /* Find the current allocation for this port, if any */
3042 drm_dp_mst_topology_put_port(port); 3060 list_for_each_entry(pos, &topology_state->vcpis, next) {
3043 return -ENOSPC; 3061 if (pos->port == port) {
3062 vcpi = pos;
3063 prev_slots = vcpi->vcpi;
3064
3065 /*
3066 * This should never happen, unless the driver tries
3067 * releasing and allocating the same VCPI allocation,
3068 * which is an error
3069 */
3070 if (WARN_ON(!prev_slots)) {
3071 DRM_ERROR("cannot allocate and release VCPI on [MST PORT:%p] in the same state\n",
3072 port);
3073 return -EINVAL;
3074 }
3075
3076 break;
3077 }
3044 } 3078 }
3079 if (!vcpi)
3080 prev_slots = 0;
3081
3082 req_slots = DIV_ROUND_UP(pbn, mgr->pbn_div);
3083
3084 DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] [MST PORT:%p] VCPI %d -> %d\n",
3085 port->connector->base.id, port->connector->name,
3086 port, prev_slots, req_slots);
3087
3088 /* Add the new allocation to the state */
3089 if (!vcpi) {
3090 vcpi = kzalloc(sizeof(*vcpi), GFP_KERNEL);
3091 if (!vcpi) {
3092 ret = -ENOMEM;
3093 goto out;
3094 }
3045 3095
3046 topology_state->avail_slots -= req_slots; 3096 drm_dp_mst_get_port_malloc(port);
3047 DRM_DEBUG_KMS("vcpi slots avail=%d", topology_state->avail_slots); 3097 vcpi->port = port;
3098 list_add(&vcpi->next, &topology_state->vcpis);
3099 }
3100 vcpi->vcpi = req_slots;
3048 3101
3102 ret = req_slots;
3103out:
3049 drm_dp_mst_topology_put_port(port); 3104 drm_dp_mst_topology_put_port(port);
3050 return req_slots; 3105 return ret;
3051} 3106}
3052EXPORT_SYMBOL(drm_dp_atomic_find_vcpi_slots); 3107EXPORT_SYMBOL(drm_dp_atomic_find_vcpi_slots);
3053 3108
@@ -3055,31 +3110,57 @@ EXPORT_SYMBOL(drm_dp_atomic_find_vcpi_slots);
3055 * drm_dp_atomic_release_vcpi_slots() - Release allocated vcpi slots 3110 * drm_dp_atomic_release_vcpi_slots() - Release allocated vcpi slots
3056 * @state: global atomic state 3111 * @state: global atomic state
3057 * @mgr: MST topology manager for the port 3112 * @mgr: MST topology manager for the port
3058 * @slots: number of vcpi slots to release 3113 * @port: The port to release the VCPI slots from
3059 * 3114 *
3060 * RETURNS: 3115 * Releases any VCPI slots that have been allocated to a port in the atomic
3061 * 0 if @slots were added back to &drm_dp_mst_topology_state->avail_slots or 3116 * state. Any atomic drivers which support MST must call this function in
3062 * negative error code 3117 * their &drm_connector_helper_funcs.atomic_check() callback when the
3118 * connector will no longer have VCPI allocated (e.g. because it's CRTC was
3119 * removed) when it had VCPI allocated in the previous atomic state.
3120 *
3121 * It is OK to call this even if @port has been removed from the system.
3122 * Additionally, it is OK to call this function multiple times on the same
3123 * @port as needed. It is not OK however, to call this function and
3124 * drm_dp_atomic_find_vcpi_slots() on the same @port in a single atomic check
3125 * phase.
3126 *
3127 * See also:
3128 * drm_dp_atomic_find_vcpi_slots()
3129 * drm_dp_mst_atomic_check()
3130 *
3131 * Returns:
3132 * 0 if all slots for this port were added back to
3133 * &drm_dp_mst_topology_state.avail_slots or negative error code
3063 */ 3134 */
3064int drm_dp_atomic_release_vcpi_slots(struct drm_atomic_state *state, 3135int drm_dp_atomic_release_vcpi_slots(struct drm_atomic_state *state,
3065 struct drm_dp_mst_topology_mgr *mgr, 3136 struct drm_dp_mst_topology_mgr *mgr,
3066 int slots) 3137 struct drm_dp_mst_port *port)
3067{ 3138{
3068 struct drm_dp_mst_topology_state *topology_state; 3139 struct drm_dp_mst_topology_state *topology_state;
3140 struct drm_dp_vcpi_allocation *pos;
3141 bool found = false;
3069 3142
3070 topology_state = drm_atomic_get_mst_topology_state(state, mgr); 3143 topology_state = drm_atomic_get_mst_topology_state(state, mgr);
3071 if (IS_ERR(topology_state)) 3144 if (IS_ERR(topology_state))
3072 return PTR_ERR(topology_state); 3145 return PTR_ERR(topology_state);
3073 3146
3074 /* We cannot rely on port->vcpi.num_slots to update 3147 list_for_each_entry(pos, &topology_state->vcpis, next) {
3075 * topology_state->avail_slots as the port may not exist if the parent 3148 if (pos->port == port) {
3076 * branch device was unplugged. This should be fixed by tracking 3149 found = true;
3077 * per-port slot allocation in drm_dp_mst_topology_state instead of 3150 break;
3078 * depending on the caller to tell us how many slots to release. 3151 }
3079 */ 3152 }
3080 topology_state->avail_slots += slots; 3153 if (WARN_ON(!found)) {
3081 DRM_DEBUG_KMS("vcpi slots released=%d, avail=%d\n", 3154 DRM_ERROR("no VCPI for [MST PORT:%p] found in mst state %p\n",
3082 slots, topology_state->avail_slots); 3155 port, &topology_state->base);
3156 return -EINVAL;
3157 }
3158
3159 DRM_DEBUG_ATOMIC("[MST PORT:%p] VCPI %d -> 0\n", port, pos->vcpi);
3160 if (pos->vcpi) {
3161 drm_dp_mst_put_port_malloc(port);
3162 pos->vcpi = 0;
3163 }
3083 3164
3084 return 0; 3165 return 0;
3085} 3166}
@@ -3501,15 +3582,41 @@ static void drm_dp_destroy_connector_work(struct work_struct *work)
3501static struct drm_private_state * 3582static struct drm_private_state *
3502drm_dp_mst_duplicate_state(struct drm_private_obj *obj) 3583drm_dp_mst_duplicate_state(struct drm_private_obj *obj)
3503{ 3584{
3504 struct drm_dp_mst_topology_state *state; 3585 struct drm_dp_mst_topology_state *state, *old_state =
3586 to_dp_mst_topology_state(obj->state);
3587 struct drm_dp_vcpi_allocation *pos, *vcpi;
3505 3588
3506 state = kmemdup(obj->state, sizeof(*state), GFP_KERNEL); 3589 state = kmemdup(old_state, sizeof(*state), GFP_KERNEL);
3507 if (!state) 3590 if (!state)
3508 return NULL; 3591 return NULL;
3509 3592
3510 __drm_atomic_helper_private_obj_duplicate_state(obj, &state->base); 3593 __drm_atomic_helper_private_obj_duplicate_state(obj, &state->base);
3511 3594
3595 INIT_LIST_HEAD(&state->vcpis);
3596
3597 list_for_each_entry(pos, &old_state->vcpis, next) {
3598 /* Prune leftover freed VCPI allocations */
3599 if (!pos->vcpi)
3600 continue;
3601
3602 vcpi = kmemdup(pos, sizeof(*vcpi), GFP_KERNEL);
3603 if (!vcpi)
3604 goto fail;
3605
3606 drm_dp_mst_get_port_malloc(vcpi->port);
3607 list_add(&vcpi->next, &state->vcpis);
3608 }
3609
3512 return &state->base; 3610 return &state->base;
3611
3612fail:
3613 list_for_each_entry_safe(pos, vcpi, &state->vcpis, next) {
3614 drm_dp_mst_put_port_malloc(pos->port);
3615 kfree(pos);
3616 }
3617 kfree(state);
3618
3619 return NULL;
3513} 3620}
3514 3621
3515static void drm_dp_mst_destroy_state(struct drm_private_obj *obj, 3622static void drm_dp_mst_destroy_state(struct drm_private_obj *obj,
@@ -3517,10 +3624,88 @@ static void drm_dp_mst_destroy_state(struct drm_private_obj *obj,
3517{ 3624{
3518 struct drm_dp_mst_topology_state *mst_state = 3625 struct drm_dp_mst_topology_state *mst_state =
3519 to_dp_mst_topology_state(state); 3626 to_dp_mst_topology_state(state);
3627 struct drm_dp_vcpi_allocation *pos, *tmp;
3628
3629 list_for_each_entry_safe(pos, tmp, &mst_state->vcpis, next) {
3630 /* We only keep references to ports with non-zero VCPIs */
3631 if (pos->vcpi)
3632 drm_dp_mst_put_port_malloc(pos->port);
3633 kfree(pos);
3634 }
3520 3635
3521 kfree(mst_state); 3636 kfree(mst_state);
3522} 3637}
3523 3638
3639static inline int
3640drm_dp_mst_atomic_check_topology_state(struct drm_dp_mst_topology_mgr *mgr,
3641 struct drm_dp_mst_topology_state *mst_state)
3642{
3643 struct drm_dp_vcpi_allocation *vcpi;
3644 int avail_slots = 63;
3645
3646 list_for_each_entry(vcpi, &mst_state->vcpis, next) {
3647 /* Releasing VCPI is always OK-even if the port is gone */
3648 if (!vcpi->vcpi) {
3649 DRM_DEBUG_ATOMIC("[MST PORT:%p] releases all VCPI slots\n",
3650 vcpi->port);
3651 continue;
3652 }
3653
3654 DRM_DEBUG_ATOMIC("[MST PORT:%p] requires %d vcpi slots\n",
3655 vcpi->port, vcpi->vcpi);
3656
3657 avail_slots -= vcpi->vcpi;
3658 if (avail_slots < 0) {
3659 DRM_DEBUG_ATOMIC("[MST PORT:%p] not enough VCPI slots in mst state %p (avail=%d)\n",
3660 vcpi->port, mst_state,
3661 avail_slots + vcpi->vcpi);
3662 return -ENOSPC;
3663 }
3664 }
3665 DRM_DEBUG_ATOMIC("[MST MGR:%p] mst state %p VCPI avail=%d used=%d\n",
3666 mgr, mst_state, avail_slots,
3667 63 - avail_slots);
3668
3669 return 0;
3670}
3671
3672/**
3673 * drm_dp_mst_atomic_check - Check that the new state of an MST topology in an
3674 * atomic update is valid
3675 * @state: Pointer to the new &struct drm_dp_mst_topology_state
3676 *
3677 * Checks the given topology state for an atomic update to ensure that it's
3678 * valid. This includes checking whether there's enough bandwidth to support
3679 * the new VCPI allocations in the atomic update.
3680 *
3681 * Any atomic drivers supporting DP MST must make sure to call this after
3682 * checking the rest of their state in their
3683 * &drm_mode_config_funcs.atomic_check() callback.
3684 *
3685 * See also:
3686 * drm_dp_atomic_find_vcpi_slots()
3687 * drm_dp_atomic_release_vcpi_slots()
3688 *
3689 * Returns:
3690 *
3691 * 0 if the new state is valid, negative error code otherwise.
3692 */
3693int drm_dp_mst_atomic_check(struct drm_atomic_state *state)
3694{
3695 struct drm_dp_mst_topology_mgr *mgr;
3696 struct drm_dp_mst_topology_state *mst_state;
3697 int i, ret = 0;
3698
3699 for_each_new_mst_mgr_in_state(state, mgr, mst_state, i) {
3700 ret = drm_dp_mst_atomic_check_topology_state(mgr, mst_state);
3701 if (ret)
3702 break;
3703 }
3704
3705 return ret;
3706}
3707EXPORT_SYMBOL(drm_dp_mst_atomic_check);
3708
3524const struct drm_private_state_funcs drm_dp_mst_topology_state_funcs = { 3709const struct drm_private_state_funcs drm_dp_mst_topology_state_funcs = {
3525 .atomic_duplicate_state = drm_dp_mst_duplicate_state, 3710 .atomic_duplicate_state = drm_dp_mst_duplicate_state,
3526 .atomic_destroy_state = drm_dp_mst_destroy_state, 3711 .atomic_destroy_state = drm_dp_mst_destroy_state,
@@ -3603,9 +3788,7 @@ int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
3603 return -ENOMEM; 3788 return -ENOMEM;
3604 3789
3605 mst_state->mgr = mgr; 3790 mst_state->mgr = mgr;
3606 3791 INIT_LIST_HEAD(&mst_state->vcpis);
3607 /* max. time slots - one slot for MTP header */
3608 mst_state->avail_slots = 63;
3609 3792
3610 drm_atomic_private_obj_init(dev, &mgr->base, 3793 drm_atomic_private_obj_init(dev, &mgr->base,
3611 &mst_state->base, 3794 &mst_state->base,
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 812ec5ae5c7b..5a679af03a04 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -12623,6 +12623,10 @@ static int intel_atomic_check(struct drm_device *dev,
12623 "[modeset]" : "[fastset]"); 12623 "[modeset]" : "[fastset]");
12624 } 12624 }
12625 12625
12626 ret = drm_dp_mst_atomic_check(state);
12627 if (ret)
12628 return ret;
12629
12626 if (any_ms) { 12630 if (any_ms) {
12627 ret = intel_modeset_checks(state); 12631 ret = intel_modeset_checks(state);
12628 12632
diff --git a/drivers/gpu/drm/i915/intel_dp_mst.c b/drivers/gpu/drm/i915/intel_dp_mst.c
index 631fd1537252..c8e2215628e6 100644
--- a/drivers/gpu/drm/i915/intel_dp_mst.c
+++ b/drivers/gpu/drm/i915/intel_dp_mst.c
@@ -41,8 +41,12 @@ static bool intel_dp_mst_compute_config(struct intel_encoder *encoder,
41 struct drm_connector *connector = conn_state->connector; 41 struct drm_connector *connector = conn_state->connector;
42 void *port = to_intel_connector(connector)->port; 42 void *port = to_intel_connector(connector)->port;
43 struct drm_atomic_state *state = pipe_config->base.state; 43 struct drm_atomic_state *state = pipe_config->base.state;
44 struct drm_crtc *crtc = pipe_config->base.crtc;
45 struct drm_crtc_state *old_crtc_state =
46 drm_atomic_get_old_crtc_state(state, crtc);
44 int bpp; 47 int bpp;
45 int lane_count, slots = 0; 48 int lane_count, slots =
49 to_intel_crtc_state(old_crtc_state)->dp_m_n.tu;
46 const struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode; 50 const struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode;
47 int mst_pbn; 51 int mst_pbn;
48 bool constant_n = drm_dp_has_quirk(&intel_dp->desc, 52 bool constant_n = drm_dp_has_quirk(&intel_dp->desc,
@@ -107,35 +111,39 @@ static bool intel_dp_mst_compute_config(struct intel_encoder *encoder,
107 return true; 111 return true;
108} 112}
109 113
110static int intel_dp_mst_atomic_check(struct drm_connector *connector, 114static int
111 struct drm_connector_state *new_conn_state) 115intel_dp_mst_atomic_check(struct drm_connector *connector,
116 struct drm_connector_state *new_conn_state)
112{ 117{
113 struct drm_atomic_state *state = new_conn_state->state; 118 struct drm_atomic_state *state = new_conn_state->state;
114 struct drm_connector_state *old_conn_state; 119 struct drm_connector_state *old_conn_state =
115 struct drm_crtc *old_crtc; 120 drm_atomic_get_old_connector_state(state, connector);
121 struct intel_connector *intel_connector =
122 to_intel_connector(connector);
123 struct drm_crtc *new_crtc = new_conn_state->crtc;
116 struct drm_crtc_state *crtc_state; 124 struct drm_crtc_state *crtc_state;
117 int slots, ret = 0; 125 struct drm_dp_mst_topology_mgr *mgr;
126 int ret = 0;
118 127
119 old_conn_state = drm_atomic_get_old_connector_state(state, connector); 128 if (!old_conn_state->crtc)
120 old_crtc = old_conn_state->crtc; 129 return 0;
121 if (!old_crtc)
122 return ret;
123
124 crtc_state = drm_atomic_get_new_crtc_state(state, old_crtc);
125 slots = to_intel_crtc_state(crtc_state)->dp_m_n.tu;
126 if (drm_atomic_crtc_needs_modeset(crtc_state) && slots > 0) {
127 struct drm_dp_mst_topology_mgr *mgr;
128 struct drm_encoder *old_encoder;
129 130
130 old_encoder = old_conn_state->best_encoder; 131 /* We only want to free VCPI if this state disables the CRTC on this
131 mgr = &enc_to_mst(old_encoder)->primary->dp.mst_mgr; 132 * connector
133 */
134 if (new_crtc) {
135 crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc);
132 136
133 ret = drm_dp_atomic_release_vcpi_slots(state, mgr, slots); 137 if (!crtc_state ||
134 if (ret) 138 !drm_atomic_crtc_needs_modeset(crtc_state) ||
135 DRM_DEBUG_KMS("failed releasing %d vcpi slots:%d\n", slots, ret); 139 crtc_state->enable)
136 else 140 return 0;
137 to_intel_crtc_state(crtc_state)->dp_m_n.tu = 0;
138 } 141 }
142
143 mgr = &enc_to_mst(old_conn_state->best_encoder)->primary->dp.mst_mgr;
144 ret = drm_dp_atomic_release_vcpi_slots(state, mgr,
145 intel_connector->port);
146
139 return ret; 147 return ret;
140} 148}
141 149
diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h
index 581163c8d7d7..451d020f0137 100644
--- a/include/drm/drm_dp_mst_helper.h
+++ b/include/drm/drm_dp_mst_helper.h
@@ -425,9 +425,15 @@ struct drm_dp_payload {
425 425
426#define to_dp_mst_topology_state(x) container_of(x, struct drm_dp_mst_topology_state, base) 426#define to_dp_mst_topology_state(x) container_of(x, struct drm_dp_mst_topology_state, base)
427 427
428struct drm_dp_vcpi_allocation {
429 struct drm_dp_mst_port *port;
430 int vcpi;
431 struct list_head next;
432};
433
428struct drm_dp_mst_topology_state { 434struct drm_dp_mst_topology_state {
429 struct drm_private_state base; 435 struct drm_private_state base;
430 int avail_slots; 436 struct list_head vcpis;
431 struct drm_dp_mst_topology_mgr *mgr; 437 struct drm_dp_mst_topology_mgr *mgr;
432}; 438};
433 439
@@ -638,14 +644,17 @@ void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr);
638int drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr); 644int drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr);
639struct drm_dp_mst_topology_state *drm_atomic_get_mst_topology_state(struct drm_atomic_state *state, 645struct drm_dp_mst_topology_state *drm_atomic_get_mst_topology_state(struct drm_atomic_state *state,
640 struct drm_dp_mst_topology_mgr *mgr); 646 struct drm_dp_mst_topology_mgr *mgr);
641int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state, 647int __must_check
642 struct drm_dp_mst_topology_mgr *mgr, 648drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state,
643 struct drm_dp_mst_port *port, int pbn); 649 struct drm_dp_mst_topology_mgr *mgr,
644int drm_dp_atomic_release_vcpi_slots(struct drm_atomic_state *state, 650 struct drm_dp_mst_port *port, int pbn);
645 struct drm_dp_mst_topology_mgr *mgr, 651int __must_check
646 int slots); 652drm_dp_atomic_release_vcpi_slots(struct drm_atomic_state *state,
653 struct drm_dp_mst_topology_mgr *mgr,
654 struct drm_dp_mst_port *port);
647int drm_dp_send_power_updown_phy(struct drm_dp_mst_topology_mgr *mgr, 655int drm_dp_send_power_updown_phy(struct drm_dp_mst_topology_mgr *mgr,
648 struct drm_dp_mst_port *port, bool power_up); 656 struct drm_dp_mst_port *port, bool power_up);
657int __must_check drm_dp_mst_atomic_check(struct drm_atomic_state *state);
649 658
650void drm_dp_mst_get_port_malloc(struct drm_dp_mst_port *port); 659void drm_dp_mst_get_port_malloc(struct drm_dp_mst_port *port);
651void drm_dp_mst_put_port_malloc(struct drm_dp_mst_port *port); 660void drm_dp_mst_put_port_malloc(struct drm_dp_mst_port *port);