xref: /linux/drivers/gpu/drm/imx/dc/dc-ed.c (revision 260f6f4fda93c8485c8037865c941b42b9cba5d2)
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/mod_devicetable.h>
10 #include <linux/module.h>
11 #include <linux/platform_device.h>
12 #include <linux/regmap.h>
13 
14 #include "dc-drv.h"
15 #include "dc-pe.h"
16 
17 #define PIXENGCFG_STATIC	0x8
18 #define  POWERDOWN		BIT(4)
19 #define  SYNC_MODE		BIT(8)
20 #define  SINGLE			0
21 #define  DIV_MASK		GENMASK(23, 16)
22 #define  DIV(x)			FIELD_PREP(DIV_MASK, (x))
23 #define  DIV_RESET		0x80
24 
25 #define PIXENGCFG_DYNAMIC	0xc
26 
27 #define PIXENGCFG_TRIGGER	0x14
28 #define  SYNC_TRIGGER		BIT(0)
29 
30 #define STATICCONTROL		0x8
31 #define  KICK_MODE		BIT(8)
32 #define  EXTERNAL		BIT(8)
33 #define  PERFCOUNTMODE		BIT(12)
34 
35 #define CONTROL			0xc
36 #define  GAMMAAPPLYENABLE	BIT(0)
37 
38 static const struct dc_subdev_info dc_ed_info[] = {
39 	{ .reg_start = 0x56180980, .id = 0, },
40 	{ .reg_start = 0x56180a00, .id = 1, },
41 	{ .reg_start = 0x561809c0, .id = 4, },
42 	{ .reg_start = 0x56180a40, .id = 5, },
43 };
44 
45 static const struct regmap_range dc_ed_pec_regmap_write_ranges[] = {
46 	regmap_reg_range(PIXENGCFG_STATIC, PIXENGCFG_STATIC),
47 	regmap_reg_range(PIXENGCFG_DYNAMIC, PIXENGCFG_DYNAMIC),
48 	regmap_reg_range(PIXENGCFG_TRIGGER, PIXENGCFG_TRIGGER),
49 };
50 
51 static const struct regmap_access_table dc_ed_pec_regmap_write_table = {
52 	.yes_ranges = dc_ed_pec_regmap_write_ranges,
53 	.n_yes_ranges = ARRAY_SIZE(dc_ed_pec_regmap_write_ranges),
54 };
55 
56 static const struct regmap_range dc_ed_pec_regmap_read_ranges[] = {
57 	regmap_reg_range(PIXENGCFG_STATIC, PIXENGCFG_STATIC),
58 	regmap_reg_range(PIXENGCFG_DYNAMIC, PIXENGCFG_DYNAMIC),
59 };
60 
61 static const struct regmap_access_table dc_ed_pec_regmap_read_table = {
62 	.yes_ranges = dc_ed_pec_regmap_read_ranges,
63 	.n_yes_ranges = ARRAY_SIZE(dc_ed_pec_regmap_read_ranges),
64 };
65 
66 static const struct regmap_range dc_ed_pec_regmap_volatile_ranges[] = {
67 	regmap_reg_range(PIXENGCFG_TRIGGER, PIXENGCFG_TRIGGER),
68 };
69 
70 static const struct regmap_access_table dc_ed_pec_regmap_volatile_table = {
71 	.yes_ranges = dc_ed_pec_regmap_volatile_ranges,
72 	.n_yes_ranges = ARRAY_SIZE(dc_ed_pec_regmap_volatile_ranges),
73 };
74 
75 static const struct regmap_config dc_ed_pec_regmap_config = {
76 	.name = "pec",
77 	.reg_bits = 32,
78 	.reg_stride = 4,
79 	.val_bits = 32,
80 	.fast_io = true,
81 	.wr_table = &dc_ed_pec_regmap_write_table,
82 	.rd_table = &dc_ed_pec_regmap_read_table,
83 	.volatile_table = &dc_ed_pec_regmap_volatile_table,
84 	.max_register = PIXENGCFG_TRIGGER,
85 };
86 
87 static const struct regmap_range dc_ed_regmap_ranges[] = {
88 	regmap_reg_range(STATICCONTROL, STATICCONTROL),
89 	regmap_reg_range(CONTROL, CONTROL),
90 };
91 
92 static const struct regmap_access_table dc_ed_regmap_access_table = {
93 	.yes_ranges = dc_ed_regmap_ranges,
94 	.n_yes_ranges = ARRAY_SIZE(dc_ed_regmap_ranges),
95 };
96 
97 static const struct regmap_config dc_ed_cfg_regmap_config = {
98 	.name = "cfg",
99 	.reg_bits = 32,
100 	.reg_stride = 4,
101 	.val_bits = 32,
102 	.fast_io = true,
103 	.wr_table = &dc_ed_regmap_access_table,
104 	.rd_table = &dc_ed_regmap_access_table,
105 	.max_register = CONTROL,
106 };
107 
108 static const enum dc_link_id src_sels[] = {
109 	LINK_ID_NONE,
110 	LINK_ID_CONSTFRAME0,
111 	LINK_ID_CONSTFRAME1,
112 	LINK_ID_CONSTFRAME4,
113 	LINK_ID_CONSTFRAME5,
114 	LINK_ID_LAYERBLEND3,
115 	LINK_ID_LAYERBLEND2,
116 	LINK_ID_LAYERBLEND1,
117 	LINK_ID_LAYERBLEND0,
118 };
119 
dc_ed_pec_enable_shden(struct dc_ed * ed)120 static inline void dc_ed_pec_enable_shden(struct dc_ed *ed)
121 {
122 	regmap_write_bits(ed->reg_pec, PIXENGCFG_STATIC, SHDEN, SHDEN);
123 }
124 
dc_ed_pec_poweron(struct dc_ed * ed)125 static inline void dc_ed_pec_poweron(struct dc_ed *ed)
126 {
127 	regmap_write_bits(ed->reg_pec, PIXENGCFG_STATIC, POWERDOWN, 0);
128 }
129 
dc_ed_pec_sync_mode_single(struct dc_ed * ed)130 static inline void dc_ed_pec_sync_mode_single(struct dc_ed *ed)
131 {
132 	regmap_write_bits(ed->reg_pec, PIXENGCFG_STATIC, SYNC_MODE, SINGLE);
133 }
134 
dc_ed_pec_div_reset(struct dc_ed * ed)135 static inline void dc_ed_pec_div_reset(struct dc_ed *ed)
136 {
137 	regmap_write_bits(ed->reg_pec, PIXENGCFG_STATIC, DIV_MASK,
138 			  DIV(DIV_RESET));
139 }
140 
dc_ed_pec_src_sel(struct dc_ed * ed,enum dc_link_id src)141 void dc_ed_pec_src_sel(struct dc_ed *ed, enum dc_link_id src)
142 {
143 	int i;
144 
145 	for (i = 0; i < ARRAY_SIZE(src_sels); i++) {
146 		if (src_sels[i] == src) {
147 			regmap_write(ed->reg_pec, PIXENGCFG_DYNAMIC, src);
148 			return;
149 		}
150 	}
151 }
152 
dc_ed_pec_sync_trigger(struct dc_ed * ed)153 void dc_ed_pec_sync_trigger(struct dc_ed *ed)
154 {
155 	regmap_write(ed->reg_pec, PIXENGCFG_TRIGGER, SYNC_TRIGGER);
156 }
157 
dc_ed_enable_shden(struct dc_ed * ed)158 static inline void dc_ed_enable_shden(struct dc_ed *ed)
159 {
160 	regmap_write_bits(ed->reg_cfg, STATICCONTROL, SHDEN, SHDEN);
161 }
162 
dc_ed_kick_mode_external(struct dc_ed * ed)163 static inline void dc_ed_kick_mode_external(struct dc_ed *ed)
164 {
165 	regmap_write_bits(ed->reg_cfg, STATICCONTROL, KICK_MODE, EXTERNAL);
166 }
167 
dc_ed_disable_perfcountmode(struct dc_ed * ed)168 static inline void dc_ed_disable_perfcountmode(struct dc_ed *ed)
169 {
170 	regmap_write_bits(ed->reg_cfg, STATICCONTROL, PERFCOUNTMODE, 0);
171 }
172 
dc_ed_disable_gamma_apply(struct dc_ed * ed)173 static inline void dc_ed_disable_gamma_apply(struct dc_ed *ed)
174 {
175 	regmap_write_bits(ed->reg_cfg, CONTROL, GAMMAAPPLYENABLE, 0);
176 }
177 
dc_ed_init(struct dc_ed * ed)178 void dc_ed_init(struct dc_ed *ed)
179 {
180 	dc_ed_pec_src_sel(ed, LINK_ID_NONE);
181 	dc_ed_pec_enable_shden(ed);
182 	dc_ed_pec_poweron(ed);
183 	dc_ed_pec_sync_mode_single(ed);
184 	dc_ed_pec_div_reset(ed);
185 	dc_ed_enable_shden(ed);
186 	dc_ed_disable_perfcountmode(ed);
187 	dc_ed_kick_mode_external(ed);
188 	dc_ed_disable_gamma_apply(ed);
189 }
190 
dc_ed_bind(struct device * dev,struct device * master,void * data)191 static int dc_ed_bind(struct device *dev, struct device *master, void *data)
192 {
193 	struct platform_device *pdev = to_platform_device(dev);
194 	struct dc_drm_device *dc_drm = data;
195 	struct resource *res_pec;
196 	void __iomem *base_pec;
197 	void __iomem *base_cfg;
198 	struct dc_ed *ed;
199 	int id;
200 
201 	ed = devm_kzalloc(dev, sizeof(*ed), GFP_KERNEL);
202 	if (!ed)
203 		return -ENOMEM;
204 
205 	base_pec = devm_platform_get_and_ioremap_resource(pdev, 0, &res_pec);
206 	if (IS_ERR(base_pec))
207 		return PTR_ERR(base_pec);
208 
209 	base_cfg = devm_platform_ioremap_resource_byname(pdev, "cfg");
210 	if (IS_ERR(base_cfg))
211 		return PTR_ERR(base_cfg);
212 
213 	ed->reg_pec = devm_regmap_init_mmio(dev, base_pec,
214 					    &dc_ed_pec_regmap_config);
215 	if (IS_ERR(ed->reg_pec))
216 		return PTR_ERR(ed->reg_pec);
217 
218 	ed->reg_cfg = devm_regmap_init_mmio(dev, base_cfg,
219 					    &dc_ed_cfg_regmap_config);
220 	if (IS_ERR(ed->reg_cfg))
221 		return PTR_ERR(ed->reg_cfg);
222 
223 	ed->irq_shdload = platform_get_irq_byname(pdev, "shdload");
224 	if (ed->irq_shdload < 0)
225 		return ed->irq_shdload;
226 
227 	ed->dev = dev;
228 
229 	id = dc_subdev_get_id(dc_ed_info, ARRAY_SIZE(dc_ed_info), res_pec);
230 	if (id < 0) {
231 		dev_err(dev, "failed to get instance number: %d\n", id);
232 		return id;
233 	}
234 
235 	switch (id) {
236 	case 0:
237 		dc_drm->ed_cont[0] = ed;
238 		break;
239 	case 1:
240 		dc_drm->ed_cont[1] = ed;
241 		break;
242 	case 4:
243 		dc_drm->ed_safe[0] = ed;
244 		break;
245 	case 5:
246 		dc_drm->ed_safe[1] = ed;
247 		break;
248 	}
249 
250 	return 0;
251 }
252 
253 static const struct component_ops dc_ed_ops = {
254 	.bind = dc_ed_bind,
255 };
256 
dc_ed_probe(struct platform_device * pdev)257 static int dc_ed_probe(struct platform_device *pdev)
258 {
259 	int ret;
260 
261 	ret = component_add(&pdev->dev, &dc_ed_ops);
262 	if (ret)
263 		return dev_err_probe(&pdev->dev, ret,
264 				     "failed to add component\n");
265 
266 	return 0;
267 }
268 
dc_ed_remove(struct platform_device * pdev)269 static void dc_ed_remove(struct platform_device *pdev)
270 {
271 	component_del(&pdev->dev, &dc_ed_ops);
272 }
273 
274 static const struct of_device_id dc_ed_dt_ids[] = {
275 	{ .compatible = "fsl,imx8qxp-dc-extdst" },
276 	{ /* sentinel */ }
277 };
278 MODULE_DEVICE_TABLE(of, dc_ed_dt_ids);
279 
280 struct platform_driver dc_ed_driver = {
281 	.probe = dc_ed_probe,
282 	.remove = dc_ed_remove,
283 	.driver = {
284 		.name = "imx8-dc-extdst",
285 		.suppress_bind_attrs = true,
286 		.of_match_table = dc_ed_dt_ids,
287 	},
288 };
289