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