On Mon, Aug 25, 2025 at 10:16:06PM +0800, Yongxing Mou wrote: > From: Abhinav Kumar <quic_abhin...@quicinc.com> > > DP MST streams share 64 MTP slots in a time-multiplexed manner. This patch > adds support for calculating the rate governor, slot allocation, and slot > reservation in the DP controller. > > Each MST stream can reserve its slots by calling > dp_display_set_stream_info() from its bridge callbacks. > > Signed-off-by: Abhinav Kumar <quic_abhin...@quicinc.com> > Signed-off-by: Yongxing Mou <yongxing....@oss.qualcomm.com> > --- > drivers/gpu/drm/msm/dp/dp_ctrl.c | 213 > +++++++++++++++++++++++++++++++++++- > drivers/gpu/drm/msm/dp/dp_ctrl.h | 7 +- > drivers/gpu/drm/msm/dp/dp_display.c | 40 ++++--- > drivers/gpu/drm/msm/dp/dp_display.h | 5 +- > drivers/gpu/drm/msm/dp/dp_panel.h | 1 + > drivers/gpu/drm/msm/dp/dp_reg.h | 14 ++- > 6 files changed, 262 insertions(+), 18 deletions(-) > > diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c > b/drivers/gpu/drm/msm/dp/dp_ctrl.c > index > c313a3b4853a1571c43a9f3c9e981fbc22d51d55..9d58d9480fc4ab33c58218ef9beb54c64805c34c > 100644 > --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c > +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c > @@ -109,6 +109,11 @@ struct msm_dp_vc_tu_mapping_table { > u8 tu_size_minus1; > }; > > +struct msm_dp_mst_ch_slot_info { > + u32 start_slot; > + u32 tot_slots; > +}; > + > struct msm_dp_ctrl_private { > struct msm_dp_ctrl msm_dp_ctrl; > struct drm_device *drm_dev; > @@ -143,6 +148,8 @@ struct msm_dp_ctrl_private { > bool link_clks_on; > bool stream_clks_on[DP_STREAM_MAX]; > bool mst_active; > + > + struct msm_dp_mst_ch_slot_info mst_ch_info[DP_STREAM_MAX]; > }; > > static inline u32 msm_dp_read_ahb(const struct msm_dp_ctrl_private *ctrl, > u32 offset) > @@ -267,6 +274,77 @@ static void msm_dp_ctrl_mst_config(struct > msm_dp_ctrl_private *ctrl, bool enable > msm_dp_write_link(ctrl, REG_DP_MAINLINK_CTRL, mainlink_ctrl); > } > > +static void msm_dp_ctrl_mst_channel_alloc(struct msm_dp_ctrl_private *ctrl, > + enum msm_dp_stream_id stream_id, u32 > ch_start_slot, > + u32 tot_slot_cnt) > +{ > + u32 i, slot_reg_1, slot_reg_2, slot; > + u32 reg_off = 0; > + int const num_slots_per_reg = 32; > + > + if (ch_start_slot > DP_MAX_TIME_SLOTS || > + (ch_start_slot + tot_slot_cnt > DP_MAX_TIME_SLOTS)) { > + DRM_ERROR("invalid slots start %d, tot %d\n", > + ch_start_slot, tot_slot_cnt); > + return; > + } > + > + drm_dbg_dp(ctrl->drm_dev, "stream_id %d, start_slot %d, tot_slot %d\n", > + stream_id, ch_start_slot, tot_slot_cnt); > + > + if (stream_id == DP_STREAM_1) > + reg_off = REG_DP_DP1_TIMESLOT_1_32 - REG_DP_DP0_TIMESLOT_1_32; > + > + slot_reg_1 = 0; > + slot_reg_2 = 0; > + > + if (ch_start_slot && tot_slot_cnt) { > + ch_start_slot--; > + for (i = 0; i < tot_slot_cnt; i++) {
You can replace loops with maths. > + if (ch_start_slot < num_slots_per_reg) { > + slot_reg_1 |= BIT(ch_start_slot); > + } else { > + slot = ch_start_slot - num_slots_per_reg; > + slot_reg_2 |= BIT(slot); > + } > + ch_start_slot++; > + } > + } > + > + drm_dbg_dp(ctrl->drm_dev, "stream_id:%d slot_reg_1:%d, > slot_reg_2:%d\n", stream_id, > + slot_reg_1, slot_reg_2); > + > + if (stream_id > DP_STREAM_1) { > + msm_dp_write_mstlink(ctrl, stream_id, > REG_DP_MSTLINK_TIMESLOT_1_32, > + slot_reg_1); > + msm_dp_write_mstlink(ctrl, stream_id, > REG_DP_MSTLINK_TIMESLOT_33_63, > + slot_reg_2); > + } else { > + msm_dp_write_link(ctrl, REG_DP_DP0_TIMESLOT_1_32 + reg_off, > slot_reg_1); > + msm_dp_write_link(ctrl, REG_DP_DP0_TIMESLOT_33_63 + reg_off, > slot_reg_2); > + } > +} > + > +static void msm_dp_ctrl_update_rg(struct msm_dp_ctrl_private *ctrl, > + enum msm_dp_stream_id stream_id, u32 x_int, > u32 y_frac_enum) > +{ > + u32 rg, reg_off = 0; > + > + rg = y_frac_enum; > + rg |= (x_int << 16); > + > + drm_dbg_dp(ctrl->drm_dev, "stream_id: %d x_int:%d y_frac_enum:%d > rg:%d\n", > + stream_id, x_int, y_frac_enum, rg); > + > + if (stream_id == DP_STREAM_1) > + reg_off = REG_DP_DP1_RG - REG_DP_DP0_RG; > + > + if (stream_id > DP_STREAM_1) > + msm_dp_write_mstlink(ctrl, stream_id, REG_DP_MSTLINK_DP_RG, rg); > + else > + msm_dp_write_link(ctrl, REG_DP_DP0_RG + reg_off, rg); > +} > + > /* > * NOTE: resetting DP controller will also clear any pending HPD related > interrupts > */ > @@ -2634,7 +2712,105 @@ int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl > *msm_dp_ctrl, bool force_li > return ret; > } > > -int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, struct > msm_dp_panel *msm_dp_panel) > +static void msm_dp_ctrl_mst_calculate_rg(struct msm_dp_ctrl_private *ctrl, > + struct msm_dp_panel *panel, > + u32 *p_x_int, u32 *p_y_frac_enum) A comment would be appreciated. > +{ > + u64 min_slot_cnt, max_slot_cnt; > + u64 raw_target_sc, target_sc_fixp; > + u64 ts_denom, ts_enum, ts_int; > + u64 pclk = panel->msm_dp_mode.drm_mode.clock; > + u64 lclk = 0; > + u64 lanes = ctrl->link->link_params.num_lanes; > + u64 bpp = panel->msm_dp_mode.bpp; > + u64 pbn = panel->pbn; > + u64 numerator, denominator, temp, temp1, temp2; > + u32 x_int = 0, y_frac_enum = 0; > + u64 target_strm_sym, ts_int_fixp, ts_frac_fixp, y_frac_enum_fixp; > + > + lclk = ctrl->link->link_params.rate; > + > + /* min_slot_cnt */ > + numerator = pclk * bpp * 64 * 1000; > + denominator = lclk * lanes * 8 * 1000; > + min_slot_cnt = drm_fixp_from_fraction(numerator, denominator); > + > + /* max_slot_cnt */ > + numerator = pbn * 54 * 1000; > + denominator = lclk * lanes; > + max_slot_cnt = drm_fixp_from_fraction(numerator, denominator); > + > + /* raw_target_sc */ > + numerator = max_slot_cnt + min_slot_cnt; > + denominator = drm_fixp_from_fraction(2, 1); > + raw_target_sc = drm_fixp_div(numerator, denominator); > + > + /* target_sc */ > + temp = drm_fixp_from_fraction(256 * lanes, 1); > + numerator = drm_fixp_mul(raw_target_sc, temp); > + denominator = drm_fixp_from_fraction(256 * lanes, 1); > + target_sc_fixp = drm_fixp_div(numerator, denominator); > + > + ts_enum = 256 * lanes; > + ts_denom = drm_fixp_from_fraction(256 * lanes, 1); > + ts_int = drm_fixp2int(target_sc_fixp); > + > + temp = drm_fixp2int_ceil(raw_target_sc); > + if (temp != ts_int) { > + temp = drm_fixp_from_fraction(ts_int, 1); > + temp1 = raw_target_sc - temp; > + temp2 = drm_fixp_mul(temp1, ts_denom); > + ts_enum = drm_fixp2int(temp2); > + } > + > + /* target_strm_sym */ > + ts_int_fixp = drm_fixp_from_fraction(ts_int, 1); > + ts_frac_fixp = drm_fixp_from_fraction(ts_enum, drm_fixp2int(ts_denom)); > + temp = ts_int_fixp + ts_frac_fixp; > + temp1 = drm_fixp_from_fraction(lanes, 1); > + target_strm_sym = drm_fixp_mul(temp, temp1); > + > + /* x_int */ > + x_int = drm_fixp2int(target_strm_sym); > + > + /* y_enum_frac */ > + temp = drm_fixp_from_fraction(x_int, 1); > + temp1 = target_strm_sym - temp; > + temp2 = drm_fixp_from_fraction(256, 1); > + y_frac_enum_fixp = drm_fixp_mul(temp1, temp2); > + > + temp1 = drm_fixp2int(y_frac_enum_fixp); > + temp2 = drm_fixp2int_ceil(y_frac_enum_fixp); > + > + y_frac_enum = (u32)((temp1 == temp2) ? temp1 : temp1 + 1); > + > + *p_x_int = x_int; > + *p_y_frac_enum = y_frac_enum; > + > + drm_dbg_dp(ctrl->drm_dev, "mst lane_cnt:%llu, rate:%llu x_int:%d, > y_frac:%d\n", globally, for all patches: s/\<mst\>/MST/g > + lanes, lclk, x_int, y_frac_enum); > +} > + > +static void msm_dp_ctrl_mst_stream_setup(struct msm_dp_ctrl_private *ctrl, > + struct msm_dp_panel *panel, > + u32 max_streams) It's not that max_streams can change... I'd totally prefer to stop passing it as params. > +{ > + u32 x_int, y_frac_enum; > + > + if (!ctrl->mst_active) > + return; > + > + drm_dbg_dp(ctrl->drm_dev, "mst stream channel allocation\n"); > + > + msm_dp_ctrl_mst_stream_channel_slot_setup(&ctrl->msm_dp_ctrl, > max_streams); > + > + msm_dp_ctrl_mst_calculate_rg(ctrl, panel, &x_int, &y_frac_enum); > + > + msm_dp_ctrl_update_rg(ctrl, panel->stream_id, x_int, y_frac_enum); > +} > + > +int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, > + struct msm_dp_panel *msm_dp_panel, u32 max_streams) > { > int ret = 0; > bool mainlink_ready = false; > @@ -2688,6 +2864,8 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl > *msm_dp_ctrl, struct msm_dp_panel * > if (!ctrl->mst_active) > msm_dp_ctrl_setup_tr_unit(ctrl); > > + msm_dp_ctrl_mst_stream_setup(ctrl, msm_dp_panel, max_streams); > + > msm_dp_write_link(ctrl, REG_DP_STATE_CTRL, DP_STATE_CTRL_SEND_VIDEO); > > msm_dp_ctrl_mst_send_act(msm_dp_ctrl); > @@ -2742,6 +2920,39 @@ void msm_dp_ctrl_off_link(struct msm_dp_ctrl > *msm_dp_ctrl) > phy, phy->init_count, phy->power_count); > } > > +void msm_dp_ctrl_set_mst_channel_info(struct msm_dp_ctrl *msm_dp_ctrl, > + enum msm_dp_stream_id stream_id, > + u32 start_slot, u32 tot_slots) > +{ > + struct msm_dp_ctrl_private *ctrl; > + > + if (!msm_dp_ctrl || stream_id >= DP_STREAM_MAX) { > + DRM_ERROR("invalid input\n"); > + return; > + } > + > + ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, > msm_dp_ctrl); > + > + ctrl->mst_ch_info[stream_id].start_slot = start_slot; > + ctrl->mst_ch_info[stream_id].tot_slots = tot_slots; > +} > + > +void msm_dp_ctrl_mst_stream_channel_slot_setup(struct msm_dp_ctrl > *msm_dp_ctrl, u32 max_streams) > +{ > + struct msm_dp_ctrl_private *ctrl; > + int i; > + > + ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, > msm_dp_ctrl); > + > + if (!ctrl->mst_active) > + return; > + > + for (i = DP_STREAM_0; i < max_streams; i++) { > + msm_dp_ctrl_mst_channel_alloc(ctrl, i, > ctrl->mst_ch_info[i].start_slot, > + ctrl->mst_ch_info[i].tot_slots); > + } > +} > + > irqreturn_t msm_dp_ctrl_isr(struct msm_dp_ctrl *msm_dp_ctrl) > { > struct msm_dp_ctrl_private *ctrl; > diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h > b/drivers/gpu/drm/msm/dp/dp_ctrl.h > index > abf84ddf463638900684f2511549a593783d2247..751f00c97b94dc3b9e8fae2a86e261f71f293425 > 100644 > --- a/drivers/gpu/drm/msm/dp/dp_ctrl.h > +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h > @@ -17,7 +17,8 @@ struct msm_dp_ctrl { > struct phy; > > int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl, bool mst_active); > -int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, struct > msm_dp_panel *msm_dp_panel); > +int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, > + struct msm_dp_panel *msm_dp_panel, u32 max_streams); > int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, bool > force_link_train); > void msm_dp_ctrl_off_link(struct msm_dp_ctrl *msm_dp_ctrl); > void msm_dp_ctrl_off_pixel_clk(struct msm_dp_ctrl *msm_dp_ctrl, enum > msm_dp_stream_id stream_id); > @@ -51,4 +52,8 @@ void msm_dp_ctrl_disable_irq(struct msm_dp_ctrl > *msm_dp_ctrl); > > void msm_dp_ctrl_reinit_phy(struct msm_dp_ctrl *msm_dp_ctrl); > void msm_dp_ctrl_mst_send_act(struct msm_dp_ctrl *msm_dp_ctrl); > +void msm_dp_ctrl_mst_stream_channel_slot_setup(struct msm_dp_ctrl > *msm_dp_ctrl, u32 max_streams); > +void msm_dp_ctrl_set_mst_channel_info(struct msm_dp_ctrl *msm_dp_ctrl, > + enum msm_dp_stream_id stream_id, > + u32 start_slot, u32 tot_slots); > #endif /* _DP_CTRL_H_ */ > diff --git a/drivers/gpu/drm/msm/dp/dp_display.c > b/drivers/gpu/drm/msm/dp/dp_display.c > index > eeba73f81c5ce7929dac88f4b47ac3741659864b..17633ba79aa7642856051b69227e8f5b23d76730 > 100644 > --- a/drivers/gpu/drm/msm/dp/dp_display.c > +++ b/drivers/gpu/drm/msm/dp/dp_display.c > @@ -733,7 +733,7 @@ static int msm_dp_display_enable(struct > msm_dp_display_private *dp) > return 0; > } > > - rc = msm_dp_ctrl_on_stream(dp->ctrl, dp->panel); > + rc = msm_dp_ctrl_on_stream(dp->ctrl, dp->panel, dp->max_stream); > if (!rc) > msm_dp_display->power_on = true; > > @@ -817,21 +817,33 @@ static int msm_dp_display_disable(struct > msm_dp_display_private *dp) > return 0; > } > > -int msm_dp_display_set_stream_id(struct msm_dp *dp, > - struct msm_dp_panel *panel, enum > msm_dp_stream_id stream_id) > +int msm_dp_display_set_stream_info(struct msm_dp *msm_dp_display, struct > msm_dp_panel *panel, > + enum msm_dp_stream_id stream_id, u32 > start_slot, > + u32 num_slots, u32 pbn, int vcpi) vcpi isn't being used at this patch, don't add it. > { > int rc = 0; > - struct msm_dp_display_private *msm_dp_display; > + struct msm_dp_display_private *dp; > + const int max_slots = 64; > > - msm_dp_display = container_of(dp, struct msm_dp_display_private, > msm_dp_display); > + dp = container_of(msm_dp_display, struct msm_dp_display_private, > msm_dp_display); > > - if (!msm_dp_display) { > + if (!dp) { > DRM_ERROR("invalid input\n"); > return -EINVAL; > } > > - if (panel) > + if (start_slot + num_slots > max_slots) { > + DRM_ERROR("invalid channel info received. start:%d, slots:%d\n", > + start_slot, num_slots); > + return -EINVAL; > + } > + > + msm_dp_ctrl_set_mst_channel_info(dp->ctrl, stream_id, start_slot, > num_slots); > + > + if (panel) { The panel is always passed, as far as I can see. > panel->stream_id = stream_id; > + panel->pbn = pbn; > + } > > return rc; > } > @@ -1533,7 +1545,7 @@ void msm_dp_display_atomic_enable(struct msm_dp > *msm_dp_display) > > dp = container_of(msm_dp_display, struct msm_dp_display_private, > msm_dp_display); > > - msm_dp_display_set_stream_id(msm_dp_display, dp->panel, 0); > + msm_dp_display_set_stream_info(msm_dp_display, dp->panel, 0, 0, 0, 0, > 0); > > if (msm_dp_display->prepared) { > rc = msm_dp_display_enable(dp); > @@ -1550,14 +1562,16 @@ void msm_dp_display_atomic_enable(struct msm_dp > *msm_dp_display) > drm_dbg_dp(msm_dp_display->drm_dev, "type=%d Done\n", > msm_dp_display->connector_type); > } > > -void msm_dp_display_atomic_disable(struct msm_dp *dp) > +void msm_dp_display_atomic_disable(struct msm_dp *msm_dp_display) > { > - struct msm_dp_display_private *msm_dp_display; > + struct msm_dp_display_private *dp; > > - msm_dp_display = container_of(dp, struct msm_dp_display_private, > msm_dp_display); > + dp = container_of(msm_dp_display, struct msm_dp_display_private, > msm_dp_display); > > - msm_dp_ctrl_push_idle(msm_dp_display->ctrl); > - msm_dp_ctrl_mst_send_act(msm_dp_display->ctrl); > + msm_dp_ctrl_push_idle(dp->ctrl); > + msm_dp_ctrl_mst_stream_channel_slot_setup(dp->ctrl, > + dp->max_stream); > + msm_dp_ctrl_mst_send_act(dp->ctrl); > } > > static void msm_dp_display_unprepare(struct msm_dp_display_private *dp) > diff --git a/drivers/gpu/drm/msm/dp/dp_display.h > b/drivers/gpu/drm/msm/dp/dp_display.h > index > 9442157bca9d63467b4c43fa644651ad2cbcbef5..fa92f763d2304f15af7c4e1e7e8aab5a6ffd3459 > 100644 > --- a/drivers/gpu/drm/msm/dp/dp_display.h > +++ b/drivers/gpu/drm/msm/dp/dp_display.h > @@ -48,7 +48,8 @@ void msm_dp_display_mode_set(struct msm_dp *dp, > enum drm_mode_status msm_dp_display_mode_valid(struct msm_dp *dp, > const struct drm_display_info > *info, > const struct drm_display_mode > *mode); > -int msm_dp_display_set_stream_id(struct msm_dp *dp, > - struct msm_dp_panel *panel, enum > msm_dp_stream_id stream_id); > +int msm_dp_display_set_stream_info(struct msm_dp *dp_display, struct > msm_dp_panel *panel, > + enum msm_dp_stream_id stream_id, > + u32 start_slot, u32 num_slots, u32 pbn, int > vcpi); > > #endif /* _DP_DISPLAY_H_ */ > diff --git a/drivers/gpu/drm/msm/dp/dp_panel.h > b/drivers/gpu/drm/msm/dp/dp_panel.h > index > 2bfe3695994235d04e209a2785915107c6a8e413..cb5bf6c99a6f7a68995f0f0ac48382dc90beca31 > 100644 > --- a/drivers/gpu/drm/msm/dp/dp_panel.h > +++ b/drivers/gpu/drm/msm/dp/dp_panel.h > @@ -50,6 +50,7 @@ struct msm_dp_panel { > u32 hw_revision; > > enum msm_dp_stream_id stream_id; > + u32 pbn; > > u32 max_dp_lanes; > u32 max_dp_link_rate; > diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h > index > fda847b33f8d0d6ec4d2589586b5a3d6c9b1ccf3..ee4debf796910e00d370ab4c687009747bae5378 > 100644 > --- a/drivers/gpu/drm/msm/dp/dp_reg.h > +++ b/drivers/gpu/drm/msm/dp/dp_reg.h > @@ -364,6 +364,19 @@ > #define REG_DP_PHY_AUX_BIST_CFG (0x00000050) > #define REG_DP_PHY_AUX_INTERRUPT_STATUS (0x000000BC) > > +/* DP MST related registers */ > +#define DP_MAX_TIME_SLOTS 64 > + > +#define REG_DP_MSTLINK_DP_RG (0X0000011C) > +#define REG_DP_DP0_TIMESLOT_1_32 (0x00000404) > +#define REG_DP_DP0_TIMESLOT_33_63 (0x00000408) > +#define REG_DP_DP1_TIMESLOT_1_32 (0x0000040C) > +#define REG_DP_DP1_TIMESLOT_33_63 (0x00000410) > +#define REG_DP_MSTLINK_TIMESLOT_1_32 (0x00000038) > +#define REG_DP_MSTLINK_TIMESLOT_33_63 (0x0000003C) > +#define REG_DP_DP0_RG (0x000004F8) > +#define REG_DP_DP1_RG (0x000004FC) > + > /* DP HDCP 1.3 registers */ > #define DP_HDCP_CTRL (0x0A0) > #define DP_HDCP_STATUS (0x0A4) > @@ -388,5 +401,4 @@ > #define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA10 (0x018) > #define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA11 (0x01C) > #define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA12 (0x020) > - > #endif /* _DP_REG_H_ */ > > -- > 2.34.1 > -- With best wishes Dmitry