1 // SPDX-License-Identifier: GPL-2.0
7 #include <linux/delay.h>
8 #include <linux/interrupt.h>
10 #include <linux/platform_device.h>
11 #include <linux/slab.h>
15 #define DCSS_DTG_TC_CONTROL_STATUS 0x00
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
38 #define TC_X_MASK GENMASK(12, 0)
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
79 struct dcss_ctxld *ctxld;
80 void __iomem *base_reg;
95 bool ctxld_kick_irq_en;
98 static void dcss_dtg_write(struct dcss_dtg *dtg, u32 val, u32 ofs)
101 dcss_writel(val, dtg->base_reg + ofs);
103 dcss_ctxld_write(dtg->ctxld, dtg->ctx_id,
104 val, dtg->base_ofs + ofs);
107 static irqreturn_t dcss_dtg_irq_handler(int irq, void *data)
109 struct dcss_dtg *dtg = data;
112 status = dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS);
114 if (!(status & LINE0_IRQ))
117 dcss_ctxld_kick(dtg->ctxld);
119 dcss_writel(status & LINE0_IRQ, dtg->base_reg + DCSS_DTG_INT_CONTROL);
124 static int dcss_dtg_irq_config(struct dcss_dtg *dtg,
125 struct platform_device *pdev)
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;
133 dcss_update(0, LINE0_IRQ | LINE1_IRQ,
134 dtg->base_reg + DCSS_DTG_INT_MASK);
136 ret = request_irq(dtg->ctxld_kick_irq, dcss_dtg_irq_handler,
137 0, "dcss_ctxld_kick", dtg);
139 dev_err(dtg->dev, "dtg: irq request failed.\n");
143 disable_irq(dtg->ctxld_kick_irq);
145 dtg->ctxld_kick_irq_en = false;
150 int dcss_dtg_init(struct dcss_dev *dcss, unsigned long dtg_base)
153 struct dcss_dtg *dtg;
155 dtg = kzalloc(sizeof(*dtg), GFP_KERNEL);
160 dtg->dev = dcss->dev;
161 dtg->ctxld = dcss->ctxld;
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");
170 dtg->base_ofs = dtg_base;
171 dtg->ctx_id = CTX_DB;
175 dtg->control_status |= OVL_DATA_MODE | BLENDER_VIDEO_ALPHA_SEL |
176 ((dtg->alpha << DEFAULT_FG_ALPHA_POS) & DEFAULT_FG_ALPHA_MASK);
178 ret = dcss_dtg_irq_config(dtg, to_platform_device(dcss->dev));
185 iounmap(dtg->base_reg);
193 void dcss_dtg_exit(struct dcss_dtg *dtg)
195 free_irq(dtg->ctxld_kick_irq, dtg);
198 iounmap(dtg->base_reg);
203 void dcss_dtg_sync_set(struct dcss_dtg *dtg, struct videomode *vm)
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;
213 dtg_lrc_x = vm->hfront_porch + vm->hback_porch + vm->hsync_len +
215 dtg_lrc_y = vm->vfront_porch + vm->vback_porch + vm->vsync_len +
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 +
223 clk_disable_unprepare(dcss->pix_clk);
224 clk_set_rate(dcss->pix_clk, vm->pixelclock);
225 clk_prepare_enable(dcss->pix_clk);
227 actual_clk = clk_get_rate(dcss->pix_clk);
228 if (pixclock != actual_clk) {
230 "Pixel clock set to %u kHz instead of %u kHz.\n",
231 (actual_clk / 1000), (pixclock / 1000));
234 dcss_dtg_write(dtg, ((dtg_lrc_y << TC_Y_POS) | dtg_lrc_x),
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);
241 dtg->dis_ulc_x = dis_ulc_x;
242 dtg->dis_ulc_y = dis_ulc_y;
244 sb_ctxld_trig = ((0 * dis_lrc_y / 100) << TC_CTXLD_SB_Y_POS) &
246 db_ctxld_trig = ((99 * dis_lrc_y / 100) << TC_CTXLD_DB_Y_POS) &
249 dcss_dtg_write(dtg, sb_ctxld_trig | db_ctxld_trig, DCSS_DTG_TC_CTXLD);
252 dcss_dtg_write(dtg, 0, DCSS_DTG_LINE1_INT);
255 dcss_dtg_write(dtg, ((90 * dis_lrc_y) / 100) << 16, DCSS_DTG_LINE0_INT);
258 void dcss_dtg_plane_pos_set(struct dcss_dtg *dtg, int ch_num,
259 int px, int py, int pw, int ph)
261 u16 p_ulc_x, p_ulc_y;
262 u16 p_lrc_x, p_lrc_y;
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;
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);
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);
280 bool dcss_dtg_global_alpha_changed(struct dcss_dtg *dtg, int ch_num, int alpha)
285 return alpha != dtg->alpha;
288 void dcss_dtg_plane_alpha_set(struct dcss_dtg *dtg, int ch_num,
289 const struct drm_format_info *format, int alpha)
291 /* we care about alpha only when channel 0 is concerned */
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).
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;
307 void dcss_dtg_css_set(struct dcss_dtg *dtg)
309 dtg->control_status |=
310 (0x5 << CSS_PIX_COMP_SWAP_POS) & CSS_PIX_COMP_SWAP_MASK;
313 void dcss_dtg_enable(struct dcss_dtg *dtg)
315 dtg->control_status |= DTG_START;
317 dtg->control_status &= ~(CH1_ALPHA_SEL | DEFAULT_FG_ALPHA_MASK);
318 dtg->control_status |= dtg->alpha_cfg;
320 dcss_dtg_write(dtg, dtg->control_status, DCSS_DTG_TC_CONTROL_STATUS);
325 void dcss_dtg_shutoff(struct dcss_dtg *dtg)
327 dtg->control_status &= ~DTG_START;
329 dcss_writel(dtg->control_status,
330 dtg->base_reg + DCSS_DTG_TC_CONTROL_STATUS);
335 bool dcss_dtg_is_enabled(struct dcss_dtg *dtg)
340 void dcss_dtg_ch_enable(struct dcss_dtg *dtg, int ch_num, bool en)
342 u32 ch_en_map[] = {CH1_EN, CH2_EN, CH3_EN};
345 control_status = dtg->control_status & ~ch_en_map[ch_num];
346 control_status |= en ? ch_en_map[ch_num] : 0;
348 control_status &= ~(CH1_ALPHA_SEL | DEFAULT_FG_ALPHA_MASK);
349 control_status |= dtg->alpha_cfg;
351 if (dtg->control_status != control_status)
352 dcss_dtg_write(dtg, control_status, DCSS_DTG_TC_CONTROL_STATUS);
354 dtg->control_status = control_status;
357 void dcss_dtg_vblank_irq_enable(struct dcss_dtg *dtg, bool en)
360 u32 mask = en ? LINE1_IRQ : 0;
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);
368 dcss_update(mask, LINE1_IRQ, dtg->base_reg + DCSS_DTG_INT_MASK);
371 void dcss_dtg_ctxld_kick_irq_enable(struct dcss_dtg *dtg, bool en)
374 u32 mask = en ? LINE0_IRQ : 0;
377 status = dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS);
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);
391 if (!dtg->ctxld_kick_irq_en)
394 disable_irq_nosync(dtg->ctxld_kick_irq);
395 dtg->ctxld_kick_irq_en = false;
397 dcss_update(mask, LINE0_IRQ, dtg->base_reg + DCSS_DTG_INT_MASK);
400 void dcss_dtg_vblank_irq_clear(struct dcss_dtg *dtg)
402 dcss_update(LINE1_IRQ, LINE1_IRQ, dtg->base_reg + DCSS_DTG_INT_CONTROL);
405 bool dcss_dtg_vblank_irq_valid(struct dcss_dtg *dtg)
407 return !!(dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS) & LINE1_IRQ);