On 1/13/23 05:25, Daniel Vetter wrote: > On Fri, Jan 13, 2023 at 12:16:57PM +0200, Jani Nikula wrote: >> >> Cc: intel-gfx, drm maintainers >> >> Please have the courtesy of Cc'ing us for changes impacting us, and >> maybe try to involve us earlier instead of surprising us like >> this. Looks like this has been debugged for at least three months, and >> the huge revert at this point is going to set us back with what's been >> developed on top of it for i915 DP MST and DSC. > > tbf I assumed this wont land when I've seen it fly by. It feels a bit much > like living under a rock for half a year and then creating a mess for > everyone else who's been building on top of this is not great. > > Like yes it's a regression, but apparently not a blantantly obvious one, > and I think if we just ram this in there's considerable community goodwill > down the drain. Someone needs to get that goodwill up the drain again. > >> It's a regression, I get that, but this is also going to be really nasty >> to deal with. It's a 2500-line commit, plus the dependencies, which I >> don't think are accounted for here. (What's the baseline for the revert >> anyway?) I don't expect all the dependent commits to be easy to revert >> or backport to v6.1 or v6.2. >> >> *sad trombone* > > Yeah that's the other thing. 2500 patch revert is not cc stable material. > So this isn't even really helping users all that much. > > Unless it also comes with full amounts of backports of the reverts on all > affected drivers for all curent stable trees, fully validated. > > This is bad. I do think we need to have some understanding first of what > "fix this in amdgpu" would look like as plan B. Because plan A does not > look like a happy one at all. Agree with your sentiments and apologies for not making this visible to you sooner. This has been on the corner of my radar but I should've given it a higher priority and more visibility sooner. Has anyone looked at the dependencies? I assume there are a number of dependent commits that would be hard/impossible to untangle but haven't had a chance to look for those myself. Could you highlight a couple? Harry > -Daniel > >> BR, >> Jani. >> >> >> On Thu, 12 Jan 2023, Wayne Lin <Wayne.Lin@xxxxxxx> wrote: >>> This reverts commit 4d07b0bc403403438d9cf88450506240c5faf92f. >>> >>> [Why] >>> Changes cause regression on amdgpu mst. >>> E.g. >>> In fill_dc_mst_payload_table_from_drm(), amdgpu expects to add/remove payload >>> one by one and call fill_dc_mst_payload_table_from_drm() to update the HW >>> maintained payload table. But previous change tries to go through all the >>> payloads in mst_state and update amdpug hw maintained table in once everytime >>> driver only tries to add/remove a specific payload stream only. The newly >>> design idea conflicts with the implementation in amdgpu nowadays. >>> >>> [How] >>> Revert this patch first. After addressing all regression problems caused by >>> this previous patch, will add it back and adjust it. >>> >>> Signed-off-by: Wayne Lin <Wayne.Lin@xxxxxxx> >>> Link: https://gitlab.freedesktop.org/drm/amd/-/issues/2171 >>> Cc: stable@xxxxxxxxxxxxxxx # 6.1 >>> Cc: Lyude Paul <lyude@xxxxxxxxxx> >>> Cc: Harry Wentland <harry.wentland@xxxxxxx> >>> Cc: Mario Limonciello <mario.limonciello@xxxxxxx> >>> Cc: Ville Syrjälä <ville.syrjala@xxxxxxxxxxxxxxx> >>> Cc: Ben Skeggs <bskeggs@xxxxxxxxxx> >>> Cc: Stanislav Lisovskiy <stanislav.lisovskiy@xxxxxxxxx> >>> Cc: Fangzhi Zuo <Jerry.Zuo@xxxxxxx> >>> --- >>> .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 53 +- >>> .../amd/display/amdgpu_dm/amdgpu_dm_helpers.c | 106 ++- >>> .../display/amdgpu_dm/amdgpu_dm_mst_types.c | 87 ++- >>> .../amd/display/include/link_service_types.h | 3 - >>> drivers/gpu/drm/display/drm_dp_mst_topology.c | 724 ++++++++++++------ >>> drivers/gpu/drm/i915/display/intel_dp_mst.c | 67 +- >>> drivers/gpu/drm/i915/display/intel_hdcp.c | 24 +- >>> drivers/gpu/drm/nouveau/dispnv50/disp.c | 167 ++-- >>> include/drm/display/drm_dp_mst_helper.h | 177 +++-- >>> 9 files changed, 878 insertions(+), 530 deletions(-) >>> >>> diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c >>> index 77277d90b6e2..674f5dc1102b 100644 >>> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c >>> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c >>> @@ -6548,7 +6548,6 @@ static int dm_encoder_helper_atomic_check(struct drm_encoder *encoder, >>> const struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode; >>> struct drm_dp_mst_topology_mgr *mst_mgr; >>> struct drm_dp_mst_port *mst_port; >>> - struct drm_dp_mst_topology_state *mst_state; >>> enum dc_color_depth color_depth; >>> int clock, bpp = 0; >>> bool is_y420 = false; >>> @@ -6562,13 +6561,6 @@ static int dm_encoder_helper_atomic_check(struct drm_encoder *encoder, >>> if (!crtc_state->connectors_changed && !crtc_state->mode_changed) >>> return 0; >>> >>> - mst_state = drm_atomic_get_mst_topology_state(state, mst_mgr); >>> - if (IS_ERR(mst_state)) >>> - return PTR_ERR(mst_state); >>> - >>> - if (!mst_state->pbn_div) >>> - mst_state->pbn_div = dm_mst_get_pbn_divider(aconnector->mst_port->dc_link); >>> - >>> if (!state->duplicated) { >>> int max_bpc = conn_state->max_requested_bpc; >>> is_y420 = drm_mode_is_420_also(&connector->display_info, adjusted_mode) && >>> @@ -6580,10 +6572,11 @@ static int dm_encoder_helper_atomic_check(struct drm_encoder *encoder, >>> clock = adjusted_mode->clock; >>> dm_new_connector_state->pbn = drm_dp_calc_pbn_mode(clock, bpp, false); >>> } >>> - >>> - dm_new_connector_state->vcpi_slots = >>> - drm_dp_atomic_find_time_slots(state, mst_mgr, mst_port, >>> - dm_new_connector_state->pbn); >>> + dm_new_connector_state->vcpi_slots = drm_dp_atomic_find_time_slots(state, >>> + mst_mgr, >>> + mst_port, >>> + dm_new_connector_state->pbn, >>> + dm_mst_get_pbn_divider(aconnector->dc_link)); >>> if (dm_new_connector_state->vcpi_slots < 0) { >>> DRM_DEBUG_ATOMIC("failed finding vcpi slots: %d\n", (int)dm_new_connector_state->vcpi_slots); >>> return dm_new_connector_state->vcpi_slots; >>> @@ -6654,14 +6647,17 @@ static int dm_update_mst_vcpi_slots_for_dsc(struct drm_atomic_state *state, >>> dm_conn_state->vcpi_slots = slot_num; >>> >>> ret = drm_dp_mst_atomic_enable_dsc(state, aconnector->port, >>> - dm_conn_state->pbn, false); >>> + dm_conn_state->pbn, 0, false); >>> if (ret < 0) >>> return ret; >>> >>> continue; >>> } >>> >>> - vcpi = drm_dp_mst_atomic_enable_dsc(state, aconnector->port, pbn, true); >>> + vcpi = drm_dp_mst_atomic_enable_dsc(state, >>> + aconnector->port, >>> + pbn, pbn_div, >>> + true); >>> if (vcpi < 0) >>> return vcpi; >>> >>> @@ -9497,6 +9493,8 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev, >>> struct dm_crtc_state *dm_old_crtc_state, *dm_new_crtc_state; >>> #if defined(CONFIG_DRM_AMD_DC_DCN) >>> struct dsc_mst_fairness_vars vars[MAX_PIPES]; >>> + struct drm_dp_mst_topology_state *mst_state; >>> + struct drm_dp_mst_topology_mgr *mgr; >>> #endif >>> >>> trace_amdgpu_dm_atomic_check_begin(state); >>> @@ -9744,6 +9742,33 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev, >>> lock_and_validation_needed = true; >>> } >>> >>> +#if defined(CONFIG_DRM_AMD_DC_DCN) >>> + /* set the slot info for each mst_state based on the link encoding format */ >>> + for_each_new_mst_mgr_in_state(state, mgr, mst_state, i) { >>> + struct amdgpu_dm_connector *aconnector; >>> + struct drm_connector *connector; >>> + struct drm_connector_list_iter iter; >>> + u8 link_coding_cap; >>> + >>> + if (!mgr->mst_state ) >>> + continue; >>> + >>> + drm_connector_list_iter_begin(dev, &iter); >>> + drm_for_each_connector_iter(connector, &iter) { >>> + int id = connector->index; >>> + >>> + if (id == mst_state->mgr->conn_base_id) { >>> + aconnector = to_amdgpu_dm_connector(connector); >>> + link_coding_cap = dc_link_dp_mst_decide_link_encoding_format(aconnector->dc_link); >>> + drm_dp_mst_update_slots(mst_state, link_coding_cap); >>> + >>> + break; >>> + } >>> + } >>> + drm_connector_list_iter_end(&iter); >>> + >>> + } >>> +#endif >>> /** >>> * Streams and planes are reset when there are changes that affect >>> * bandwidth. Anything that affects bandwidth needs to go through >>> diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c >>> index 6994c9a1ed85..ac5a8cad0695 100644 >>> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c >>> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c >>> @@ -27,7 +27,6 @@ >>> #include <linux/acpi.h> >>> #include <linux/i2c.h> >>> >>> -#include <drm/drm_atomic.h> >>> #include <drm/drm_probe_helper.h> >>> #include <drm/amdgpu_drm.h> >>> #include <drm/drm_edid.h> >>> @@ -120,27 +119,40 @@ enum dc_edid_status dm_helpers_parse_edid_caps( >>> } >>> >>> static void >>> -fill_dc_mst_payload_table_from_drm(struct drm_dp_mst_topology_state *mst_state, >>> - struct amdgpu_dm_connector *aconnector, >>> - struct dc_dp_mst_stream_allocation_table *table) >>> -{ >>> - struct dc_dp_mst_stream_allocation_table new_table = { 0 }; >>> - struct dc_dp_mst_stream_allocation *sa; >>> - struct drm_dp_mst_atomic_payload *payload; >>> - >>> - /* Fill payload info*/ >>> - list_for_each_entry(payload, &mst_state->payloads, next) { >>> - if (payload->delete) >>> - continue; >>> - >>> - sa = &new_table.stream_allocations[new_table.stream_count]; >>> - sa->slot_count = payload->time_slots; >>> - sa->vcp_id = payload->vcpi; >>> - new_table.stream_count++; >>> +fill_dc_mst_payload_table_from_drm(struct amdgpu_dm_connector *aconnector, >>> + struct dc_dp_mst_stream_allocation_table *proposed_table) >>> +{ >>> + int i; >>> + struct drm_dp_mst_topology_mgr *mst_mgr = >>> + &aconnector->mst_port->mst_mgr; >>> + >>> + mutex_lock(&mst_mgr->payload_lock); >>> + >>> + proposed_table->stream_count = 0; >>> + >>> + /* number of active streams */ >>> + for (i = 0; i < mst_mgr->max_payloads; i++) { >>> + if (mst_mgr->payloads[i].num_slots == 0) >>> + break; /* end of vcp_id table */ >>> + >>> + ASSERT(mst_mgr->payloads[i].payload_state != >>> + DP_PAYLOAD_DELETE_LOCAL); >>> + >>> + if (mst_mgr->payloads[i].payload_state == DP_PAYLOAD_LOCAL || >>> + mst_mgr->payloads[i].payload_state == >>> + DP_PAYLOAD_REMOTE) { >>> + >>> + struct dc_dp_mst_stream_allocation *sa = >>> + &proposed_table->stream_allocations[ >>> + proposed_table->stream_count]; >>> + >>> + sa->slot_count = mst_mgr->payloads[i].num_slots; >>> + sa->vcp_id = mst_mgr->proposed_vcpis[i]->vcpi; >>> + proposed_table->stream_count++; >>> + } >>> } >>> >>> - /* Overwrite the old table */ >>> - *table = new_table; >>> + mutex_unlock(&mst_mgr->payload_lock); >>> } >>> >>> void dm_helpers_dp_update_branch_info( >>> @@ -158,9 +170,11 @@ bool dm_helpers_dp_mst_write_payload_allocation_table( >>> bool enable) >>> { >>> struct amdgpu_dm_connector *aconnector; >>> - struct drm_dp_mst_topology_state *mst_state; >>> - struct drm_dp_mst_atomic_payload *payload; >>> + struct dm_connector_state *dm_conn_state; >>> struct drm_dp_mst_topology_mgr *mst_mgr; >>> + struct drm_dp_mst_port *mst_port; >>> + bool ret; >>> + u8 link_coding_cap = DP_8b_10b_ENCODING; >>> >>> aconnector = (struct amdgpu_dm_connector *)stream->dm_stream_context; >>> /* Accessing the connector state is required for vcpi_slots allocation >>> @@ -171,21 +185,40 @@ bool dm_helpers_dp_mst_write_payload_allocation_table( >>> if (!aconnector || !aconnector->mst_port) >>> return false; >>> >>> + dm_conn_state = to_dm_connector_state(aconnector->base.state); >>> + >>> mst_mgr = &aconnector->mst_port->mst_mgr; >>> - mst_state = to_drm_dp_mst_topology_state(mst_mgr->base.state); >>> + >>> + if (!mst_mgr->mst_state) >>> + return false; >>> + >>> + mst_port = aconnector->port; >>> + >>> +#if defined(CONFIG_DRM_AMD_DC_DCN) >>> + link_coding_cap = dc_link_dp_mst_decide_link_encoding_format(aconnector->dc_link); >>> +#endif >>> + >>> + if (enable) { >>> + >>> + ret = drm_dp_mst_allocate_vcpi(mst_mgr, mst_port, >>> + dm_conn_state->pbn, >>> + dm_conn_state->vcpi_slots); >>> + if (!ret) >>> + return false; >>> + >>> + } else { >>> + drm_dp_mst_reset_vcpi_slots(mst_mgr, mst_port); >>> + } >>> >>> /* It's OK for this to fail */ >>> - payload = drm_atomic_get_mst_payload_state(mst_state, aconnector->port); >>> - if (enable) >>> - drm_dp_add_payload_part1(mst_mgr, mst_state, payload); >>> - else >>> - drm_dp_remove_payload(mst_mgr, mst_state, payload); >>> + drm_dp_update_payload_part1(mst_mgr, (link_coding_cap == DP_CAP_ANSI_128B132B) ? 0:1); >>> >>> /* mst_mgr->->payloads are VC payload notify MST branch using DPCD or >>> * AUX message. The sequence is slot 1-63 allocated sequence for each >>> * stream. AMD ASIC stream slot allocation should follow the same >>> * sequence. copy DRM MST allocation to dc */ >>> - fill_dc_mst_payload_table_from_drm(mst_state, aconnector, proposed_table); >>> + >>> + fill_dc_mst_payload_table_from_drm(aconnector, proposed_table); >>> >>> return true; >>> } >>> @@ -242,9 +275,8 @@ bool dm_helpers_dp_mst_send_payload_allocation( >>> bool enable) >>> { >>> struct amdgpu_dm_connector *aconnector; >>> - struct drm_dp_mst_topology_state *mst_state; >>> struct drm_dp_mst_topology_mgr *mst_mgr; >>> - struct drm_dp_mst_atomic_payload *payload; >>> + struct drm_dp_mst_port *mst_port; >>> enum mst_progress_status set_flag = MST_ALLOCATE_NEW_PAYLOAD; >>> enum mst_progress_status clr_flag = MST_CLEAR_ALLOCATED_PAYLOAD; >>> >>> @@ -253,16 +285,19 @@ bool dm_helpers_dp_mst_send_payload_allocation( >>> if (!aconnector || !aconnector->mst_port) >>> return false; >>> >>> + mst_port = aconnector->port; >>> + >>> mst_mgr = &aconnector->mst_port->mst_mgr; >>> - mst_state = to_drm_dp_mst_topology_state(mst_mgr->base.state); >>> >>> - payload = drm_atomic_get_mst_payload_state(mst_state, aconnector->port); >>> + if (!mst_mgr->mst_state) >>> + return false; >>> + >>> if (!enable) { >>> set_flag = MST_CLEAR_ALLOCATED_PAYLOAD; >>> clr_flag = MST_ALLOCATE_NEW_PAYLOAD; >>> } >>> >>> - if (enable && drm_dp_add_payload_part2(mst_mgr, mst_state->base.state, payload)) { >>> + if (drm_dp_update_payload_part2(mst_mgr)) { >>> amdgpu_dm_set_mst_status(&aconnector->mst_status, >>> set_flag, false); >>> } else { >>> @@ -272,6 +307,9 @@ bool dm_helpers_dp_mst_send_payload_allocation( >>> clr_flag, false); >>> } >>> >>> + if (!enable) >>> + drm_dp_mst_deallocate_vcpi(mst_mgr, mst_port); >>> + >>> return true; >>> } >>> >>> diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c >>> index 1edf7385f8d8..6207026a200f 100644 >>> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c >>> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c >>> @@ -598,8 +598,15 @@ void amdgpu_dm_initialize_dp_connector(struct amdgpu_display_manager *dm, >>> >>> dc_link_dp_get_max_link_enc_cap(aconnector->dc_link, &max_link_enc_cap); >>> aconnector->mst_mgr.cbs = &dm_mst_cbs; >>> - drm_dp_mst_topology_mgr_init(&aconnector->mst_mgr, adev_to_drm(dm->adev), >>> - &aconnector->dm_dp_aux.aux, 16, 4, aconnector->connector_id); >>> + drm_dp_mst_topology_mgr_init( >>> + &aconnector->mst_mgr, >>> + adev_to_drm(dm->adev), >>> + &aconnector->dm_dp_aux.aux, >>> + 16, >>> + 4, >>> + max_link_enc_cap.lane_count, >>> + drm_dp_bw_code_to_link_rate(max_link_enc_cap.link_rate), >>> + aconnector->connector_id); >>> >>> drm_connector_attach_dp_subconnector_property(&aconnector->base); >>> } >>> @@ -710,13 +717,12 @@ static int bpp_x16_from_pbn(struct dsc_mst_fairness_params param, int pbn) >>> return dsc_config.bits_per_pixel; >>> } >>> >>> -static int increase_dsc_bpp(struct drm_atomic_state *state, >>> - struct drm_dp_mst_topology_state *mst_state, >>> - struct dc_link *dc_link, >>> - struct dsc_mst_fairness_params *params, >>> - struct dsc_mst_fairness_vars *vars, >>> - int count, >>> - int k) >>> +static bool increase_dsc_bpp(struct drm_atomic_state *state, >>> + struct dc_link *dc_link, >>> + struct dsc_mst_fairness_params *params, >>> + struct dsc_mst_fairness_vars *vars, >>> + int count, >>> + int k) >>> { >>> int i; >>> bool bpp_increased[MAX_PIPES]; >>> @@ -724,10 +730,13 @@ static int increase_dsc_bpp(struct drm_atomic_state *state, >>> int min_initial_slack; >>> int next_index; >>> int remaining_to_increase = 0; >>> + int pbn_per_timeslot; >>> int link_timeslots_used; >>> int fair_pbn_alloc; >>> int ret = 0; >>> >>> + pbn_per_timeslot = dm_mst_get_pbn_divider(dc_link); >>> + >>> for (i = 0; i < count; i++) { >>> if (vars[i + k].dsc_enabled) { >>> initial_slack[i] = >>> @@ -758,17 +767,18 @@ static int increase_dsc_bpp(struct drm_atomic_state *state, >>> link_timeslots_used = 0; >>> >>> for (i = 0; i < count; i++) >>> - link_timeslots_used += DIV_ROUND_UP(vars[i + k].pbn, mst_state->pbn_div); >>> + link_timeslots_used += DIV_ROUND_UP(vars[i + k].pbn, pbn_per_timeslot); >>> >>> - fair_pbn_alloc = >>> - (63 - link_timeslots_used) / remaining_to_increase * mst_state->pbn_div; >>> + fair_pbn_alloc = (63 - link_timeslots_used) / remaining_to_increase * pbn_per_timeslot; >>> >>> if (initial_slack[next_index] > fair_pbn_alloc) { >>> vars[next_index].pbn += fair_pbn_alloc; >>> + >>> ret = drm_dp_atomic_find_time_slots(state, >>> params[next_index].port->mgr, >>> params[next_index].port, >>> - vars[next_index].pbn); >>> + vars[next_index].pbn, >>> + pbn_per_timeslot); >>> if (ret < 0) >>> return ret; >>> >>> @@ -780,7 +790,8 @@ static int increase_dsc_bpp(struct drm_atomic_state *state, >>> ret = drm_dp_atomic_find_time_slots(state, >>> params[next_index].port->mgr, >>> params[next_index].port, >>> - vars[next_index].pbn); >>> + vars[next_index].pbn, >>> + pbn_per_timeslot); >>> if (ret < 0) >>> return ret; >>> } >>> @@ -789,7 +800,8 @@ static int increase_dsc_bpp(struct drm_atomic_state *state, >>> ret = drm_dp_atomic_find_time_slots(state, >>> params[next_index].port->mgr, >>> params[next_index].port, >>> - vars[next_index].pbn); >>> + vars[next_index].pbn, >>> + pbn_per_timeslot); >>> if (ret < 0) >>> return ret; >>> >>> @@ -801,7 +813,8 @@ static int increase_dsc_bpp(struct drm_atomic_state *state, >>> ret = drm_dp_atomic_find_time_slots(state, >>> params[next_index].port->mgr, >>> params[next_index].port, >>> - vars[next_index].pbn); >>> + vars[next_index].pbn, >>> + pbn_per_timeslot); >>> if (ret < 0) >>> return ret; >>> } >>> @@ -857,10 +870,12 @@ static int try_disable_dsc(struct drm_atomic_state *state, >>> break; >>> >>> vars[next_index].pbn = kbps_to_peak_pbn(params[next_index].bw_range.stream_kbps); >>> + >>> ret = drm_dp_atomic_find_time_slots(state, >>> params[next_index].port->mgr, >>> params[next_index].port, >>> - vars[next_index].pbn); >>> + vars[next_index].pbn, >>> + dm_mst_get_pbn_divider(dc_link)); >>> if (ret < 0) >>> return ret; >>> >>> @@ -870,10 +885,12 @@ static int try_disable_dsc(struct drm_atomic_state *state, >>> vars[next_index].bpp_x16 = 0; >>> } else { >>> vars[next_index].pbn = kbps_to_peak_pbn(params[next_index].bw_range.max_kbps); >>> + >>> ret = drm_dp_atomic_find_time_slots(state, >>> params[next_index].port->mgr, >>> params[next_index].port, >>> - vars[next_index].pbn); >>> + vars[next_index].pbn, >>> + dm_mst_get_pbn_divider(dc_link)); >>> if (ret < 0) >>> return ret; >>> } >>> @@ -884,31 +901,21 @@ static int try_disable_dsc(struct drm_atomic_state *state, >>> return 0; >>> } >>> >>> -static int compute_mst_dsc_configs_for_link(struct drm_atomic_state *state, >>> - struct dc_state *dc_state, >>> - struct dc_link *dc_link, >>> - struct dsc_mst_fairness_vars *vars, >>> - struct drm_dp_mst_topology_mgr *mgr, >>> - int *link_vars_start_index) >>> +static bool compute_mst_dsc_configs_for_link(struct drm_atomic_state *state, >>> + struct dc_state *dc_state, >>> + struct dc_link *dc_link, >>> + struct dsc_mst_fairness_vars *vars, >>> + int *link_vars_start_index) >>> { >>> + int i, k, ret; >>> struct dc_stream_state *stream; >>> struct dsc_mst_fairness_params params[MAX_PIPES]; >>> struct amdgpu_dm_connector *aconnector; >>> - struct drm_dp_mst_topology_state *mst_state = drm_atomic_get_mst_topology_state(state, mgr); >>> int count = 0; >>> - int i, k, ret; >>> bool debugfs_overwrite = false; >>> >>> memset(params, 0, sizeof(params)); >>> >>> - if (IS_ERR(mst_state)) >>> - return PTR_ERR(mst_state); >>> - >>> - mst_state->pbn_div = dm_mst_get_pbn_divider(dc_link); >>> -#if defined(CONFIG_DRM_AMD_DC_DCN) >>> - drm_dp_mst_update_slots(mst_state, dc_link_dp_mst_decide_link_encoding_format(dc_link)); >>> -#endif >>> - >>> /* Set up params */ >>> for (i = 0; i < dc_state->stream_count; i++) { >>> struct dc_dsc_policy dsc_policy = {0}; >>> @@ -968,7 +975,7 @@ static int compute_mst_dsc_configs_for_link(struct drm_atomic_state *state, >>> vars[i + k].dsc_enabled = false; >>> vars[i + k].bpp_x16 = 0; >>> ret = drm_dp_atomic_find_time_slots(state, params[i].port->mgr, params[i].port, >>> - vars[i + k].pbn); >>> + vars[i + k].pbn, dm_mst_get_pbn_divider(dc_link)); >>> if (ret < 0) >>> return ret; >>> } >>> @@ -987,7 +994,7 @@ static int compute_mst_dsc_configs_for_link(struct drm_atomic_state *state, >>> vars[i + k].dsc_enabled = true; >>> vars[i + k].bpp_x16 = params[i].bw_range.min_target_bpp_x16; >>> ret = drm_dp_atomic_find_time_slots(state, params[i].port->mgr, >>> - params[i].port, vars[i + k].pbn); >>> + params[i].port, vars[i + k].pbn, dm_mst_get_pbn_divider(dc_link)); >>> if (ret < 0) >>> return ret; >>> } else { >>> @@ -995,7 +1002,7 @@ static int compute_mst_dsc_configs_for_link(struct drm_atomic_state *state, >>> vars[i + k].dsc_enabled = false; >>> vars[i + k].bpp_x16 = 0; >>> ret = drm_dp_atomic_find_time_slots(state, params[i].port->mgr, >>> - params[i].port, vars[i + k].pbn); >>> + params[i].port, vars[i + k].pbn, dm_mst_get_pbn_divider(dc_link)); >>> if (ret < 0) >>> return ret; >>> } >>> @@ -1005,7 +1012,7 @@ static int compute_mst_dsc_configs_for_link(struct drm_atomic_state *state, >>> return ret; >>> >>> /* Optimize degree of compression */ >>> - ret = increase_dsc_bpp(state, mst_state, dc_link, params, vars, count, k); >>> + ret = increase_dsc_bpp(state, dc_link, params, vars, count, k); >>> if (ret < 0) >>> return ret; >>> >>> @@ -1155,7 +1162,7 @@ int compute_mst_dsc_configs_for_state(struct drm_atomic_state *state, >>> continue; >>> >>> mst_mgr = aconnector->port->mgr; >>> - ret = compute_mst_dsc_configs_for_link(state, dc_state, stream->link, vars, mst_mgr, >>> + ret = compute_mst_dsc_configs_for_link(state, dc_state, stream->link, vars, >>> &link_vars_start_index); >>> if (ret != 0) >>> return ret; >>> @@ -1213,7 +1220,7 @@ static int pre_compute_mst_dsc_configs_for_state(struct drm_atomic_state *state, >>> continue; >>> >>> mst_mgr = aconnector->port->mgr; >>> - ret = compute_mst_dsc_configs_for_link(state, dc_state, stream->link, vars, mst_mgr, >>> + ret = compute_mst_dsc_configs_for_link(state, dc_state, stream->link, vars, >>> &link_vars_start_index); >>> if (ret != 0) >>> return ret; >>> diff --git a/drivers/gpu/drm/amd/display/include/link_service_types.h b/drivers/gpu/drm/amd/display/include/link_service_types.h >>> index d1e91d31d151..0889c2a86733 100644 >>> --- a/drivers/gpu/drm/amd/display/include/link_service_types.h >>> +++ b/drivers/gpu/drm/amd/display/include/link_service_types.h >>> @@ -252,9 +252,6 @@ union dpcd_training_lane_set { >>> * _ONLY_ be filled out from DM and then passed to DC, do NOT use these for _any_ kind of atomic >>> * state calculations in DM, or you will break something. >>> */ >>> - >>> -struct drm_dp_mst_port; >>> - >>> /* DP MST stream allocation (payload bandwidth number) */ >>> struct dc_dp_mst_stream_allocation { >>> uint8_t vcp_id; >>> diff --git a/drivers/gpu/drm/display/drm_dp_mst_topology.c b/drivers/gpu/drm/display/drm_dp_mst_topology.c >>> index 51a46689cda7..95ff57d20216 100644 >>> --- a/drivers/gpu/drm/display/drm_dp_mst_topology.c >>> +++ b/drivers/gpu/drm/display/drm_dp_mst_topology.c >>> @@ -68,7 +68,8 @@ static bool dump_dp_payload_table(struct drm_dp_mst_topology_mgr *mgr, >>> static void drm_dp_mst_topology_put_port(struct drm_dp_mst_port *port); >>> >>> static int drm_dp_dpcd_write_payload(struct drm_dp_mst_topology_mgr *mgr, >>> - int id, u8 start_slot, u8 num_slots); >>> + int id, >>> + struct drm_dp_payload *payload); >>> >>> static int drm_dp_send_dpcd_read(struct drm_dp_mst_topology_mgr *mgr, >>> struct drm_dp_mst_port *port, >>> @@ -1234,6 +1235,57 @@ build_query_stream_enc_status(struct drm_dp_sideband_msg_tx *msg, u8 stream_id, >>> return 0; >>> } >>> >>> +static int drm_dp_mst_assign_payload_id(struct drm_dp_mst_topology_mgr *mgr, >>> + struct drm_dp_vcpi *vcpi) >>> +{ >>> + int ret, vcpi_ret; >>> + >>> + mutex_lock(&mgr->payload_lock); >>> + ret = find_first_zero_bit(&mgr->payload_mask, mgr->max_payloads + 1); >>> + if (ret > mgr->max_payloads) { >>> + ret = -EINVAL; >>> + drm_dbg_kms(mgr->dev, "out of payload ids %d\n", ret); >>> + goto out_unlock; >>> + } >>> + >>> + vcpi_ret = find_first_zero_bit(&mgr->vcpi_mask, mgr->max_payloads + 1); >>> + if (vcpi_ret > mgr->max_payloads) { >>> + ret = -EINVAL; >>> + drm_dbg_kms(mgr->dev, "out of vcpi ids %d\n", ret); >>> + goto out_unlock; >>> + } >>> + >>> + set_bit(ret, &mgr->payload_mask); >>> + set_bit(vcpi_ret, &mgr->vcpi_mask); >>> + vcpi->vcpi = vcpi_ret + 1; >>> + mgr->proposed_vcpis[ret - 1] = vcpi; >>> +out_unlock: >>> + mutex_unlock(&mgr->payload_lock); >>> + return ret; >>> +} >>> + >>> +static void drm_dp_mst_put_payload_id(struct drm_dp_mst_topology_mgr *mgr, >>> + int vcpi) >>> +{ >>> + int i; >>> + >>> + if (vcpi == 0) >>> + return; >>> + >>> + mutex_lock(&mgr->payload_lock); >>> + drm_dbg_kms(mgr->dev, "putting payload %d\n", vcpi); >>> + clear_bit(vcpi - 1, &mgr->vcpi_mask); >>> + >>> + for (i = 0; i < mgr->max_payloads; i++) { >>> + if (mgr->proposed_vcpis[i] && >>> + mgr->proposed_vcpis[i]->vcpi == vcpi) { >>> + mgr->proposed_vcpis[i] = NULL; >>> + clear_bit(i + 1, &mgr->payload_mask); >>> + } >>> + } >>> + mutex_unlock(&mgr->payload_lock); >>> +} >>> + >>> static bool check_txmsg_state(struct drm_dp_mst_topology_mgr *mgr, >>> struct drm_dp_sideband_msg_tx *txmsg) >>> { >>> @@ -1686,7 +1738,7 @@ drm_dp_mst_dump_port_topology_history(struct drm_dp_mst_port *port) {} >>> #define save_port_topology_ref(port, type) >>> #endif >>> >>> -struct drm_dp_mst_atomic_payload * >>> +static struct drm_dp_mst_atomic_payload * >>> drm_atomic_get_mst_payload_state(struct drm_dp_mst_topology_state *state, >>> struct drm_dp_mst_port *port) >>> { >>> @@ -1698,7 +1750,6 @@ drm_atomic_get_mst_payload_state(struct drm_dp_mst_topology_state *state, >>> >>> return NULL; >>> } >>> -EXPORT_SYMBOL(drm_atomic_get_mst_payload_state); >>> >>> static void drm_dp_destroy_mst_branch_device(struct kref *kref) >>> { >>> @@ -3201,8 +3252,6 @@ int drm_dp_send_query_stream_enc_status(struct drm_dp_mst_topology_mgr *mgr, >>> struct drm_dp_mst_port *port, >>> struct drm_dp_query_stream_enc_status_ack_reply *status) >>> { >>> - struct drm_dp_mst_topology_state *state; >>> - struct drm_dp_mst_atomic_payload *payload; >>> struct drm_dp_sideband_msg_tx *txmsg; >>> u8 nonce[7]; >>> int ret; >>> @@ -3219,10 +3268,6 @@ int drm_dp_send_query_stream_enc_status(struct drm_dp_mst_topology_mgr *mgr, >>> >>> get_random_bytes(nonce, sizeof(nonce)); >>> >>> - drm_modeset_lock(&mgr->base.lock, NULL); >>> - state = to_drm_dp_mst_topology_state(mgr->base.state); >>> - payload = drm_atomic_get_mst_payload_state(state, port); >>> - >>> /* >>> * "Source device targets the QUERY_STREAM_ENCRYPTION_STATUS message >>> * transaction at the MST Branch device directly connected to the >>> @@ -3230,7 +3275,7 @@ int drm_dp_send_query_stream_enc_status(struct drm_dp_mst_topology_mgr *mgr, >>> */ >>> txmsg->dst = mgr->mst_primary; >>> >>> - build_query_stream_enc_status(txmsg, payload->vcpi, nonce); >>> + build_query_stream_enc_status(txmsg, port->vcpi.vcpi, nonce); >>> >>> drm_dp_queue_down_tx(mgr, txmsg); >>> >>> @@ -3247,7 +3292,6 @@ int drm_dp_send_query_stream_enc_status(struct drm_dp_mst_topology_mgr *mgr, >>> memcpy(status, &txmsg->reply.u.enc_status, sizeof(*status)); >>> >>> out: >>> - drm_modeset_unlock(&mgr->base.lock); >>> drm_dp_mst_topology_put_port(port); >>> out_get_port: >>> kfree(txmsg); >>> @@ -3256,162 +3300,238 @@ int drm_dp_send_query_stream_enc_status(struct drm_dp_mst_topology_mgr *mgr, >>> EXPORT_SYMBOL(drm_dp_send_query_stream_enc_status); >>> >>> static int drm_dp_create_payload_step1(struct drm_dp_mst_topology_mgr *mgr, >>> - struct drm_dp_mst_atomic_payload *payload) >>> + int id, >>> + struct drm_dp_payload *payload) >>> { >>> - return drm_dp_dpcd_write_payload(mgr, payload->vcpi, payload->vc_start_slot, >>> - payload->time_slots); >>> + int ret; >>> + >>> + ret = drm_dp_dpcd_write_payload(mgr, id, payload); >>> + if (ret < 0) { >>> + payload->payload_state = 0; >>> + return ret; >>> + } >>> + payload->payload_state = DP_PAYLOAD_LOCAL; >>> + return 0; >>> } >>> >>> static int drm_dp_create_payload_step2(struct drm_dp_mst_topology_mgr *mgr, >>> - struct drm_dp_mst_atomic_payload *payload) >>> + struct drm_dp_mst_port *port, >>> + int id, >>> + struct drm_dp_payload *payload) >>> { >>> int ret; >>> - struct drm_dp_mst_port *port = drm_dp_mst_topology_get_port_validated(mgr, payload->port); >>> - >>> - if (!port) >>> - return -EIO; >>> >>> - ret = drm_dp_payload_send_msg(mgr, port, payload->vcpi, payload->pbn); >>> - drm_dp_mst_topology_put_port(port); >>> + ret = drm_dp_payload_send_msg(mgr, port, id, port->vcpi.pbn); >>> + if (ret < 0) >>> + return ret; >>> + payload->payload_state = DP_PAYLOAD_REMOTE; >>> return ret; >>> } >>> >>> static int drm_dp_destroy_payload_step1(struct drm_dp_mst_topology_mgr *mgr, >>> - struct drm_dp_mst_topology_state *mst_state, >>> - struct drm_dp_mst_atomic_payload *payload) >>> + struct drm_dp_mst_port *port, >>> + int id, >>> + struct drm_dp_payload *payload) >>> { >>> drm_dbg_kms(mgr->dev, "\n"); >>> - >>> /* it's okay for these to fail */ >>> - drm_dp_payload_send_msg(mgr, payload->port, payload->vcpi, 0); >>> - drm_dp_dpcd_write_payload(mgr, payload->vcpi, payload->vc_start_slot, 0); >>> + if (port) { >>> + drm_dp_payload_send_msg(mgr, port, id, 0); >>> + } >>> >>> + drm_dp_dpcd_write_payload(mgr, id, payload); >>> + payload->payload_state = DP_PAYLOAD_DELETE_LOCAL; >>> + return 0; >>> +} >>> + >>> +static int drm_dp_destroy_payload_step2(struct drm_dp_mst_topology_mgr *mgr, >>> + int id, >>> + struct drm_dp_payload *payload) >>> +{ >>> + payload->payload_state = 0; >>> return 0; >>> } >>> >>> /** >>> - * drm_dp_add_payload_part1() - Execute payload update part 1 >>> - * @mgr: Manager to use. >>> - * @mst_state: The MST atomic state >>> - * @payload: The payload to write >>> + * drm_dp_update_payload_part1() - Execute payload update part 1 >>> + * @mgr: manager to use. >>> + * @start_slot: this is the cur slot >>> + * >>> + * NOTE: start_slot is a temporary workaround for non-atomic drivers, >>> + * this will be removed when non-atomic mst helpers are moved out of the helper >>> * >>> - * Determines the starting time slot for the given payload, and programs the VCPI for this payload >>> - * into hardware. After calling this, the driver should generate ACT and payload packets. >>> + * This iterates over all proposed virtual channels, and tries to >>> + * allocate space in the link for them. For 0->slots transitions, >>> + * this step just writes the VCPI to the MST device. For slots->0 >>> + * transitions, this writes the updated VCPIs and removes the >>> + * remote VC payloads. >>> * >>> - * Returns: 0 on success, error code on failure. In the event that this fails, >>> - * @payload.vc_start_slot will also be set to -1. >>> + * after calling this the driver should generate ACT and payload >>> + * packets. >>> */ >>> -int drm_dp_add_payload_part1(struct drm_dp_mst_topology_mgr *mgr, >>> - struct drm_dp_mst_topology_state *mst_state, >>> - struct drm_dp_mst_atomic_payload *payload) >>> +int drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr, int start_slot) >>> { >>> + struct drm_dp_payload req_payload; >>> struct drm_dp_mst_port *port; >>> - int ret; >>> + int i, j; >>> + int cur_slots = start_slot; >>> + bool skip; >>> >>> - port = drm_dp_mst_topology_get_port_validated(mgr, payload->port); >>> - if (!port) >>> - return 0; >>> + mutex_lock(&mgr->payload_lock); >>> + for (i = 0; i < mgr->max_payloads; i++) { >>> + struct drm_dp_vcpi *vcpi = mgr->proposed_vcpis[i]; >>> + struct drm_dp_payload *payload = &mgr->payloads[i]; >>> + bool put_port = false; >>> >>> - if (mgr->payload_count == 0) >>> - mgr->next_start_slot = mst_state->start_slot; >>> + /* solve the current payloads - compare to the hw ones >>> + - update the hw view */ >>> + req_payload.start_slot = cur_slots; >>> + if (vcpi) { >>> + port = container_of(vcpi, struct drm_dp_mst_port, >>> + vcpi); >>> >>> - payload->vc_start_slot = mgr->next_start_slot; >>> + mutex_lock(&mgr->lock); >>> + skip = !drm_dp_mst_port_downstream_of_branch(port, mgr->mst_primary); >>> + mutex_unlock(&mgr->lock); >>> >>> - ret = drm_dp_create_payload_step1(mgr, payload); >>> - drm_dp_mst_topology_put_port(port); >>> - if (ret < 0) { >>> - drm_warn(mgr->dev, "Failed to create MST payload for port %p: %d\n", >>> - payload->port, ret); >>> - payload->vc_start_slot = -1; >>> - return ret; >>> - } >>> + if (skip) { >>> + drm_dbg_kms(mgr->dev, >>> + "Virtual channel %d is not in current topology\n", >>> + i); >>> + continue; >>> + } >>> + /* Validated ports don't matter if we're releasing >>> + * VCPI >>> + */ >>> + if (vcpi->num_slots) { >>> + port = drm_dp_mst_topology_get_port_validated( >>> + mgr, port); >>> + if (!port) { >>> + if (vcpi->num_slots == payload->num_slots) { >>> + cur_slots += vcpi->num_slots; >>> + payload->start_slot = req_payload.start_slot; >>> + continue; >>> + } else { >>> + drm_dbg_kms(mgr->dev, >>> + "Fail:set payload to invalid sink"); >>> + mutex_unlock(&mgr->payload_lock); >>> + return -EINVAL; >>> + } >>> + } >>> + put_port = true; >>> + } >>> >>> - mgr->payload_count++; >>> - mgr->next_start_slot += payload->time_slots; >>> + req_payload.num_slots = vcpi->num_slots; >>> + req_payload.vcpi = vcpi->vcpi; >>> + } else { >>> + port = NULL; >>> + req_payload.num_slots = 0; >>> + } >>> >>> - return 0; >>> -} >>> -EXPORT_SYMBOL(drm_dp_add_payload_part1); >>> + payload->start_slot = req_payload.start_slot; >>> + /* work out what is required to happen with this payload */ >>> + if (payload->num_slots != req_payload.num_slots) { >>> + >>> + /* need to push an update for this payload */ >>> + if (req_payload.num_slots) { >>> + drm_dp_create_payload_step1(mgr, vcpi->vcpi, >>> + &req_payload); >>> + payload->num_slots = req_payload.num_slots; >>> + payload->vcpi = req_payload.vcpi; >>> + >>> + } else if (payload->num_slots) { >>> + payload->num_slots = 0; >>> + drm_dp_destroy_payload_step1(mgr, port, >>> + payload->vcpi, >>> + payload); >>> + req_payload.payload_state = >>> + payload->payload_state; >>> + payload->start_slot = 0; >>> + } >>> + payload->payload_state = req_payload.payload_state; >>> + } >>> + cur_slots += req_payload.num_slots; >>> >>> -/** >>> - * drm_dp_remove_payload() - Remove an MST payload >>> - * @mgr: Manager to use. >>> - * @mst_state: The MST atomic state >>> - * @payload: The payload to write >>> - * >>> - * Removes a payload from an MST topology if it was successfully assigned a start slot. Also updates >>> - * the starting time slots of all other payloads which would have been shifted towards the start of >>> - * the VC table as a result. After calling this, the driver should generate ACT and payload packets. >>> - */ >>> -void drm_dp_remove_payload(struct drm_dp_mst_topology_mgr *mgr, >>> - struct drm_dp_mst_topology_state *mst_state, >>> - struct drm_dp_mst_atomic_payload *payload) >>> -{ >>> - struct drm_dp_mst_atomic_payload *pos; >>> - bool send_remove = false; >>> + if (put_port) >>> + drm_dp_mst_topology_put_port(port); >>> + } >>> >>> - /* We failed to make the payload, so nothing to do */ >>> - if (payload->vc_start_slot == -1) >>> - return; >>> + for (i = 0; i < mgr->max_payloads; /* do nothing */) { >>> + if (mgr->payloads[i].payload_state != DP_PAYLOAD_DELETE_LOCAL) { >>> + i++; >>> + continue; >>> + } >>> >>> - mutex_lock(&mgr->lock); >>> - send_remove = drm_dp_mst_port_downstream_of_branch(payload->port, mgr->mst_primary); >>> - mutex_unlock(&mgr->lock); >>> + drm_dbg_kms(mgr->dev, "removing payload %d\n", i); >>> + for (j = i; j < mgr->max_payloads - 1; j++) { >>> + mgr->payloads[j] = mgr->payloads[j + 1]; >>> + mgr->proposed_vcpis[j] = mgr->proposed_vcpis[j + 1]; >>> >>> - if (send_remove) >>> - drm_dp_destroy_payload_step1(mgr, mst_state, payload); >>> - else >>> - drm_dbg_kms(mgr->dev, "Payload for VCPI %d not in topology, not sending remove\n", >>> - payload->vcpi); >>> + if (mgr->proposed_vcpis[j] && >>> + mgr->proposed_vcpis[j]->num_slots) { >>> + set_bit(j + 1, &mgr->payload_mask); >>> + } else { >>> + clear_bit(j + 1, &mgr->payload_mask); >>> + } >>> + } >>> >>> - list_for_each_entry(pos, &mst_state->payloads, next) { >>> - if (pos != payload && pos->vc_start_slot > payload->vc_start_slot) >>> - pos->vc_start_slot -= payload->time_slots; >>> + memset(&mgr->payloads[mgr->max_payloads - 1], 0, >>> + sizeof(struct drm_dp_payload)); >>> + mgr->proposed_vcpis[mgr->max_payloads - 1] = NULL; >>> + clear_bit(mgr->max_payloads, &mgr->payload_mask); >>> } >>> - payload->vc_start_slot = -1; >>> + mutex_unlock(&mgr->payload_lock); >>> >>> - mgr->payload_count--; >>> - mgr->next_start_slot -= payload->time_slots; >>> + return 0; >>> } >>> -EXPORT_SYMBOL(drm_dp_remove_payload); >>> +EXPORT_SYMBOL(drm_dp_update_payload_part1); >>> >>> /** >>> - * drm_dp_add_payload_part2() - Execute payload update part 2 >>> - * @mgr: Manager to use. >>> - * @state: The global atomic state >>> - * @payload: The payload to update >>> - * >>> - * If @payload was successfully assigned a starting time slot by drm_dp_add_payload_part1(), this >>> - * function will send the sideband messages to finish allocating this payload. >>> + * drm_dp_update_payload_part2() - Execute payload update part 2 >>> + * @mgr: manager to use. >>> * >>> - * Returns: 0 on success, negative error code on failure. >>> + * This iterates over all proposed virtual channels, and tries to >>> + * allocate space in the link for them. For 0->slots transitions, >>> + * this step writes the remote VC payload commands. For slots->0 >>> + * this just resets some internal state. >>> */ >>> -int drm_dp_add_payload_part2(struct drm_dp_mst_topology_mgr *mgr, >>> - struct drm_atomic_state *state, >>> - struct drm_dp_mst_atomic_payload *payload) >>> +int drm_dp_update_payload_part2(struct drm_dp_mst_topology_mgr *mgr) >>> { >>> + struct drm_dp_mst_port *port; >>> + int i; >>> int ret = 0; >>> + bool skip; >>> >>> - /* Skip failed payloads */ >>> - if (payload->vc_start_slot == -1) { >>> - drm_dbg_kms(state->dev, "Part 1 of payload creation for %s failed, skipping part 2\n", >>> - payload->port->connector->name); >>> - return -EIO; >>> - } >>> + mutex_lock(&mgr->payload_lock); >>> + for (i = 0; i < mgr->max_payloads; i++) { >>> >>> - ret = drm_dp_create_payload_step2(mgr, payload); >>> - if (ret < 0) { >>> - if (!payload->delete) >>> - drm_err(mgr->dev, "Step 2 of creating MST payload for %p failed: %d\n", >>> - payload->port, ret); >>> - else >>> - drm_dbg_kms(mgr->dev, "Step 2 of removing MST payload for %p failed: %d\n", >>> - payload->port, ret); >>> - } >>> + if (!mgr->proposed_vcpis[i]) >>> + continue; >>> >>> - return ret; >>> + port = container_of(mgr->proposed_vcpis[i], struct drm_dp_mst_port, vcpi); >>> + >>> + mutex_lock(&mgr->lock); >>> + skip = !drm_dp_mst_port_downstream_of_branch(port, mgr->mst_primary); >>> + mutex_unlock(&mgr->lock); >>> + >>> + if (skip) >>> + continue; >>> + >>> + drm_dbg_kms(mgr->dev, "payload %d %d\n", i, mgr->payloads[i].payload_state); >>> + if (mgr->payloads[i].payload_state == DP_PAYLOAD_LOCAL) { >>> + ret = drm_dp_create_payload_step2(mgr, port, mgr->proposed_vcpis[i]->vcpi, &mgr->payloads[i]); >>> + } else if (mgr->payloads[i].payload_state == DP_PAYLOAD_DELETE_LOCAL) { >>> + ret = drm_dp_destroy_payload_step2(mgr, mgr->proposed_vcpis[i]->vcpi, &mgr->payloads[i]); >>> + } >>> + if (ret) { >>> + mutex_unlock(&mgr->payload_lock); >>> + return ret; >>> + } >>> + } >>> + mutex_unlock(&mgr->payload_lock); >>> + return 0; >>> } >>> -EXPORT_SYMBOL(drm_dp_add_payload_part2); >>> +EXPORT_SYMBOL(drm_dp_update_payload_part2); >>> >>> static int drm_dp_send_dpcd_read(struct drm_dp_mst_topology_mgr *mgr, >>> struct drm_dp_mst_port *port, >>> @@ -3591,6 +3711,7 @@ int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool ms >>> int ret = 0; >>> struct drm_dp_mst_branch *mstb = NULL; >>> >>> + mutex_lock(&mgr->payload_lock); >>> mutex_lock(&mgr->lock); >>> if (mst_state == mgr->mst_state) >>> goto out_unlock; >>> @@ -3598,6 +3719,10 @@ int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool ms >>> mgr->mst_state = mst_state; >>> /* set the device into MST mode */ >>> if (mst_state) { >>> + struct drm_dp_payload reset_pay; >>> + int lane_count; >>> + int link_rate; >>> + >>> WARN_ON(mgr->mst_primary); >>> >>> /* get dpcd info */ >>> @@ -3608,6 +3733,16 @@ int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool ms >>> goto out_unlock; >>> } >>> >>> + lane_count = min_t(int, mgr->dpcd[2] & DP_MAX_LANE_COUNT_MASK, mgr->max_lane_count); >>> + link_rate = min_t(int, drm_dp_bw_code_to_link_rate(mgr->dpcd[1]), mgr->max_link_rate); >>> + mgr->pbn_div = drm_dp_get_vc_payload_bw(mgr, >>> + link_rate, >>> + lane_count); >>> + if (mgr->pbn_div == 0) { >>> + ret = -EINVAL; >>> + goto out_unlock; >>> + } >>> + >>> /* add initial branch device at LCT 1 */ >>> mstb = drm_dp_add_mst_branch_device(1, NULL); >>> if (mstb == NULL) { >>> @@ -3627,8 +3762,9 @@ int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool ms >>> if (ret < 0) >>> goto out_unlock; >>> >>> - /* Write reset payload */ >>> - drm_dp_dpcd_write_payload(mgr, 0, 0, 0x3f); >>> + reset_pay.start_slot = 0; >>> + reset_pay.num_slots = 0x3f; >>> + drm_dp_dpcd_write_payload(mgr, 0, &reset_pay); >>> >>> queue_work(system_long_wq, &mgr->work); >>> >>> @@ -3640,11 +3776,19 @@ int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool ms >>> /* this can fail if the device is gone */ >>> drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL, 0); >>> ret = 0; >>> + memset(mgr->payloads, 0, >>> + mgr->max_payloads * sizeof(mgr->payloads[0])); >>> + memset(mgr->proposed_vcpis, 0, >>> + mgr->max_payloads * sizeof(mgr->proposed_vcpis[0])); >>> + mgr->payload_mask = 0; >>> + set_bit(0, &mgr->payload_mask); >>> + mgr->vcpi_mask = 0; >>> mgr->payload_id_table_cleared = false; >>> } >>> >>> out_unlock: >>> mutex_unlock(&mgr->lock); >>> + mutex_unlock(&mgr->payload_lock); >>> if (mstb) >>> drm_dp_mst_topology_put_mstb(mstb); >>> return ret; >>> @@ -4163,18 +4307,62 @@ struct edid *drm_dp_mst_get_edid(struct drm_connector *connector, struct drm_dp_ >>> } >>> EXPORT_SYMBOL(drm_dp_mst_get_edid); >>> >>> +/** >>> + * drm_dp_find_vcpi_slots() - Find time slots for this PBN value >>> + * @mgr: manager to use >>> + * @pbn: payload bandwidth to convert into slots. >>> + * >>> + * Calculate the number of time slots that will be required for the given PBN >>> + * value. This function is deprecated, and should not be used in atomic >>> + * drivers. >>> + * >>> + * RETURNS: >>> + * The total slots required for this port, or error. >>> + */ >>> +int drm_dp_find_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, >>> + int pbn) >>> +{ >>> + int num_slots; >>> + >>> + num_slots = DIV_ROUND_UP(pbn, mgr->pbn_div); >>> + >>> + /* max. time slots - one slot for MTP header */ >>> + if (num_slots > 63) >>> + return -ENOSPC; >>> + return num_slots; >>> +} >>> +EXPORT_SYMBOL(drm_dp_find_vcpi_slots); >>> + >>> +static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr, >>> + struct drm_dp_vcpi *vcpi, int pbn, int slots) >>> +{ >>> + int ret; >>> + >>> + vcpi->pbn = pbn; >>> + vcpi->aligned_pbn = slots * mgr->pbn_div; >>> + vcpi->num_slots = slots; >>> + >>> + ret = drm_dp_mst_assign_payload_id(mgr, vcpi); >>> + if (ret < 0) >>> + return ret; >>> + return 0; >>> +} >>> + >>> /** >>> * drm_dp_atomic_find_time_slots() - Find and add time slots to the state >>> * @state: global atomic state >>> * @mgr: MST topology manager for the port >>> * @port: port to find time slots for >>> * @pbn: bandwidth required for the mode in PBN >>> + * @pbn_div: divider for DSC mode that takes FEC into account >>> * >>> - * Allocates time slots to @port, replacing any previous time slot allocations it may >>> - * have had. Any atomic drivers which support MST must call this function in >>> - * their &drm_encoder_helper_funcs.atomic_check() callback unconditionally to >>> - * change the current time slot allocation for the new state, and ensure the MST >>> - * atomic state is added whenever the state of payloads in the topology changes. >>> + * Allocates time slots to @port, replacing any previous timeslot allocations it >>> + * may have had. Any atomic drivers which support MST must call this function >>> + * in their &drm_encoder_helper_funcs.atomic_check() callback to change the >>> + * current timeslot allocation for the new state, but only when >>> + * &drm_crtc_state.mode_changed or &drm_crtc_state.connectors_changed is set >>> + * to ensure compatibility with userspace applications that still use the >>> + * legacy modesetting UAPI. >>> * >>> * Allocations set by this function are not checked against the bandwidth >>> * restraints of @mgr until the driver calls drm_dp_mst_atomic_check(). >>> @@ -4193,7 +4381,8 @@ EXPORT_SYMBOL(drm_dp_mst_get_edid); >>> */ >>> int drm_dp_atomic_find_time_slots(struct drm_atomic_state *state, >>> struct drm_dp_mst_topology_mgr *mgr, >>> - struct drm_dp_mst_port *port, int pbn) >>> + struct drm_dp_mst_port *port, int pbn, >>> + int pbn_div) >>> { >>> struct drm_dp_mst_topology_state *topology_state; >>> struct drm_dp_mst_atomic_payload *payload = NULL; >>> @@ -4226,7 +4415,10 @@ int drm_dp_atomic_find_time_slots(struct drm_atomic_state *state, >>> } >>> } >>> >>> - req_slots = DIV_ROUND_UP(pbn, topology_state->pbn_div); >>> + if (pbn_div <= 0) >>> + pbn_div = mgr->pbn_div; >>> + >>> + req_slots = DIV_ROUND_UP(pbn, pbn_div); >>> >>> drm_dbg_atomic(mgr->dev, "[CONNECTOR:%d:%s] [MST PORT:%p] TU %d -> %d\n", >>> port->connector->base.id, port->connector->name, >>> @@ -4235,7 +4427,7 @@ int drm_dp_atomic_find_time_slots(struct drm_atomic_state *state, >>> port->connector->base.id, port->connector->name, >>> port, prev_bw, pbn); >>> >>> - /* Add the new allocation to the state, note the VCPI isn't assigned until the end */ >>> + /* Add the new allocation to the state */ >>> if (!payload) { >>> payload = kzalloc(sizeof(*payload), GFP_KERNEL); >>> if (!payload) >>> @@ -4243,7 +4435,6 @@ int drm_dp_atomic_find_time_slots(struct drm_atomic_state *state, >>> >>> drm_dp_mst_get_port_malloc(port); >>> payload->port = port; >>> - payload->vc_start_slot = -1; >>> list_add(&payload->next, &topology_state->payloads); >>> } >>> payload->time_slots = req_slots; >>> @@ -4260,12 +4451,10 @@ EXPORT_SYMBOL(drm_dp_atomic_find_time_slots); >>> * @port: The port to release the time slots from >>> * >>> * Releases any time slots that have been allocated to a port in the atomic >>> - * state. Any atomic drivers which support MST must call this function >>> - * unconditionally in their &drm_connector_helper_funcs.atomic_check() callback. >>> - * This helper will check whether time slots would be released by the new state and >>> - * respond accordingly, along with ensuring the MST state is always added to the >>> - * atomic state whenever a new state would modify the state of payloads on the >>> - * topology. >>> + * state. Any atomic drivers which support MST must call this function in >>> + * their &drm_connector_helper_funcs.atomic_check() callback when the >>> + * connector will no longer have VCPI allocated (e.g. because its CRTC was >>> + * removed) when it had VCPI allocated in the previous atomic state. >>> * >>> * It is OK to call this even if @port has been removed from the system. >>> * Additionally, it is OK to call this function multiple times on the same >>> @@ -4330,7 +4519,6 @@ int drm_dp_atomic_release_time_slots(struct drm_atomic_state *state, >>> drm_dp_mst_put_port_malloc(port); >>> payload->pbn = 0; >>> payload->delete = true; >>> - topology_state->payload_mask &= ~BIT(payload->vcpi - 1); >>> } >>> >>> return 0; >>> @@ -4381,8 +4569,7 @@ int drm_dp_mst_atomic_setup_commit(struct drm_atomic_state *state) >>> EXPORT_SYMBOL(drm_dp_mst_atomic_setup_commit); >>> >>> /** >>> - * drm_dp_mst_atomic_wait_for_dependencies() - Wait for all pending commits on MST topologies, >>> - * prepare new MST state for commit >>> + * drm_dp_mst_atomic_wait_for_dependencies() - Wait for all pending commits on MST topologies >>> * @state: global atomic state >>> * >>> * Goes through any MST topologies in this atomic state, and waits for any pending commits which >>> @@ -4400,30 +4587,17 @@ EXPORT_SYMBOL(drm_dp_mst_atomic_setup_commit); >>> */ >>> void drm_dp_mst_atomic_wait_for_dependencies(struct drm_atomic_state *state) >>> { >>> - struct drm_dp_mst_topology_state *old_mst_state, *new_mst_state; >>> + struct drm_dp_mst_topology_state *old_mst_state; >>> struct drm_dp_mst_topology_mgr *mgr; >>> - struct drm_dp_mst_atomic_payload *old_payload, *new_payload; >>> int i, j, ret; >>> >>> - for_each_oldnew_mst_mgr_in_state(state, mgr, old_mst_state, new_mst_state, i) { >>> + for_each_old_mst_mgr_in_state(state, mgr, old_mst_state, i) { >>> for (j = 0; j < old_mst_state->num_commit_deps; j++) { >>> ret = drm_crtc_commit_wait(old_mst_state->commit_deps[j]); >>> if (ret < 0) >>> drm_err(state->dev, "Failed to wait for %s: %d\n", >>> old_mst_state->commit_deps[j]->crtc->name, ret); >>> } >>> - >>> - /* Now that previous state is committed, it's safe to copy over the start slot >>> - * assignments >>> - */ >>> - list_for_each_entry(old_payload, &old_mst_state->payloads, next) { >>> - if (old_payload->delete) >>> - continue; >>> - >>> - new_payload = drm_atomic_get_mst_payload_state(new_mst_state, >>> - old_payload->port); >>> - new_payload->vc_start_slot = old_payload->vc_start_slot; >>> - } >>> } >>> } >>> EXPORT_SYMBOL(drm_dp_mst_atomic_wait_for_dependencies); >>> @@ -4508,8 +4682,119 @@ void drm_dp_mst_update_slots(struct drm_dp_mst_topology_state *mst_state, uint8_ >>> } >>> EXPORT_SYMBOL(drm_dp_mst_update_slots); >>> >>> +/** >>> + * drm_dp_mst_allocate_vcpi() - Allocate a virtual channel >>> + * @mgr: manager for this port >>> + * @port: port to allocate a virtual channel for. >>> + * @pbn: payload bandwidth number to request >>> + * @slots: returned number of slots for this PBN. >>> + */ >>> +bool drm_dp_mst_allocate_vcpi(struct drm_dp_mst_topology_mgr *mgr, >>> + struct drm_dp_mst_port *port, int pbn, int slots) >>> +{ >>> + int ret; >>> + >>> + if (slots < 0) >>> + return false; >>> + >>> + port = drm_dp_mst_topology_get_port_validated(mgr, port); >>> + if (!port) >>> + return false; >>> + >>> + if (port->vcpi.vcpi > 0) { >>> + drm_dbg_kms(mgr->dev, >>> + "payload: vcpi %d already allocated for pbn %d - requested pbn %d\n", >>> + port->vcpi.vcpi, port->vcpi.pbn, pbn); >>> + if (pbn == port->vcpi.pbn) { >>> + drm_dp_mst_topology_put_port(port); >>> + return true; >>> + } >>> + } >>> + >>> + ret = drm_dp_init_vcpi(mgr, &port->vcpi, pbn, slots); >>> + if (ret) { >>> + drm_dbg_kms(mgr->dev, "failed to init time slots=%d ret=%d\n", >>> + DIV_ROUND_UP(pbn, mgr->pbn_div), ret); >>> + drm_dp_mst_topology_put_port(port); >>> + goto out; >>> + } >>> + drm_dbg_kms(mgr->dev, "initing vcpi for pbn=%d slots=%d\n", pbn, port->vcpi.num_slots); >>> + >>> + /* Keep port allocated until its payload has been removed */ >>> + drm_dp_mst_get_port_malloc(port); >>> + drm_dp_mst_topology_put_port(port); >>> + return true; >>> +out: >>> + return false; >>> +} >>> +EXPORT_SYMBOL(drm_dp_mst_allocate_vcpi); >>> + >>> +int drm_dp_mst_get_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port) >>> +{ >>> + int slots = 0; >>> + >>> + port = drm_dp_mst_topology_get_port_validated(mgr, port); >>> + if (!port) >>> + return slots; >>> + >>> + slots = port->vcpi.num_slots; >>> + drm_dp_mst_topology_put_port(port); >>> + return slots; >>> +} >>> +EXPORT_SYMBOL(drm_dp_mst_get_vcpi_slots); >>> + >>> +/** >>> + * drm_dp_mst_reset_vcpi_slots() - Reset number of slots to 0 for VCPI >>> + * @mgr: manager for this port >>> + * @port: unverified pointer to a port. >>> + * >>> + * This just resets the number of slots for the ports VCPI for later programming. >>> + */ >>> +void drm_dp_mst_reset_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port) >>> +{ >>> + /* >>> + * A port with VCPI will remain allocated until its VCPI is >>> + * released, no verified ref needed >>> + */ >>> + >>> + port->vcpi.num_slots = 0; >>> +} >>> +EXPORT_SYMBOL(drm_dp_mst_reset_vcpi_slots); >>> + >>> +/** >>> + * drm_dp_mst_deallocate_vcpi() - deallocate a VCPI >>> + * @mgr: manager for this port >>> + * @port: port to deallocate vcpi for >>> + * >>> + * This can be called unconditionally, regardless of whether >>> + * drm_dp_mst_allocate_vcpi() succeeded or not. >>> + */ >>> +void drm_dp_mst_deallocate_vcpi(struct drm_dp_mst_topology_mgr *mgr, >>> + struct drm_dp_mst_port *port) >>> +{ >>> + bool skip; >>> + >>> + if (!port->vcpi.vcpi) >>> + return; >>> + >>> + mutex_lock(&mgr->lock); >>> + skip = !drm_dp_mst_port_downstream_of_branch(port, mgr->mst_primary); >>> + mutex_unlock(&mgr->lock); >>> + >>> + if (skip) >>> + return; >>> + >>> + drm_dp_mst_put_payload_id(mgr, port->vcpi.vcpi); >>> + port->vcpi.num_slots = 0; >>> + port->vcpi.pbn = 0; >>> + port->vcpi.aligned_pbn = 0; >>> + port->vcpi.vcpi = 0; >>> + drm_dp_mst_put_port_malloc(port); >>> +} >>> +EXPORT_SYMBOL(drm_dp_mst_deallocate_vcpi); >>> + >>> static int drm_dp_dpcd_write_payload(struct drm_dp_mst_topology_mgr *mgr, >>> - int id, u8 start_slot, u8 num_slots) >>> + int id, struct drm_dp_payload *payload) >>> { >>> u8 payload_alloc[3], status; >>> int ret; >>> @@ -4519,8 +4804,8 @@ static int drm_dp_dpcd_write_payload(struct drm_dp_mst_topology_mgr *mgr, >>> DP_PAYLOAD_TABLE_UPDATED); >>> >>> payload_alloc[0] = id; >>> - payload_alloc[1] = start_slot; >>> - payload_alloc[2] = num_slots; >>> + payload_alloc[1] = payload->start_slot; >>> + payload_alloc[2] = payload->num_slots; >>> >>> ret = drm_dp_dpcd_write(mgr->aux, DP_PAYLOAD_ALLOCATE_SET, payload_alloc, 3); >>> if (ret != 3) { >>> @@ -4735,9 +5020,8 @@ static void fetch_monitor_name(struct drm_dp_mst_topology_mgr *mgr, >>> void drm_dp_mst_dump_topology(struct seq_file *m, >>> struct drm_dp_mst_topology_mgr *mgr) >>> { >>> - struct drm_dp_mst_topology_state *state; >>> - struct drm_dp_mst_atomic_payload *payload; >>> - int i, ret; >>> + int i; >>> + struct drm_dp_mst_port *port; >>> >>> mutex_lock(&mgr->lock); >>> if (mgr->mst_primary) >>> @@ -4746,35 +5030,36 @@ void drm_dp_mst_dump_topology(struct seq_file *m, >>> /* dump VCPIs */ >>> mutex_unlock(&mgr->lock); >>> >>> - ret = drm_modeset_lock_single_interruptible(&mgr->base.lock); >>> - if (ret < 0) >>> - return; >>> + mutex_lock(&mgr->payload_lock); >>> + seq_printf(m, "\n*** VCPI Info ***\n"); >>> + seq_printf(m, "payload_mask: %lx, vcpi_mask: %lx, max_payloads: %d\n", mgr->payload_mask, mgr->vcpi_mask, mgr->max_payloads); >>> >>> - state = to_drm_dp_mst_topology_state(mgr->base.state); >>> - seq_printf(m, "\n*** Atomic state info ***\n"); >>> - seq_printf(m, "payload_mask: %x, max_payloads: %d, start_slot: %u, pbn_div: %d\n", >>> - state->payload_mask, mgr->max_payloads, state->start_slot, state->pbn_div); >>> - >>> - seq_printf(m, "\n| idx | port | vcpi | slots | pbn | dsc | sink name |\n"); >>> + seq_printf(m, "\n| idx | port # | vcp_id | # slots | sink name |\n"); >>> for (i = 0; i < mgr->max_payloads; i++) { >>> - list_for_each_entry(payload, &state->payloads, next) { >>> + if (mgr->proposed_vcpis[i]) { >>> char name[14]; >>> >>> - if (payload->vcpi != i || payload->delete) >>> - continue; >>> - >>> - fetch_monitor_name(mgr, payload->port, name, sizeof(name)); >>> - seq_printf(m, " %5d %6d %6d %02d - %02d %5d %5s %19s\n", >>> + port = container_of(mgr->proposed_vcpis[i], struct drm_dp_mst_port, vcpi); >>> + fetch_monitor_name(mgr, port, name, sizeof(name)); >>> + seq_printf(m, "%10d%10d%10d%10d%20s\n", >>> i, >>> - payload->port->port_num, >>> - payload->vcpi, >>> - payload->vc_start_slot, >>> - payload->vc_start_slot + payload->time_slots - 1, >>> - payload->pbn, >>> - payload->dsc_enabled ? "Y" : "N", >>> + port->port_num, >>> + port->vcpi.vcpi, >>> + port->vcpi.num_slots, >>> (*name != 0) ? name : "Unknown"); >>> - } >>> + } else >>> + seq_printf(m, "%6d - Unused\n", i); >>> + } >>> + seq_printf(m, "\n*** Payload Info ***\n"); >>> + seq_printf(m, "| idx | state | start slot | # slots |\n"); >>> + for (i = 0; i < mgr->max_payloads; i++) { >>> + seq_printf(m, "%10d%10d%15d%10d\n", >>> + i, >>> + mgr->payloads[i].payload_state, >>> + mgr->payloads[i].start_slot, >>> + mgr->payloads[i].num_slots); >>> } >>> + mutex_unlock(&mgr->payload_lock); >>> >>> seq_printf(m, "\n*** DPCD Info ***\n"); >>> mutex_lock(&mgr->lock); >>> @@ -4820,7 +5105,7 @@ void drm_dp_mst_dump_topology(struct seq_file *m, >>> >>> out: >>> mutex_unlock(&mgr->lock); >>> - drm_modeset_unlock(&mgr->base.lock); >>> + >>> } >>> EXPORT_SYMBOL(drm_dp_mst_dump_topology); >>> >>> @@ -5141,22 +5426,9 @@ drm_dp_mst_atomic_check_payload_alloc_limits(struct drm_dp_mst_topology_mgr *mgr >>> mgr, mst_state, mgr->max_payloads); >>> return -EINVAL; >>> } >>> - >>> - /* Assign a VCPI */ >>> - if (!payload->vcpi) { >>> - payload->vcpi = ffz(mst_state->payload_mask) + 1; >>> - drm_dbg_atomic(mgr->dev, "[MST PORT:%p] assigned VCPI #%d\n", >>> - payload->port, payload->vcpi); >>> - mst_state->payload_mask |= BIT(payload->vcpi - 1); >>> - } >>> } >>> - >>> - if (!payload_count) >>> - mst_state->pbn_div = 0; >>> - >>> - drm_dbg_atomic(mgr->dev, "[MST MGR:%p] mst state %p TU pbn_div=%d avail=%d used=%d\n", >>> - mgr, mst_state, mst_state->pbn_div, avail_slots, >>> - mst_state->total_avail_slots - avail_slots); >>> + drm_dbg_atomic(mgr->dev, "[MST MGR:%p] mst state %p TU avail=%d used=%d\n", >>> + mgr, mst_state, avail_slots, mst_state->total_avail_slots - avail_slots); >>> >>> return 0; >>> } >>> @@ -5227,6 +5499,7 @@ EXPORT_SYMBOL(drm_dp_mst_add_affected_dsc_crtcs); >>> * @state: Pointer to the new drm_atomic_state >>> * @port: Pointer to the affected MST Port >>> * @pbn: Newly recalculated bw required for link with DSC enabled >>> + * @pbn_div: Divider to calculate correct number of pbn per slot >>> * @enable: Boolean flag to enable or disable DSC on the port >>> * >>> * This function enables DSC on the given Port >>> @@ -5237,7 +5510,8 @@ EXPORT_SYMBOL(drm_dp_mst_add_affected_dsc_crtcs); >>> */ >>> int drm_dp_mst_atomic_enable_dsc(struct drm_atomic_state *state, >>> struct drm_dp_mst_port *port, >>> - int pbn, bool enable) >>> + int pbn, int pbn_div, >>> + bool enable) >>> { >>> struct drm_dp_mst_topology_state *mst_state; >>> struct drm_dp_mst_atomic_payload *payload; >>> @@ -5263,7 +5537,7 @@ int drm_dp_mst_atomic_enable_dsc(struct drm_atomic_state *state, >>> } >>> >>> if (enable) { >>> - time_slots = drm_dp_atomic_find_time_slots(state, port->mgr, port, pbn); >>> + time_slots = drm_dp_atomic_find_time_slots(state, port->mgr, port, pbn, pbn_div); >>> drm_dbg_atomic(state->dev, >>> "[MST PORT:%p] Enabling DSC flag, reallocating %d time slots on the port\n", >>> port, time_slots); >>> @@ -5276,7 +5550,6 @@ int drm_dp_mst_atomic_enable_dsc(struct drm_atomic_state *state, >>> return time_slots; >>> } >>> EXPORT_SYMBOL(drm_dp_mst_atomic_enable_dsc); >>> - >>> /** >>> * drm_dp_mst_atomic_check - Check that the new state of an MST topology in an >>> * atomic update is valid >>> @@ -5334,6 +5607,7 @@ EXPORT_SYMBOL(drm_dp_mst_topology_state_funcs); >>> >>> /** >>> * drm_atomic_get_mst_topology_state: get MST topology state >>> + * >>> * @state: global atomic state >>> * @mgr: MST topology manager, also the private object in this case >>> * >>> @@ -5352,31 +5626,6 @@ struct drm_dp_mst_topology_state *drm_atomic_get_mst_topology_state(struct drm_a >>> } >>> EXPORT_SYMBOL(drm_atomic_get_mst_topology_state); >>> >>> -/** >>> - * drm_atomic_get_new_mst_topology_state: get new MST topology state in atomic state, if any >>> - * @state: global atomic state >>> - * @mgr: MST topology manager, also the private object in this case >>> - * >>> - * This function wraps drm_atomic_get_priv_obj_state() passing in the MST atomic >>> - * state vtable so that the private object state returned is that of a MST >>> - * topology object. >>> - * >>> - * Returns: >>> - * >>> - * The MST topology state, or NULL if there's no topology state for this MST mgr >>> - * in the global atomic state >>> - */ >>> -struct drm_dp_mst_topology_state * >>> -drm_atomic_get_new_mst_topology_state(struct drm_atomic_state *state, >>> - struct drm_dp_mst_topology_mgr *mgr) >>> -{ >>> - struct drm_private_state *priv_state = >>> - drm_atomic_get_new_private_obj_state(state, &mgr->base); >>> - >>> - return priv_state ? to_dp_mst_topology_state(priv_state) : NULL; >>> -} >>> -EXPORT_SYMBOL(drm_atomic_get_new_mst_topology_state); >>> - >>> /** >>> * drm_dp_mst_topology_mgr_init - initialise a topology manager >>> * @mgr: manager struct to initialise >>> @@ -5384,6 +5633,8 @@ EXPORT_SYMBOL(drm_atomic_get_new_mst_topology_state); >>> * @aux: DP helper aux channel to talk to this device >>> * @max_dpcd_transaction_bytes: hw specific DPCD transaction limit >>> * @max_payloads: maximum number of payloads this GPU can source >>> + * @max_lane_count: maximum number of lanes this GPU supports >>> + * @max_link_rate: maximum link rate per lane this GPU supports in kHz >>> * @conn_base_id: the connector object ID the MST device is connected to. >>> * >>> * Return 0 for success, or negative error code on failure >>> @@ -5391,12 +5642,14 @@ EXPORT_SYMBOL(drm_atomic_get_new_mst_topology_state); >>> int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr, >>> struct drm_device *dev, struct drm_dp_aux *aux, >>> int max_dpcd_transaction_bytes, int max_payloads, >>> + int max_lane_count, int max_link_rate, >>> int conn_base_id) >>> { >>> struct drm_dp_mst_topology_state *mst_state; >>> >>> mutex_init(&mgr->lock); >>> mutex_init(&mgr->qlock); >>> + mutex_init(&mgr->payload_lock); >>> mutex_init(&mgr->delayed_destroy_lock); >>> mutex_init(&mgr->up_req_lock); >>> mutex_init(&mgr->probe_lock); >>> @@ -5426,7 +5679,19 @@ int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr, >>> mgr->aux = aux; >>> mgr->max_dpcd_transaction_bytes = max_dpcd_transaction_bytes; >>> mgr->max_payloads = max_payloads; >>> + mgr->max_lane_count = max_lane_count; >>> + mgr->max_link_rate = max_link_rate; >>> mgr->conn_base_id = conn_base_id; >>> + if (max_payloads + 1 > sizeof(mgr->payload_mask) * 8 || >>> + max_payloads + 1 > sizeof(mgr->vcpi_mask) * 8) >>> + return -EINVAL; >>> + mgr->payloads = kcalloc(max_payloads, sizeof(struct drm_dp_payload), GFP_KERNEL); >>> + if (!mgr->payloads) >>> + return -ENOMEM; >>> + mgr->proposed_vcpis = kcalloc(max_payloads, sizeof(struct drm_dp_vcpi *), GFP_KERNEL); >>> + if (!mgr->proposed_vcpis) >>> + return -ENOMEM; >>> + set_bit(0, &mgr->payload_mask); >>> >>> mst_state = kzalloc(sizeof(*mst_state), GFP_KERNEL); >>> if (mst_state == NULL) >>> @@ -5459,12 +5724,19 @@ void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr) >>> destroy_workqueue(mgr->delayed_destroy_wq); >>> mgr->delayed_destroy_wq = NULL; >>> } >>> + mutex_lock(&mgr->payload_lock); >>> + kfree(mgr->payloads); >>> + mgr->payloads = NULL; >>> + kfree(mgr->proposed_vcpis); >>> + mgr->proposed_vcpis = NULL; >>> + mutex_unlock(&mgr->payload_lock); >>> mgr->dev = NULL; >>> mgr->aux = NULL; >>> drm_atomic_private_obj_fini(&mgr->base); >>> mgr->funcs = NULL; >>> >>> mutex_destroy(&mgr->delayed_destroy_lock); >>> + mutex_destroy(&mgr->payload_lock); >>> mutex_destroy(&mgr->qlock); >>> mutex_destroy(&mgr->lock); >>> mutex_destroy(&mgr->up_req_lock); >>> diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c >>> index 8b0e4defa3f1..4428eabc6ff7 100644 >>> --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c >>> +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c >>> @@ -57,7 +57,6 @@ static int intel_dp_mst_find_vcpi_slots_for_bpp(struct intel_encoder *encoder, >>> struct drm_atomic_state *state = crtc_state->uapi.state; >>> struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder); >>> struct intel_dp *intel_dp = &intel_mst->primary->dp; >>> - struct drm_dp_mst_topology_state *mst_state; >>> struct intel_connector *connector = >>> to_intel_connector(conn_state->connector); >>> struct drm_i915_private *i915 = to_i915(connector->base.dev); >>> @@ -66,30 +65,23 @@ static int intel_dp_mst_find_vcpi_slots_for_bpp(struct intel_encoder *encoder, >>> int bpp, slots = -EINVAL; >>> int ret = 0; >>> >>> - mst_state = drm_atomic_get_mst_topology_state(state, &intel_dp->mst_mgr); >>> - if (IS_ERR(mst_state)) >>> - return PTR_ERR(mst_state); >>> - >>> crtc_state->lane_count = limits->max_lane_count; >>> crtc_state->port_clock = limits->max_rate; >>> >>> - // TODO: Handle pbn_div changes by adding a new MST helper >>> - if (!mst_state->pbn_div) { >>> - mst_state->pbn_div = drm_dp_get_vc_payload_bw(&intel_dp->mst_mgr, >>> - crtc_state->port_clock, >>> - crtc_state->lane_count); >>> - } >>> + for (bpp = limits->max_bpp; bpp >= limits->min_bpp; bpp -= 2 * 3) { >>> + crtc_state->pipe_bpp = bpp; >>> >>> - for (bpp = max_bpp; bpp >= min_bpp; bpp -= step) { >>> crtc_state->pbn = drm_dp_calc_pbn_mode(adjusted_mode->crtc_clock, >>> dsc ? bpp << 4 : bpp, >>> dsc); >>> >>> - drm_dbg_kms(&i915->drm, "Trying bpp %d\n", bpp); >>> - >>> slots = drm_dp_atomic_find_time_slots(state, &intel_dp->mst_mgr, >>> connector->port, >>> - crtc_state->pbn); >>> + crtc_state->pbn, >>> + drm_dp_get_vc_payload_bw(&intel_dp->mst_mgr, >>> + crtc_state->port_clock, >>> + crtc_state->lane_count)); >>> + >>> if (slots == -EDEADLK) >>> return slots; >>> >>> @@ -524,17 +516,21 @@ static void intel_mst_disable_dp(struct intel_atomic_state *state, >>> struct intel_dp *intel_dp = &dig_port->dp; >>> struct intel_connector *connector = >>> to_intel_connector(old_conn_state->connector); >>> - struct drm_dp_mst_topology_state *mst_state = >>> - drm_atomic_get_mst_topology_state(&state->base, &intel_dp->mst_mgr); >>> struct drm_i915_private *i915 = to_i915(connector->base.dev); >>> + int start_slot = intel_dp_is_uhbr(old_crtc_state) ? 0 : 1; >>> + int ret; >>> >>> drm_dbg_kms(&i915->drm, "active links %d\n", >>> intel_dp->active_mst_links); >>> >>> intel_hdcp_disable(intel_mst->connector); >>> >>> - drm_dp_remove_payload(&intel_dp->mst_mgr, mst_state, >>> - drm_atomic_get_mst_payload_state(mst_state, connector->port)); >>> + drm_dp_mst_reset_vcpi_slots(&intel_dp->mst_mgr, connector->port); >>> + >>> + ret = drm_dp_update_payload_part1(&intel_dp->mst_mgr, start_slot); >>> + if (ret) { >>> + drm_dbg_kms(&i915->drm, "failed to update payload %d\n", ret); >>> + } >>> >>> intel_audio_codec_disable(encoder, old_crtc_state, old_conn_state); >>> } >>> @@ -562,6 +558,8 @@ static void intel_mst_post_disable_dp(struct intel_atomic_state *state, >>> >>> intel_disable_transcoder(old_crtc_state); >>> >>> + drm_dp_update_payload_part2(&intel_dp->mst_mgr); >>> + >>> clear_act_sent(encoder, old_crtc_state); >>> >>> intel_de_rmw(dev_priv, TRANS_DDI_FUNC_CTL(old_crtc_state->cpu_transcoder), >>> @@ -569,6 +567,8 @@ static void intel_mst_post_disable_dp(struct intel_atomic_state *state, >>> >>> wait_for_act_sent(encoder, old_crtc_state); >>> >>> + drm_dp_mst_deallocate_vcpi(&intel_dp->mst_mgr, connector->port); >>> + >>> intel_ddi_disable_transcoder_func(old_crtc_state); >>> >>> if (DISPLAY_VER(dev_priv) >= 9) >>> @@ -635,8 +635,7 @@ static void intel_mst_pre_enable_dp(struct intel_atomic_state *state, >>> struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); >>> struct intel_connector *connector = >>> to_intel_connector(conn_state->connector); >>> - struct drm_dp_mst_topology_state *mst_state = >>> - drm_atomic_get_new_mst_topology_state(&state->base, &intel_dp->mst_mgr); >>> + int start_slot = intel_dp_is_uhbr(pipe_config) ? 0 : 1; >>> int ret; >>> bool first_mst_stream; >>> >>> @@ -662,13 +661,16 @@ static void intel_mst_pre_enable_dp(struct intel_atomic_state *state, >>> dig_port->base.pre_enable(state, &dig_port->base, >>> pipe_config, NULL); >>> >>> + ret = drm_dp_mst_allocate_vcpi(&intel_dp->mst_mgr, >>> + connector->port, >>> + pipe_config->pbn, >>> + pipe_config->dp_m_n.tu); >>> + if (!ret) >>> + drm_err(&dev_priv->drm, "failed to allocate vcpi\n"); >>> + >>> intel_dp->active_mst_links++; >>> >>> - ret = drm_dp_add_payload_part1(&intel_dp->mst_mgr, mst_state, >>> - drm_atomic_get_mst_payload_state(mst_state, connector->port)); >>> - if (ret < 0) >>> - drm_err(&dev_priv->drm, "Failed to create MST payload for %s: %d\n", >>> - connector->base.name, ret); >>> + ret = drm_dp_update_payload_part1(&intel_dp->mst_mgr, start_slot); >>> >>> /* >>> * Before Gen 12 this is not done as part of >>> @@ -691,10 +693,7 @@ static void intel_mst_enable_dp(struct intel_atomic_state *state, >>> struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder); >>> struct intel_digital_port *dig_port = intel_mst->primary; >>> struct intel_dp *intel_dp = &dig_port->dp; >>> - struct intel_connector *connector = to_intel_connector(conn_state->connector); >>> struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); >>> - struct drm_dp_mst_topology_state *mst_state = >>> - drm_atomic_get_new_mst_topology_state(&state->base, &intel_dp->mst_mgr); >>> enum transcoder trans = pipe_config->cpu_transcoder; >>> >>> drm_WARN_ON(&dev_priv->drm, pipe_config->has_pch_encoder); >>> @@ -722,8 +721,7 @@ static void intel_mst_enable_dp(struct intel_atomic_state *state, >>> >>> wait_for_act_sent(encoder, pipe_config); >>> >>> - drm_dp_add_payload_part2(&intel_dp->mst_mgr, &state->base, >>> - drm_atomic_get_mst_payload_state(mst_state, connector->port)); >>> + drm_dp_update_payload_part2(&intel_dp->mst_mgr); >>> >>> if (DISPLAY_VER(dev_priv) >= 14 && pipe_config->fec_enable) >>> intel_de_rmw(dev_priv, MTL_CHICKEN_TRANS(trans), 0, >>> @@ -1170,6 +1168,8 @@ intel_dp_mst_encoder_init(struct intel_digital_port *dig_port, int conn_base_id) >>> struct intel_dp *intel_dp = &dig_port->dp; >>> enum port port = dig_port->base.port; >>> int ret; >>> + int max_source_rate = >>> + intel_dp->source_rates[intel_dp->num_source_rates - 1]; >>> >>> if (!HAS_DP_MST(i915) || intel_dp_is_edp(intel_dp)) >>> return 0; >>> @@ -1185,7 +1185,10 @@ intel_dp_mst_encoder_init(struct intel_digital_port *dig_port, int conn_base_id) >>> /* create encoders */ >>> intel_dp_create_fake_mst_encoders(dig_port); >>> ret = drm_dp_mst_topology_mgr_init(&intel_dp->mst_mgr, &i915->drm, >>> - &intel_dp->aux, 16, 3, conn_base_id); >>> + &intel_dp->aux, 16, 3, >>> + dig_port->max_lanes, >>> + max_source_rate, >>> + conn_base_id); >>> if (ret) { >>> intel_dp->mst_mgr.cbs = NULL; >>> return ret; >>> diff --git a/drivers/gpu/drm/i915/display/intel_hdcp.c b/drivers/gpu/drm/i915/display/intel_hdcp.c >>> index 6406fd487ee5..987e02eea66a 100644 >>> --- a/drivers/gpu/drm/i915/display/intel_hdcp.c >>> +++ b/drivers/gpu/drm/i915/display/intel_hdcp.c >>> @@ -31,30 +31,8 @@ >>> >>> static int intel_conn_to_vcpi(struct intel_connector *connector) >>> { >>> - struct drm_dp_mst_topology_mgr *mgr; >>> - struct drm_dp_mst_atomic_payload *payload; >>> - struct drm_dp_mst_topology_state *mst_state; >>> - int vcpi = 0; >>> - >>> /* For HDMI this is forced to be 0x0. For DP SST also this is 0x0. */ >>> - if (!connector->port) >>> - return 0; >>> - mgr = connector->port->mgr; >>> - >>> - drm_modeset_lock(&mgr->base.lock, NULL); >>> - mst_state = to_drm_dp_mst_topology_state(mgr->base.state); >>> - payload = drm_atomic_get_mst_payload_state(mst_state, connector->port); >>> - if (drm_WARN_ON(mgr->dev, !payload)) >>> - goto out; >>> - >>> - vcpi = payload->vcpi; >>> - if (drm_WARN_ON(mgr->dev, vcpi < 0)) { >>> - vcpi = 0; >>> - goto out; >>> - } >>> -out: >>> - drm_modeset_unlock(&mgr->base.lock); >>> - return vcpi; >>> + return connector->port ? connector->port->vcpi.vcpi : 0; >>> } >>> >>> /* >>> diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c >>> index edcb2529b402..9bebe2a5e284 100644 >>> --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c >>> +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c >>> @@ -831,7 +831,6 @@ struct nv50_msto { >>> struct nv50_head *head; >>> struct nv50_mstc *mstc; >>> bool disabled; >>> - bool enabled; >>> }; >>> >>> struct nouveau_encoder *nv50_real_outp(struct drm_encoder *encoder) >>> @@ -847,55 +846,82 @@ struct nouveau_encoder *nv50_real_outp(struct drm_encoder *encoder) >>> return msto->mstc->mstm->outp; >>> } >>> >>> +static struct drm_dp_payload * >>> +nv50_msto_payload(struct nv50_msto *msto) >>> +{ >>> + struct nouveau_drm *drm = nouveau_drm(msto->encoder.dev); >>> + struct nv50_mstc *mstc = msto->mstc; >>> + struct nv50_mstm *mstm = mstc->mstm; >>> + int vcpi = mstc->port->vcpi.vcpi, i; >>> + >>> + WARN_ON(!mutex_is_locked(&mstm->mgr.payload_lock)); >>> + >>> + NV_ATOMIC(drm, "%s: vcpi %d\n", msto->encoder.name, vcpi); >>> + for (i = 0; i < mstm->mgr.max_payloads; i++) { >>> + struct drm_dp_payload *payload = &mstm->mgr.payloads[i]; >>> + NV_ATOMIC(drm, "%s: %d: vcpi %d start 0x%02x slots 0x%02x\n", >>> + mstm->outp->base.base.name, i, payload->vcpi, >>> + payload->start_slot, payload->num_slots); >>> + } >>> + >>> + for (i = 0; i < mstm->mgr.max_payloads; i++) { >>> + struct drm_dp_payload *payload = &mstm->mgr.payloads[i]; >>> + if (payload->vcpi == vcpi) >>> + return payload; >>> + } >>> + >>> + return NULL; >>> +} >>> + >>> static void >>> -nv50_msto_cleanup(struct drm_atomic_state *state, >>> - struct drm_dp_mst_topology_state *mst_state, >>> - struct drm_dp_mst_topology_mgr *mgr, >>> - struct nv50_msto *msto) >>> +nv50_msto_cleanup(struct nv50_msto *msto) >>> { >>> struct nouveau_drm *drm = nouveau_drm(msto->encoder.dev); >>> - struct drm_dp_mst_atomic_payload *payload = >>> - drm_atomic_get_mst_payload_state(mst_state, msto->mstc->port); >>> + struct nv50_mstc *mstc = msto->mstc; >>> + struct nv50_mstm *mstm = mstc->mstm; >>> + >>> + if (!msto->disabled) >>> + return; >>> >>> NV_ATOMIC(drm, "%s: msto cleanup\n", msto->encoder.name); >>> >>> - if (msto->disabled) { >>> - msto->mstc = NULL; >>> - msto->disabled = false; >>> - } else if (msto->enabled) { >>> - drm_dp_add_payload_part2(mgr, state, payload); >>> - msto->enabled = false; >>> - } >>> + drm_dp_mst_deallocate_vcpi(&mstm->mgr, mstc->port); >>> + >>> + msto->mstc = NULL; >>> + msto->disabled = false; >>> } >>> >>> static void >>> -nv50_msto_prepare(struct drm_atomic_state *state, >>> - struct drm_dp_mst_topology_state *mst_state, >>> - struct drm_dp_mst_topology_mgr *mgr, >>> - struct nv50_msto *msto) >>> +nv50_msto_prepare(struct nv50_msto *msto) >>> { >>> struct nouveau_drm *drm = nouveau_drm(msto->encoder.dev); >>> struct nv50_mstc *mstc = msto->mstc; >>> struct nv50_mstm *mstm = mstc->mstm; >>> - struct drm_dp_mst_atomic_payload *payload; >>> + struct drm_dp_payload *payload = NULL; >>> >>> - NV_ATOMIC(drm, "%s: msto prepare\n", msto->encoder.name); >>> + mutex_lock(&mstm->mgr.payload_lock); >>> >>> - payload = drm_atomic_get_mst_payload_state(mst_state, mstc->port); >>> + NV_ATOMIC(drm, "%s: msto prepare\n", msto->encoder.name); >>> >>> - // TODO: Figure out if we want to do a better job of handling VCPI allocation failures here? >>> - if (msto->disabled) { >>> - drm_dp_remove_payload(mgr, mst_state, payload); >>> + if (mstc->port->vcpi.vcpi > 0) >>> + payload = nv50_msto_payload(msto); >>> >>> - nvif_outp_dp_mst_vcpi(&mstm->outp->outp, msto->head->base.index, 0, 0, 0, 0); >>> - } else { >>> - if (msto->enabled) >>> - drm_dp_add_payload_part1(mgr, mst_state, payload); >>> + if (payload) { >>> + NV_ATOMIC(drm, "%s: %s: %02x %02x %04x %04x\n", >>> + msto->encoder.name, msto->head->base.base.name, >>> + payload->start_slot, payload->num_slots, >>> + mstc->port->vcpi.pbn, mstc->port->vcpi.aligned_pbn); >>> >>> nvif_outp_dp_mst_vcpi(&mstm->outp->outp, msto->head->base.index, >>> - payload->vc_start_slot, payload->time_slots, >>> - payload->pbn, payload->time_slots * mst_state->pbn_div); >>> + payload->start_slot, payload->num_slots, >>> + mstc->port->vcpi.pbn, mstc->port->vcpi.aligned_pbn); >>> + } else { >>> + NV_ATOMIC(drm, "%s: %s: %02x %02x %04x %04x\n", >>> + msto->encoder.name, msto->head->base.base.name, 0, 0, 0, 0); >>> + nvif_outp_dp_mst_vcpi(&mstm->outp->outp, msto->head->base.index, 0, 0, 0, 0); >>> } >>> + >>> + mutex_unlock(&mstm->mgr.payload_lock); >>> } >>> >>> static int >>> @@ -905,7 +931,6 @@ nv50_msto_atomic_check(struct drm_encoder *encoder, >>> { >>> struct drm_atomic_state *state = crtc_state->state; >>> struct drm_connector *connector = conn_state->connector; >>> - struct drm_dp_mst_topology_state *mst_state; >>> struct nv50_mstc *mstc = nv50_mstc(connector); >>> struct nv50_mstm *mstm = mstc->mstm; >>> struct nv50_head_atom *asyh = nv50_head_atom(crtc_state); >>> @@ -933,18 +958,8 @@ nv50_msto_atomic_check(struct drm_encoder *encoder, >>> false); >>> } >>> >>> - mst_state = drm_atomic_get_mst_topology_state(state, &mstm->mgr); >>> - if (IS_ERR(mst_state)) >>> - return PTR_ERR(mst_state); >>> - >>> - if (!mst_state->pbn_div) { >>> - struct nouveau_encoder *outp = mstc->mstm->outp; >>> - >>> - mst_state->pbn_div = drm_dp_get_vc_payload_bw(&mstm->mgr, >>> - outp->dp.link_bw, outp->dp.link_nr); >>> - } >>> - >>> - slots = drm_dp_atomic_find_time_slots(state, &mstm->mgr, mstc->port, asyh->dp.pbn); >>> + slots = drm_dp_atomic_find_time_slots(state, &mstm->mgr, mstc->port, >>> + asyh->dp.pbn, 0); >>> if (slots < 0) >>> return slots; >>> >>> @@ -976,6 +991,7 @@ nv50_msto_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *st >>> struct drm_connector *connector; >>> struct drm_connector_list_iter conn_iter; >>> u8 proto; >>> + bool r; >>> >>> drm_connector_list_iter_begin(encoder->dev, &conn_iter); >>> drm_for_each_connector_iter(connector, &conn_iter) { >>> @@ -990,6 +1006,10 @@ nv50_msto_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *st >>> if (WARN_ON(!mstc)) >>> return; >>> >>> + r = drm_dp_mst_allocate_vcpi(&mstm->mgr, mstc->port, asyh->dp.pbn, asyh->dp.tu); >>> + if (!r) >>> + DRM_DEBUG_KMS("Failed to allocate VCPI\n"); >>> + >>> if (!mstm->links++) { >>> /*XXX: MST audio. */ >>> nvif_outp_acquire_dp(&mstm->outp->outp, mstm->outp->dp.dpcd, 0, 0, false, true); >>> @@ -1004,7 +1024,6 @@ nv50_msto_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *st >>> nv50_dp_bpc_to_depth(asyh->or.bpc)); >>> >>> msto->mstc = mstc; >>> - msto->enabled = true; >>> mstm->modified = true; >>> } >>> >>> @@ -1015,6 +1034,8 @@ nv50_msto_atomic_disable(struct drm_encoder *encoder, struct drm_atomic_state *s >>> struct nv50_mstc *mstc = msto->mstc; >>> struct nv50_mstm *mstm = mstc->mstm; >>> >>> + drm_dp_mst_reset_vcpi_slots(&mstm->mgr, mstc->port); >>> + >>> mstm->outp->update(mstm->outp, msto->head->base.index, NULL, 0, 0); >>> mstm->modified = true; >>> if (!--mstm->links) >>> @@ -1234,9 +1255,7 @@ nv50_mstc_new(struct nv50_mstm *mstm, struct drm_dp_mst_port *port, >>> } >>> >>> static void >>> -nv50_mstm_cleanup(struct drm_atomic_state *state, >>> - struct drm_dp_mst_topology_state *mst_state, >>> - struct nv50_mstm *mstm) >>> +nv50_mstm_cleanup(struct nv50_mstm *mstm) >>> { >>> struct nouveau_drm *drm = nouveau_drm(mstm->outp->base.base.dev); >>> struct drm_encoder *encoder; >>> @@ -1244,12 +1263,14 @@ nv50_mstm_cleanup(struct drm_atomic_state *state, >>> NV_ATOMIC(drm, "%s: mstm cleanup\n", mstm->outp->base.base.name); >>> drm_dp_check_act_status(&mstm->mgr); >>> >>> + drm_dp_update_payload_part2(&mstm->mgr); >>> + >>> drm_for_each_encoder(encoder, mstm->outp->base.base.dev) { >>> if (encoder->encoder_type == DRM_MODE_ENCODER_DPMST) { >>> struct nv50_msto *msto = nv50_msto(encoder); >>> struct nv50_mstc *mstc = msto->mstc; >>> if (mstc && mstc->mstm == mstm) >>> - nv50_msto_cleanup(state, mst_state, &mstm->mgr, msto); >>> + nv50_msto_cleanup(msto); >>> } >>> } >>> >>> @@ -1257,34 +1278,20 @@ nv50_mstm_cleanup(struct drm_atomic_state *state, >>> } >>> >>> static void >>> -nv50_mstm_prepare(struct drm_atomic_state *state, >>> - struct drm_dp_mst_topology_state *mst_state, >>> - struct nv50_mstm *mstm) >>> +nv50_mstm_prepare(struct nv50_mstm *mstm) >>> { >>> struct nouveau_drm *drm = nouveau_drm(mstm->outp->base.base.dev); >>> struct drm_encoder *encoder; >>> >>> NV_ATOMIC(drm, "%s: mstm prepare\n", mstm->outp->base.base.name); >>> + drm_dp_update_payload_part1(&mstm->mgr, 1); >>> >>> - /* Disable payloads first */ >>> - drm_for_each_encoder(encoder, mstm->outp->base.base.dev) { >>> - if (encoder->encoder_type == DRM_MODE_ENCODER_DPMST) { >>> - struct nv50_msto *msto = nv50_msto(encoder); >>> - struct nv50_mstc *mstc = msto->mstc; >>> - if (mstc && mstc->mstm == mstm && msto->disabled) >>> - nv50_msto_prepare(state, mst_state, &mstm->mgr, msto); >>> - } >>> - } >>> - >>> - /* Add payloads for new heads, while also updating the start slots of any unmodified (but >>> - * active) heads that may have had their VC slots shifted left after the previous step >>> - */ >>> drm_for_each_encoder(encoder, mstm->outp->base.base.dev) { >>> if (encoder->encoder_type == DRM_MODE_ENCODER_DPMST) { >>> struct nv50_msto *msto = nv50_msto(encoder); >>> struct nv50_mstc *mstc = msto->mstc; >>> - if (mstc && mstc->mstm == mstm && !msto->disabled) >>> - nv50_msto_prepare(state, mst_state, &mstm->mgr, msto); >>> + if (mstc && mstc->mstm == mstm) >>> + nv50_msto_prepare(msto); >>> } >>> } >>> >>> @@ -1455,7 +1462,9 @@ nv50_mstm_new(struct nouveau_encoder *outp, struct drm_dp_aux *aux, int aux_max, >>> mstm->mgr.cbs = &nv50_mstm; >>> >>> ret = drm_dp_mst_topology_mgr_init(&mstm->mgr, dev, aux, aux_max, >>> - max_payloads, conn_base_id); >>> + max_payloads, outp->dcb->dpconf.link_nr, >>> + drm_dp_bw_code_to_link_rate(outp->dcb->dpconf.link_bw), >>> + conn_base_id); >>> if (ret) >>> return ret; >>> >>> @@ -1902,20 +1911,20 @@ nv50_pior_create(struct drm_connector *connector, struct dcb_output *dcbe) >>> static void >>> nv50_disp_atomic_commit_core(struct drm_atomic_state *state, u32 *interlock) >>> { >>> - struct drm_dp_mst_topology_mgr *mgr; >>> - struct drm_dp_mst_topology_state *mst_state; >>> struct nouveau_drm *drm = nouveau_drm(state->dev); >>> struct nv50_disp *disp = nv50_disp(drm->dev); >>> struct nv50_core *core = disp->core; >>> struct nv50_mstm *mstm; >>> - int i; >>> + struct drm_encoder *encoder; >>> >>> NV_ATOMIC(drm, "commit core %08x\n", interlock[NV50_DISP_INTERLOCK_BASE]); >>> >>> - for_each_new_mst_mgr_in_state(state, mgr, mst_state, i) { >>> - mstm = nv50_mstm(mgr); >>> - if (mstm->modified) >>> - nv50_mstm_prepare(state, mst_state, mstm); >>> + drm_for_each_encoder(encoder, drm->dev) { >>> + if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) { >>> + mstm = nouveau_encoder(encoder)->dp.mstm; >>> + if (mstm && mstm->modified) >>> + nv50_mstm_prepare(mstm); >>> + } >>> } >>> >>> core->func->ntfy_init(disp->sync, NV50_DISP_CORE_NTFY); >>> @@ -1924,10 +1933,12 @@ nv50_disp_atomic_commit_core(struct drm_atomic_state *state, u32 *interlock) >>> disp->core->chan.base.device)) >>> NV_ERROR(drm, "core notifier timeout\n"); >>> >>> - for_each_new_mst_mgr_in_state(state, mgr, mst_state, i) { >>> - mstm = nv50_mstm(mgr); >>> - if (mstm->modified) >>> - nv50_mstm_cleanup(state, mst_state, mstm); >>> + drm_for_each_encoder(encoder, drm->dev) { >>> + if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) { >>> + mstm = nouveau_encoder(encoder)->dp.mstm; >>> + if (mstm && mstm->modified) >>> + nv50_mstm_cleanup(mstm); >>> + } >>> } >>> } >>> >>> diff --git a/include/drm/display/drm_dp_mst_helper.h b/include/drm/display/drm_dp_mst_helper.h >>> index 41fd8352ab65..6b8f738bd622 100644 >>> --- a/include/drm/display/drm_dp_mst_helper.h >>> +++ b/include/drm/display/drm_dp_mst_helper.h >>> @@ -48,6 +48,20 @@ struct drm_dp_mst_topology_ref_history { >>> >>> struct drm_dp_mst_branch; >>> >>> +/** >>> + * struct drm_dp_vcpi - Virtual Channel Payload Identifier >>> + * @vcpi: Virtual channel ID. >>> + * @pbn: Payload Bandwidth Number for this channel >>> + * @aligned_pbn: PBN aligned with slot size >>> + * @num_slots: number of slots for this PBN >>> + */ >>> +struct drm_dp_vcpi { >>> + int vcpi; >>> + int pbn; >>> + int aligned_pbn; >>> + int num_slots; >>> +}; >>> + >>> /** >>> * struct drm_dp_mst_port - MST port >>> * @port_num: port number >>> @@ -131,6 +145,7 @@ struct drm_dp_mst_port { >>> struct drm_dp_aux *passthrough_aux; >>> struct drm_dp_mst_branch *parent; >>> >>> + struct drm_dp_vcpi vcpi; >>> struct drm_connector *connector; >>> struct drm_dp_mst_topology_mgr *mgr; >>> >>> @@ -515,6 +530,19 @@ struct drm_dp_mst_topology_cbs { >>> void (*poll_hpd_irq)(struct drm_dp_mst_topology_mgr *mgr); >>> }; >>> >>> +#define DP_MAX_PAYLOAD (sizeof(unsigned long) * 8) >>> + >>> +#define DP_PAYLOAD_LOCAL 1 >>> +#define DP_PAYLOAD_REMOTE 2 >>> +#define DP_PAYLOAD_DELETE_LOCAL 3 >>> + >>> +struct drm_dp_payload { >>> + int payload_state; >>> + int start_slot; >>> + int num_slots; >>> + int vcpi; >>> +}; >>> + >>> #define to_dp_mst_topology_state(x) container_of(x, struct drm_dp_mst_topology_state, base) >>> >>> /** >>> @@ -527,34 +555,6 @@ struct drm_dp_mst_atomic_payload { >>> /** @port: The MST port assigned to this payload */ >>> struct drm_dp_mst_port *port; >>> >>> - /** >>> - * @vc_start_slot: The time slot that this payload starts on. Because payload start slots >>> - * can't be determined ahead of time, the contents of this value are UNDEFINED at atomic >>> - * check time. This shouldn't usually matter, as the start slot should never be relevant for >>> - * atomic state computations. >>> - * >>> - * Since this value is determined at commit time instead of check time, this value is >>> - * protected by the MST helpers ensuring that async commits operating on the given topology >>> - * never run in parallel. In the event that a driver does need to read this value (e.g. to >>> - * inform hardware of the starting timeslot for a payload), the driver may either: >>> - * >>> - * * Read this field during the atomic commit after >>> - * drm_dp_mst_atomic_wait_for_dependencies() has been called, which will ensure the >>> - * previous MST states payload start slots have been copied over to the new state. Note >>> - * that a new start slot won't be assigned/removed from this payload until >>> - * drm_dp_add_payload_part1()/drm_dp_remove_payload() have been called. >>> - * * Acquire the MST modesetting lock, and then wait for any pending MST-related commits to >>> - * get committed to hardware by calling drm_crtc_commit_wait() on each of the >>> - * &drm_crtc_commit structs in &drm_dp_mst_topology_state.commit_deps. >>> - * >>> - * If neither of the two above solutions suffice (e.g. the driver needs to read the start >>> - * slot in the middle of an atomic commit without waiting for some reason), then drivers >>> - * should cache this value themselves after changing payloads. >>> - */ >>> - s8 vc_start_slot; >>> - >>> - /** @vcpi: The Virtual Channel Payload Identifier */ >>> - u8 vcpi; >>> /** >>> * @time_slots: >>> * The number of timeslots allocated to this payload from the source DP Tx to >>> @@ -582,6 +582,8 @@ struct drm_dp_mst_topology_state { >>> /** @base: Base private state for atomic */ >>> struct drm_private_state base; >>> >>> + /** @payloads: The list of payloads being created/destroyed in this state */ >>> + struct list_head payloads; >>> /** @mgr: The topology manager */ >>> struct drm_dp_mst_topology_mgr *mgr; >>> >>> @@ -598,21 +600,10 @@ struct drm_dp_mst_topology_state { >>> /** @num_commit_deps: The number of CRTC commits in @commit_deps */ >>> size_t num_commit_deps; >>> >>> - /** @payload_mask: A bitmask of allocated VCPIs, used for VCPI assignments */ >>> - u32 payload_mask; >>> - /** @payloads: The list of payloads being created/destroyed in this state */ >>> - struct list_head payloads; >>> - >>> /** @total_avail_slots: The total number of slots this topology can handle (63 or 64) */ >>> u8 total_avail_slots; >>> /** @start_slot: The first usable time slot in this topology (1 or 0) */ >>> u8 start_slot; >>> - >>> - /** >>> - * @pbn_div: The current PBN divisor for this topology. The driver is expected to fill this >>> - * out itself. >>> - */ >>> - int pbn_div; >>> }; >>> >>> #define to_dp_mst_topology_mgr(x) container_of(x, struct drm_dp_mst_topology_mgr, base) >>> @@ -652,6 +643,14 @@ struct drm_dp_mst_topology_mgr { >>> * @max_payloads: maximum number of payloads the GPU can generate. >>> */ >>> int max_payloads; >>> + /** >>> + * @max_lane_count: maximum number of lanes the GPU can drive. >>> + */ >>> + int max_lane_count; >>> + /** >>> + * @max_link_rate: maximum link rate per lane GPU can output, in kHz. >>> + */ >>> + int max_link_rate; >>> /** >>> * @conn_base_id: DRM connector ID this mgr is connected to. Only used >>> * to build the MST connector path value. >>> @@ -694,20 +693,6 @@ struct drm_dp_mst_topology_mgr { >>> */ >>> bool payload_id_table_cleared : 1; >>> >>> - /** >>> - * @payload_count: The number of currently active payloads in hardware. This value is only >>> - * intended to be used internally by MST helpers for payload tracking, and is only safe to >>> - * read/write from the atomic commit (not check) context. >>> - */ >>> - u8 payload_count; >>> - >>> - /** >>> - * @next_start_slot: The starting timeslot to use for new VC payloads. This value is used >>> - * internally by MST helpers for payload tracking, and is only safe to read/write from the >>> - * atomic commit (not check) context. >>> - */ >>> - u8 next_start_slot; >>> - >>> /** >>> * @mst_primary: Pointer to the primary/first branch device. >>> */ >>> @@ -721,6 +706,10 @@ struct drm_dp_mst_topology_mgr { >>> * @sink_count: Sink count from DEVICE_SERVICE_IRQ_VECTOR_ESI0. >>> */ >>> u8 sink_count; >>> + /** >>> + * @pbn_div: PBN to slots divisor. >>> + */ >>> + int pbn_div; >>> >>> /** >>> * @funcs: Atomic helper callbacks >>> @@ -737,6 +726,32 @@ struct drm_dp_mst_topology_mgr { >>> */ >>> struct list_head tx_msg_downq; >>> >>> + /** >>> + * @payload_lock: Protect payload information. >>> + */ >>> + struct mutex payload_lock; >>> + /** >>> + * @proposed_vcpis: Array of pointers for the new VCPI allocation. The >>> + * VCPI structure itself is &drm_dp_mst_port.vcpi, and the size of >>> + * this array is determined by @max_payloads. >>> + */ >>> + struct drm_dp_vcpi **proposed_vcpis; >>> + /** >>> + * @payloads: Array of payloads. The size of this array is determined >>> + * by @max_payloads. >>> + */ >>> + struct drm_dp_payload *payloads; >>> + /** >>> + * @payload_mask: Elements of @payloads actually in use. Since >>> + * reallocation of active outputs isn't possible gaps can be created by >>> + * disabling outputs out of order compared to how they've been enabled. >>> + */ >>> + unsigned long payload_mask; >>> + /** >>> + * @vcpi_mask: Similar to @payload_mask, but for @proposed_vcpis. >>> + */ >>> + unsigned long vcpi_mask; >>> + >>> /** >>> * @tx_waitq: Wait to queue stall for the tx worker. >>> */ >>> @@ -808,7 +823,9 @@ struct drm_dp_mst_topology_mgr { >>> int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr, >>> struct drm_device *dev, struct drm_dp_aux *aux, >>> int max_dpcd_transaction_bytes, >>> - int max_payloads, int conn_base_id); >>> + int max_payloads, >>> + int max_lane_count, int max_link_rate, >>> + int conn_base_id); >>> >>> void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr); >>> >>> @@ -831,17 +848,28 @@ int drm_dp_get_vc_payload_bw(const struct drm_dp_mst_topology_mgr *mgr, >>> >>> int drm_dp_calc_pbn_mode(int clock, int bpp, bool dsc); >>> >>> +bool drm_dp_mst_allocate_vcpi(struct drm_dp_mst_topology_mgr *mgr, >>> + struct drm_dp_mst_port *port, int pbn, int slots); >>> + >>> +int drm_dp_mst_get_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port); >>> + >>> + >>> +void drm_dp_mst_reset_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port); >>> + >>> void drm_dp_mst_update_slots(struct drm_dp_mst_topology_state *mst_state, uint8_t link_encoding_cap); >>> >>> -int drm_dp_add_payload_part1(struct drm_dp_mst_topology_mgr *mgr, >>> - struct drm_dp_mst_topology_state *mst_state, >>> - struct drm_dp_mst_atomic_payload *payload); >>> -int drm_dp_add_payload_part2(struct drm_dp_mst_topology_mgr *mgr, >>> - struct drm_atomic_state *state, >>> - struct drm_dp_mst_atomic_payload *payload); >>> -void drm_dp_remove_payload(struct drm_dp_mst_topology_mgr *mgr, >>> - struct drm_dp_mst_topology_state *mst_state, >>> - struct drm_dp_mst_atomic_payload *payload); >>> +void drm_dp_mst_deallocate_vcpi(struct drm_dp_mst_topology_mgr *mgr, >>> + struct drm_dp_mst_port *port); >>> + >>> + >>> +int drm_dp_find_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, >>> + int pbn); >>> + >>> + >>> +int drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr, int start_slot); >>> + >>> + >>> +int drm_dp_update_payload_part2(struct drm_dp_mst_topology_mgr *mgr); >>> >>> int drm_dp_check_act_status(struct drm_dp_mst_topology_mgr *mgr); >>> >>> @@ -863,22 +891,17 @@ int drm_dp_mst_connector_late_register(struct drm_connector *connector, >>> void drm_dp_mst_connector_early_unregister(struct drm_connector *connector, >>> struct drm_dp_mst_port *port); >>> >>> -struct drm_dp_mst_topology_state * >>> -drm_atomic_get_mst_topology_state(struct drm_atomic_state *state, >>> - struct drm_dp_mst_topology_mgr *mgr); >>> -struct drm_dp_mst_topology_state * >>> -drm_atomic_get_new_mst_topology_state(struct drm_atomic_state *state, >>> - struct drm_dp_mst_topology_mgr *mgr); >>> -struct drm_dp_mst_atomic_payload * >>> -drm_atomic_get_mst_payload_state(struct drm_dp_mst_topology_state *state, >>> - struct drm_dp_mst_port *port); >>> +struct drm_dp_mst_topology_state *drm_atomic_get_mst_topology_state(struct drm_atomic_state *state, >>> + struct drm_dp_mst_topology_mgr *mgr); >>> int __must_check >>> drm_dp_atomic_find_time_slots(struct drm_atomic_state *state, >>> struct drm_dp_mst_topology_mgr *mgr, >>> - struct drm_dp_mst_port *port, int pbn); >>> + struct drm_dp_mst_port *port, int pbn, >>> + int pbn_div); >>> int drm_dp_mst_atomic_enable_dsc(struct drm_atomic_state *state, >>> struct drm_dp_mst_port *port, >>> - int pbn, bool enable); >>> + int pbn, int pbn_div, >>> + bool enable); >>> int __must_check >>> drm_dp_mst_add_affected_dsc_crtcs(struct drm_atomic_state *state, >>> struct drm_dp_mst_topology_mgr *mgr); >>> @@ -902,12 +925,6 @@ void drm_dp_mst_put_port_malloc(struct drm_dp_mst_port *port); >>> >>> struct drm_dp_aux *drm_dp_mst_dsc_aux_for_port(struct drm_dp_mst_port *port); >>> >>> -static inline struct drm_dp_mst_topology_state * >>> -to_drm_dp_mst_topology_state(struct drm_private_state *state) >>> -{ >>> - return container_of(state, struct drm_dp_mst_topology_state, base); >>> -} >>> - >>> extern const struct drm_private_state_funcs drm_dp_mst_topology_state_funcs; >>> >>> /** >> >> -- >> Jani Nikula, Intel Open Source Graphics Center >