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 -EINVAL;
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