1 /* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. 2 * 3 * This program is free software; you can redistribute it and/or modify 4 * it under the terms of the GNU General Public License version 2 and 5 * only version 2 as published by the Free Software Foundation. 6 * 7 * This program is distributed in the hope that it will be useful, 8 * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 * GNU General Public License for more details. 11 */ 12 13 #include "dpu_hwio.h" 14 #include "dpu_hw_catalog.h" 15 #include "dpu_hw_top.h" 16 #include "dpu_kms.h" 17 18 #define SSPP_SPARE 0x28 19 #define UBWC_STATIC 0x144 20 21 #define FLD_SPLIT_DISPLAY_CMD BIT(1) 22 #define FLD_SMART_PANEL_FREE_RUN BIT(2) 23 #define FLD_INTF_1_SW_TRG_MUX BIT(4) 24 #define FLD_INTF_2_SW_TRG_MUX BIT(8) 25 #define FLD_TE_LINE_INTER_WATERLEVEL_MASK 0xFFFF 26 27 #define DANGER_STATUS 0x360 28 #define SAFE_STATUS 0x364 29 30 #define TE_LINE_INTERVAL 0x3F4 31 32 #define TRAFFIC_SHAPER_EN BIT(31) 33 #define TRAFFIC_SHAPER_RD_CLIENT(num) (0x030 + (num * 4)) 34 #define TRAFFIC_SHAPER_WR_CLIENT(num) (0x060 + (num * 4)) 35 #define TRAFFIC_SHAPER_FIXPOINT_FACTOR 4 36 37 #define MDP_WD_TIMER_0_CTL 0x380 38 #define MDP_WD_TIMER_0_CTL2 0x384 39 #define MDP_WD_TIMER_0_LOAD_VALUE 0x388 40 #define MDP_WD_TIMER_1_CTL 0x390 41 #define MDP_WD_TIMER_1_CTL2 0x394 42 #define MDP_WD_TIMER_1_LOAD_VALUE 0x398 43 #define MDP_WD_TIMER_2_CTL 0x420 44 #define MDP_WD_TIMER_2_CTL2 0x424 45 #define MDP_WD_TIMER_2_LOAD_VALUE 0x428 46 #define MDP_WD_TIMER_3_CTL 0x430 47 #define MDP_WD_TIMER_3_CTL2 0x434 48 #define MDP_WD_TIMER_3_LOAD_VALUE 0x438 49 #define MDP_WD_TIMER_4_CTL 0x440 50 #define MDP_WD_TIMER_4_CTL2 0x444 51 #define MDP_WD_TIMER_4_LOAD_VALUE 0x448 52 53 #define MDP_TICK_COUNT 16 54 #define XO_CLK_RATE 19200 55 #define MS_TICKS_IN_SEC 1000 56 57 #define CALCULATE_WD_LOAD_VALUE(fps) \ 58 ((uint32_t)((MS_TICKS_IN_SEC * XO_CLK_RATE)/(MDP_TICK_COUNT * fps))) 59 60 #define DCE_SEL 0x450 61 62 static void dpu_hw_setup_split_pipe(struct dpu_hw_mdp *mdp, 63 struct split_pipe_cfg *cfg) 64 { 65 struct dpu_hw_blk_reg_map *c; 66 u32 upper_pipe = 0; 67 u32 lower_pipe = 0; 68 69 if (!mdp || !cfg) 70 return; 71 72 c = &mdp->hw; 73 74 if (cfg->en) { 75 if (cfg->mode == INTF_MODE_CMD) { 76 lower_pipe = FLD_SPLIT_DISPLAY_CMD; 77 /* interface controlling sw trigger */ 78 if (cfg->intf == INTF_2) 79 lower_pipe |= FLD_INTF_1_SW_TRG_MUX; 80 else 81 lower_pipe |= FLD_INTF_2_SW_TRG_MUX; 82 upper_pipe = lower_pipe; 83 } else { 84 if (cfg->intf == INTF_2) { 85 lower_pipe = FLD_INTF_1_SW_TRG_MUX; 86 upper_pipe = FLD_INTF_2_SW_TRG_MUX; 87 } else { 88 lower_pipe = FLD_INTF_2_SW_TRG_MUX; 89 upper_pipe = FLD_INTF_1_SW_TRG_MUX; 90 } 91 } 92 } 93 94 DPU_REG_WRITE(c, SSPP_SPARE, cfg->split_flush_en ? 0x1 : 0x0); 95 DPU_REG_WRITE(c, SPLIT_DISPLAY_LOWER_PIPE_CTRL, lower_pipe); 96 DPU_REG_WRITE(c, SPLIT_DISPLAY_UPPER_PIPE_CTRL, upper_pipe); 97 DPU_REG_WRITE(c, SPLIT_DISPLAY_EN, cfg->en & 0x1); 98 } 99 100 static bool dpu_hw_setup_clk_force_ctrl(struct dpu_hw_mdp *mdp, 101 enum dpu_clk_ctrl_type clk_ctrl, bool enable) 102 { 103 struct dpu_hw_blk_reg_map *c; 104 u32 reg_off, bit_off; 105 u32 reg_val, new_val; 106 bool clk_forced_on; 107 108 if (!mdp) 109 return false; 110 111 c = &mdp->hw; 112 113 if (clk_ctrl <= DPU_CLK_CTRL_NONE || clk_ctrl >= DPU_CLK_CTRL_MAX) 114 return false; 115 116 reg_off = mdp->caps->clk_ctrls[clk_ctrl].reg_off; 117 bit_off = mdp->caps->clk_ctrls[clk_ctrl].bit_off; 118 119 reg_val = DPU_REG_READ(c, reg_off); 120 121 if (enable) 122 new_val = reg_val | BIT(bit_off); 123 else 124 new_val = reg_val & ~BIT(bit_off); 125 126 DPU_REG_WRITE(c, reg_off, new_val); 127 128 clk_forced_on = !(reg_val & BIT(bit_off)); 129 130 return clk_forced_on; 131 } 132 133 134 static void dpu_hw_get_danger_status(struct dpu_hw_mdp *mdp, 135 struct dpu_danger_safe_status *status) 136 { 137 struct dpu_hw_blk_reg_map *c; 138 u32 value; 139 140 if (!mdp || !status) 141 return; 142 143 c = &mdp->hw; 144 145 value = DPU_REG_READ(c, DANGER_STATUS); 146 status->mdp = (value >> 0) & 0x3; 147 status->sspp[SSPP_VIG0] = (value >> 4) & 0x3; 148 status->sspp[SSPP_VIG1] = (value >> 6) & 0x3; 149 status->sspp[SSPP_VIG2] = (value >> 8) & 0x3; 150 status->sspp[SSPP_VIG3] = (value >> 10) & 0x3; 151 status->sspp[SSPP_RGB0] = (value >> 12) & 0x3; 152 status->sspp[SSPP_RGB1] = (value >> 14) & 0x3; 153 status->sspp[SSPP_RGB2] = (value >> 16) & 0x3; 154 status->sspp[SSPP_RGB3] = (value >> 18) & 0x3; 155 status->sspp[SSPP_DMA0] = (value >> 20) & 0x3; 156 status->sspp[SSPP_DMA1] = (value >> 22) & 0x3; 157 status->sspp[SSPP_DMA2] = (value >> 28) & 0x3; 158 status->sspp[SSPP_DMA3] = (value >> 30) & 0x3; 159 status->sspp[SSPP_CURSOR0] = (value >> 24) & 0x3; 160 status->sspp[SSPP_CURSOR1] = (value >> 26) & 0x3; 161 } 162 163 static void dpu_hw_setup_vsync_source(struct dpu_hw_mdp *mdp, 164 struct dpu_vsync_source_cfg *cfg) 165 { 166 struct dpu_hw_blk_reg_map *c; 167 u32 reg, wd_load_value, wd_ctl, wd_ctl2, i; 168 static const u32 pp_offset[PINGPONG_MAX] = {0xC, 0x8, 0x4, 0x13, 0x18}; 169 170 if (!mdp || !cfg || (cfg->pp_count > ARRAY_SIZE(cfg->ppnumber))) 171 return; 172 173 c = &mdp->hw; 174 reg = DPU_REG_READ(c, MDP_VSYNC_SEL); 175 for (i = 0; i < cfg->pp_count; i++) { 176 int pp_idx = cfg->ppnumber[i] - PINGPONG_0; 177 178 if (pp_idx >= ARRAY_SIZE(pp_offset)) 179 continue; 180 181 reg &= ~(0xf << pp_offset[pp_idx]); 182 reg |= (cfg->vsync_source & 0xf) << pp_offset[pp_idx]; 183 } 184 DPU_REG_WRITE(c, MDP_VSYNC_SEL, reg); 185 186 if (cfg->vsync_source >= DPU_VSYNC_SOURCE_WD_TIMER_4 && 187 cfg->vsync_source <= DPU_VSYNC_SOURCE_WD_TIMER_0) { 188 switch (cfg->vsync_source) { 189 case DPU_VSYNC_SOURCE_WD_TIMER_4: 190 wd_load_value = MDP_WD_TIMER_4_LOAD_VALUE; 191 wd_ctl = MDP_WD_TIMER_4_CTL; 192 wd_ctl2 = MDP_WD_TIMER_4_CTL2; 193 break; 194 case DPU_VSYNC_SOURCE_WD_TIMER_3: 195 wd_load_value = MDP_WD_TIMER_3_LOAD_VALUE; 196 wd_ctl = MDP_WD_TIMER_3_CTL; 197 wd_ctl2 = MDP_WD_TIMER_3_CTL2; 198 break; 199 case DPU_VSYNC_SOURCE_WD_TIMER_2: 200 wd_load_value = MDP_WD_TIMER_2_LOAD_VALUE; 201 wd_ctl = MDP_WD_TIMER_2_CTL; 202 wd_ctl2 = MDP_WD_TIMER_2_CTL2; 203 break; 204 case DPU_VSYNC_SOURCE_WD_TIMER_1: 205 wd_load_value = MDP_WD_TIMER_1_LOAD_VALUE; 206 wd_ctl = MDP_WD_TIMER_1_CTL; 207 wd_ctl2 = MDP_WD_TIMER_1_CTL2; 208 break; 209 case DPU_VSYNC_SOURCE_WD_TIMER_0: 210 default: 211 wd_load_value = MDP_WD_TIMER_0_LOAD_VALUE; 212 wd_ctl = MDP_WD_TIMER_0_CTL; 213 wd_ctl2 = MDP_WD_TIMER_0_CTL2; 214 break; 215 } 216 217 DPU_REG_WRITE(c, wd_load_value, 218 CALCULATE_WD_LOAD_VALUE(cfg->frame_rate)); 219 220 DPU_REG_WRITE(c, wd_ctl, BIT(0)); /* clear timer */ 221 reg = DPU_REG_READ(c, wd_ctl2); 222 reg |= BIT(8); /* enable heartbeat timer */ 223 reg |= BIT(0); /* enable WD timer */ 224 DPU_REG_WRITE(c, wd_ctl2, reg); 225 226 /* make sure that timers are enabled/disabled for vsync state */ 227 wmb(); 228 } 229 } 230 231 static void dpu_hw_get_safe_status(struct dpu_hw_mdp *mdp, 232 struct dpu_danger_safe_status *status) 233 { 234 struct dpu_hw_blk_reg_map *c; 235 u32 value; 236 237 if (!mdp || !status) 238 return; 239 240 c = &mdp->hw; 241 242 value = DPU_REG_READ(c, SAFE_STATUS); 243 status->mdp = (value >> 0) & 0x1; 244 status->sspp[SSPP_VIG0] = (value >> 4) & 0x1; 245 status->sspp[SSPP_VIG1] = (value >> 6) & 0x1; 246 status->sspp[SSPP_VIG2] = (value >> 8) & 0x1; 247 status->sspp[SSPP_VIG3] = (value >> 10) & 0x1; 248 status->sspp[SSPP_RGB0] = (value >> 12) & 0x1; 249 status->sspp[SSPP_RGB1] = (value >> 14) & 0x1; 250 status->sspp[SSPP_RGB2] = (value >> 16) & 0x1; 251 status->sspp[SSPP_RGB3] = (value >> 18) & 0x1; 252 status->sspp[SSPP_DMA0] = (value >> 20) & 0x1; 253 status->sspp[SSPP_DMA1] = (value >> 22) & 0x1; 254 status->sspp[SSPP_DMA2] = (value >> 28) & 0x1; 255 status->sspp[SSPP_DMA3] = (value >> 30) & 0x1; 256 status->sspp[SSPP_CURSOR0] = (value >> 24) & 0x1; 257 status->sspp[SSPP_CURSOR1] = (value >> 26) & 0x1; 258 } 259 260 static void dpu_hw_reset_ubwc(struct dpu_hw_mdp *mdp, struct dpu_mdss_cfg *m) 261 { 262 struct dpu_hw_blk_reg_map c; 263 264 if (!mdp || !m) 265 return; 266 267 if (!IS_UBWC_20_SUPPORTED(m->caps->ubwc_version)) 268 return; 269 270 /* force blk offset to zero to access beginning of register region */ 271 c = mdp->hw; 272 c.blk_off = 0x0; 273 DPU_REG_WRITE(&c, UBWC_STATIC, m->mdp[0].ubwc_static); 274 } 275 276 static void dpu_hw_intf_audio_select(struct dpu_hw_mdp *mdp) 277 { 278 struct dpu_hw_blk_reg_map *c; 279 280 if (!mdp) 281 return; 282 283 c = &mdp->hw; 284 285 DPU_REG_WRITE(c, HDMI_DP_CORE_SELECT, 0x1); 286 } 287 288 static void _setup_mdp_ops(struct dpu_hw_mdp_ops *ops, 289 unsigned long cap) 290 { 291 ops->setup_split_pipe = dpu_hw_setup_split_pipe; 292 ops->setup_clk_force_ctrl = dpu_hw_setup_clk_force_ctrl; 293 ops->get_danger_status = dpu_hw_get_danger_status; 294 ops->setup_vsync_source = dpu_hw_setup_vsync_source; 295 ops->get_safe_status = dpu_hw_get_safe_status; 296 ops->reset_ubwc = dpu_hw_reset_ubwc; 297 ops->intf_audio_select = dpu_hw_intf_audio_select; 298 } 299 300 static const struct dpu_mdp_cfg *_top_offset(enum dpu_mdp mdp, 301 const struct dpu_mdss_cfg *m, 302 void __iomem *addr, 303 struct dpu_hw_blk_reg_map *b) 304 { 305 int i; 306 307 if (!m || !addr || !b) 308 return ERR_PTR(-EINVAL); 309 310 for (i = 0; i < m->mdp_count; i++) { 311 if (mdp == m->mdp[i].id) { 312 b->base_off = addr; 313 b->blk_off = m->mdp[i].base; 314 b->length = m->mdp[i].len; 315 b->hwversion = m->hwversion; 316 b->log_mask = DPU_DBG_MASK_TOP; 317 return &m->mdp[i]; 318 } 319 } 320 321 return ERR_PTR(-EINVAL); 322 } 323 324 static struct dpu_hw_blk_ops dpu_hw_ops; 325 326 struct dpu_hw_mdp *dpu_hw_mdptop_init(enum dpu_mdp idx, 327 void __iomem *addr, 328 const struct dpu_mdss_cfg *m) 329 { 330 struct dpu_hw_mdp *mdp; 331 const struct dpu_mdp_cfg *cfg; 332 333 if (!addr || !m) 334 return ERR_PTR(-EINVAL); 335 336 mdp = kzalloc(sizeof(*mdp), GFP_KERNEL); 337 if (!mdp) 338 return ERR_PTR(-ENOMEM); 339 340 cfg = _top_offset(idx, m, addr, &mdp->hw); 341 if (IS_ERR_OR_NULL(cfg)) { 342 kfree(mdp); 343 return ERR_PTR(-EINVAL); 344 } 345 346 /* 347 * Assign ops 348 */ 349 mdp->idx = idx; 350 mdp->caps = cfg; 351 _setup_mdp_ops(&mdp->ops, mdp->caps->features); 352 353 dpu_hw_blk_init(&mdp->base, DPU_HW_BLK_TOP, idx, &dpu_hw_ops); 354 355 return mdp; 356 } 357 358 void dpu_hw_mdp_destroy(struct dpu_hw_mdp *mdp) 359 { 360 if (mdp) 361 dpu_hw_blk_destroy(&mdp->base); 362 kfree(mdp); 363 } 364 365