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