1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* drivers/gpu/drm/exynos5433_drm_decon.c |
3 | * |
4 | * Copyright (C) 2015 Samsung Electronics Co.Ltd |
5 | * Authors: |
6 | * Joonyoung Shim <jy0922.shim@samsung.com> |
7 | * Hyungwon Hwang <human.hwang@samsung.com> |
8 | */ |
9 | |
10 | #include <linux/clk.h> |
11 | #include <linux/component.h> |
12 | #include <linux/iopoll.h> |
13 | #include <linux/irq.h> |
14 | #include <linux/mfd/syscon.h> |
15 | #include <linux/of.h> |
16 | #include <linux/platform_device.h> |
17 | #include <linux/pm_runtime.h> |
18 | #include <linux/regmap.h> |
19 | |
20 | #include <drm/drm_blend.h> |
21 | #include <drm/drm_fourcc.h> |
22 | #include <drm/drm_framebuffer.h> |
23 | #include <drm/drm_vblank.h> |
24 | |
25 | #include "exynos_drm_crtc.h" |
26 | #include "exynos_drm_drv.h" |
27 | #include "exynos_drm_fb.h" |
28 | #include "exynos_drm_plane.h" |
29 | #include "regs-decon5433.h" |
30 | |
31 | #define DSD_CFG_MUX 0x1004 |
32 | #define DSD_CFG_MUX_TE_UNMASK_GLOBAL BIT(13) |
33 | |
34 | #define WINDOWS_NR 5 |
35 | #define PRIMARY_WIN 2 |
36 | #define CURSON_WIN 4 |
37 | |
38 | #define MIN_FB_WIDTH_FOR_16WORD_BURST 128 |
39 | |
40 | #define I80_HW_TRG (1 << 0) |
41 | #define IFTYPE_HDMI (1 << 1) |
42 | |
43 | static const char * const decon_clks_name[] = { |
44 | "pclk" , |
45 | "aclk_decon" , |
46 | "aclk_smmu_decon0x" , |
47 | "aclk_xiu_decon0x" , |
48 | "pclk_smmu_decon0x" , |
49 | "aclk_smmu_decon1x" , |
50 | "aclk_xiu_decon1x" , |
51 | "pclk_smmu_decon1x" , |
52 | "sclk_decon_vclk" , |
53 | "sclk_decon_eclk" , |
54 | }; |
55 | |
56 | struct decon_context { |
57 | struct device *dev; |
58 | struct drm_device *drm_dev; |
59 | void *dma_priv; |
60 | struct exynos_drm_crtc *crtc; |
61 | struct exynos_drm_plane planes[WINDOWS_NR]; |
62 | struct exynos_drm_plane_config configs[WINDOWS_NR]; |
63 | void __iomem *addr; |
64 | struct regmap *sysreg; |
65 | struct clk *clks[ARRAY_SIZE(decon_clks_name)]; |
66 | unsigned int irq; |
67 | unsigned int irq_vsync; |
68 | unsigned int irq_lcd_sys; |
69 | unsigned int te_irq; |
70 | unsigned long out_type; |
71 | int first_win; |
72 | spinlock_t vblank_lock; |
73 | u32 frame_id; |
74 | }; |
75 | |
76 | static const uint32_t decon_formats[] = { |
77 | DRM_FORMAT_XRGB1555, |
78 | DRM_FORMAT_RGB565, |
79 | DRM_FORMAT_XRGB8888, |
80 | DRM_FORMAT_ARGB8888, |
81 | }; |
82 | |
83 | static const enum drm_plane_type decon_win_types[WINDOWS_NR] = { |
84 | [PRIMARY_WIN] = DRM_PLANE_TYPE_PRIMARY, |
85 | [CURSON_WIN] = DRM_PLANE_TYPE_CURSOR, |
86 | }; |
87 | |
88 | static const unsigned int capabilities[WINDOWS_NR] = { |
89 | 0, |
90 | EXYNOS_DRM_PLANE_CAP_WIN_BLEND | EXYNOS_DRM_PLANE_CAP_PIX_BLEND, |
91 | EXYNOS_DRM_PLANE_CAP_WIN_BLEND | EXYNOS_DRM_PLANE_CAP_PIX_BLEND, |
92 | EXYNOS_DRM_PLANE_CAP_WIN_BLEND | EXYNOS_DRM_PLANE_CAP_PIX_BLEND, |
93 | EXYNOS_DRM_PLANE_CAP_WIN_BLEND | EXYNOS_DRM_PLANE_CAP_PIX_BLEND, |
94 | }; |
95 | |
96 | static inline void decon_set_bits(struct decon_context *ctx, u32 reg, u32 mask, |
97 | u32 val) |
98 | { |
99 | val = (val & mask) | (readl(addr: ctx->addr + reg) & ~mask); |
100 | writel(val, addr: ctx->addr + reg); |
101 | } |
102 | |
103 | static int decon_enable_vblank(struct exynos_drm_crtc *crtc) |
104 | { |
105 | struct decon_context *ctx = crtc->ctx; |
106 | u32 val; |
107 | |
108 | val = VIDINTCON0_INTEN; |
109 | if (crtc->i80_mode) |
110 | val |= VIDINTCON0_FRAMEDONE; |
111 | else |
112 | val |= VIDINTCON0_INTFRMEN | VIDINTCON0_FRAMESEL_FP; |
113 | |
114 | writel(val, addr: ctx->addr + DECON_VIDINTCON0); |
115 | |
116 | enable_irq(irq: ctx->irq); |
117 | if (!(ctx->out_type & I80_HW_TRG)) |
118 | enable_irq(irq: ctx->te_irq); |
119 | |
120 | return 0; |
121 | } |
122 | |
123 | static void decon_disable_vblank(struct exynos_drm_crtc *crtc) |
124 | { |
125 | struct decon_context *ctx = crtc->ctx; |
126 | |
127 | if (!(ctx->out_type & I80_HW_TRG)) |
128 | disable_irq_nosync(irq: ctx->te_irq); |
129 | disable_irq_nosync(irq: ctx->irq); |
130 | |
131 | writel(val: 0, addr: ctx->addr + DECON_VIDINTCON0); |
132 | } |
133 | |
134 | /* return number of starts/ends of frame transmissions since reset */ |
135 | static u32 decon_get_frame_count(struct decon_context *ctx, bool end) |
136 | { |
137 | u32 frm, pfrm, status, cnt = 2; |
138 | |
139 | /* To get consistent result repeat read until frame id is stable. |
140 | * Usually the loop will be executed once, in rare cases when the loop |
141 | * is executed at frame change time 2nd pass will be needed. |
142 | */ |
143 | frm = readl(addr: ctx->addr + DECON_CRFMID); |
144 | do { |
145 | status = readl(addr: ctx->addr + DECON_VIDCON1); |
146 | pfrm = frm; |
147 | frm = readl(addr: ctx->addr + DECON_CRFMID); |
148 | } while (frm != pfrm && --cnt); |
149 | |
150 | /* CRFMID is incremented on BPORCH in case of I80 and on VSYNC in case |
151 | * of RGB, it should be taken into account. |
152 | */ |
153 | if (!frm) |
154 | return 0; |
155 | |
156 | switch (status & (VIDCON1_VSTATUS_MASK | VIDCON1_I80_ACTIVE)) { |
157 | case VIDCON1_VSTATUS_VS: |
158 | if (!(ctx->crtc->i80_mode)) |
159 | --frm; |
160 | break; |
161 | case VIDCON1_VSTATUS_BP: |
162 | --frm; |
163 | break; |
164 | case VIDCON1_I80_ACTIVE: |
165 | case VIDCON1_VSTATUS_AC: |
166 | if (end) |
167 | --frm; |
168 | break; |
169 | default: |
170 | break; |
171 | } |
172 | |
173 | return frm; |
174 | } |
175 | |
176 | static void decon_setup_trigger(struct decon_context *ctx) |
177 | { |
178 | if (!ctx->crtc->i80_mode && !(ctx->out_type & I80_HW_TRG)) |
179 | return; |
180 | |
181 | if (!(ctx->out_type & I80_HW_TRG)) { |
182 | writel(TRIGCON_TRIGEN_PER_F | TRIGCON_TRIGEN_F | |
183 | TRIGCON_TE_AUTO_MASK | TRIGCON_SWTRIGEN, |
184 | addr: ctx->addr + DECON_TRIGCON); |
185 | return; |
186 | } |
187 | |
188 | writel(TRIGCON_TRIGEN_PER_F | TRIGCON_TRIGEN_F | TRIGCON_HWTRIGMASK |
189 | | TRIGCON_HWTRIGEN, addr: ctx->addr + DECON_TRIGCON); |
190 | |
191 | if (regmap_update_bits(map: ctx->sysreg, DSD_CFG_MUX, |
192 | DSD_CFG_MUX_TE_UNMASK_GLOBAL, val: ~0)) |
193 | DRM_DEV_ERROR(ctx->dev, "Cannot update sysreg.\n" ); |
194 | } |
195 | |
196 | static void decon_commit(struct exynos_drm_crtc *crtc) |
197 | { |
198 | struct decon_context *ctx = crtc->ctx; |
199 | struct drm_display_mode *m = &crtc->base.mode; |
200 | bool interlaced = false; |
201 | u32 val; |
202 | |
203 | if (ctx->out_type & IFTYPE_HDMI) { |
204 | m->crtc_hsync_start = m->crtc_hdisplay + 10; |
205 | m->crtc_hsync_end = m->crtc_htotal - 92; |
206 | m->crtc_vsync_start = m->crtc_vdisplay + 1; |
207 | m->crtc_vsync_end = m->crtc_vsync_start + 1; |
208 | if (m->flags & DRM_MODE_FLAG_INTERLACE) |
209 | interlaced = true; |
210 | } |
211 | |
212 | decon_setup_trigger(ctx); |
213 | |
214 | /* lcd on and use command if */ |
215 | val = VIDOUT_LCD_ON; |
216 | if (interlaced) |
217 | val |= VIDOUT_INTERLACE_EN_F; |
218 | if (crtc->i80_mode) { |
219 | val |= VIDOUT_COMMAND_IF; |
220 | } else { |
221 | val |= VIDOUT_RGB_IF; |
222 | } |
223 | |
224 | writel(val, addr: ctx->addr + DECON_VIDOUTCON0); |
225 | |
226 | if (interlaced) |
227 | val = VIDTCON2_LINEVAL(m->vdisplay / 2 - 1) | |
228 | VIDTCON2_HOZVAL(m->hdisplay - 1); |
229 | else |
230 | val = VIDTCON2_LINEVAL(m->vdisplay - 1) | |
231 | VIDTCON2_HOZVAL(m->hdisplay - 1); |
232 | writel(val, addr: ctx->addr + DECON_VIDTCON2); |
233 | |
234 | if (!crtc->i80_mode) { |
235 | int vbp = m->crtc_vtotal - m->crtc_vsync_end; |
236 | int vfp = m->crtc_vsync_start - m->crtc_vdisplay; |
237 | |
238 | if (interlaced) |
239 | vbp = vbp / 2 - 1; |
240 | val = VIDTCON00_VBPD_F(vbp - 1) | VIDTCON00_VFPD_F(vfp - 1); |
241 | writel(val, addr: ctx->addr + DECON_VIDTCON00); |
242 | |
243 | val = VIDTCON01_VSPW_F( |
244 | m->crtc_vsync_end - m->crtc_vsync_start - 1); |
245 | writel(val, addr: ctx->addr + DECON_VIDTCON01); |
246 | |
247 | val = VIDTCON10_HBPD_F( |
248 | m->crtc_htotal - m->crtc_hsync_end - 1) | |
249 | VIDTCON10_HFPD_F( |
250 | m->crtc_hsync_start - m->crtc_hdisplay - 1); |
251 | writel(val, addr: ctx->addr + DECON_VIDTCON10); |
252 | |
253 | val = VIDTCON11_HSPW_F( |
254 | m->crtc_hsync_end - m->crtc_hsync_start - 1); |
255 | writel(val, addr: ctx->addr + DECON_VIDTCON11); |
256 | } |
257 | |
258 | /* enable output and display signal */ |
259 | decon_set_bits(ctx, DECON_VIDCON0, VIDCON0_ENVID | VIDCON0_ENVID_F, val: ~0); |
260 | |
261 | decon_set_bits(ctx, DECON_UPDATE, STANDALONE_UPDATE_F, val: ~0); |
262 | } |
263 | |
264 | static void decon_win_set_bldeq(struct decon_context *ctx, unsigned int win, |
265 | unsigned int alpha, unsigned int pixel_alpha) |
266 | { |
267 | u32 mask = BLENDERQ_A_FUNC_F(0xf) | BLENDERQ_B_FUNC_F(0xf); |
268 | u32 val = 0; |
269 | |
270 | switch (pixel_alpha) { |
271 | case DRM_MODE_BLEND_PIXEL_NONE: |
272 | case DRM_MODE_BLEND_COVERAGE: |
273 | val |= BLENDERQ_A_FUNC_F(BLENDERQ_ALPHA_A); |
274 | val |= BLENDERQ_B_FUNC_F(BLENDERQ_ONE_MINUS_ALPHA_A); |
275 | break; |
276 | case DRM_MODE_BLEND_PREMULTI: |
277 | default: |
278 | if (alpha != DRM_BLEND_ALPHA_OPAQUE) { |
279 | val |= BLENDERQ_A_FUNC_F(BLENDERQ_ALPHA0); |
280 | val |= BLENDERQ_B_FUNC_F(BLENDERQ_ONE_MINUS_ALPHA_A); |
281 | } else { |
282 | val |= BLENDERQ_A_FUNC_F(BLENDERQ_ONE); |
283 | val |= BLENDERQ_B_FUNC_F(BLENDERQ_ONE_MINUS_ALPHA_A); |
284 | } |
285 | break; |
286 | } |
287 | decon_set_bits(ctx, DECON_BLENDERQx(win), mask, val); |
288 | } |
289 | |
290 | static void decon_win_set_bldmod(struct decon_context *ctx, unsigned int win, |
291 | unsigned int alpha, unsigned int pixel_alpha) |
292 | { |
293 | u32 win_alpha = alpha >> 8; |
294 | u32 val = 0; |
295 | |
296 | switch (pixel_alpha) { |
297 | case DRM_MODE_BLEND_PIXEL_NONE: |
298 | break; |
299 | case DRM_MODE_BLEND_COVERAGE: |
300 | case DRM_MODE_BLEND_PREMULTI: |
301 | default: |
302 | val |= WINCONx_ALPHA_SEL_F; |
303 | val |= WINCONx_BLD_PIX_F; |
304 | val |= WINCONx_ALPHA_MUL_F; |
305 | break; |
306 | } |
307 | decon_set_bits(ctx, DECON_WINCONx(win), WINCONx_BLEND_MODE_MASK, val); |
308 | |
309 | if (alpha != DRM_BLEND_ALPHA_OPAQUE) { |
310 | val = VIDOSD_Wx_ALPHA_R_F(win_alpha) | |
311 | VIDOSD_Wx_ALPHA_G_F(win_alpha) | |
312 | VIDOSD_Wx_ALPHA_B_F(win_alpha); |
313 | decon_set_bits(ctx, DECON_VIDOSDxC(win), |
314 | VIDOSDxC_ALPHA0_RGB_MASK, val); |
315 | decon_set_bits(ctx, DECON_BLENDCON, BLEND_NEW, BLEND_NEW); |
316 | } |
317 | } |
318 | |
319 | static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win, |
320 | struct drm_framebuffer *fb) |
321 | { |
322 | struct exynos_drm_plane *plane = &ctx->planes[win]; |
323 | struct exynos_drm_plane_state *state = |
324 | to_exynos_plane_state(state: plane->base.state); |
325 | unsigned int alpha = state->base.alpha; |
326 | unsigned int pixel_alpha; |
327 | unsigned long val; |
328 | |
329 | if (fb->format->has_alpha) |
330 | pixel_alpha = state->base.pixel_blend_mode; |
331 | else |
332 | pixel_alpha = DRM_MODE_BLEND_PIXEL_NONE; |
333 | |
334 | val = readl(addr: ctx->addr + DECON_WINCONx(win)); |
335 | val &= WINCONx_ENWIN_F; |
336 | |
337 | switch (fb->format->format) { |
338 | case DRM_FORMAT_XRGB1555: |
339 | val |= WINCONx_BPPMODE_16BPP_I1555; |
340 | val |= WINCONx_HAWSWP_F; |
341 | val |= WINCONx_BURSTLEN_16WORD; |
342 | break; |
343 | case DRM_FORMAT_RGB565: |
344 | val |= WINCONx_BPPMODE_16BPP_565; |
345 | val |= WINCONx_HAWSWP_F; |
346 | val |= WINCONx_BURSTLEN_16WORD; |
347 | break; |
348 | case DRM_FORMAT_XRGB8888: |
349 | val |= WINCONx_BPPMODE_24BPP_888; |
350 | val |= WINCONx_WSWP_F; |
351 | val |= WINCONx_BURSTLEN_16WORD; |
352 | break; |
353 | case DRM_FORMAT_ARGB8888: |
354 | default: |
355 | val |= WINCONx_BPPMODE_32BPP_A8888; |
356 | val |= WINCONx_WSWP_F; |
357 | val |= WINCONx_BURSTLEN_16WORD; |
358 | break; |
359 | } |
360 | |
361 | DRM_DEV_DEBUG_KMS(ctx->dev, "cpp = %u\n" , fb->format->cpp[0]); |
362 | |
363 | /* |
364 | * In case of exynos, setting dma-burst to 16Word causes permanent |
365 | * tearing for very small buffers, e.g. cursor buffer. Burst Mode |
366 | * switching which is based on plane size is not recommended as |
367 | * plane size varies a lot towards the end of the screen and rapid |
368 | * movement causes unstable DMA which results into iommu crash/tear. |
369 | */ |
370 | |
371 | if (fb->width < MIN_FB_WIDTH_FOR_16WORD_BURST) { |
372 | val &= ~WINCONx_BURSTLEN_MASK; |
373 | val |= WINCONx_BURSTLEN_8WORD; |
374 | } |
375 | decon_set_bits(ctx, DECON_WINCONx(win), mask: ~WINCONx_BLEND_MODE_MASK, val); |
376 | |
377 | if (win > 0) { |
378 | decon_win_set_bldmod(ctx, win, alpha, pixel_alpha); |
379 | decon_win_set_bldeq(ctx, win, alpha, pixel_alpha); |
380 | } |
381 | } |
382 | |
383 | static void decon_shadow_protect(struct decon_context *ctx, bool protect) |
384 | { |
385 | decon_set_bits(ctx, DECON_SHADOWCON, SHADOWCON_PROTECT_MASK, |
386 | val: protect ? ~0 : 0); |
387 | } |
388 | |
389 | static void decon_atomic_begin(struct exynos_drm_crtc *crtc) |
390 | { |
391 | struct decon_context *ctx = crtc->ctx; |
392 | |
393 | decon_shadow_protect(ctx, protect: true); |
394 | } |
395 | |
396 | #define BIT_VAL(x, e, s) (((x) & ((1 << ((e) - (s) + 1)) - 1)) << (s)) |
397 | #define COORDINATE_X(x) BIT_VAL((x), 23, 12) |
398 | #define COORDINATE_Y(x) BIT_VAL((x), 11, 0) |
399 | |
400 | static void decon_update_plane(struct exynos_drm_crtc *crtc, |
401 | struct exynos_drm_plane *plane) |
402 | { |
403 | struct exynos_drm_plane_state *state = |
404 | to_exynos_plane_state(state: plane->base.state); |
405 | struct decon_context *ctx = crtc->ctx; |
406 | struct drm_framebuffer *fb = state->base.fb; |
407 | unsigned int win = plane->index; |
408 | unsigned int cpp = fb->format->cpp[0]; |
409 | unsigned int pitch = fb->pitches[0]; |
410 | dma_addr_t dma_addr = exynos_drm_fb_dma_addr(fb, index: 0); |
411 | u32 val; |
412 | |
413 | if (crtc->base.mode.flags & DRM_MODE_FLAG_INTERLACE) { |
414 | val = COORDINATE_X(state->crtc.x) | |
415 | COORDINATE_Y(state->crtc.y / 2); |
416 | writel(val, addr: ctx->addr + DECON_VIDOSDxA(win)); |
417 | |
418 | val = COORDINATE_X(state->crtc.x + state->crtc.w - 1) | |
419 | COORDINATE_Y((state->crtc.y + state->crtc.h) / 2 - 1); |
420 | writel(val, addr: ctx->addr + DECON_VIDOSDxB(win)); |
421 | } else { |
422 | val = COORDINATE_X(state->crtc.x) | COORDINATE_Y(state->crtc.y); |
423 | writel(val, addr: ctx->addr + DECON_VIDOSDxA(win)); |
424 | |
425 | val = COORDINATE_X(state->crtc.x + state->crtc.w - 1) | |
426 | COORDINATE_Y(state->crtc.y + state->crtc.h - 1); |
427 | writel(val, addr: ctx->addr + DECON_VIDOSDxB(win)); |
428 | } |
429 | |
430 | val = VIDOSD_Wx_ALPHA_R_F(0xff) | VIDOSD_Wx_ALPHA_G_F(0xff) | |
431 | VIDOSD_Wx_ALPHA_B_F(0xff); |
432 | writel(val, addr: ctx->addr + DECON_VIDOSDxC(win)); |
433 | |
434 | val = VIDOSD_Wx_ALPHA_R_F(0x0) | VIDOSD_Wx_ALPHA_G_F(0x0) | |
435 | VIDOSD_Wx_ALPHA_B_F(0x0); |
436 | writel(val, addr: ctx->addr + DECON_VIDOSDxD(win)); |
437 | |
438 | writel(val: dma_addr, addr: ctx->addr + DECON_VIDW0xADD0B0(win)); |
439 | |
440 | val = dma_addr + pitch * state->src.h; |
441 | writel(val, addr: ctx->addr + DECON_VIDW0xADD1B0(win)); |
442 | |
443 | if (!(ctx->out_type & IFTYPE_HDMI)) |
444 | val = BIT_VAL(pitch - state->crtc.w * cpp, 27, 14) |
445 | | BIT_VAL(state->crtc.w * cpp, 13, 0); |
446 | else |
447 | val = BIT_VAL(pitch - state->crtc.w * cpp, 29, 15) |
448 | | BIT_VAL(state->crtc.w * cpp, 14, 0); |
449 | writel(val, addr: ctx->addr + DECON_VIDW0xADD2(win)); |
450 | |
451 | decon_win_set_pixfmt(ctx, win, fb); |
452 | |
453 | /* window enable */ |
454 | decon_set_bits(ctx, DECON_WINCONx(win), WINCONx_ENWIN_F, val: ~0); |
455 | } |
456 | |
457 | static void decon_disable_plane(struct exynos_drm_crtc *crtc, |
458 | struct exynos_drm_plane *plane) |
459 | { |
460 | struct decon_context *ctx = crtc->ctx; |
461 | unsigned int win = plane->index; |
462 | |
463 | decon_set_bits(ctx, DECON_WINCONx(win), WINCONx_ENWIN_F, val: 0); |
464 | } |
465 | |
466 | static void decon_atomic_flush(struct exynos_drm_crtc *crtc) |
467 | { |
468 | struct decon_context *ctx = crtc->ctx; |
469 | unsigned long flags; |
470 | |
471 | spin_lock_irqsave(&ctx->vblank_lock, flags); |
472 | |
473 | decon_shadow_protect(ctx, protect: false); |
474 | |
475 | decon_set_bits(ctx, DECON_UPDATE, STANDALONE_UPDATE_F, val: ~0); |
476 | |
477 | ctx->frame_id = decon_get_frame_count(ctx, end: true); |
478 | |
479 | exynos_crtc_handle_event(exynos_crtc: crtc); |
480 | |
481 | spin_unlock_irqrestore(lock: &ctx->vblank_lock, flags); |
482 | } |
483 | |
484 | static void decon_swreset(struct decon_context *ctx) |
485 | { |
486 | unsigned long flags; |
487 | u32 val; |
488 | int ret; |
489 | |
490 | writel(val: 0, addr: ctx->addr + DECON_VIDCON0); |
491 | readl_poll_timeout(ctx->addr + DECON_VIDCON0, val, |
492 | ~val & VIDCON0_STOP_STATUS, 12, 20000); |
493 | |
494 | writel(VIDCON0_SWRESET, addr: ctx->addr + DECON_VIDCON0); |
495 | ret = readl_poll_timeout(ctx->addr + DECON_VIDCON0, val, |
496 | ~val & VIDCON0_SWRESET, 12, 20000); |
497 | |
498 | WARN(ret < 0, "failed to software reset DECON\n" ); |
499 | |
500 | spin_lock_irqsave(&ctx->vblank_lock, flags); |
501 | ctx->frame_id = 0; |
502 | spin_unlock_irqrestore(lock: &ctx->vblank_lock, flags); |
503 | |
504 | if (!(ctx->out_type & IFTYPE_HDMI)) |
505 | return; |
506 | |
507 | writel(VIDCON0_CLKVALUP | VIDCON0_VLCKFREE, addr: ctx->addr + DECON_VIDCON0); |
508 | decon_set_bits(ctx, DECON_CMU, |
509 | CMU_CLKGAGE_MODE_SFR_F | CMU_CLKGAGE_MODE_MEM_F, val: ~0); |
510 | writel(VIDCON1_VCLK_RUN_VDEN_DISABLE, addr: ctx->addr + DECON_VIDCON1); |
511 | writel(CRCCTRL_CRCEN | CRCCTRL_CRCSTART_F | CRCCTRL_CRCCLKEN, |
512 | addr: ctx->addr + DECON_CRCCTRL); |
513 | } |
514 | |
515 | static void decon_atomic_enable(struct exynos_drm_crtc *crtc) |
516 | { |
517 | struct decon_context *ctx = crtc->ctx; |
518 | int ret; |
519 | |
520 | ret = pm_runtime_resume_and_get(dev: ctx->dev); |
521 | if (ret < 0) { |
522 | DRM_DEV_ERROR(ctx->dev, "failed to enable DECON device.\n" ); |
523 | return; |
524 | } |
525 | |
526 | exynos_drm_pipe_clk_enable(crtc, enable: true); |
527 | |
528 | decon_swreset(ctx); |
529 | |
530 | decon_commit(crtc: ctx->crtc); |
531 | } |
532 | |
533 | static void decon_atomic_disable(struct exynos_drm_crtc *crtc) |
534 | { |
535 | struct decon_context *ctx = crtc->ctx; |
536 | int i; |
537 | |
538 | if (!(ctx->out_type & I80_HW_TRG)) |
539 | synchronize_irq(irq: ctx->te_irq); |
540 | synchronize_irq(irq: ctx->irq); |
541 | |
542 | /* |
543 | * We need to make sure that all windows are disabled before we |
544 | * suspend that connector. Otherwise we might try to scan from |
545 | * a destroyed buffer later. |
546 | */ |
547 | for (i = ctx->first_win; i < WINDOWS_NR; i++) |
548 | decon_disable_plane(crtc, plane: &ctx->planes[i]); |
549 | |
550 | decon_swreset(ctx); |
551 | |
552 | exynos_drm_pipe_clk_enable(crtc, enable: false); |
553 | |
554 | pm_runtime_put_sync(dev: ctx->dev); |
555 | } |
556 | |
557 | static irqreturn_t decon_te_irq_handler(int irq, void *dev_id) |
558 | { |
559 | struct decon_context *ctx = dev_id; |
560 | |
561 | decon_set_bits(ctx, DECON_TRIGCON, TRIGCON_SWTRIGCMD, val: ~0); |
562 | |
563 | return IRQ_HANDLED; |
564 | } |
565 | |
566 | static void decon_clear_channels(struct exynos_drm_crtc *crtc) |
567 | { |
568 | struct decon_context *ctx = crtc->ctx; |
569 | int win, i, ret; |
570 | |
571 | for (i = 0; i < ARRAY_SIZE(decon_clks_name); i++) { |
572 | ret = clk_prepare_enable(clk: ctx->clks[i]); |
573 | if (ret < 0) |
574 | goto err; |
575 | } |
576 | |
577 | decon_shadow_protect(ctx, protect: true); |
578 | for (win = 0; win < WINDOWS_NR; win++) |
579 | decon_set_bits(ctx, DECON_WINCONx(win), WINCONx_ENWIN_F, val: 0); |
580 | decon_shadow_protect(ctx, protect: false); |
581 | |
582 | decon_set_bits(ctx, DECON_UPDATE, STANDALONE_UPDATE_F, val: ~0); |
583 | |
584 | /* TODO: wait for possible vsync */ |
585 | msleep(msecs: 50); |
586 | |
587 | err: |
588 | while (--i >= 0) |
589 | clk_disable_unprepare(clk: ctx->clks[i]); |
590 | } |
591 | |
592 | static enum drm_mode_status decon_mode_valid(struct exynos_drm_crtc *crtc, |
593 | const struct drm_display_mode *mode) |
594 | { |
595 | struct decon_context *ctx = crtc->ctx; |
596 | |
597 | ctx->irq = crtc->i80_mode ? ctx->irq_lcd_sys : ctx->irq_vsync; |
598 | |
599 | if (ctx->irq) |
600 | return MODE_OK; |
601 | |
602 | dev_info(ctx->dev, "Sink requires %s mode, but appropriate interrupt is not provided.\n" , |
603 | crtc->i80_mode ? "command" : "video" ); |
604 | |
605 | return MODE_BAD; |
606 | } |
607 | |
608 | static const struct exynos_drm_crtc_ops decon_crtc_ops = { |
609 | .atomic_enable = decon_atomic_enable, |
610 | .atomic_disable = decon_atomic_disable, |
611 | .enable_vblank = decon_enable_vblank, |
612 | .disable_vblank = decon_disable_vblank, |
613 | .atomic_begin = decon_atomic_begin, |
614 | .update_plane = decon_update_plane, |
615 | .disable_plane = decon_disable_plane, |
616 | .mode_valid = decon_mode_valid, |
617 | .atomic_flush = decon_atomic_flush, |
618 | }; |
619 | |
620 | static int decon_bind(struct device *dev, struct device *master, void *data) |
621 | { |
622 | struct decon_context *ctx = dev_get_drvdata(dev); |
623 | struct drm_device *drm_dev = data; |
624 | struct exynos_drm_plane *exynos_plane; |
625 | enum exynos_drm_output_type out_type; |
626 | unsigned int win; |
627 | int ret; |
628 | |
629 | ctx->drm_dev = drm_dev; |
630 | |
631 | for (win = ctx->first_win; win < WINDOWS_NR; win++) { |
632 | ctx->configs[win].pixel_formats = decon_formats; |
633 | ctx->configs[win].num_pixel_formats = ARRAY_SIZE(decon_formats); |
634 | ctx->configs[win].zpos = win - ctx->first_win; |
635 | ctx->configs[win].type = decon_win_types[win]; |
636 | ctx->configs[win].capabilities = capabilities[win]; |
637 | |
638 | ret = exynos_plane_init(dev: drm_dev, exynos_plane: &ctx->planes[win], index: win, |
639 | config: &ctx->configs[win]); |
640 | if (ret) |
641 | return ret; |
642 | } |
643 | |
644 | exynos_plane = &ctx->planes[PRIMARY_WIN]; |
645 | out_type = (ctx->out_type & IFTYPE_HDMI) ? EXYNOS_DISPLAY_TYPE_HDMI |
646 | : EXYNOS_DISPLAY_TYPE_LCD; |
647 | ctx->crtc = exynos_drm_crtc_create(drm_dev, plane: &exynos_plane->base, |
648 | out_type, ops: &decon_crtc_ops, context: ctx); |
649 | if (IS_ERR(ptr: ctx->crtc)) |
650 | return PTR_ERR(ptr: ctx->crtc); |
651 | |
652 | decon_clear_channels(crtc: ctx->crtc); |
653 | |
654 | return exynos_drm_register_dma(drm: drm_dev, dev, dma_priv: &ctx->dma_priv); |
655 | } |
656 | |
657 | static void decon_unbind(struct device *dev, struct device *master, void *data) |
658 | { |
659 | struct decon_context *ctx = dev_get_drvdata(dev); |
660 | |
661 | decon_atomic_disable(crtc: ctx->crtc); |
662 | |
663 | /* detach this sub driver from iommu mapping if supported. */ |
664 | exynos_drm_unregister_dma(drm: ctx->drm_dev, dev: ctx->dev, dma_priv: &ctx->dma_priv); |
665 | } |
666 | |
667 | static const struct component_ops decon_component_ops = { |
668 | .bind = decon_bind, |
669 | .unbind = decon_unbind, |
670 | }; |
671 | |
672 | static void decon_handle_vblank(struct decon_context *ctx) |
673 | { |
674 | u32 frm; |
675 | |
676 | spin_lock(lock: &ctx->vblank_lock); |
677 | |
678 | frm = decon_get_frame_count(ctx, end: true); |
679 | |
680 | if (frm != ctx->frame_id) { |
681 | /* handle only if incremented, take care of wrap-around */ |
682 | if ((s32)(frm - ctx->frame_id) > 0) |
683 | drm_crtc_handle_vblank(crtc: &ctx->crtc->base); |
684 | ctx->frame_id = frm; |
685 | } |
686 | |
687 | spin_unlock(lock: &ctx->vblank_lock); |
688 | } |
689 | |
690 | static irqreturn_t decon_irq_handler(int irq, void *dev_id) |
691 | { |
692 | struct decon_context *ctx = dev_id; |
693 | u32 val; |
694 | |
695 | val = readl(addr: ctx->addr + DECON_VIDINTCON1); |
696 | val &= VIDINTCON1_INTFRMDONEPEND | VIDINTCON1_INTFRMPEND; |
697 | |
698 | if (val) { |
699 | writel(val, addr: ctx->addr + DECON_VIDINTCON1); |
700 | if (ctx->out_type & IFTYPE_HDMI) { |
701 | val = readl(addr: ctx->addr + DECON_VIDOUTCON0); |
702 | val &= VIDOUT_INTERLACE_EN_F | VIDOUT_INTERLACE_FIELD_F; |
703 | if (val == |
704 | (VIDOUT_INTERLACE_EN_F | VIDOUT_INTERLACE_FIELD_F)) |
705 | return IRQ_HANDLED; |
706 | } |
707 | decon_handle_vblank(ctx); |
708 | } |
709 | |
710 | return IRQ_HANDLED; |
711 | } |
712 | |
713 | static int exynos5433_decon_suspend(struct device *dev) |
714 | { |
715 | struct decon_context *ctx = dev_get_drvdata(dev); |
716 | int i = ARRAY_SIZE(decon_clks_name); |
717 | |
718 | while (--i >= 0) |
719 | clk_disable_unprepare(clk: ctx->clks[i]); |
720 | |
721 | return 0; |
722 | } |
723 | |
724 | static int exynos5433_decon_resume(struct device *dev) |
725 | { |
726 | struct decon_context *ctx = dev_get_drvdata(dev); |
727 | int i, ret; |
728 | |
729 | for (i = 0; i < ARRAY_SIZE(decon_clks_name); i++) { |
730 | ret = clk_prepare_enable(clk: ctx->clks[i]); |
731 | if (ret < 0) |
732 | goto err; |
733 | } |
734 | |
735 | return 0; |
736 | |
737 | err: |
738 | while (--i >= 0) |
739 | clk_disable_unprepare(clk: ctx->clks[i]); |
740 | |
741 | return ret; |
742 | } |
743 | |
744 | static DEFINE_RUNTIME_DEV_PM_OPS(exynos5433_decon_pm_ops, |
745 | exynos5433_decon_suspend, |
746 | exynos5433_decon_resume, NULL); |
747 | |
748 | static const struct of_device_id exynos5433_decon_driver_dt_match[] = { |
749 | { |
750 | .compatible = "samsung,exynos5433-decon" , |
751 | .data = (void *)I80_HW_TRG |
752 | }, |
753 | { |
754 | .compatible = "samsung,exynos5433-decon-tv" , |
755 | .data = (void *)(I80_HW_TRG | IFTYPE_HDMI) |
756 | }, |
757 | {}, |
758 | }; |
759 | MODULE_DEVICE_TABLE(of, exynos5433_decon_driver_dt_match); |
760 | |
761 | static int decon_conf_irq(struct decon_context *ctx, const char *name, |
762 | irq_handler_t handler, unsigned long int flags) |
763 | { |
764 | struct platform_device *pdev = to_platform_device(ctx->dev); |
765 | int ret, irq = platform_get_irq_byname(pdev, name); |
766 | |
767 | if (irq < 0) { |
768 | switch (irq) { |
769 | case -EPROBE_DEFER: |
770 | return irq; |
771 | case -ENODATA: |
772 | case -ENXIO: |
773 | return 0; |
774 | default: |
775 | dev_err(ctx->dev, "IRQ %s get failed, %d\n" , name, irq); |
776 | return irq; |
777 | } |
778 | } |
779 | ret = devm_request_irq(dev: ctx->dev, irq, handler, |
780 | irqflags: flags | IRQF_NO_AUTOEN, devname: "drm_decon" , dev_id: ctx); |
781 | if (ret < 0) { |
782 | dev_err(ctx->dev, "IRQ %s request failed\n" , name); |
783 | return ret; |
784 | } |
785 | |
786 | return irq; |
787 | } |
788 | |
789 | static int exynos5433_decon_probe(struct platform_device *pdev) |
790 | { |
791 | struct device *dev = &pdev->dev; |
792 | struct decon_context *ctx; |
793 | int ret; |
794 | int i; |
795 | |
796 | ctx = devm_kzalloc(dev, size: sizeof(*ctx), GFP_KERNEL); |
797 | if (!ctx) |
798 | return -ENOMEM; |
799 | |
800 | ctx->dev = dev; |
801 | ctx->out_type = (unsigned long)of_device_get_match_data(dev); |
802 | spin_lock_init(&ctx->vblank_lock); |
803 | |
804 | if (ctx->out_type & IFTYPE_HDMI) |
805 | ctx->first_win = 1; |
806 | |
807 | for (i = 0; i < ARRAY_SIZE(decon_clks_name); i++) { |
808 | struct clk *clk; |
809 | |
810 | clk = devm_clk_get(dev: ctx->dev, id: decon_clks_name[i]); |
811 | if (IS_ERR(ptr: clk)) |
812 | return PTR_ERR(ptr: clk); |
813 | |
814 | ctx->clks[i] = clk; |
815 | } |
816 | |
817 | ctx->addr = devm_platform_ioremap_resource(pdev, index: 0); |
818 | if (IS_ERR(ptr: ctx->addr)) |
819 | return PTR_ERR(ptr: ctx->addr); |
820 | |
821 | ret = decon_conf_irq(ctx, name: "vsync" , handler: decon_irq_handler, flags: 0); |
822 | if (ret < 0) |
823 | return ret; |
824 | ctx->irq_vsync = ret; |
825 | |
826 | ret = decon_conf_irq(ctx, name: "lcd_sys" , handler: decon_irq_handler, flags: 0); |
827 | if (ret < 0) |
828 | return ret; |
829 | ctx->irq_lcd_sys = ret; |
830 | |
831 | ret = decon_conf_irq(ctx, name: "te" , handler: decon_te_irq_handler, |
832 | IRQF_TRIGGER_RISING); |
833 | if (ret < 0) |
834 | return ret; |
835 | if (ret) { |
836 | ctx->te_irq = ret; |
837 | ctx->out_type &= ~I80_HW_TRG; |
838 | } |
839 | |
840 | if (ctx->out_type & I80_HW_TRG) { |
841 | ctx->sysreg = syscon_regmap_lookup_by_phandle(np: dev->of_node, |
842 | property: "samsung,disp-sysreg" ); |
843 | if (IS_ERR(ptr: ctx->sysreg)) { |
844 | dev_err(dev, "failed to get system register\n" ); |
845 | return PTR_ERR(ptr: ctx->sysreg); |
846 | } |
847 | } |
848 | |
849 | platform_set_drvdata(pdev, data: ctx); |
850 | |
851 | pm_runtime_enable(dev); |
852 | |
853 | ret = component_add(dev, &decon_component_ops); |
854 | if (ret) |
855 | goto err_disable_pm_runtime; |
856 | |
857 | return 0; |
858 | |
859 | err_disable_pm_runtime: |
860 | pm_runtime_disable(dev); |
861 | |
862 | return ret; |
863 | } |
864 | |
865 | static void exynos5433_decon_remove(struct platform_device *pdev) |
866 | { |
867 | pm_runtime_disable(dev: &pdev->dev); |
868 | |
869 | component_del(&pdev->dev, &decon_component_ops); |
870 | } |
871 | |
872 | struct platform_driver exynos5433_decon_driver = { |
873 | .probe = exynos5433_decon_probe, |
874 | .remove_new = exynos5433_decon_remove, |
875 | .driver = { |
876 | .name = "exynos5433-decon" , |
877 | .pm = pm_ptr(&exynos5433_decon_pm_ops), |
878 | .of_match_table = exynos5433_decon_driver_dt_match, |
879 | }, |
880 | }; |
881 | |