xref: /linux/drivers/gpu/drm/imx/dcss/dcss-dtg.c (revision 7f71507851fc7764b36a3221839607d3a45c2025)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright 2019 NXP.
4  */
5 
6 #include <linux/clk.h>
7 #include <linux/delay.h>
8 #include <linux/interrupt.h>
9 #include <linux/of.h>
10 #include <linux/platform_device.h>
11 #include <linux/slab.h>
12 
13 #include "dcss-dev.h"
14 
15 #define DCSS_DTG_TC_CONTROL_STATUS			0x00
16 #define   CH3_EN					BIT(0)
17 #define   CH2_EN					BIT(1)
18 #define   CH1_EN					BIT(2)
19 #define   OVL_DATA_MODE					BIT(3)
20 #define   BLENDER_VIDEO_ALPHA_SEL			BIT(7)
21 #define   DTG_START					BIT(8)
22 #define   DBY_MODE_EN					BIT(9)
23 #define   CH1_ALPHA_SEL					BIT(10)
24 #define   CSS_PIX_COMP_SWAP_POS				12
25 #define   CSS_PIX_COMP_SWAP_MASK			GENMASK(14, 12)
26 #define   DEFAULT_FG_ALPHA_POS				24
27 #define   DEFAULT_FG_ALPHA_MASK				GENMASK(31, 24)
28 #define DCSS_DTG_TC_DTG					0x04
29 #define DCSS_DTG_TC_DISP_TOP				0x08
30 #define DCSS_DTG_TC_DISP_BOT				0x0C
31 #define DCSS_DTG_TC_CH1_TOP				0x10
32 #define DCSS_DTG_TC_CH1_BOT				0x14
33 #define DCSS_DTG_TC_CH2_TOP				0x18
34 #define DCSS_DTG_TC_CH2_BOT				0x1C
35 #define DCSS_DTG_TC_CH3_TOP				0x20
36 #define DCSS_DTG_TC_CH3_BOT				0x24
37 #define   TC_X_POS					0
38 #define   TC_X_MASK					GENMASK(12, 0)
39 #define   TC_Y_POS					16
40 #define   TC_Y_MASK					GENMASK(28, 16)
41 #define DCSS_DTG_TC_CTXLD				0x28
42 #define   TC_CTXLD_DB_Y_POS				0
43 #define   TC_CTXLD_DB_Y_MASK				GENMASK(12, 0)
44 #define   TC_CTXLD_SB_Y_POS				16
45 #define   TC_CTXLD_SB_Y_MASK				GENMASK(28, 16)
46 #define DCSS_DTG_TC_CH1_BKRND				0x2C
47 #define DCSS_DTG_TC_CH2_BKRND				0x30
48 #define   BKRND_R_Y_COMP_POS				20
49 #define   BKRND_R_Y_COMP_MASK				GENMASK(29, 20)
50 #define   BKRND_G_U_COMP_POS				10
51 #define   BKRND_G_U_COMP_MASK				GENMASK(19, 10)
52 #define   BKRND_B_V_COMP_POS				0
53 #define   BKRND_B_V_COMP_MASK				GENMASK(9, 0)
54 #define DCSS_DTG_BLENDER_DBY_RANGEINV			0x38
55 #define DCSS_DTG_BLENDER_DBY_RANGEMIN			0x3C
56 #define DCSS_DTG_BLENDER_DBY_BDP			0x40
57 #define DCSS_DTG_BLENDER_BKRND_I			0x44
58 #define DCSS_DTG_BLENDER_BKRND_P			0x48
59 #define DCSS_DTG_BLENDER_BKRND_T			0x4C
60 #define DCSS_DTG_LINE0_INT				0x50
61 #define DCSS_DTG_LINE1_INT				0x54
62 #define DCSS_DTG_BG_ALPHA_DEFAULT			0x58
63 #define DCSS_DTG_INT_STATUS				0x5C
64 #define DCSS_DTG_INT_CONTROL				0x60
65 #define DCSS_DTG_TC_CH3_BKRND				0x64
66 #define DCSS_DTG_INT_MASK				0x68
67 #define   LINE0_IRQ					BIT(0)
68 #define   LINE1_IRQ					BIT(1)
69 #define   LINE2_IRQ					BIT(2)
70 #define   LINE3_IRQ					BIT(3)
71 #define DCSS_DTG_LINE2_INT				0x6C
72 #define DCSS_DTG_LINE3_INT				0x70
73 #define DCSS_DTG_DBY_OL					0x74
74 #define DCSS_DTG_DBY_BL					0x78
75 #define DCSS_DTG_DBY_EL					0x7C
76 
77 struct dcss_dtg {
78 	struct device *dev;
79 	struct dcss_ctxld *ctxld;
80 	void __iomem *base_reg;
81 	u32 base_ofs;
82 
83 	u32 ctx_id;
84 
85 	bool in_use;
86 
87 	u32 dis_ulc_x;
88 	u32 dis_ulc_y;
89 
90 	u32 control_status;
91 	u32 alpha;
92 	u32 alpha_cfg;
93 
94 	int ctxld_kick_irq;
95 	bool ctxld_kick_irq_en;
96 };
97 
98 static void dcss_dtg_write(struct dcss_dtg *dtg, u32 val, u32 ofs)
99 {
100 	if (!dtg->in_use)
101 		dcss_writel(val, dtg->base_reg + ofs);
102 
103 	dcss_ctxld_write(dtg->ctxld, dtg->ctx_id,
104 			 val, dtg->base_ofs + ofs);
105 }
106 
107 static irqreturn_t dcss_dtg_irq_handler(int irq, void *data)
108 {
109 	struct dcss_dtg *dtg = data;
110 	u32 status;
111 
112 	status = dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS);
113 
114 	if (!(status & LINE0_IRQ))
115 		return IRQ_NONE;
116 
117 	dcss_ctxld_kick(dtg->ctxld);
118 
119 	dcss_writel(status & LINE0_IRQ, dtg->base_reg + DCSS_DTG_INT_CONTROL);
120 
121 	return IRQ_HANDLED;
122 }
123 
124 static int dcss_dtg_irq_config(struct dcss_dtg *dtg,
125 			       struct platform_device *pdev)
126 {
127 	int ret;
128 
129 	dtg->ctxld_kick_irq = platform_get_irq_byname(pdev, "ctxld_kick");
130 	if (dtg->ctxld_kick_irq < 0)
131 		return dtg->ctxld_kick_irq;
132 
133 	dcss_update(0, LINE0_IRQ | LINE1_IRQ,
134 		    dtg->base_reg + DCSS_DTG_INT_MASK);
135 
136 	ret = request_irq(dtg->ctxld_kick_irq, dcss_dtg_irq_handler,
137 			  IRQF_NO_AUTOEN, "dcss_ctxld_kick", dtg);
138 	if (ret) {
139 		dev_err(dtg->dev, "dtg: irq request failed.\n");
140 		return ret;
141 	}
142 
143 	dtg->ctxld_kick_irq_en = false;
144 
145 	return 0;
146 }
147 
148 int dcss_dtg_init(struct dcss_dev *dcss, unsigned long dtg_base)
149 {
150 	int ret = 0;
151 	struct dcss_dtg *dtg;
152 
153 	dtg = devm_kzalloc(dcss->dev, sizeof(*dtg), GFP_KERNEL);
154 	if (!dtg)
155 		return -ENOMEM;
156 
157 	dcss->dtg = dtg;
158 	dtg->dev = dcss->dev;
159 	dtg->ctxld = dcss->ctxld;
160 
161 	dtg->base_reg = devm_ioremap(dtg->dev, dtg_base, SZ_4K);
162 	if (!dtg->base_reg) {
163 		dev_err(dtg->dev, "dtg: unable to remap dtg base\n");
164 		return -ENOMEM;
165 	}
166 
167 	dtg->base_ofs = dtg_base;
168 	dtg->ctx_id = CTX_DB;
169 
170 	dtg->alpha = 255;
171 
172 	dtg->control_status |= OVL_DATA_MODE | BLENDER_VIDEO_ALPHA_SEL |
173 		((dtg->alpha << DEFAULT_FG_ALPHA_POS) & DEFAULT_FG_ALPHA_MASK);
174 
175 	ret = dcss_dtg_irq_config(dtg, to_platform_device(dtg->dev));
176 
177 	return ret;
178 }
179 
180 void dcss_dtg_exit(struct dcss_dtg *dtg)
181 {
182 	free_irq(dtg->ctxld_kick_irq, dtg);
183 }
184 
185 void dcss_dtg_sync_set(struct dcss_dtg *dtg, struct videomode *vm)
186 {
187 	struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dtg->dev);
188 	u16 dtg_lrc_x, dtg_lrc_y;
189 	u16 dis_ulc_x, dis_ulc_y;
190 	u16 dis_lrc_x, dis_lrc_y;
191 	u32 sb_ctxld_trig, db_ctxld_trig;
192 	u32 pixclock = vm->pixelclock;
193 	u32 actual_clk;
194 
195 	dtg_lrc_x = vm->hfront_porch + vm->hback_porch + vm->hsync_len +
196 		    vm->hactive - 1;
197 	dtg_lrc_y = vm->vfront_porch + vm->vback_porch + vm->vsync_len +
198 		    vm->vactive - 1;
199 	dis_ulc_x = vm->hsync_len + vm->hback_porch - 1;
200 	dis_ulc_y = vm->vsync_len + vm->vfront_porch + vm->vback_porch - 1;
201 	dis_lrc_x = vm->hsync_len + vm->hback_porch + vm->hactive - 1;
202 	dis_lrc_y = vm->vsync_len + vm->vfront_porch + vm->vback_porch +
203 		    vm->vactive - 1;
204 
205 	clk_disable_unprepare(dcss->pix_clk);
206 	clk_set_rate(dcss->pix_clk, vm->pixelclock);
207 	clk_prepare_enable(dcss->pix_clk);
208 
209 	actual_clk = clk_get_rate(dcss->pix_clk);
210 	if (pixclock != actual_clk) {
211 		dev_info(dtg->dev,
212 			 "Pixel clock set to %u kHz instead of %u kHz.\n",
213 			 (actual_clk / 1000), (pixclock / 1000));
214 	}
215 
216 	dcss_dtg_write(dtg, ((dtg_lrc_y << TC_Y_POS) | dtg_lrc_x),
217 		       DCSS_DTG_TC_DTG);
218 	dcss_dtg_write(dtg, ((dis_ulc_y << TC_Y_POS) | dis_ulc_x),
219 		       DCSS_DTG_TC_DISP_TOP);
220 	dcss_dtg_write(dtg, ((dis_lrc_y << TC_Y_POS) | dis_lrc_x),
221 		       DCSS_DTG_TC_DISP_BOT);
222 
223 	dtg->dis_ulc_x = dis_ulc_x;
224 	dtg->dis_ulc_y = dis_ulc_y;
225 
226 	sb_ctxld_trig = ((0 * dis_lrc_y / 100) << TC_CTXLD_SB_Y_POS) &
227 							TC_CTXLD_SB_Y_MASK;
228 	db_ctxld_trig = ((99 * dis_lrc_y / 100) << TC_CTXLD_DB_Y_POS) &
229 							TC_CTXLD_DB_Y_MASK;
230 
231 	dcss_dtg_write(dtg, sb_ctxld_trig | db_ctxld_trig, DCSS_DTG_TC_CTXLD);
232 
233 	/* vblank trigger */
234 	dcss_dtg_write(dtg, 0, DCSS_DTG_LINE1_INT);
235 
236 	/* CTXLD trigger */
237 	dcss_dtg_write(dtg, ((90 * dis_lrc_y) / 100) << 16, DCSS_DTG_LINE0_INT);
238 }
239 
240 void dcss_dtg_plane_pos_set(struct dcss_dtg *dtg, int ch_num,
241 			    int px, int py, int pw, int ph)
242 {
243 	u16 p_ulc_x, p_ulc_y;
244 	u16 p_lrc_x, p_lrc_y;
245 
246 	p_ulc_x = dtg->dis_ulc_x + px;
247 	p_ulc_y = dtg->dis_ulc_y + py;
248 	p_lrc_x = p_ulc_x + pw;
249 	p_lrc_y = p_ulc_y + ph;
250 
251 	if (!px && !py && !pw && !ph) {
252 		dcss_dtg_write(dtg, 0, DCSS_DTG_TC_CH1_TOP + 0x8 * ch_num);
253 		dcss_dtg_write(dtg, 0, DCSS_DTG_TC_CH1_BOT + 0x8 * ch_num);
254 	} else {
255 		dcss_dtg_write(dtg, ((p_ulc_y << TC_Y_POS) | p_ulc_x),
256 			       DCSS_DTG_TC_CH1_TOP + 0x8 * ch_num);
257 		dcss_dtg_write(dtg, ((p_lrc_y << TC_Y_POS) | p_lrc_x),
258 			       DCSS_DTG_TC_CH1_BOT + 0x8 * ch_num);
259 	}
260 }
261 
262 bool dcss_dtg_global_alpha_changed(struct dcss_dtg *dtg, int ch_num, int alpha)
263 {
264 	if (ch_num)
265 		return false;
266 
267 	return alpha != dtg->alpha;
268 }
269 
270 void dcss_dtg_plane_alpha_set(struct dcss_dtg *dtg, int ch_num,
271 			      const struct drm_format_info *format, int alpha)
272 {
273 	/* we care about alpha only when channel 0 is concerned */
274 	if (ch_num)
275 		return;
276 
277 	/*
278 	 * Use global alpha if pixel format does not have alpha channel or the
279 	 * user explicitly chose to use global alpha (i.e. alpha is not OPAQUE).
280 	 */
281 	if (!format->has_alpha || alpha != 255)
282 		dtg->alpha_cfg = (alpha << DEFAULT_FG_ALPHA_POS) & DEFAULT_FG_ALPHA_MASK;
283 	else /* use per-pixel alpha otherwise */
284 		dtg->alpha_cfg = CH1_ALPHA_SEL;
285 
286 	dtg->alpha = alpha;
287 }
288 
289 void dcss_dtg_css_set(struct dcss_dtg *dtg)
290 {
291 	dtg->control_status |=
292 			(0x5 << CSS_PIX_COMP_SWAP_POS) & CSS_PIX_COMP_SWAP_MASK;
293 }
294 
295 void dcss_dtg_enable(struct dcss_dtg *dtg)
296 {
297 	dtg->control_status |= DTG_START;
298 
299 	dtg->control_status &= ~(CH1_ALPHA_SEL | DEFAULT_FG_ALPHA_MASK);
300 	dtg->control_status |= dtg->alpha_cfg;
301 
302 	dcss_dtg_write(dtg, dtg->control_status, DCSS_DTG_TC_CONTROL_STATUS);
303 
304 	dtg->in_use = true;
305 }
306 
307 void dcss_dtg_shutoff(struct dcss_dtg *dtg)
308 {
309 	dtg->control_status &= ~DTG_START;
310 
311 	dcss_writel(dtg->control_status,
312 		    dtg->base_reg + DCSS_DTG_TC_CONTROL_STATUS);
313 
314 	dtg->in_use = false;
315 }
316 
317 bool dcss_dtg_is_enabled(struct dcss_dtg *dtg)
318 {
319 	return dtg->in_use;
320 }
321 
322 void dcss_dtg_ch_enable(struct dcss_dtg *dtg, int ch_num, bool en)
323 {
324 	u32 ch_en_map[] = {CH1_EN, CH2_EN, CH3_EN};
325 	u32 control_status;
326 
327 	control_status = dtg->control_status & ~ch_en_map[ch_num];
328 	control_status |= en ? ch_en_map[ch_num] : 0;
329 
330 	control_status &= ~(CH1_ALPHA_SEL | DEFAULT_FG_ALPHA_MASK);
331 	control_status |= dtg->alpha_cfg;
332 
333 	if (dtg->control_status != control_status)
334 		dcss_dtg_write(dtg, control_status, DCSS_DTG_TC_CONTROL_STATUS);
335 
336 	dtg->control_status = control_status;
337 }
338 
339 void dcss_dtg_vblank_irq_enable(struct dcss_dtg *dtg, bool en)
340 {
341 	u32 status;
342 	u32 mask = en ? LINE1_IRQ : 0;
343 
344 	if (en) {
345 		status = dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS);
346 		dcss_writel(status & LINE1_IRQ,
347 			    dtg->base_reg + DCSS_DTG_INT_CONTROL);
348 	}
349 
350 	dcss_update(mask, LINE1_IRQ, dtg->base_reg + DCSS_DTG_INT_MASK);
351 }
352 
353 void dcss_dtg_ctxld_kick_irq_enable(struct dcss_dtg *dtg, bool en)
354 {
355 	u32 status;
356 	u32 mask = en ? LINE0_IRQ : 0;
357 
358 	if (en) {
359 		status = dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS);
360 
361 		if (!dtg->ctxld_kick_irq_en) {
362 			dcss_writel(status & LINE0_IRQ,
363 				    dtg->base_reg + DCSS_DTG_INT_CONTROL);
364 			enable_irq(dtg->ctxld_kick_irq);
365 			dtg->ctxld_kick_irq_en = true;
366 			dcss_update(mask, LINE0_IRQ,
367 				    dtg->base_reg + DCSS_DTG_INT_MASK);
368 		}
369 
370 		return;
371 	}
372 
373 	if (!dtg->ctxld_kick_irq_en)
374 		return;
375 
376 	disable_irq_nosync(dtg->ctxld_kick_irq);
377 	dtg->ctxld_kick_irq_en = false;
378 
379 	dcss_update(mask, LINE0_IRQ, dtg->base_reg + DCSS_DTG_INT_MASK);
380 }
381 
382 void dcss_dtg_vblank_irq_clear(struct dcss_dtg *dtg)
383 {
384 	dcss_update(LINE1_IRQ, LINE1_IRQ, dtg->base_reg + DCSS_DTG_INT_CONTROL);
385 }
386 
387 bool dcss_dtg_vblank_irq_valid(struct dcss_dtg *dtg)
388 {
389 	return !!(dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS) & LINE1_IRQ);
390 }
391 
392