FILE
dpu_hw_top.c
File preview with syntax highlighting.
linux-6.19.14/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_top.c
1// SPDX-License-Identifier: GPL-2.0-only 2/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. 3 */ 4 5#include <linux/bitfield.h> 6 7#include <drm/drm_managed.h> 8 9#include "dpu_hwio.h" 10#include "dpu_hw_catalog.h" 11#include "dpu_hw_top.h" 12#include "dpu_kms.h" 13 14#define FLD_SPLIT_DISPLAY_CMD BIT(1) 15#define FLD_SMART_PANEL_FREE_RUN BIT(2) 16#define FLD_INTF_1_SW_TRG_MUX BIT(4) 17#define FLD_INTF_2_SW_TRG_MUX BIT(8) 18#define FLD_TE_LINE_INTER_WATERLEVEL_MASK 0xFFFF 19 20#define TRAFFIC_SHAPER_EN BIT(31) 21#define TRAFFIC_SHAPER_RD_CLIENT(num) (0x030 + (num * 4)) 22#define TRAFFIC_SHAPER_WR_CLIENT(num) (0x060 + (num * 4)) 23#define TRAFFIC_SHAPER_FIXPOINT_FACTOR 4 24 25static void dpu_hw_setup_split_pipe(struct dpu_hw_mdp *mdp, 26 struct split_pipe_cfg *cfg) 27{ 28 struct dpu_hw_blk_reg_map *c; 29 u32 upper_pipe = 0; 30 u32 lower_pipe = 0; 31 32 if (!mdp || !cfg) 33 return; 34 35 c = &mdp->hw; 36 37 if (cfg->en) { 38 if (cfg->mode == INTF_MODE_CMD) { 39 lower_pipe = FLD_SPLIT_DISPLAY_CMD; 40 /* interface controlling sw trigger */ 41 if (cfg->intf == INTF_2) 42 lower_pipe |= FLD_INTF_1_SW_TRG_MUX; 43 else 44 lower_pipe |= FLD_INTF_2_SW_TRG_MUX; 45 upper_pipe = lower_pipe; 46 } else { 47 if (cfg->intf == INTF_2) { 48 lower_pipe = FLD_INTF_1_SW_TRG_MUX; 49 upper_pipe = FLD_INTF_2_SW_TRG_MUX; 50 } else { 51 lower_pipe = FLD_INTF_2_SW_TRG_MUX; 52 upper_pipe = FLD_INTF_1_SW_TRG_MUX; 53 } 54 } 55 } 56 57 DPU_REG_WRITE(c, SSPP_SPARE, cfg->split_flush_en ? 0x1 : 0x0); 58 DPU_REG_WRITE(c, SPLIT_DISPLAY_LOWER_PIPE_CTRL, lower_pipe); 59 DPU_REG_WRITE(c, SPLIT_DISPLAY_UPPER_PIPE_CTRL, upper_pipe); 60 DPU_REG_WRITE(c, SPLIT_DISPLAY_EN, cfg->en & 0x1); 61} 62 63static bool dpu_hw_setup_clk_force_ctrl(struct dpu_hw_mdp *mdp, 64 enum dpu_clk_ctrl_type clk_ctrl, bool enable) 65{ 66 if (!mdp) 67 return false; 68 69 if (clk_ctrl <= DPU_CLK_CTRL_NONE || clk_ctrl >= DPU_CLK_CTRL_MAX) 70 return false; 71 72 return dpu_hw_clk_force_ctrl(&mdp->hw, &mdp->caps->clk_ctrls[clk_ctrl], enable); 73} 74 75 76static void dpu_hw_get_danger_status(struct dpu_hw_mdp *mdp, 77 struct dpu_danger_safe_status *status) 78{ 79 struct dpu_hw_blk_reg_map *c; 80 u32 value; 81 82 if (!mdp || !status) 83 return; 84 85 c = &mdp->hw; 86 87 value = DPU_REG_READ(c, DANGER_STATUS); 88 status->mdp = (value >> 0) & 0x3; 89 status->sspp[SSPP_VIG0] = (value >> 4) & 0x3; 90 status->sspp[SSPP_VIG1] = (value >> 6) & 0x3; 91 status->sspp[SSPP_VIG2] = (value >> 8) & 0x3; 92 status->sspp[SSPP_VIG3] = (value >> 10) & 0x3; 93 status->sspp[SSPP_RGB0] = (value >> 12) & 0x3; 94 status->sspp[SSPP_RGB1] = (value >> 14) & 0x3; 95 status->sspp[SSPP_RGB2] = (value >> 16) & 0x3; 96 status->sspp[SSPP_RGB3] = (value >> 18) & 0x3; 97 status->sspp[SSPP_DMA0] = (value >> 20) & 0x3; 98 status->sspp[SSPP_DMA1] = (value >> 22) & 0x3; 99 status->sspp[SSPP_DMA2] = (value >> 28) & 0x3; 100 status->sspp[SSPP_DMA3] = (value >> 30) & 0x3; 101 status->sspp[SSPP_CURSOR0] = (value >> 24) & 0x3; 102 status->sspp[SSPP_CURSOR1] = (value >> 26) & 0x3; 103} 104 105static void dpu_hw_setup_wd_timer(struct dpu_hw_mdp *mdp, 106 struct dpu_vsync_source_cfg *cfg) 107{ 108 struct dpu_hw_blk_reg_map *c; 109 u32 reg, wd_load_value, wd_ctl, wd_ctl2; 110 111 if (!mdp || !cfg) 112 return; 113 114 c = &mdp->hw; 115 116 if (cfg->vsync_source >= DPU_VSYNC_SOURCE_WD_TIMER_4 && 117 cfg->vsync_source <= DPU_VSYNC_SOURCE_WD_TIMER_0) { 118 switch (cfg->vsync_source) { 119 case DPU_VSYNC_SOURCE_WD_TIMER_4: 120 wd_load_value = MDP_WD_TIMER_4_LOAD_VALUE; 121 wd_ctl = MDP_WD_TIMER_4_CTL; 122 wd_ctl2 = MDP_WD_TIMER_4_CTL2; 123 break; 124 case DPU_VSYNC_SOURCE_WD_TIMER_3: 125 wd_load_value = MDP_WD_TIMER_3_LOAD_VALUE; 126 wd_ctl = MDP_WD_TIMER_3_CTL; 127 wd_ctl2 = MDP_WD_TIMER_3_CTL2; 128 break; 129 case DPU_VSYNC_SOURCE_WD_TIMER_2: 130 wd_load_value = MDP_WD_TIMER_2_LOAD_VALUE; 131 wd_ctl = MDP_WD_TIMER_2_CTL; 132 wd_ctl2 = MDP_WD_TIMER_2_CTL2; 133 break; 134 case DPU_VSYNC_SOURCE_WD_TIMER_1: 135 wd_load_value = MDP_WD_TIMER_1_LOAD_VALUE; 136 wd_ctl = MDP_WD_TIMER_1_CTL; 137 wd_ctl2 = MDP_WD_TIMER_1_CTL2; 138 break; 139 case DPU_VSYNC_SOURCE_WD_TIMER_0: 140 default: 141 wd_load_value = MDP_WD_TIMER_0_LOAD_VALUE; 142 wd_ctl = MDP_WD_TIMER_0_CTL; 143 wd_ctl2 = MDP_WD_TIMER_0_CTL2; 144 break; 145 } 146 147 DPU_REG_WRITE(c, wd_load_value, 148 CALCULATE_WD_LOAD_VALUE(cfg->frame_rate)); 149 150 DPU_REG_WRITE(c, wd_ctl, BIT(0)); /* clear timer */ 151 reg = DPU_REG_READ(c, wd_ctl2); 152 reg |= BIT(8); /* enable heartbeat timer */ 153 reg |= BIT(0); /* enable WD timer */ 154 DPU_REG_WRITE(c, wd_ctl2, reg); 155 156 /* make sure that timers are enabled/disabled for vsync state */ 157 wmb(); 158 } 159} 160 161static void dpu_hw_setup_vsync_sel(struct dpu_hw_mdp *mdp, 162 struct dpu_vsync_source_cfg *cfg) 163{ 164 struct dpu_hw_blk_reg_map *c; 165 u32 reg, i; 166 static const u32 pp_offset[PINGPONG_MAX] = {0xC, 0x8, 0x4, 0x13, 0x18}; 167 168 if (!mdp || !cfg || (cfg->pp_count > ARRAY_SIZE(cfg->ppnumber))) 169 return; 170 171 c = &mdp->hw; 172 173 reg = DPU_REG_READ(c, MDP_VSYNC_SEL); 174 for (i = 0; i < cfg->pp_count; i++) { 175 int pp_idx = cfg->ppnumber[i] - PINGPONG_0; 176 177 if (pp_idx >= ARRAY_SIZE(pp_offset)) 178 continue; 179 180 reg &= ~(0xf << pp_offset[pp_idx]); 181 reg |= (cfg->vsync_source & 0xf) << pp_offset[pp_idx]; 182 } 183 DPU_REG_WRITE(c, MDP_VSYNC_SEL, reg); 184 185 dpu_hw_setup_wd_timer(mdp, cfg); 186} 187 188static void dpu_hw_get_safe_status(struct dpu_hw_mdp *mdp, 189 struct dpu_danger_safe_status *status) 190{ 191 struct dpu_hw_blk_reg_map *c; 192 u32 value; 193 194 if (!mdp || !status) 195 return; 196 197 c = &mdp->hw; 198 199 value = DPU_REG_READ(c, SAFE_STATUS); 200 status->mdp = (value >> 0) & 0x1; 201 status->sspp[SSPP_VIG0] = (value >> 4) & 0x1; 202 status->sspp[SSPP_VIG1] = (value >> 6) & 0x1; 203 status->sspp[SSPP_VIG2] = (value >> 8) & 0x1; 204 status->sspp[SSPP_VIG3] = (value >> 10) & 0x1; 205 status->sspp[SSPP_RGB0] = (value >> 12) & 0x1; 206 status->sspp[SSPP_RGB1] = (value >> 14) & 0x1; 207 status->sspp[SSPP_RGB2] = (value >> 16) & 0x1; 208 status->sspp[SSPP_RGB3] = (value >> 18) & 0x1; 209 status->sspp[SSPP_DMA0] = (value >> 20) & 0x1; 210 status->sspp[SSPP_DMA1] = (value >> 22) & 0x1; 211 status->sspp[SSPP_DMA2] = (value >> 28) & 0x1; 212 status->sspp[SSPP_DMA3] = (value >> 30) & 0x1; 213 status->sspp[SSPP_CURSOR0] = (value >> 24) & 0x1; 214 status->sspp[SSPP_CURSOR1] = (value >> 26) & 0x1; 215} 216 217static void dpu_hw_intf_audio_select(struct dpu_hw_mdp *mdp) 218{ 219 struct dpu_hw_blk_reg_map *c; 220 221 if (!mdp) 222 return; 223 224 c = &mdp->hw; 225 226 DPU_REG_WRITE(c, HDMI_DP_CORE_SELECT, 0x1); 227} 228 229static void dpu_hw_dp_phy_intf_sel(struct dpu_hw_mdp *mdp, 230 enum dpu_dp_phy_sel phys[2]) 231{ 232 struct dpu_hw_blk_reg_map *c = &mdp->hw; 233 unsigned int intf; 234 u32 sel = 0; 235 236 sel |= FIELD_PREP(MDP_DP_PHY_INTF_SEL_INTF0, phys[0]); 237 sel |= FIELD_PREP(MDP_DP_PHY_INTF_SEL_INTF1, phys[1]); 238 239 for (intf = 0; intf < 2; intf++) { 240 switch (phys[intf]) { 241 case DPU_DP_PHY_0: 242 sel |= FIELD_PREP(MDP_DP_PHY_INTF_SEL_PHY0, intf + 1); 243 break; 244 case DPU_DP_PHY_1: 245 sel |= FIELD_PREP(MDP_DP_PHY_INTF_SEL_PHY1, intf + 1); 246 break; 247 case DPU_DP_PHY_2: 248 sel |= FIELD_PREP(MDP_DP_PHY_INTF_SEL_PHY2, intf + 1); 249 break; 250 default: 251 /* ignore */ 252 break; 253 } 254 } 255 256 DPU_REG_WRITE(c, MDP_DP_PHY_INTF_SEL, sel); 257} 258 259static void _setup_mdp_ops(struct dpu_hw_mdp_ops *ops, 260 const struct dpu_mdss_version *mdss_rev) 261{ 262 ops->setup_split_pipe = dpu_hw_setup_split_pipe; 263 ops->setup_clk_force_ctrl = dpu_hw_setup_clk_force_ctrl; 264 ops->get_danger_status = dpu_hw_get_danger_status; 265 266 if (mdss_rev->core_major_ver < 5) 267 ops->setup_vsync_source = dpu_hw_setup_vsync_sel; 268 else if (mdss_rev->core_major_ver < 8) 269 ops->setup_vsync_source = dpu_hw_setup_wd_timer; 270 271 ops->get_safe_status = dpu_hw_get_safe_status; 272 273 if (mdss_rev->core_major_ver >= 5) 274 ops->dp_phy_intf_sel = dpu_hw_dp_phy_intf_sel; 275 276 if (mdss_rev->core_major_ver == 4 || 277 mdss_rev->core_major_ver == 5) 278 ops->intf_audio_select = dpu_hw_intf_audio_select; 279} 280 281/** 282 * dpu_hw_mdptop_init - initializes the top driver for the passed config 283 * @dev: Corresponding device for devres management 284 * @cfg: MDP TOP configuration from catalog 285 * @addr: Mapped register io address of MDP 286 * @mdss_rev: dpu core's major and minor versions 287 */ 288struct dpu_hw_mdp *dpu_hw_mdptop_init(struct drm_device *dev, 289 const struct dpu_mdp_cfg *cfg, 290 void __iomem *addr, 291 const struct dpu_mdss_version *mdss_rev) 292{ 293 struct dpu_hw_mdp *mdp; 294 295 if (!addr) 296 return ERR_PTR(-EINVAL); 297 298 mdp = drmm_kzalloc(dev, sizeof(*mdp), GFP_KERNEL); 299 if (!mdp) 300 return ERR_PTR(-ENOMEM); 301 302 mdp->hw.blk_addr = addr + cfg->base; 303 mdp->hw.log_mask = DPU_DBG_MASK_TOP; 304 305 /* 306 * Assign ops 307 */ 308 mdp->caps = cfg; 309 _setup_mdp_ops(&mdp->ops, mdss_rev); 310 311 return mdp; 312}
~/
Read-only file preview.