1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved. 4 */ 5 6 #include "dpu_kms.h" 7 #include "dpu_hw_catalog.h" 8 #include "dpu_hwio.h" 9 #include "dpu_hw_lm.h" 10 #include "dpu_hw_mdss.h" 11 12 #define LM_OP_MODE 0x00 13 #define LM_OUT_SIZE 0x04 14 #define LM_BORDER_COLOR_0 0x08 15 #define LM_BORDER_COLOR_1 0x010 16 17 /* These register are offset to mixer base + stage base */ 18 #define LM_BLEND0_OP 0x00 19 #define LM_BLEND0_CONST_ALPHA 0x04 20 #define LM_FG_COLOR_FILL_COLOR_0 0x08 21 #define LM_FG_COLOR_FILL_COLOR_1 0x0C 22 #define LM_FG_COLOR_FILL_SIZE 0x10 23 #define LM_FG_COLOR_FILL_XY 0x14 24 25 #define LM_BLEND0_FG_ALPHA 0x04 26 #define LM_BLEND0_BG_ALPHA 0x08 27 28 #define LM_MISR_CTRL 0x310 29 #define LM_MISR_SIGNATURE 0x314 30 #define LM_MISR_FRAME_COUNT_MASK 0xFF 31 #define LM_MISR_CTRL_ENABLE BIT(8) 32 #define LM_MISR_CTRL_STATUS BIT(9) 33 #define LM_MISR_CTRL_STATUS_CLEAR BIT(10) 34 #define LM_MISR_CTRL_FREE_RUN_MASK BIT(31) 35 36 37 static const struct dpu_lm_cfg *_lm_offset(enum dpu_lm mixer, 38 const struct dpu_mdss_cfg *m, 39 void __iomem *addr, 40 struct dpu_hw_blk_reg_map *b) 41 { 42 int i; 43 44 for (i = 0; i < m->mixer_count; i++) { 45 if (mixer == m->mixer[i].id) { 46 b->base_off = addr; 47 b->blk_off = m->mixer[i].base; 48 b->length = m->mixer[i].len; 49 b->hwversion = m->hwversion; 50 b->log_mask = DPU_DBG_MASK_LM; 51 return &m->mixer[i]; 52 } 53 } 54 55 return ERR_PTR(-ENOMEM); 56 } 57 58 /** 59 * _stage_offset(): returns the relative offset of the blend registers 60 * for the stage to be setup 61 * @ctx: mixer ctx contains the mixer to be programmed 62 * @stage: stage index to setup 63 */ 64 static inline int _stage_offset(struct dpu_hw_mixer *ctx, enum dpu_stage stage) 65 { 66 const struct dpu_lm_sub_blks *sblk = ctx->cap->sblk; 67 if (stage != DPU_STAGE_BASE && stage <= sblk->maxblendstages) 68 return sblk->blendstage_base[stage - DPU_STAGE_0]; 69 70 return -EINVAL; 71 } 72 73 static void dpu_hw_lm_setup_out(struct dpu_hw_mixer *ctx, 74 struct dpu_hw_mixer_cfg *mixer) 75 { 76 struct dpu_hw_blk_reg_map *c = &ctx->hw; 77 u32 outsize; 78 u32 op_mode; 79 80 op_mode = DPU_REG_READ(c, LM_OP_MODE); 81 82 outsize = mixer->out_height << 16 | mixer->out_width; 83 DPU_REG_WRITE(c, LM_OUT_SIZE, outsize); 84 85 /* SPLIT_LEFT_RIGHT */ 86 if (mixer->right_mixer) 87 op_mode |= BIT(31); 88 else 89 op_mode &= ~BIT(31); 90 DPU_REG_WRITE(c, LM_OP_MODE, op_mode); 91 } 92 93 static void dpu_hw_lm_setup_border_color(struct dpu_hw_mixer *ctx, 94 struct dpu_mdss_color *color, 95 u8 border_en) 96 { 97 struct dpu_hw_blk_reg_map *c = &ctx->hw; 98 99 if (border_en) { 100 DPU_REG_WRITE(c, LM_BORDER_COLOR_0, 101 (color->color_0 & 0xFFF) | 102 ((color->color_1 & 0xFFF) << 0x10)); 103 DPU_REG_WRITE(c, LM_BORDER_COLOR_1, 104 (color->color_2 & 0xFFF) | 105 ((color->color_3 & 0xFFF) << 0x10)); 106 } 107 } 108 109 static void dpu_hw_lm_setup_misr(struct dpu_hw_mixer *ctx, bool enable, u32 frame_count) 110 { 111 struct dpu_hw_blk_reg_map *c = &ctx->hw; 112 u32 config = 0; 113 114 DPU_REG_WRITE(c, LM_MISR_CTRL, LM_MISR_CTRL_STATUS_CLEAR); 115 116 /* Clear old MISR value (in case it's read before a new value is calculated)*/ 117 wmb(); 118 119 if (enable) { 120 config = (frame_count & LM_MISR_FRAME_COUNT_MASK) | 121 LM_MISR_CTRL_ENABLE | LM_MISR_CTRL_FREE_RUN_MASK; 122 123 DPU_REG_WRITE(c, LM_MISR_CTRL, config); 124 } else { 125 DPU_REG_WRITE(c, LM_MISR_CTRL, 0); 126 } 127 128 } 129 130 static int dpu_hw_lm_collect_misr(struct dpu_hw_mixer *ctx, u32 *misr_value) 131 { 132 struct dpu_hw_blk_reg_map *c = &ctx->hw; 133 u32 ctrl = 0; 134 135 if (!misr_value) 136 return -EINVAL; 137 138 ctrl = DPU_REG_READ(c, LM_MISR_CTRL); 139 140 if (!(ctrl & LM_MISR_CTRL_ENABLE)) 141 return -ENODATA; 142 143 if (!(ctrl & LM_MISR_CTRL_STATUS)) 144 return -EINVAL; 145 146 *misr_value = DPU_REG_READ(c, LM_MISR_SIGNATURE); 147 148 return 0; 149 } 150 151 static void dpu_hw_lm_setup_blend_config_sdm845(struct dpu_hw_mixer *ctx, 152 u32 stage, u32 fg_alpha, u32 bg_alpha, u32 blend_op) 153 { 154 struct dpu_hw_blk_reg_map *c = &ctx->hw; 155 int stage_off; 156 u32 const_alpha; 157 158 if (stage == DPU_STAGE_BASE) 159 return; 160 161 stage_off = _stage_offset(ctx, stage); 162 if (WARN_ON(stage_off < 0)) 163 return; 164 165 const_alpha = (bg_alpha & 0xFF) | ((fg_alpha & 0xFF) << 16); 166 DPU_REG_WRITE(c, LM_BLEND0_CONST_ALPHA + stage_off, const_alpha); 167 DPU_REG_WRITE(c, LM_BLEND0_OP + stage_off, blend_op); 168 } 169 170 static void dpu_hw_lm_setup_blend_config(struct dpu_hw_mixer *ctx, 171 u32 stage, u32 fg_alpha, u32 bg_alpha, u32 blend_op) 172 { 173 struct dpu_hw_blk_reg_map *c = &ctx->hw; 174 int stage_off; 175 176 if (stage == DPU_STAGE_BASE) 177 return; 178 179 stage_off = _stage_offset(ctx, stage); 180 if (WARN_ON(stage_off < 0)) 181 return; 182 183 DPU_REG_WRITE(c, LM_BLEND0_FG_ALPHA + stage_off, fg_alpha); 184 DPU_REG_WRITE(c, LM_BLEND0_BG_ALPHA + stage_off, bg_alpha); 185 DPU_REG_WRITE(c, LM_BLEND0_OP + stage_off, blend_op); 186 } 187 188 static void dpu_hw_lm_setup_color3(struct dpu_hw_mixer *ctx, 189 uint32_t mixer_op_mode) 190 { 191 struct dpu_hw_blk_reg_map *c = &ctx->hw; 192 int op_mode; 193 194 /* read the existing op_mode configuration */ 195 op_mode = DPU_REG_READ(c, LM_OP_MODE); 196 197 op_mode = (op_mode & (BIT(31) | BIT(30))) | mixer_op_mode; 198 199 DPU_REG_WRITE(c, LM_OP_MODE, op_mode); 200 } 201 202 static void _setup_mixer_ops(const struct dpu_mdss_cfg *m, 203 struct dpu_hw_lm_ops *ops, 204 unsigned long features) 205 { 206 ops->setup_mixer_out = dpu_hw_lm_setup_out; 207 if (m->hwversion >= DPU_HW_VER_400) 208 ops->setup_blend_config = dpu_hw_lm_setup_blend_config_sdm845; 209 else 210 ops->setup_blend_config = dpu_hw_lm_setup_blend_config; 211 ops->setup_alpha_out = dpu_hw_lm_setup_color3; 212 ops->setup_border_color = dpu_hw_lm_setup_border_color; 213 ops->setup_misr = dpu_hw_lm_setup_misr; 214 ops->collect_misr = dpu_hw_lm_collect_misr; 215 } 216 217 struct dpu_hw_mixer *dpu_hw_lm_init(enum dpu_lm idx, 218 void __iomem *addr, 219 const struct dpu_mdss_cfg *m) 220 { 221 struct dpu_hw_mixer *c; 222 const struct dpu_lm_cfg *cfg; 223 224 c = kzalloc(sizeof(*c), GFP_KERNEL); 225 if (!c) 226 return ERR_PTR(-ENOMEM); 227 228 cfg = _lm_offset(idx, m, addr, &c->hw); 229 if (IS_ERR_OR_NULL(cfg)) { 230 kfree(c); 231 return ERR_PTR(-EINVAL); 232 } 233 234 /* Assign ops */ 235 c->idx = idx; 236 c->cap = cfg; 237 _setup_mixer_ops(m, &c->ops, c->cap->features); 238 239 return c; 240 } 241 242 void dpu_hw_lm_destroy(struct dpu_hw_mixer *lm) 243 { 244 kfree(lm); 245 } 246