xref: /linux/drivers/gpu/drm/imx/dc/dc-lb.c (revision 72856afd33f2fa166c06f1536003e300e51ecf09)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright 2024 NXP
4  */
5 
6 #include <linux/bitfield.h>
7 #include <linux/bits.h>
8 #include <linux/component.h>
9 #include <linux/module.h>
10 #include <linux/platform_device.h>
11 #include <linux/regmap.h>
12 
13 #include <drm/drm_blend.h>
14 
15 #include "dc-drv.h"
16 #include "dc-pe.h"
17 
18 #define PIXENGCFG_DYNAMIC			0x8
19 #define  PIXENGCFG_DYNAMIC_SEC_SEL_MASK		GENMASK(13, 8)
20 #define  PIXENGCFG_DYNAMIC_SEC_SEL(x)		\
21 		FIELD_PREP(PIXENGCFG_DYNAMIC_SEC_SEL_MASK, (x))
22 #define  PIXENGCFG_DYNAMIC_PRIM_SEL_MASK	GENMASK(5, 0)
23 #define  PIXENGCFG_DYNAMIC_PRIM_SEL(x)		\
24 		FIELD_PREP(PIXENGCFG_DYNAMIC_PRIM_SEL_MASK, (x))
25 
26 #define STATICCONTROL				0x8
27 #define  SHDTOKSEL_MASK				GENMASK(4, 3)
28 #define  SHDTOKSEL(x)				FIELD_PREP(SHDTOKSEL_MASK, (x))
29 #define  SHDLDSEL_MASK				GENMASK(2, 1)
30 #define  SHDLDSEL(x)				FIELD_PREP(SHDLDSEL_MASK, (x))
31 
32 #define CONTROL					0xc
33 #define  CTRL_MODE_MASK				BIT(0)
34 #define  CTRL_MODE(x)				FIELD_PREP(CTRL_MODE_MASK, (x))
35 
36 #define BLENDCONTROL				0x10
37 #define  ALPHA_MASK				GENMASK(23, 16)
38 #define  ALPHA(x)				FIELD_PREP(ALPHA_MASK, (x))
39 #define  SEC_A_BLD_FUNC_MASK			GENMASK(14, 12)
40 #define  SEC_A_BLD_FUNC(x)			\
41 		FIELD_PREP(SEC_A_BLD_FUNC_MASK, (x))
42 #define  PRIM_A_BLD_FUNC_MASK			GENMASK(10, 8)
43 #define  PRIM_A_BLD_FUNC(x)			\
44 		FIELD_PREP(PRIM_A_BLD_FUNC_MASK, (x))
45 #define  SEC_C_BLD_FUNC_MASK			GENMASK(6, 4)
46 #define  SEC_C_BLD_FUNC(x)			\
47 		FIELD_PREP(SEC_C_BLD_FUNC_MASK, (x))
48 #define  PRIM_C_BLD_FUNC_MASK			GENMASK(2, 0)
49 #define  PRIM_C_BLD_FUNC(x)			\
50 		FIELD_PREP(PRIM_C_BLD_FUNC_MASK, (x))
51 
52 #define POSITION				0x14
53 #define  YPOS_MASK				GENMASK(31, 16)
54 #define  YPOS(x)				FIELD_PREP(YPOS_MASK, (x))
55 #define  XPOS_MASK				GENMASK(15, 0)
56 #define  XPOS(x)				FIELD_PREP(XPOS_MASK, (x))
57 
58 enum dc_lb_blend_func {
59 	DC_LAYERBLEND_BLEND_ZERO,
60 	DC_LAYERBLEND_BLEND_ONE,
61 	DC_LAYERBLEND_BLEND_PRIM_ALPHA,
62 	DC_LAYERBLEND_BLEND_ONE_MINUS_PRIM_ALPHA,
63 	DC_LAYERBLEND_BLEND_SEC_ALPHA,
64 	DC_LAYERBLEND_BLEND_ONE_MINUS_SEC_ALPHA,
65 	DC_LAYERBLEND_BLEND_CONST_ALPHA,
66 	DC_LAYERBLEND_BLEND_ONE_MINUS_CONST_ALPHA,
67 };
68 
69 enum dc_lb_shadow_sel {
70 	BOTH = 0x2,
71 };
72 
73 static const struct dc_subdev_info dc_lb_info[] = {
74 	{ .reg_start = 0x56180ba0, .id = 0, },
75 	{ .reg_start = 0x56180bc0, .id = 1, },
76 	{ .reg_start = 0x56180be0, .id = 2, },
77 	{ .reg_start = 0x56180c00, .id = 3, },
78 };
79 
80 static const struct regmap_range dc_lb_pec_regmap_access_ranges[] = {
81 	regmap_reg_range(PIXENGCFG_DYNAMIC, PIXENGCFG_DYNAMIC),
82 };
83 
84 static const struct regmap_access_table dc_lb_pec_regmap_access_table = {
85 	.yes_ranges = dc_lb_pec_regmap_access_ranges,
86 	.n_yes_ranges = ARRAY_SIZE(dc_lb_pec_regmap_access_ranges),
87 };
88 
89 static const struct regmap_config dc_lb_pec_regmap_config = {
90 	.name = "pec",
91 	.reg_bits = 32,
92 	.reg_stride = 4,
93 	.val_bits = 32,
94 	.fast_io = true,
95 	.wr_table = &dc_lb_pec_regmap_access_table,
96 	.rd_table = &dc_lb_pec_regmap_access_table,
97 	.max_register = PIXENGCFG_DYNAMIC,
98 };
99 
100 static const struct regmap_range dc_lb_regmap_ranges[] = {
101 	regmap_reg_range(STATICCONTROL, POSITION),
102 };
103 
104 static const struct regmap_access_table dc_lb_regmap_access_table = {
105 	.yes_ranges = dc_lb_regmap_ranges,
106 	.n_yes_ranges = ARRAY_SIZE(dc_lb_regmap_ranges),
107 };
108 
109 static const struct regmap_config dc_lb_cfg_regmap_config = {
110 	.name = "cfg",
111 	.reg_bits = 32,
112 	.reg_stride = 4,
113 	.val_bits = 32,
114 	.fast_io = true,
115 	.wr_table = &dc_lb_regmap_access_table,
116 	.rd_table = &dc_lb_regmap_access_table,
117 	.max_register = POSITION,
118 };
119 
120 static const enum dc_link_id prim_sels[] = {
121 	/* common options */
122 	LINK_ID_NONE,
123 	LINK_ID_CONSTFRAME0,
124 	LINK_ID_CONSTFRAME1,
125 	LINK_ID_CONSTFRAME4,
126 	LINK_ID_CONSTFRAME5,
127 	/*
128 	 * special options:
129 	 * layerblend(n) has n special options,
130 	 * from layerblend0 to layerblend(n - 1), e.g.,
131 	 * layerblend3 has 3 special options -
132 	 * layerblend0/1/2.
133 	 */
134 	LINK_ID_LAYERBLEND0,
135 	LINK_ID_LAYERBLEND1,
136 	LINK_ID_LAYERBLEND2,
137 	LINK_ID_LAYERBLEND3,
138 };
139 
140 static const enum dc_link_id sec_sels[] = {
141 	LINK_ID_NONE,
142 	LINK_ID_FETCHWARP2,
143 	LINK_ID_FETCHLAYER0,
144 };
145 
146 enum dc_link_id dc_lb_get_link_id(struct dc_lb *lb)
147 {
148 	return lb->link;
149 }
150 
151 void dc_lb_pec_dynamic_prim_sel(struct dc_lb *lb, enum dc_link_id prim)
152 {
153 	int fixed_sels_num = ARRAY_SIZE(prim_sels) - 4;
154 	int i;
155 
156 	for (i = 0; i < fixed_sels_num + lb->id; i++) {
157 		if (prim_sels[i] == prim) {
158 			regmap_write_bits(lb->reg_pec, PIXENGCFG_DYNAMIC,
159 					  PIXENGCFG_DYNAMIC_PRIM_SEL_MASK,
160 					  PIXENGCFG_DYNAMIC_PRIM_SEL(prim));
161 			return;
162 		}
163 	}
164 
165 	dev_warn(lb->dev, "invalid primary input selection:%d\n", prim);
166 }
167 
168 void dc_lb_pec_dynamic_sec_sel(struct dc_lb *lb, enum dc_link_id sec)
169 {
170 	int i;
171 
172 	for (i = 0; i < ARRAY_SIZE(sec_sels); i++) {
173 		if (sec_sels[i] == sec) {
174 			regmap_write_bits(lb->reg_pec, PIXENGCFG_DYNAMIC,
175 					  PIXENGCFG_DYNAMIC_SEC_SEL_MASK,
176 					  PIXENGCFG_DYNAMIC_SEC_SEL(sec));
177 			return;
178 		}
179 	}
180 
181 	dev_warn(lb->dev, "invalid secondary input selection:%d\n", sec);
182 }
183 
184 void dc_lb_pec_clken(struct dc_lb *lb, enum dc_pec_clken clken)
185 {
186 	regmap_write_bits(lb->reg_pec, PIXENGCFG_DYNAMIC, CLKEN_MASK,
187 			  CLKEN(clken));
188 }
189 
190 static inline void dc_lb_enable_shden(struct dc_lb *lb)
191 {
192 	regmap_write_bits(lb->reg_cfg, STATICCONTROL, SHDEN, SHDEN);
193 }
194 
195 static inline void dc_lb_shdtoksel(struct dc_lb *lb, enum dc_lb_shadow_sel sel)
196 {
197 	regmap_write_bits(lb->reg_cfg, STATICCONTROL, SHDTOKSEL_MASK,
198 			  SHDTOKSEL(sel));
199 }
200 
201 static inline void dc_lb_shdldsel(struct dc_lb *lb, enum dc_lb_shadow_sel sel)
202 {
203 	regmap_write_bits(lb->reg_cfg, STATICCONTROL, SHDLDSEL_MASK,
204 			  SHDLDSEL(sel));
205 }
206 
207 void dc_lb_mode(struct dc_lb *lb, enum dc_lb_mode mode)
208 {
209 	regmap_write_bits(lb->reg_cfg, CONTROL, CTRL_MODE_MASK, mode);
210 }
211 
212 static inline void dc_lb_blendcontrol(struct dc_lb *lb)
213 {
214 	u32 val = PRIM_A_BLD_FUNC(DC_LAYERBLEND_BLEND_ZERO) |
215 		  SEC_A_BLD_FUNC(DC_LAYERBLEND_BLEND_ZERO) |
216 		  PRIM_C_BLD_FUNC(DC_LAYERBLEND_BLEND_ZERO) |
217 		  SEC_C_BLD_FUNC(DC_LAYERBLEND_BLEND_CONST_ALPHA) |
218 		  ALPHA(DRM_BLEND_ALPHA_OPAQUE >> 8);
219 
220 	regmap_write(lb->reg_cfg, BLENDCONTROL, val);
221 }
222 
223 void dc_lb_position(struct dc_lb *lb, int x, int y)
224 {
225 	regmap_write(lb->reg_cfg, POSITION, XPOS(x) | YPOS(y));
226 }
227 
228 int dc_lb_get_id(struct dc_lb *lb)
229 {
230 	return lb->id;
231 }
232 
233 void dc_lb_init(struct dc_lb *lb)
234 {
235 	dc_lb_pec_dynamic_prim_sel(lb, LINK_ID_NONE);
236 	dc_lb_pec_dynamic_sec_sel(lb, LINK_ID_NONE);
237 	dc_lb_pec_clken(lb, CLKEN_DISABLE);
238 	dc_lb_shdldsel(lb, BOTH);
239 	dc_lb_shdtoksel(lb, BOTH);
240 	dc_lb_blendcontrol(lb);
241 	dc_lb_enable_shden(lb);
242 }
243 
244 static int dc_lb_bind(struct device *dev, struct device *master, void *data)
245 {
246 	struct platform_device *pdev = to_platform_device(dev);
247 	struct dc_drm_device *dc_drm = data;
248 	struct resource *res_pec;
249 	void __iomem *base_pec;
250 	void __iomem *base_cfg;
251 	struct dc_lb *lb;
252 
253 	lb = devm_kzalloc(dev, sizeof(*lb), GFP_KERNEL);
254 	if (!lb)
255 		return -ENOMEM;
256 
257 	base_pec = devm_platform_get_and_ioremap_resource(pdev, 0, &res_pec);
258 	if (IS_ERR(base_pec))
259 		return PTR_ERR(base_pec);
260 
261 	base_cfg = devm_platform_ioremap_resource_byname(pdev, "cfg");
262 	if (IS_ERR(base_cfg))
263 		return PTR_ERR(base_cfg);
264 
265 	lb->reg_pec = devm_regmap_init_mmio(dev, base_pec,
266 					    &dc_lb_pec_regmap_config);
267 	if (IS_ERR(lb->reg_pec))
268 		return PTR_ERR(lb->reg_pec);
269 
270 	lb->reg_cfg = devm_regmap_init_mmio(dev, base_cfg,
271 					    &dc_lb_cfg_regmap_config);
272 	if (IS_ERR(lb->reg_cfg))
273 		return PTR_ERR(lb->reg_cfg);
274 
275 	lb->id = dc_subdev_get_id(dc_lb_info, ARRAY_SIZE(dc_lb_info), res_pec);
276 	if (lb->id < 0) {
277 		dev_err(dev, "failed to get instance number: %d\n", lb->id);
278 		return lb->id;
279 	}
280 
281 	lb->dev = dev;
282 	lb->link = LINK_ID_LAYERBLEND0 + lb->id;
283 
284 	dc_drm->lb[lb->id] = lb;
285 
286 	return 0;
287 }
288 
289 static const struct component_ops dc_lb_ops = {
290 	.bind = dc_lb_bind,
291 };
292 
293 static int dc_lb_probe(struct platform_device *pdev)
294 {
295 	int ret;
296 
297 	ret = component_add(&pdev->dev, &dc_lb_ops);
298 	if (ret)
299 		return dev_err_probe(&pdev->dev, ret,
300 				     "failed to add component\n");
301 
302 	return 0;
303 }
304 
305 static void dc_lb_remove(struct platform_device *pdev)
306 {
307 	component_del(&pdev->dev, &dc_lb_ops);
308 }
309 
310 static const struct of_device_id dc_lb_dt_ids[] = {
311 	{ .compatible = "fsl,imx8qxp-dc-layerblend" },
312 	{ /* sentinel */ }
313 };
314 MODULE_DEVICE_TABLE(of, dc_lb_dt_ids);
315 
316 struct platform_driver dc_lb_driver = {
317 	.probe = dc_lb_probe,
318 	.remove = dc_lb_remove,
319 	.driver = {
320 		.name = "imx8-dc-layerblend",
321 		.suppress_bind_attrs = true,
322 		.of_match_table = dc_lb_dt_ids,
323 	},
324 };
325