]> Git Repo - linux.git/blob - drivers/gpu/drm/imx/dcss/dcss-dtg.c
Merge tag 'x86_cleanups_for_v5.11' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux.git] / drivers / gpu / drm / imx / dcss / dcss-dtg.c
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                           0, "dcss_ctxld_kick", dtg);
138         if (ret) {
139                 dev_err(dtg->dev, "dtg: irq request failed.\n");
140                 return ret;
141         }
142
143         disable_irq(dtg->ctxld_kick_irq);
144
145         dtg->ctxld_kick_irq_en = false;
146
147         return 0;
148 }
149
150 int dcss_dtg_init(struct dcss_dev *dcss, unsigned long dtg_base)
151 {
152         int ret = 0;
153         struct dcss_dtg *dtg;
154
155         dtg = kzalloc(sizeof(*dtg), GFP_KERNEL);
156         if (!dtg)
157                 return -ENOMEM;
158
159         dcss->dtg = dtg;
160         dtg->dev = dcss->dev;
161         dtg->ctxld = dcss->ctxld;
162
163         dtg->base_reg = ioremap(dtg_base, SZ_4K);
164         if (!dtg->base_reg) {
165                 dev_err(dcss->dev, "dtg: unable to remap dtg base\n");
166                 ret = -ENOMEM;
167                 goto err_ioremap;
168         }
169
170         dtg->base_ofs = dtg_base;
171         dtg->ctx_id = CTX_DB;
172
173         dtg->alpha = 255;
174
175         dtg->control_status |= OVL_DATA_MODE | BLENDER_VIDEO_ALPHA_SEL |
176                 ((dtg->alpha << DEFAULT_FG_ALPHA_POS) & DEFAULT_FG_ALPHA_MASK);
177
178         ret = dcss_dtg_irq_config(dtg, to_platform_device(dcss->dev));
179         if (ret)
180                 goto err_irq;
181
182         return 0;
183
184 err_irq:
185         iounmap(dtg->base_reg);
186
187 err_ioremap:
188         kfree(dtg);
189
190         return ret;
191 }
192
193 void dcss_dtg_exit(struct dcss_dtg *dtg)
194 {
195         free_irq(dtg->ctxld_kick_irq, dtg);
196
197         if (dtg->base_reg)
198                 iounmap(dtg->base_reg);
199
200         kfree(dtg);
201 }
202
203 void dcss_dtg_sync_set(struct dcss_dtg *dtg, struct videomode *vm)
204 {
205         struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dtg->dev);
206         u16 dtg_lrc_x, dtg_lrc_y;
207         u16 dis_ulc_x, dis_ulc_y;
208         u16 dis_lrc_x, dis_lrc_y;
209         u32 sb_ctxld_trig, db_ctxld_trig;
210         u32 pixclock = vm->pixelclock;
211         u32 actual_clk;
212
213         dtg_lrc_x = vm->hfront_porch + vm->hback_porch + vm->hsync_len +
214                     vm->hactive - 1;
215         dtg_lrc_y = vm->vfront_porch + vm->vback_porch + vm->vsync_len +
216                     vm->vactive - 1;
217         dis_ulc_x = vm->hsync_len + vm->hback_porch - 1;
218         dis_ulc_y = vm->vsync_len + vm->vfront_porch + vm->vback_porch - 1;
219         dis_lrc_x = vm->hsync_len + vm->hback_porch + vm->hactive - 1;
220         dis_lrc_y = vm->vsync_len + vm->vfront_porch + vm->vback_porch +
221                     vm->vactive - 1;
222
223         clk_disable_unprepare(dcss->pix_clk);
224         clk_set_rate(dcss->pix_clk, vm->pixelclock);
225         clk_prepare_enable(dcss->pix_clk);
226
227         actual_clk = clk_get_rate(dcss->pix_clk);
228         if (pixclock != actual_clk) {
229                 dev_info(dtg->dev,
230                          "Pixel clock set to %u kHz instead of %u kHz.\n",
231                          (actual_clk / 1000), (pixclock / 1000));
232         }
233
234         dcss_dtg_write(dtg, ((dtg_lrc_y << TC_Y_POS) | dtg_lrc_x),
235                        DCSS_DTG_TC_DTG);
236         dcss_dtg_write(dtg, ((dis_ulc_y << TC_Y_POS) | dis_ulc_x),
237                        DCSS_DTG_TC_DISP_TOP);
238         dcss_dtg_write(dtg, ((dis_lrc_y << TC_Y_POS) | dis_lrc_x),
239                        DCSS_DTG_TC_DISP_BOT);
240
241         dtg->dis_ulc_x = dis_ulc_x;
242         dtg->dis_ulc_y = dis_ulc_y;
243
244         sb_ctxld_trig = ((0 * dis_lrc_y / 100) << TC_CTXLD_SB_Y_POS) &
245                                                         TC_CTXLD_SB_Y_MASK;
246         db_ctxld_trig = ((99 * dis_lrc_y / 100) << TC_CTXLD_DB_Y_POS) &
247                                                         TC_CTXLD_DB_Y_MASK;
248
249         dcss_dtg_write(dtg, sb_ctxld_trig | db_ctxld_trig, DCSS_DTG_TC_CTXLD);
250
251         /* vblank trigger */
252         dcss_dtg_write(dtg, 0, DCSS_DTG_LINE1_INT);
253
254         /* CTXLD trigger */
255         dcss_dtg_write(dtg, ((90 * dis_lrc_y) / 100) << 16, DCSS_DTG_LINE0_INT);
256 }
257
258 void dcss_dtg_plane_pos_set(struct dcss_dtg *dtg, int ch_num,
259                             int px, int py, int pw, int ph)
260 {
261         u16 p_ulc_x, p_ulc_y;
262         u16 p_lrc_x, p_lrc_y;
263
264         p_ulc_x = dtg->dis_ulc_x + px;
265         p_ulc_y = dtg->dis_ulc_y + py;
266         p_lrc_x = p_ulc_x + pw;
267         p_lrc_y = p_ulc_y + ph;
268
269         if (!px && !py && !pw && !ph) {
270                 dcss_dtg_write(dtg, 0, DCSS_DTG_TC_CH1_TOP + 0x8 * ch_num);
271                 dcss_dtg_write(dtg, 0, DCSS_DTG_TC_CH1_BOT + 0x8 * ch_num);
272         } else {
273                 dcss_dtg_write(dtg, ((p_ulc_y << TC_Y_POS) | p_ulc_x),
274                                DCSS_DTG_TC_CH1_TOP + 0x8 * ch_num);
275                 dcss_dtg_write(dtg, ((p_lrc_y << TC_Y_POS) | p_lrc_x),
276                                DCSS_DTG_TC_CH1_BOT + 0x8 * ch_num);
277         }
278 }
279
280 bool dcss_dtg_global_alpha_changed(struct dcss_dtg *dtg, int ch_num, int alpha)
281 {
282         if (ch_num)
283                 return false;
284
285         return alpha != dtg->alpha;
286 }
287
288 void dcss_dtg_plane_alpha_set(struct dcss_dtg *dtg, int ch_num,
289                               const struct drm_format_info *format, int alpha)
290 {
291         /* we care about alpha only when channel 0 is concerned */
292         if (ch_num)
293                 return;
294
295         /*
296          * Use global alpha if pixel format does not have alpha channel or the
297          * user explicitly chose to use global alpha (i.e. alpha is not OPAQUE).
298          */
299         if (!format->has_alpha || alpha != 255)
300                 dtg->alpha_cfg = (alpha << DEFAULT_FG_ALPHA_POS) & DEFAULT_FG_ALPHA_MASK;
301         else /* use per-pixel alpha otherwise */
302                 dtg->alpha_cfg = CH1_ALPHA_SEL;
303
304         dtg->alpha = alpha;
305 }
306
307 void dcss_dtg_css_set(struct dcss_dtg *dtg)
308 {
309         dtg->control_status |=
310                         (0x5 << CSS_PIX_COMP_SWAP_POS) & CSS_PIX_COMP_SWAP_MASK;
311 }
312
313 void dcss_dtg_enable(struct dcss_dtg *dtg)
314 {
315         dtg->control_status |= DTG_START;
316
317         dtg->control_status &= ~(CH1_ALPHA_SEL | DEFAULT_FG_ALPHA_MASK);
318         dtg->control_status |= dtg->alpha_cfg;
319
320         dcss_dtg_write(dtg, dtg->control_status, DCSS_DTG_TC_CONTROL_STATUS);
321
322         dtg->in_use = true;
323 }
324
325 void dcss_dtg_shutoff(struct dcss_dtg *dtg)
326 {
327         dtg->control_status &= ~DTG_START;
328
329         dcss_writel(dtg->control_status,
330                     dtg->base_reg + DCSS_DTG_TC_CONTROL_STATUS);
331
332         dtg->in_use = false;
333 }
334
335 bool dcss_dtg_is_enabled(struct dcss_dtg *dtg)
336 {
337         return dtg->in_use;
338 }
339
340 void dcss_dtg_ch_enable(struct dcss_dtg *dtg, int ch_num, bool en)
341 {
342         u32 ch_en_map[] = {CH1_EN, CH2_EN, CH3_EN};
343         u32 control_status;
344
345         control_status = dtg->control_status & ~ch_en_map[ch_num];
346         control_status |= en ? ch_en_map[ch_num] : 0;
347
348         control_status &= ~(CH1_ALPHA_SEL | DEFAULT_FG_ALPHA_MASK);
349         control_status |= dtg->alpha_cfg;
350
351         if (dtg->control_status != control_status)
352                 dcss_dtg_write(dtg, control_status, DCSS_DTG_TC_CONTROL_STATUS);
353
354         dtg->control_status = control_status;
355 }
356
357 void dcss_dtg_vblank_irq_enable(struct dcss_dtg *dtg, bool en)
358 {
359         u32 status;
360         u32 mask = en ? LINE1_IRQ : 0;
361
362         if (en) {
363                 status = dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS);
364                 dcss_writel(status & LINE1_IRQ,
365                             dtg->base_reg + DCSS_DTG_INT_CONTROL);
366         }
367
368         dcss_update(mask, LINE1_IRQ, dtg->base_reg + DCSS_DTG_INT_MASK);
369 }
370
371 void dcss_dtg_ctxld_kick_irq_enable(struct dcss_dtg *dtg, bool en)
372 {
373         u32 status;
374         u32 mask = en ? LINE0_IRQ : 0;
375
376         if (en) {
377                 status = dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS);
378
379                 if (!dtg->ctxld_kick_irq_en) {
380                         dcss_writel(status & LINE0_IRQ,
381                                     dtg->base_reg + DCSS_DTG_INT_CONTROL);
382                         enable_irq(dtg->ctxld_kick_irq);
383                         dtg->ctxld_kick_irq_en = true;
384                         dcss_update(mask, LINE0_IRQ,
385                                     dtg->base_reg + DCSS_DTG_INT_MASK);
386                 }
387
388                 return;
389         }
390
391         if (!dtg->ctxld_kick_irq_en)
392                 return;
393
394         disable_irq_nosync(dtg->ctxld_kick_irq);
395         dtg->ctxld_kick_irq_en = false;
396
397         dcss_update(mask, LINE0_IRQ, dtg->base_reg + DCSS_DTG_INT_MASK);
398 }
399
400 void dcss_dtg_vblank_irq_clear(struct dcss_dtg *dtg)
401 {
402         dcss_update(LINE1_IRQ, LINE1_IRQ, dtg->base_reg + DCSS_DTG_INT_CONTROL);
403 }
404
405 bool dcss_dtg_vblank_irq_valid(struct dcss_dtg *dtg)
406 {
407         return !!(dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS) & LINE1_IRQ);
408 }
409
This page took 0.06313 seconds and 4 git commands to generate.