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
_lm_offset(enum dpu_lm mixer,const struct dpu_mdss_cfg * m,void __iomem * addr,struct dpu_hw_blk_reg_map * b)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 */
_stage_offset(struct dpu_hw_mixer * ctx,enum dpu_stage stage)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
dpu_hw_lm_setup_out(struct dpu_hw_mixer * ctx,struct dpu_hw_mixer_cfg * mixer)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
dpu_hw_lm_setup_border_color(struct dpu_hw_mixer * ctx,struct dpu_mdss_color * color,u8 border_en)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
dpu_hw_lm_setup_misr(struct dpu_hw_mixer * ctx,bool enable,u32 frame_count)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
dpu_hw_lm_collect_misr(struct dpu_hw_mixer * ctx,u32 * misr_value)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
dpu_hw_lm_setup_blend_config_sdm845(struct dpu_hw_mixer * ctx,u32 stage,u32 fg_alpha,u32 bg_alpha,u32 blend_op)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
dpu_hw_lm_setup_blend_config(struct dpu_hw_mixer * ctx,u32 stage,u32 fg_alpha,u32 bg_alpha,u32 blend_op)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
dpu_hw_lm_setup_color3(struct dpu_hw_mixer * ctx,uint32_t mixer_op_mode)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
_setup_mixer_ops(const struct dpu_mdss_cfg * m,struct dpu_hw_lm_ops * ops,unsigned long features)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
dpu_hw_lm_init(enum dpu_lm idx,void __iomem * addr,const struct dpu_mdss_cfg * m)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
dpu_hw_lm_destroy(struct dpu_hw_mixer * lm)242 void dpu_hw_lm_destroy(struct dpu_hw_mixer *lm)
243 {
244 kfree(lm);
245 }
246