1 // SPDX-License-Identifier: GPL-2.0-only 2 /* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. 3 */ 4 5 #include "dpu_hwio.h" 6 #include "dpu_hw_catalog.h" 7 #include "dpu_hw_intf.h" 8 #include "dpu_kms.h" 9 10 #define INTF_TIMING_ENGINE_EN 0x000 11 #define INTF_CONFIG 0x004 12 #define INTF_HSYNC_CTL 0x008 13 #define INTF_VSYNC_PERIOD_F0 0x00C 14 #define INTF_VSYNC_PERIOD_F1 0x010 15 #define INTF_VSYNC_PULSE_WIDTH_F0 0x014 16 #define INTF_VSYNC_PULSE_WIDTH_F1 0x018 17 #define INTF_DISPLAY_V_START_F0 0x01C 18 #define INTF_DISPLAY_V_START_F1 0x020 19 #define INTF_DISPLAY_V_END_F0 0x024 20 #define INTF_DISPLAY_V_END_F1 0x028 21 #define INTF_ACTIVE_V_START_F0 0x02C 22 #define INTF_ACTIVE_V_START_F1 0x030 23 #define INTF_ACTIVE_V_END_F0 0x034 24 #define INTF_ACTIVE_V_END_F1 0x038 25 #define INTF_DISPLAY_HCTL 0x03C 26 #define INTF_ACTIVE_HCTL 0x040 27 #define INTF_BORDER_COLOR 0x044 28 #define INTF_UNDERFLOW_COLOR 0x048 29 #define INTF_HSYNC_SKEW 0x04C 30 #define INTF_POLARITY_CTL 0x050 31 #define INTF_TEST_CTL 0x054 32 #define INTF_TP_COLOR0 0x058 33 #define INTF_TP_COLOR1 0x05C 34 #define INTF_FRAME_LINE_COUNT_EN 0x0A8 35 #define INTF_FRAME_COUNT 0x0AC 36 #define INTF_LINE_COUNT 0x0B0 37 38 #define INTF_DEFLICKER_CONFIG 0x0F0 39 #define INTF_DEFLICKER_STRNG_COEFF 0x0F4 40 #define INTF_DEFLICKER_WEAK_COEFF 0x0F8 41 42 #define INTF_DSI_CMD_MODE_TRIGGER_EN 0x084 43 #define INTF_PANEL_FORMAT 0x090 44 #define INTF_TPG_ENABLE 0x100 45 #define INTF_TPG_MAIN_CONTROL 0x104 46 #define INTF_TPG_VIDEO_CONFIG 0x108 47 #define INTF_TPG_COMPONENT_LIMITS 0x10C 48 #define INTF_TPG_RECTANGLE 0x110 49 #define INTF_TPG_INITIAL_VALUE 0x114 50 #define INTF_TPG_BLK_WHITE_PATTERN_FRAMES 0x118 51 #define INTF_TPG_RGB_MAPPING 0x11C 52 #define INTF_PROG_FETCH_START 0x170 53 #define INTF_PROG_ROT_START 0x174 54 55 #define INTF_FRAME_LINE_COUNT_EN 0x0A8 56 #define INTF_FRAME_COUNT 0x0AC 57 #define INTF_LINE_COUNT 0x0B0 58 59 static struct dpu_intf_cfg *_intf_offset(enum dpu_intf intf, 60 struct dpu_mdss_cfg *m, 61 void __iomem *addr, 62 struct dpu_hw_blk_reg_map *b) 63 { 64 int i; 65 66 for (i = 0; i < m->intf_count; i++) { 67 if ((intf == m->intf[i].id) && 68 (m->intf[i].type != INTF_NONE)) { 69 b->base_off = addr; 70 b->blk_off = m->intf[i].base; 71 b->length = m->intf[i].len; 72 b->hwversion = m->hwversion; 73 b->log_mask = DPU_DBG_MASK_INTF; 74 return &m->intf[i]; 75 } 76 } 77 78 return ERR_PTR(-EINVAL); 79 } 80 81 static void dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *ctx, 82 const struct intf_timing_params *p, 83 const struct dpu_format *fmt) 84 { 85 struct dpu_hw_blk_reg_map *c = &ctx->hw; 86 u32 hsync_period, vsync_period; 87 u32 display_v_start, display_v_end; 88 u32 hsync_start_x, hsync_end_x; 89 u32 active_h_start, active_h_end; 90 u32 active_v_start, active_v_end; 91 u32 active_hctl, display_hctl, hsync_ctl; 92 u32 polarity_ctl, den_polarity, hsync_polarity, vsync_polarity; 93 u32 panel_format; 94 u32 intf_cfg; 95 96 /* read interface_cfg */ 97 intf_cfg = DPU_REG_READ(c, INTF_CONFIG); 98 hsync_period = p->hsync_pulse_width + p->h_back_porch + p->width + 99 p->h_front_porch; 100 vsync_period = p->vsync_pulse_width + p->v_back_porch + p->height + 101 p->v_front_porch; 102 103 display_v_start = ((p->vsync_pulse_width + p->v_back_porch) * 104 hsync_period) + p->hsync_skew; 105 display_v_end = ((vsync_period - p->v_front_porch) * hsync_period) + 106 p->hsync_skew - 1; 107 108 if (ctx->cap->type == INTF_EDP || ctx->cap->type == INTF_DP) { 109 display_v_start += p->hsync_pulse_width + p->h_back_porch; 110 display_v_end -= p->h_front_porch; 111 } 112 113 hsync_start_x = p->h_back_porch + p->hsync_pulse_width; 114 hsync_end_x = hsync_period - p->h_front_porch - 1; 115 116 if (p->width != p->xres) { 117 active_h_start = hsync_start_x; 118 active_h_end = active_h_start + p->xres - 1; 119 } else { 120 active_h_start = 0; 121 active_h_end = 0; 122 } 123 124 if (p->height != p->yres) { 125 active_v_start = display_v_start; 126 active_v_end = active_v_start + (p->yres * hsync_period) - 1; 127 } else { 128 active_v_start = 0; 129 active_v_end = 0; 130 } 131 132 if (active_h_end) { 133 active_hctl = (active_h_end << 16) | active_h_start; 134 intf_cfg |= BIT(29); /* ACTIVE_H_ENABLE */ 135 } else { 136 active_hctl = 0; 137 } 138 139 if (active_v_end) 140 intf_cfg |= BIT(30); /* ACTIVE_V_ENABLE */ 141 142 hsync_ctl = (hsync_period << 16) | p->hsync_pulse_width; 143 display_hctl = (hsync_end_x << 16) | hsync_start_x; 144 145 den_polarity = 0; 146 if (ctx->cap->type == INTF_HDMI) { 147 hsync_polarity = p->yres >= 720 ? 0 : 1; 148 vsync_polarity = p->yres >= 720 ? 0 : 1; 149 } else { 150 hsync_polarity = 0; 151 vsync_polarity = 0; 152 } 153 polarity_ctl = (den_polarity << 2) | /* DEN Polarity */ 154 (vsync_polarity << 1) | /* VSYNC Polarity */ 155 (hsync_polarity << 0); /* HSYNC Polarity */ 156 157 if (!DPU_FORMAT_IS_YUV(fmt)) 158 panel_format = (fmt->bits[C0_G_Y] | 159 (fmt->bits[C1_B_Cb] << 2) | 160 (fmt->bits[C2_R_Cr] << 4) | 161 (0x21 << 8)); 162 else 163 /* Interface treats all the pixel data in RGB888 format */ 164 panel_format = (COLOR_8BIT | 165 (COLOR_8BIT << 2) | 166 (COLOR_8BIT << 4) | 167 (0x21 << 8)); 168 169 DPU_REG_WRITE(c, INTF_HSYNC_CTL, hsync_ctl); 170 DPU_REG_WRITE(c, INTF_VSYNC_PERIOD_F0, vsync_period * hsync_period); 171 DPU_REG_WRITE(c, INTF_VSYNC_PULSE_WIDTH_F0, 172 p->vsync_pulse_width * hsync_period); 173 DPU_REG_WRITE(c, INTF_DISPLAY_HCTL, display_hctl); 174 DPU_REG_WRITE(c, INTF_DISPLAY_V_START_F0, display_v_start); 175 DPU_REG_WRITE(c, INTF_DISPLAY_V_END_F0, display_v_end); 176 DPU_REG_WRITE(c, INTF_ACTIVE_HCTL, active_hctl); 177 DPU_REG_WRITE(c, INTF_ACTIVE_V_START_F0, active_v_start); 178 DPU_REG_WRITE(c, INTF_ACTIVE_V_END_F0, active_v_end); 179 DPU_REG_WRITE(c, INTF_BORDER_COLOR, p->border_clr); 180 DPU_REG_WRITE(c, INTF_UNDERFLOW_COLOR, p->underflow_clr); 181 DPU_REG_WRITE(c, INTF_HSYNC_SKEW, p->hsync_skew); 182 DPU_REG_WRITE(c, INTF_POLARITY_CTL, polarity_ctl); 183 DPU_REG_WRITE(c, INTF_FRAME_LINE_COUNT_EN, 0x3); 184 DPU_REG_WRITE(c, INTF_CONFIG, intf_cfg); 185 DPU_REG_WRITE(c, INTF_PANEL_FORMAT, panel_format); 186 } 187 188 static void dpu_hw_intf_enable_timing_engine( 189 struct dpu_hw_intf *intf, 190 u8 enable) 191 { 192 struct dpu_hw_blk_reg_map *c = &intf->hw; 193 /* Note: Display interface select is handled in top block hw layer */ 194 DPU_REG_WRITE(c, INTF_TIMING_ENGINE_EN, enable != 0); 195 } 196 197 static void dpu_hw_intf_setup_prg_fetch( 198 struct dpu_hw_intf *intf, 199 const struct intf_prog_fetch *fetch) 200 { 201 struct dpu_hw_blk_reg_map *c = &intf->hw; 202 int fetch_enable; 203 204 /* 205 * Fetch should always be outside the active lines. If the fetching 206 * is programmed within active region, hardware behavior is unknown. 207 */ 208 209 fetch_enable = DPU_REG_READ(c, INTF_CONFIG); 210 if (fetch->enable) { 211 fetch_enable |= BIT(31); 212 DPU_REG_WRITE(c, INTF_PROG_FETCH_START, 213 fetch->fetch_start); 214 } else { 215 fetch_enable &= ~BIT(31); 216 } 217 218 DPU_REG_WRITE(c, INTF_CONFIG, fetch_enable); 219 } 220 221 static void dpu_hw_intf_get_status( 222 struct dpu_hw_intf *intf, 223 struct intf_status *s) 224 { 225 struct dpu_hw_blk_reg_map *c = &intf->hw; 226 227 s->is_en = DPU_REG_READ(c, INTF_TIMING_ENGINE_EN); 228 if (s->is_en) { 229 s->frame_count = DPU_REG_READ(c, INTF_FRAME_COUNT); 230 s->line_count = DPU_REG_READ(c, INTF_LINE_COUNT); 231 } else { 232 s->line_count = 0; 233 s->frame_count = 0; 234 } 235 } 236 237 static u32 dpu_hw_intf_get_line_count(struct dpu_hw_intf *intf) 238 { 239 struct dpu_hw_blk_reg_map *c; 240 241 if (!intf) 242 return 0; 243 244 c = &intf->hw; 245 246 return DPU_REG_READ(c, INTF_LINE_COUNT); 247 } 248 249 static void _setup_intf_ops(struct dpu_hw_intf_ops *ops, 250 unsigned long cap) 251 { 252 ops->setup_timing_gen = dpu_hw_intf_setup_timing_engine; 253 ops->setup_prg_fetch = dpu_hw_intf_setup_prg_fetch; 254 ops->get_status = dpu_hw_intf_get_status; 255 ops->enable_timing = dpu_hw_intf_enable_timing_engine; 256 ops->get_line_count = dpu_hw_intf_get_line_count; 257 } 258 259 static struct dpu_hw_blk_ops dpu_hw_ops; 260 261 struct dpu_hw_intf *dpu_hw_intf_init(enum dpu_intf idx, 262 void __iomem *addr, 263 struct dpu_mdss_cfg *m) 264 { 265 struct dpu_hw_intf *c; 266 struct dpu_intf_cfg *cfg; 267 268 c = kzalloc(sizeof(*c), GFP_KERNEL); 269 if (!c) 270 return ERR_PTR(-ENOMEM); 271 272 cfg = _intf_offset(idx, m, addr, &c->hw); 273 if (IS_ERR_OR_NULL(cfg)) { 274 kfree(c); 275 pr_err("failed to create dpu_hw_intf %d\n", idx); 276 return ERR_PTR(-EINVAL); 277 } 278 279 /* 280 * Assign ops 281 */ 282 c->idx = idx; 283 c->cap = cfg; 284 c->mdss = m; 285 _setup_intf_ops(&c->ops, c->cap->features); 286 287 dpu_hw_blk_init(&c->base, DPU_HW_BLK_INTF, idx, &dpu_hw_ops); 288 289 return c; 290 } 291 292 void dpu_hw_intf_destroy(struct dpu_hw_intf *intf) 293 { 294 if (intf) 295 dpu_hw_blk_destroy(&intf->base); 296 kfree(intf); 297 } 298 299