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