1 // SPDX-License-Identifier: GPL-2.0-only 2 /* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. 3 */ 4 5 #include <linux/iopoll.h> 6 7 #include <drm/drm_managed.h> 8 9 #include "dpu_hw_mdss.h" 10 #include "dpu_hwio.h" 11 #include "dpu_hw_catalog.h" 12 #include "dpu_hw_pingpong.h" 13 #include "dpu_kms.h" 14 #include "dpu_trace.h" 15 16 #define PP_TEAR_CHECK_EN 0x000 17 #define PP_SYNC_CONFIG_VSYNC 0x004 18 #define PP_SYNC_CONFIG_HEIGHT 0x008 19 #define PP_SYNC_WRCOUNT 0x00C 20 #define PP_VSYNC_INIT_VAL 0x010 21 #define PP_INT_COUNT_VAL 0x014 22 #define PP_SYNC_THRESH 0x018 23 #define PP_START_POS 0x01C 24 #define PP_RD_PTR_IRQ 0x020 25 #define PP_WR_PTR_IRQ 0x024 26 #define PP_OUT_LINE_COUNT 0x028 27 #define PP_LINE_COUNT 0x02C 28 #define PP_AUTOREFRESH_CONFIG 0x030 29 30 #define PP_FBC_MODE 0x034 31 #define PP_FBC_BUDGET_CTL 0x038 32 #define PP_FBC_LOSSY_MODE 0x03C 33 #define PP_DSC_MODE 0x0a0 34 #define PP_DCE_DATA_IN_SWAP 0x0ac 35 #define PP_DCE_DATA_OUT_SWAP 0x0c8 36 37 #define PP_DITHER_EN 0x000 38 #define PP_DITHER_BITDEPTH 0x004 39 #define PP_DITHER_MATRIX 0x008 40 41 #define DITHER_DEPTH_MAP_INDEX 9 42 43 static u32 dither_depth_map[DITHER_DEPTH_MAP_INDEX] = { 44 0, 0, 0, 0, 0, 0, 0, 1, 2 45 }; 46 47 static void dpu_hw_pp_setup_dither(struct dpu_hw_pingpong *pp, 48 struct dpu_hw_dither_cfg *cfg) 49 { 50 struct dpu_hw_blk_reg_map *c; 51 u32 i, base, data = 0; 52 53 c = &pp->hw; 54 base = pp->caps->sblk->dither.base; 55 if (!cfg) { 56 DPU_REG_WRITE(c, base + PP_DITHER_EN, 0); 57 return; 58 } 59 60 data = dither_depth_map[cfg->c0_bitdepth] & REG_MASK(2); 61 data |= (dither_depth_map[cfg->c1_bitdepth] & REG_MASK(2)) << 2; 62 data |= (dither_depth_map[cfg->c2_bitdepth] & REG_MASK(2)) << 4; 63 data |= (dither_depth_map[cfg->c3_bitdepth] & REG_MASK(2)) << 6; 64 data |= (cfg->temporal_en) ? (1 << 8) : 0; 65 66 DPU_REG_WRITE(c, base + PP_DITHER_BITDEPTH, data); 67 68 for (i = 0; i < DITHER_MATRIX_SZ - 3; i += 4) { 69 data = (cfg->matrix[i] & REG_MASK(4)) | 70 ((cfg->matrix[i + 1] & REG_MASK(4)) << 4) | 71 ((cfg->matrix[i + 2] & REG_MASK(4)) << 8) | 72 ((cfg->matrix[i + 3] & REG_MASK(4)) << 12); 73 DPU_REG_WRITE(c, base + PP_DITHER_MATRIX + i, data); 74 } 75 DPU_REG_WRITE(c, base + PP_DITHER_EN, 1); 76 } 77 78 static int dpu_hw_pp_enable_te(struct dpu_hw_pingpong *pp, 79 struct dpu_hw_tear_check *te) 80 { 81 struct dpu_hw_blk_reg_map *c; 82 int cfg; 83 84 if (!pp || !te) 85 return -EINVAL; 86 c = &pp->hw; 87 88 cfg = BIT(19); /*VSYNC_COUNTER_EN */ 89 if (te->hw_vsync_mode) 90 cfg |= BIT(20); 91 92 cfg |= te->vsync_count; 93 94 DPU_REG_WRITE(c, PP_SYNC_CONFIG_VSYNC, cfg); 95 DPU_REG_WRITE(c, PP_SYNC_CONFIG_HEIGHT, te->sync_cfg_height); 96 DPU_REG_WRITE(c, PP_VSYNC_INIT_VAL, te->vsync_init_val); 97 DPU_REG_WRITE(c, PP_RD_PTR_IRQ, te->rd_ptr_irq); 98 DPU_REG_WRITE(c, PP_START_POS, te->start_pos); 99 DPU_REG_WRITE(c, PP_SYNC_THRESH, 100 ((te->sync_threshold_continue << 16) | 101 te->sync_threshold_start)); 102 DPU_REG_WRITE(c, PP_SYNC_WRCOUNT, 103 (te->start_pos + te->sync_threshold_start + 1)); 104 105 DPU_REG_WRITE(c, PP_TEAR_CHECK_EN, 1); 106 107 return 0; 108 } 109 110 static void dpu_hw_pp_setup_autorefresh_config(struct dpu_hw_pingpong *pp, 111 u32 frame_count, bool enable) 112 { 113 DPU_REG_WRITE(&pp->hw, PP_AUTOREFRESH_CONFIG, 114 enable ? (BIT(31) | frame_count) : 0); 115 } 116 117 /* 118 * dpu_hw_pp_get_autorefresh_config - Get autorefresh config from HW 119 * @pp: DPU pingpong structure 120 * @frame_count: Used to return the current frame count from hw 121 * 122 * Returns: True if autorefresh enabled, false if disabled. 123 */ 124 static bool dpu_hw_pp_get_autorefresh_config(struct dpu_hw_pingpong *pp, 125 u32 *frame_count) 126 { 127 u32 val = DPU_REG_READ(&pp->hw, PP_AUTOREFRESH_CONFIG); 128 if (frame_count != NULL) 129 *frame_count = val & 0xffff; 130 return !!((val & BIT(31)) >> 31); 131 } 132 133 static int dpu_hw_pp_disable_te(struct dpu_hw_pingpong *pp) 134 { 135 struct dpu_hw_blk_reg_map *c; 136 137 if (!pp) 138 return -EINVAL; 139 c = &pp->hw; 140 141 DPU_REG_WRITE(c, PP_TEAR_CHECK_EN, 0); 142 return 0; 143 } 144 145 static int dpu_hw_pp_connect_external_te(struct dpu_hw_pingpong *pp, 146 bool enable_external_te) 147 { 148 struct dpu_hw_blk_reg_map *c = &pp->hw; 149 u32 cfg; 150 int orig; 151 152 if (!pp) 153 return -EINVAL; 154 155 c = &pp->hw; 156 cfg = DPU_REG_READ(c, PP_SYNC_CONFIG_VSYNC); 157 orig = (bool)(cfg & BIT(20)); 158 if (enable_external_te) 159 cfg |= BIT(20); 160 else 161 cfg &= ~BIT(20); 162 DPU_REG_WRITE(c, PP_SYNC_CONFIG_VSYNC, cfg); 163 trace_dpu_pp_connect_ext_te(pp->idx - PINGPONG_0, cfg); 164 165 return orig; 166 } 167 168 static int dpu_hw_pp_get_vsync_info(struct dpu_hw_pingpong *pp, 169 struct dpu_hw_pp_vsync_info *info) 170 { 171 struct dpu_hw_blk_reg_map *c; 172 u32 val; 173 174 if (!pp || !info) 175 return -EINVAL; 176 c = &pp->hw; 177 178 val = DPU_REG_READ(c, PP_VSYNC_INIT_VAL); 179 info->rd_ptr_init_val = val & 0xffff; 180 181 val = DPU_REG_READ(c, PP_INT_COUNT_VAL); 182 info->rd_ptr_frame_count = (val & 0xffff0000) >> 16; 183 info->rd_ptr_line_count = val & 0xffff; 184 185 val = DPU_REG_READ(c, PP_LINE_COUNT); 186 info->wr_ptr_line_count = val & 0xffff; 187 188 return 0; 189 } 190 191 static u32 dpu_hw_pp_get_line_count(struct dpu_hw_pingpong *pp) 192 { 193 struct dpu_hw_blk_reg_map *c = &pp->hw; 194 u32 height, init; 195 u32 line = 0xFFFF; 196 197 if (!pp) 198 return 0; 199 c = &pp->hw; 200 201 init = DPU_REG_READ(c, PP_VSYNC_INIT_VAL) & 0xFFFF; 202 height = DPU_REG_READ(c, PP_SYNC_CONFIG_HEIGHT) & 0xFFFF; 203 204 if (height < init) 205 return line; 206 207 line = DPU_REG_READ(c, PP_INT_COUNT_VAL) & 0xFFFF; 208 209 if (line < init) 210 line += (0xFFFF - init); 211 else 212 line -= init; 213 214 return line; 215 } 216 217 static void dpu_hw_pp_disable_autorefresh(struct dpu_hw_pingpong *pp, 218 uint32_t encoder_id, u16 vdisplay) 219 { 220 struct dpu_hw_pp_vsync_info info; 221 int trial = 0; 222 223 /* If autorefresh is already disabled, we have nothing to do */ 224 if (!dpu_hw_pp_get_autorefresh_config(pp, NULL)) 225 return; 226 227 /* 228 * If autorefresh is enabled, disable it and make sure it is safe to 229 * proceed with current frame commit/push. Sequence followed is, 230 * 1. Disable TE 231 * 2. Disable autorefresh config 232 * 4. Poll for frame transfer ongoing to be false 233 * 5. Enable TE back 234 */ 235 236 dpu_hw_pp_connect_external_te(pp, false); 237 dpu_hw_pp_setup_autorefresh_config(pp, 0, false); 238 239 do { 240 udelay(DPU_ENC_MAX_POLL_TIMEOUT_US); 241 if ((trial * DPU_ENC_MAX_POLL_TIMEOUT_US) 242 > (KICKOFF_TIMEOUT_MS * USEC_PER_MSEC)) { 243 DPU_ERROR("enc%d pp%d disable autorefresh failed\n", 244 encoder_id, pp->idx - PINGPONG_0); 245 break; 246 } 247 248 trial++; 249 250 dpu_hw_pp_get_vsync_info(pp, &info); 251 } while (info.wr_ptr_line_count > 0 && 252 info.wr_ptr_line_count < vdisplay); 253 254 dpu_hw_pp_connect_external_te(pp, true); 255 256 DPU_DEBUG("enc%d pp%d disabled autorefresh\n", 257 encoder_id, pp->idx - PINGPONG_0); 258 } 259 260 static int dpu_hw_pp_dsc_enable(struct dpu_hw_pingpong *pp) 261 { 262 struct dpu_hw_blk_reg_map *c = &pp->hw; 263 264 DPU_REG_WRITE(c, PP_DSC_MODE, 1); 265 return 0; 266 } 267 268 static void dpu_hw_pp_dsc_disable(struct dpu_hw_pingpong *pp) 269 { 270 struct dpu_hw_blk_reg_map *c = &pp->hw; 271 272 DPU_REG_WRITE(c, PP_DSC_MODE, 0); 273 } 274 275 static int dpu_hw_pp_setup_dsc(struct dpu_hw_pingpong *pp) 276 { 277 struct dpu_hw_blk_reg_map *pp_c = &pp->hw; 278 int data; 279 280 data = DPU_REG_READ(pp_c, PP_DCE_DATA_OUT_SWAP); 281 data |= BIT(18); /* endian flip */ 282 DPU_REG_WRITE(pp_c, PP_DCE_DATA_OUT_SWAP, data); 283 return 0; 284 } 285 286 /** 287 * dpu_hw_pingpong_init() - initializes the pingpong driver for the passed 288 * pingpong catalog entry. 289 * @dev: Corresponding device for devres management 290 * @cfg: Pingpong catalog entry for which driver object is required 291 * @addr: Mapped register io address of MDP 292 * @mdss_rev: dpu core's major and minor versions 293 * Return: Error code or allocated dpu_hw_pingpong context 294 */ 295 struct dpu_hw_pingpong *dpu_hw_pingpong_init(struct drm_device *dev, 296 const struct dpu_pingpong_cfg *cfg, 297 void __iomem *addr, 298 const struct dpu_mdss_version *mdss_rev) 299 { 300 struct dpu_hw_pingpong *c; 301 302 c = drmm_kzalloc(dev, sizeof(*c), GFP_KERNEL); 303 if (!c) 304 return ERR_PTR(-ENOMEM); 305 306 c->hw.blk_addr = addr + cfg->base; 307 c->hw.log_mask = DPU_DBG_MASK_PINGPONG; 308 309 c->idx = cfg->id; 310 c->caps = cfg; 311 312 if (mdss_rev->core_major_ver < 5) { 313 WARN_ON(!cfg->intr_rdptr); 314 315 c->ops.enable_tearcheck = dpu_hw_pp_enable_te; 316 c->ops.disable_tearcheck = dpu_hw_pp_disable_te; 317 c->ops.connect_external_te = dpu_hw_pp_connect_external_te; 318 c->ops.get_line_count = dpu_hw_pp_get_line_count; 319 c->ops.disable_autorefresh = dpu_hw_pp_disable_autorefresh; 320 } 321 322 if (test_bit(DPU_PINGPONG_DSC, &cfg->features)) { 323 c->ops.setup_dsc = dpu_hw_pp_setup_dsc; 324 c->ops.enable_dsc = dpu_hw_pp_dsc_enable; 325 c->ops.disable_dsc = dpu_hw_pp_dsc_disable; 326 } 327 328 if (test_bit(DPU_PINGPONG_DITHER, &cfg->features)) 329 c->ops.setup_dither = dpu_hw_pp_setup_dither; 330 331 return c; 332 } 333