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 27 #define PP_FBC_MODE 0x034 28 #define PP_FBC_BUDGET_CTL 0x038 29 #define PP_FBC_LOSSY_MODE 0x03C 30 31 static struct dpu_pingpong_cfg *_pingpong_offset(enum dpu_pingpong pp, 32 struct dpu_mdss_cfg *m, 33 void __iomem *addr, 34 struct dpu_hw_blk_reg_map *b) 35 { 36 int i; 37 38 for (i = 0; i < m->pingpong_count; i++) { 39 if (pp == m->pingpong[i].id) { 40 b->base_off = addr; 41 b->blk_off = m->pingpong[i].base; 42 b->length = m->pingpong[i].len; 43 b->hwversion = m->hwversion; 44 b->log_mask = DPU_DBG_MASK_PINGPONG; 45 return &m->pingpong[i]; 46 } 47 } 48 49 return ERR_PTR(-EINVAL); 50 } 51 52 static int dpu_hw_pp_setup_te_config(struct dpu_hw_pingpong *pp, 53 struct dpu_hw_tear_check *te) 54 { 55 struct dpu_hw_blk_reg_map *c; 56 int cfg; 57 58 if (!pp || !te) 59 return -EINVAL; 60 c = &pp->hw; 61 62 cfg = BIT(19); /*VSYNC_COUNTER_EN */ 63 if (te->hw_vsync_mode) 64 cfg |= BIT(20); 65 66 cfg |= te->vsync_count; 67 68 DPU_REG_WRITE(c, PP_SYNC_CONFIG_VSYNC, cfg); 69 DPU_REG_WRITE(c, PP_SYNC_CONFIG_HEIGHT, te->sync_cfg_height); 70 DPU_REG_WRITE(c, PP_VSYNC_INIT_VAL, te->vsync_init_val); 71 DPU_REG_WRITE(c, PP_RD_PTR_IRQ, te->rd_ptr_irq); 72 DPU_REG_WRITE(c, PP_START_POS, te->start_pos); 73 DPU_REG_WRITE(c, PP_SYNC_THRESH, 74 ((te->sync_threshold_continue << 16) | 75 te->sync_threshold_start)); 76 DPU_REG_WRITE(c, PP_SYNC_WRCOUNT, 77 (te->start_pos + te->sync_threshold_start + 1)); 78 79 return 0; 80 } 81 82 static int dpu_hw_pp_poll_timeout_wr_ptr(struct dpu_hw_pingpong *pp, 83 u32 timeout_us) 84 { 85 struct dpu_hw_blk_reg_map *c; 86 u32 val; 87 int rc; 88 89 if (!pp) 90 return -EINVAL; 91 92 c = &pp->hw; 93 rc = readl_poll_timeout(c->base_off + c->blk_off + PP_LINE_COUNT, 94 val, (val & 0xffff) >= 1, 10, timeout_us); 95 96 return rc; 97 } 98 99 static int dpu_hw_pp_enable_te(struct dpu_hw_pingpong *pp, bool enable) 100 { 101 struct dpu_hw_blk_reg_map *c; 102 103 if (!pp) 104 return -EINVAL; 105 c = &pp->hw; 106 107 DPU_REG_WRITE(c, PP_TEAR_CHECK_EN, enable); 108 return 0; 109 } 110 111 static int dpu_hw_pp_connect_external_te(struct dpu_hw_pingpong *pp, 112 bool enable_external_te) 113 { 114 struct dpu_hw_blk_reg_map *c = &pp->hw; 115 u32 cfg; 116 int orig; 117 118 if (!pp) 119 return -EINVAL; 120 121 c = &pp->hw; 122 cfg = DPU_REG_READ(c, PP_SYNC_CONFIG_VSYNC); 123 orig = (bool)(cfg & BIT(20)); 124 if (enable_external_te) 125 cfg |= BIT(20); 126 else 127 cfg &= ~BIT(20); 128 DPU_REG_WRITE(c, PP_SYNC_CONFIG_VSYNC, cfg); 129 trace_dpu_pp_connect_ext_te(pp->idx - PINGPONG_0, cfg); 130 131 return orig; 132 } 133 134 static int dpu_hw_pp_get_vsync_info(struct dpu_hw_pingpong *pp, 135 struct dpu_hw_pp_vsync_info *info) 136 { 137 struct dpu_hw_blk_reg_map *c; 138 u32 val; 139 140 if (!pp || !info) 141 return -EINVAL; 142 c = &pp->hw; 143 144 val = DPU_REG_READ(c, PP_VSYNC_INIT_VAL); 145 info->rd_ptr_init_val = val & 0xffff; 146 147 val = DPU_REG_READ(c, PP_INT_COUNT_VAL); 148 info->rd_ptr_frame_count = (val & 0xffff0000) >> 16; 149 info->rd_ptr_line_count = val & 0xffff; 150 151 val = DPU_REG_READ(c, PP_LINE_COUNT); 152 info->wr_ptr_line_count = val & 0xffff; 153 154 return 0; 155 } 156 157 static u32 dpu_hw_pp_get_line_count(struct dpu_hw_pingpong *pp) 158 { 159 struct dpu_hw_blk_reg_map *c = &pp->hw; 160 u32 height, init; 161 u32 line = 0xFFFF; 162 163 if (!pp) 164 return 0; 165 c = &pp->hw; 166 167 init = DPU_REG_READ(c, PP_VSYNC_INIT_VAL) & 0xFFFF; 168 height = DPU_REG_READ(c, PP_SYNC_CONFIG_HEIGHT) & 0xFFFF; 169 170 if (height < init) 171 return line; 172 173 line = DPU_REG_READ(c, PP_INT_COUNT_VAL) & 0xFFFF; 174 175 if (line < init) 176 line += (0xFFFF - init); 177 else 178 line -= init; 179 180 return line; 181 } 182 183 static void _setup_pingpong_ops(struct dpu_hw_pingpong_ops *ops, 184 const struct dpu_pingpong_cfg *hw_cap) 185 { 186 ops->setup_tearcheck = dpu_hw_pp_setup_te_config; 187 ops->enable_tearcheck = dpu_hw_pp_enable_te; 188 ops->connect_external_te = dpu_hw_pp_connect_external_te; 189 ops->get_vsync_info = dpu_hw_pp_get_vsync_info; 190 ops->poll_timeout_wr_ptr = dpu_hw_pp_poll_timeout_wr_ptr; 191 ops->get_line_count = dpu_hw_pp_get_line_count; 192 }; 193 194 static struct dpu_hw_blk_ops dpu_hw_ops; 195 196 struct dpu_hw_pingpong *dpu_hw_pingpong_init(enum dpu_pingpong idx, 197 void __iomem *addr, 198 struct dpu_mdss_cfg *m) 199 { 200 struct dpu_hw_pingpong *c; 201 struct dpu_pingpong_cfg *cfg; 202 203 c = kzalloc(sizeof(*c), GFP_KERNEL); 204 if (!c) 205 return ERR_PTR(-ENOMEM); 206 207 cfg = _pingpong_offset(idx, m, addr, &c->hw); 208 if (IS_ERR_OR_NULL(cfg)) { 209 kfree(c); 210 return ERR_PTR(-EINVAL); 211 } 212 213 c->idx = idx; 214 c->caps = cfg; 215 _setup_pingpong_ops(&c->ops, c->caps); 216 217 dpu_hw_blk_init(&c->base, DPU_HW_BLK_PINGPONG, idx, &dpu_hw_ops); 218 219 return c; 220 } 221 222 void dpu_hw_pingpong_destroy(struct dpu_hw_pingpong *pp) 223 { 224 if (pp) 225 dpu_hw_blk_destroy(&pp->base); 226 kfree(pp); 227 } 228