Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/msm/dpu: Configure DP INTF/PHY selector

Some platforms provides a mechanism for configuring the mapping between
(one or two) DisplayPort intfs and their PHYs.

In particular SC8180X requires this to be configured, since on this
platform there are fewer controllers than PHYs.

The change implements the logic for optionally configuring which PHY
each of the DP INTFs should be connected to and marks the SC8180X DPU to
program 2 entries.

For now the request is simply to program the mapping 1:1, any support
for alternative mappings is left until the use case arrise.

Note that e.g. msm-4.14 unconditionally maps INTF 0 to PHY 0 on all
platforms, so perhaps this is needed in order to get DisplayPort working
on some other platforms as well.

Co-developed-by: Bjorn Andersson <andersson@kernel.org>
Signed-off-by: Bjorn Andersson <andersson@kernel.org>
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Reviewed-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Patchwork: https://patchwork.freedesktop.org/patch/600895/
Link: https://lore.kernel.org/r/20240625-dp-phy-sel-v3-1-c77c7066c454@linaro.org

+72 -6
+38 -3
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_top.c
··· 2 2 /* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. 3 3 */ 4 4 5 + #include <linux/bitfield.h> 6 + 5 7 #include <drm/drm_managed.h> 6 8 7 9 #include "dpu_hwio.h" ··· 233 231 DPU_REG_WRITE(c, HDMI_DP_CORE_SELECT, 0x1); 234 232 } 235 233 234 + static void dpu_hw_dp_phy_intf_sel(struct dpu_hw_mdp *mdp, 235 + enum dpu_dp_phy_sel phys[2]) 236 + { 237 + struct dpu_hw_blk_reg_map *c = &mdp->hw; 238 + unsigned int intf; 239 + u32 sel = 0; 240 + 241 + sel |= FIELD_PREP(MDP_DP_PHY_INTF_SEL_INTF0, phys[0]); 242 + sel |= FIELD_PREP(MDP_DP_PHY_INTF_SEL_INTF1, phys[1]); 243 + 244 + for (intf = 0; intf < 2; intf++) { 245 + switch (phys[intf]) { 246 + case DPU_DP_PHY_0: 247 + sel |= FIELD_PREP(MDP_DP_PHY_INTF_SEL_PHY0, intf + 1); 248 + break; 249 + case DPU_DP_PHY_1: 250 + sel |= FIELD_PREP(MDP_DP_PHY_INTF_SEL_PHY1, intf + 1); 251 + break; 252 + case DPU_DP_PHY_2: 253 + sel |= FIELD_PREP(MDP_DP_PHY_INTF_SEL_PHY2, intf + 1); 254 + break; 255 + default: 256 + /* ignore */ 257 + break; 258 + } 259 + } 260 + 261 + DPU_REG_WRITE(c, MDP_DP_PHY_INTF_SEL, sel); 262 + } 263 + 236 264 static void _setup_mdp_ops(struct dpu_hw_mdp_ops *ops, 237 - unsigned long cap) 265 + unsigned long cap, const struct dpu_mdss_version *mdss_rev) 238 266 { 239 267 ops->setup_split_pipe = dpu_hw_setup_split_pipe; 240 268 ops->setup_clk_force_ctrl = dpu_hw_setup_clk_force_ctrl; ··· 277 245 278 246 ops->get_safe_status = dpu_hw_get_safe_status; 279 247 248 + if (mdss_rev->core_major_ver >= 5) 249 + ops->dp_phy_intf_sel = dpu_hw_dp_phy_intf_sel; 250 + 280 251 if (cap & BIT(DPU_MDP_AUDIO_SELECT)) 281 252 ops->intf_audio_select = dpu_hw_intf_audio_select; 282 253 } ··· 287 252 struct dpu_hw_mdp *dpu_hw_mdptop_init(struct drm_device *dev, 288 253 const struct dpu_mdp_cfg *cfg, 289 254 void __iomem *addr, 290 - const struct dpu_mdss_cfg *m) 255 + const struct dpu_mdss_version *mdss_rev) 291 256 { 292 257 struct dpu_hw_mdp *mdp; 293 258 ··· 305 270 * Assign ops 306 271 */ 307 272 mdp->caps = cfg; 308 - _setup_mdp_ops(&mdp->ops, mdp->caps->features); 273 + _setup_mdp_ops(&mdp->ops, mdp->caps->features, mdss_rev); 309 274 310 275 return mdp; 311 276 }
+16 -2
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_top.h
··· 67 67 enum dpu_vsync_source vsync_source; 68 68 }; 69 69 70 + enum dpu_dp_phy_sel { 71 + DPU_DP_PHY_NONE, 72 + DPU_DP_PHY_0, 73 + DPU_DP_PHY_1, 74 + DPU_DP_PHY_2, 75 + }; 76 + 70 77 /** 71 78 * struct dpu_hw_mdp_ops - interface to the MDP TOP Hw driver functions 72 79 * Assumption is these functions will be called after clocks are enabled. ··· 133 126 struct dpu_danger_safe_status *status); 134 127 135 128 /** 129 + * dp_phy_intf_sel - configure intf to phy mapping 130 + * @mdp: mdp top context driver 131 + * @phys: list of phys the DP interfaces should be connected to. 0 disables the INTF. 132 + */ 133 + void (*dp_phy_intf_sel)(struct dpu_hw_mdp *mdp, enum dpu_dp_phy_sel phys[2]); 134 + 135 + /** 136 136 * intf_audio_select - select the external interface for audio 137 137 * @mdp: mdp top context driver 138 138 */ ··· 162 148 * @dev: Corresponding device for devres management 163 149 * @cfg: MDP TOP configuration from catalog 164 150 * @addr: Mapped register io address of MDP 165 - * @m: Pointer to mdss catalog data 151 + * @mdss_rev: dpu core's major and minor versions 166 152 */ 167 153 struct dpu_hw_mdp *dpu_hw_mdptop_init(struct drm_device *dev, 168 154 const struct dpu_mdp_cfg *cfg, 169 155 void __iomem *addr, 170 - const struct dpu_mdss_cfg *m); 156 + const struct dpu_mdss_version *mdss_rev); 171 157 172 158 void dpu_hw_mdp_destroy(struct dpu_hw_mdp *mdp); 173 159
+7
drivers/gpu/drm/msm/disp/dpu1/dpu_hwio.h
··· 60 60 #define MDP_WD_TIMER_4_LOAD_VALUE 0x448 61 61 #define DCE_SEL 0x450 62 62 63 + #define MDP_DP_PHY_INTF_SEL 0x460 64 + #define MDP_DP_PHY_INTF_SEL_INTF0 GENMASK(2, 0) 65 + #define MDP_DP_PHY_INTF_SEL_INTF1 GENMASK(5, 3) 66 + #define MDP_DP_PHY_INTF_SEL_PHY0 GENMASK(8, 6) 67 + #define MDP_DP_PHY_INTF_SEL_PHY1 GENMASK(11, 9) 68 + #define MDP_DP_PHY_INTF_SEL_PHY2 GENMASK(14, 12) 69 + 63 70 #define MDP_PERIPH_TOP0 MDP_WD_TIMER_0_CTL 64 71 #define MDP_PERIPH_TOP0_END CLK_CTRL3 65 72
+11 -1
drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
··· 1146 1146 dpu_kms->hw_mdp = dpu_hw_mdptop_init(dev, 1147 1147 dpu_kms->catalog->mdp, 1148 1148 dpu_kms->mmio, 1149 - dpu_kms->catalog); 1149 + dpu_kms->catalog->mdss_ver); 1150 1150 if (IS_ERR(dpu_kms->hw_mdp)) { 1151 1151 rc = PTR_ERR(dpu_kms->hw_mdp); 1152 1152 DPU_ERROR("failed to get hw_mdp: %d\n", rc); ··· 1180 1180 DPU_ERROR("failed to init perf %d\n", rc); 1181 1181 goto err_pm_put; 1182 1182 } 1183 + 1184 + /* 1185 + * We need to program DP <-> PHY relationship only for SC8180X since it 1186 + * has fewer DP controllers than DP PHYs. 1187 + * If any other platform requires the same kind of programming, or if 1188 + * the INTF <->DP relationship isn't static anymore, this needs to be 1189 + * configured through the DT. 1190 + */ 1191 + if (of_device_is_compatible(dpu_kms->pdev->dev.of_node, "qcom,sc8180x-dpu")) 1192 + dpu_kms->hw_mdp->ops.dp_phy_intf_sel(dpu_kms->hw_mdp, (unsigned int[]){ 1, 2, }); 1183 1193 1184 1194 dpu_kms->hw_intr = dpu_hw_intr_init(dev, dpu_kms->mmio, dpu_kms->catalog); 1185 1195 if (IS_ERR(dpu_kms->hw_intr)) {