dpu_hw_pingpong.c (8177B)
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 "dpu_hw_mdss.h" 8#include "dpu_hwio.h" 9#include "dpu_hw_catalog.h" 10#include "dpu_hw_pingpong.h" 11#include "dpu_kms.h" 12#include "dpu_trace.h" 13 14#define PP_TEAR_CHECK_EN 0x000 15#define PP_SYNC_CONFIG_VSYNC 0x004 16#define PP_SYNC_CONFIG_HEIGHT 0x008 17#define PP_SYNC_WRCOUNT 0x00C 18#define PP_VSYNC_INIT_VAL 0x010 19#define PP_INT_COUNT_VAL 0x014 20#define PP_SYNC_THRESH 0x018 21#define PP_START_POS 0x01C 22#define PP_RD_PTR_IRQ 0x020 23#define PP_WR_PTR_IRQ 0x024 24#define PP_OUT_LINE_COUNT 0x028 25#define PP_LINE_COUNT 0x02C 26#define PP_AUTOREFRESH_CONFIG 0x030 27 28#define PP_FBC_MODE 0x034 29#define PP_FBC_BUDGET_CTL 0x038 30#define PP_FBC_LOSSY_MODE 0x03C 31#define PP_DSC_MODE 0x0a0 32#define PP_DCE_DATA_IN_SWAP 0x0ac 33#define PP_DCE_DATA_OUT_SWAP 0x0c8 34 35#define PP_DITHER_EN 0x000 36#define PP_DITHER_BITDEPTH 0x004 37#define PP_DITHER_MATRIX 0x008 38 39#define DITHER_DEPTH_MAP_INDEX 9 40 41static u32 dither_depth_map[DITHER_DEPTH_MAP_INDEX] = { 42 0, 0, 0, 0, 0, 0, 0, 1, 2 43}; 44 45static const struct dpu_pingpong_cfg *_pingpong_offset(enum dpu_pingpong pp, 46 const struct dpu_mdss_cfg *m, 47 void __iomem *addr, 48 struct dpu_hw_blk_reg_map *b) 49{ 50 int i; 51 52 for (i = 0; i < m->pingpong_count; i++) { 53 if (pp == m->pingpong[i].id) { 54 b->base_off = addr; 55 b->blk_off = m->pingpong[i].base; 56 b->length = m->pingpong[i].len; 57 b->hwversion = m->hwversion; 58 b->log_mask = DPU_DBG_MASK_PINGPONG; 59 return &m->pingpong[i]; 60 } 61 } 62 63 return ERR_PTR(-EINVAL); 64} 65 66static void dpu_hw_pp_setup_dither(struct dpu_hw_pingpong *pp, 67 struct dpu_hw_dither_cfg *cfg) 68{ 69 struct dpu_hw_blk_reg_map *c; 70 u32 i, base, data = 0; 71 72 c = &pp->hw; 73 base = pp->caps->sblk->dither.base; 74 if (!cfg) { 75 DPU_REG_WRITE(c, base + PP_DITHER_EN, 0); 76 return; 77 } 78 79 data = dither_depth_map[cfg->c0_bitdepth] & REG_MASK(2); 80 data |= (dither_depth_map[cfg->c1_bitdepth] & REG_MASK(2)) << 2; 81 data |= (dither_depth_map[cfg->c2_bitdepth] & REG_MASK(2)) << 4; 82 data |= (dither_depth_map[cfg->c3_bitdepth] & REG_MASK(2)) << 6; 83 data |= (cfg->temporal_en) ? (1 << 8) : 0; 84 85 DPU_REG_WRITE(c, base + PP_DITHER_BITDEPTH, data); 86 87 for (i = 0; i < DITHER_MATRIX_SZ - 3; i += 4) { 88 data = (cfg->matrix[i] & REG_MASK(4)) | 89 ((cfg->matrix[i + 1] & REG_MASK(4)) << 4) | 90 ((cfg->matrix[i + 2] & REG_MASK(4)) << 8) | 91 ((cfg->matrix[i + 3] & REG_MASK(4)) << 12); 92 DPU_REG_WRITE(c, base + PP_DITHER_MATRIX + i, data); 93 } 94 DPU_REG_WRITE(c, base + PP_DITHER_EN, 1); 95} 96 97static int dpu_hw_pp_setup_te_config(struct dpu_hw_pingpong *pp, 98 struct dpu_hw_tear_check *te) 99{ 100 struct dpu_hw_blk_reg_map *c; 101 int cfg; 102 103 if (!pp || !te) 104 return -EINVAL; 105 c = &pp->hw; 106 107 cfg = BIT(19); /*VSYNC_COUNTER_EN */ 108 if (te->hw_vsync_mode) 109 cfg |= BIT(20); 110 111 cfg |= te->vsync_count; 112 113 DPU_REG_WRITE(c, PP_SYNC_CONFIG_VSYNC, cfg); 114 DPU_REG_WRITE(c, PP_SYNC_CONFIG_HEIGHT, te->sync_cfg_height); 115 DPU_REG_WRITE(c, PP_VSYNC_INIT_VAL, te->vsync_init_val); 116 DPU_REG_WRITE(c, PP_RD_PTR_IRQ, te->rd_ptr_irq); 117 DPU_REG_WRITE(c, PP_START_POS, te->start_pos); 118 DPU_REG_WRITE(c, PP_SYNC_THRESH, 119 ((te->sync_threshold_continue << 16) | 120 te->sync_threshold_start)); 121 DPU_REG_WRITE(c, PP_SYNC_WRCOUNT, 122 (te->start_pos + te->sync_threshold_start + 1)); 123 124 return 0; 125} 126 127static void dpu_hw_pp_setup_autorefresh_config(struct dpu_hw_pingpong *pp, 128 u32 frame_count, bool enable) 129{ 130 DPU_REG_WRITE(&pp->hw, PP_AUTOREFRESH_CONFIG, 131 enable ? (BIT(31) | frame_count) : 0); 132} 133 134/* 135 * dpu_hw_pp_get_autorefresh_config - Get autorefresh config from HW 136 * @pp: DPU pingpong structure 137 * @frame_count: Used to return the current frame count from hw 138 * 139 * Returns: True if autorefresh enabled, false if disabled. 140 */ 141static bool dpu_hw_pp_get_autorefresh_config(struct dpu_hw_pingpong *pp, 142 u32 *frame_count) 143{ 144 u32 val = DPU_REG_READ(&pp->hw, PP_AUTOREFRESH_CONFIG); 145 if (frame_count != NULL) 146 *frame_count = val & 0xffff; 147 return !!((val & BIT(31)) >> 31); 148} 149 150static int dpu_hw_pp_poll_timeout_wr_ptr(struct dpu_hw_pingpong *pp, 151 u32 timeout_us) 152{ 153 struct dpu_hw_blk_reg_map *c; 154 u32 val; 155 int rc; 156 157 if (!pp) 158 return -EINVAL; 159 160 c = &pp->hw; 161 rc = readl_poll_timeout(c->base_off + c->blk_off + PP_LINE_COUNT, 162 val, (val & 0xffff) >= 1, 10, timeout_us); 163 164 return rc; 165} 166 167static int dpu_hw_pp_enable_te(struct dpu_hw_pingpong *pp, bool enable) 168{ 169 struct dpu_hw_blk_reg_map *c; 170 171 if (!pp) 172 return -EINVAL; 173 c = &pp->hw; 174 175 DPU_REG_WRITE(c, PP_TEAR_CHECK_EN, enable); 176 return 0; 177} 178 179static int dpu_hw_pp_connect_external_te(struct dpu_hw_pingpong *pp, 180 bool enable_external_te) 181{ 182 struct dpu_hw_blk_reg_map *c = &pp->hw; 183 u32 cfg; 184 int orig; 185 186 if (!pp) 187 return -EINVAL; 188 189 c = &pp->hw; 190 cfg = DPU_REG_READ(c, PP_SYNC_CONFIG_VSYNC); 191 orig = (bool)(cfg & BIT(20)); 192 if (enable_external_te) 193 cfg |= BIT(20); 194 else 195 cfg &= ~BIT(20); 196 DPU_REG_WRITE(c, PP_SYNC_CONFIG_VSYNC, cfg); 197 trace_dpu_pp_connect_ext_te(pp->idx - PINGPONG_0, cfg); 198 199 return orig; 200} 201 202static int dpu_hw_pp_get_vsync_info(struct dpu_hw_pingpong *pp, 203 struct dpu_hw_pp_vsync_info *info) 204{ 205 struct dpu_hw_blk_reg_map *c; 206 u32 val; 207 208 if (!pp || !info) 209 return -EINVAL; 210 c = &pp->hw; 211 212 val = DPU_REG_READ(c, PP_VSYNC_INIT_VAL); 213 info->rd_ptr_init_val = val & 0xffff; 214 215 val = DPU_REG_READ(c, PP_INT_COUNT_VAL); 216 info->rd_ptr_frame_count = (val & 0xffff0000) >> 16; 217 info->rd_ptr_line_count = val & 0xffff; 218 219 val = DPU_REG_READ(c, PP_LINE_COUNT); 220 info->wr_ptr_line_count = val & 0xffff; 221 222 return 0; 223} 224 225static u32 dpu_hw_pp_get_line_count(struct dpu_hw_pingpong *pp) 226{ 227 struct dpu_hw_blk_reg_map *c = &pp->hw; 228 u32 height, init; 229 u32 line = 0xFFFF; 230 231 if (!pp) 232 return 0; 233 c = &pp->hw; 234 235 init = DPU_REG_READ(c, PP_VSYNC_INIT_VAL) & 0xFFFF; 236 height = DPU_REG_READ(c, PP_SYNC_CONFIG_HEIGHT) & 0xFFFF; 237 238 if (height < init) 239 return line; 240 241 line = DPU_REG_READ(c, PP_INT_COUNT_VAL) & 0xFFFF; 242 243 if (line < init) 244 line += (0xFFFF - init); 245 else 246 line -= init; 247 248 return line; 249} 250 251static int dpu_hw_pp_dsc_enable(struct dpu_hw_pingpong *pp) 252{ 253 struct dpu_hw_blk_reg_map *c = &pp->hw; 254 255 DPU_REG_WRITE(c, PP_DSC_MODE, 1); 256 return 0; 257} 258 259static void dpu_hw_pp_dsc_disable(struct dpu_hw_pingpong *pp) 260{ 261 struct dpu_hw_blk_reg_map *c = &pp->hw; 262 263 DPU_REG_WRITE(c, PP_DSC_MODE, 0); 264} 265 266static int dpu_hw_pp_setup_dsc(struct dpu_hw_pingpong *pp) 267{ 268 struct dpu_hw_blk_reg_map *pp_c = &pp->hw; 269 int data; 270 271 data = DPU_REG_READ(pp_c, PP_DCE_DATA_OUT_SWAP); 272 data |= BIT(18); /* endian flip */ 273 DPU_REG_WRITE(pp_c, PP_DCE_DATA_OUT_SWAP, data); 274 return 0; 275} 276 277static void _setup_pingpong_ops(struct dpu_hw_pingpong *c, 278 unsigned long features) 279{ 280 c->ops.setup_tearcheck = dpu_hw_pp_setup_te_config; 281 c->ops.enable_tearcheck = dpu_hw_pp_enable_te; 282 c->ops.connect_external_te = dpu_hw_pp_connect_external_te; 283 c->ops.get_vsync_info = dpu_hw_pp_get_vsync_info; 284 c->ops.setup_autorefresh = dpu_hw_pp_setup_autorefresh_config; 285 c->ops.get_autorefresh = dpu_hw_pp_get_autorefresh_config; 286 c->ops.poll_timeout_wr_ptr = dpu_hw_pp_poll_timeout_wr_ptr; 287 c->ops.get_line_count = dpu_hw_pp_get_line_count; 288 c->ops.setup_dsc = dpu_hw_pp_setup_dsc; 289 c->ops.enable_dsc = dpu_hw_pp_dsc_enable; 290 c->ops.disable_dsc = dpu_hw_pp_dsc_disable; 291 292 if (test_bit(DPU_PINGPONG_DITHER, &features)) 293 c->ops.setup_dither = dpu_hw_pp_setup_dither; 294}; 295 296struct dpu_hw_pingpong *dpu_hw_pingpong_init(enum dpu_pingpong idx, 297 void __iomem *addr, 298 const struct dpu_mdss_cfg *m) 299{ 300 struct dpu_hw_pingpong *c; 301 const struct dpu_pingpong_cfg *cfg; 302 303 c = kzalloc(sizeof(*c), GFP_KERNEL); 304 if (!c) 305 return ERR_PTR(-ENOMEM); 306 307 cfg = _pingpong_offset(idx, m, addr, &c->hw); 308 if (IS_ERR_OR_NULL(cfg)) { 309 kfree(c); 310 return ERR_PTR(-EINVAL); 311 } 312 313 c->idx = idx; 314 c->caps = cfg; 315 _setup_pingpong_ops(c, c->caps->features); 316 317 return c; 318} 319 320void dpu_hw_pingpong_destroy(struct dpu_hw_pingpong *pp) 321{ 322 kfree(pp); 323}