[go: nahoru, domu]

1/* exynos_drm_fimd.c
2 *
3 * Copyright (C) 2011 Samsung Electronics Co.Ltd
4 * Authors:
5 *	Joonyoung Shim <jy0922.shim@samsung.com>
6 *	Inki Dae <inki.dae@samsung.com>
7 *
8 * This program is free software; you can redistribute  it and/or modify it
9 * under  the terms of  the GNU General  Public License as published by the
10 * Free Software Foundation;  either version 2 of the  License, or (at your
11 * option) any later version.
12 *
13 */
14#include <drm/drmP.h>
15
16#include <linux/kernel.h>
17#include <linux/platform_device.h>
18#include <linux/clk.h>
19#include <linux/of.h>
20#include <linux/of_device.h>
21#include <linux/pm_runtime.h>
22#include <linux/component.h>
23#include <linux/mfd/syscon.h>
24#include <linux/regmap.h>
25
26#include <video/of_display_timing.h>
27#include <video/of_videomode.h>
28#include <video/samsung_fimd.h>
29#include <drm/exynos_drm.h>
30
31#include "exynos_drm_drv.h"
32#include "exynos_drm_fbdev.h"
33#include "exynos_drm_crtc.h"
34#include "exynos_drm_iommu.h"
35
36/*
37 * FIMD stands for Fully Interactive Mobile Display and
38 * as a display controller, it transfers contents drawn on memory
39 * to a LCD Panel through Display Interfaces such as RGB or
40 * CPU Interface.
41 */
42
43#define FIMD_DEFAULT_FRAMERATE 60
44#define MIN_FB_WIDTH_FOR_16WORD_BURST 128
45
46/* position control register for hardware window 0, 2 ~ 4.*/
47#define VIDOSD_A(win)		(VIDOSD_BASE + 0x00 + (win) * 16)
48#define VIDOSD_B(win)		(VIDOSD_BASE + 0x04 + (win) * 16)
49/*
50 * size control register for hardware windows 0 and alpha control register
51 * for hardware windows 1 ~ 4
52 */
53#define VIDOSD_C(win)		(VIDOSD_BASE + 0x08 + (win) * 16)
54/* size control register for hardware windows 1 ~ 2. */
55#define VIDOSD_D(win)		(VIDOSD_BASE + 0x0C + (win) * 16)
56
57#define VIDWx_BUF_START(win, buf)	(VIDW_BUF_START(buf) + (win) * 8)
58#define VIDWx_BUF_END(win, buf)		(VIDW_BUF_END(buf) + (win) * 8)
59#define VIDWx_BUF_SIZE(win, buf)	(VIDW_BUF_SIZE(buf) + (win) * 4)
60
61/* color key control register for hardware window 1 ~ 4. */
62#define WKEYCON0_BASE(x)		((WKEYCON0 + 0x140) + ((x - 1) * 8))
63/* color key value register for hardware window 1 ~ 4. */
64#define WKEYCON1_BASE(x)		((WKEYCON1 + 0x140) + ((x - 1) * 8))
65
66/* I80 / RGB trigger control register */
67#define TRIGCON				0x1A4
68#define TRGMODE_I80_RGB_ENABLE_I80	(1 << 0)
69#define SWTRGCMD_I80_RGB_ENABLE		(1 << 1)
70
71/* display mode change control register except exynos4 */
72#define VIDOUT_CON			0x000
73#define VIDOUT_CON_F_I80_LDI0		(0x2 << 8)
74
75/* I80 interface control for main LDI register */
76#define I80IFCONFAx(x)			(0x1B0 + (x) * 4)
77#define I80IFCONFBx(x)			(0x1B8 + (x) * 4)
78#define LCD_CS_SETUP(x)			((x) << 16)
79#define LCD_WR_SETUP(x)			((x) << 12)
80#define LCD_WR_ACTIVE(x)		((x) << 8)
81#define LCD_WR_HOLD(x)			((x) << 4)
82#define I80IFEN_ENABLE			(1 << 0)
83
84/* FIMD has totally five hardware windows. */
85#define WINDOWS_NR	5
86
87#define get_fimd_manager(mgr)	platform_get_drvdata(to_platform_device(dev))
88
89struct fimd_driver_data {
90	unsigned int timing_base;
91	unsigned int lcdblk_offset;
92	unsigned int lcdblk_vt_shift;
93	unsigned int lcdblk_bypass_shift;
94
95	unsigned int has_shadowcon:1;
96	unsigned int has_clksel:1;
97	unsigned int has_limited_fmt:1;
98	unsigned int has_vidoutcon:1;
99};
100
101static struct fimd_driver_data s3c64xx_fimd_driver_data = {
102	.timing_base = 0x0,
103	.has_clksel = 1,
104	.has_limited_fmt = 1,
105};
106
107static struct fimd_driver_data exynos3_fimd_driver_data = {
108	.timing_base = 0x20000,
109	.lcdblk_offset = 0x210,
110	.lcdblk_bypass_shift = 1,
111	.has_shadowcon = 1,
112	.has_vidoutcon = 1,
113};
114
115static struct fimd_driver_data exynos4_fimd_driver_data = {
116	.timing_base = 0x0,
117	.lcdblk_offset = 0x210,
118	.lcdblk_vt_shift = 10,
119	.lcdblk_bypass_shift = 1,
120	.has_shadowcon = 1,
121};
122
123static struct fimd_driver_data exynos5_fimd_driver_data = {
124	.timing_base = 0x20000,
125	.lcdblk_offset = 0x214,
126	.lcdblk_vt_shift = 24,
127	.lcdblk_bypass_shift = 15,
128	.has_shadowcon = 1,
129	.has_vidoutcon = 1,
130};
131
132struct fimd_win_data {
133	unsigned int		offset_x;
134	unsigned int		offset_y;
135	unsigned int		ovl_width;
136	unsigned int		ovl_height;
137	unsigned int		fb_width;
138	unsigned int		fb_height;
139	unsigned int		bpp;
140	unsigned int		pixel_format;
141	dma_addr_t		dma_addr;
142	unsigned int		buf_offsize;
143	unsigned int		line_size;	/* bytes */
144	bool			enabled;
145	bool			resume;
146};
147
148struct fimd_context {
149	struct device			*dev;
150	struct drm_device		*drm_dev;
151	struct clk			*bus_clk;
152	struct clk			*lcd_clk;
153	void __iomem			*regs;
154	struct regmap			*sysreg;
155	struct drm_display_mode		mode;
156	struct fimd_win_data		win_data[WINDOWS_NR];
157	unsigned int			default_win;
158	unsigned long			irq_flags;
159	u32				vidcon0;
160	u32				vidcon1;
161	u32				vidout_con;
162	u32				i80ifcon;
163	bool				i80_if;
164	bool				suspended;
165	int				pipe;
166	wait_queue_head_t		wait_vsync_queue;
167	atomic_t			wait_vsync_event;
168	atomic_t			win_updated;
169	atomic_t			triggering;
170
171	struct exynos_drm_panel_info panel;
172	struct fimd_driver_data *driver_data;
173	struct exynos_drm_display *display;
174};
175
176static const struct of_device_id fimd_driver_dt_match[] = {
177	{ .compatible = "samsung,s3c6400-fimd",
178	  .data = &s3c64xx_fimd_driver_data },
179	{ .compatible = "samsung,exynos3250-fimd",
180	  .data = &exynos3_fimd_driver_data },
181	{ .compatible = "samsung,exynos4210-fimd",
182	  .data = &exynos4_fimd_driver_data },
183	{ .compatible = "samsung,exynos5250-fimd",
184	  .data = &exynos5_fimd_driver_data },
185	{},
186};
187MODULE_DEVICE_TABLE(of, fimd_driver_dt_match);
188
189static inline struct fimd_driver_data *drm_fimd_get_driver_data(
190	struct platform_device *pdev)
191{
192	const struct of_device_id *of_id =
193			of_match_device(fimd_driver_dt_match, &pdev->dev);
194
195	return (struct fimd_driver_data *)of_id->data;
196}
197
198static void fimd_wait_for_vblank(struct exynos_drm_manager *mgr)
199{
200	struct fimd_context *ctx = mgr->ctx;
201
202	if (ctx->suspended)
203		return;
204
205	atomic_set(&ctx->wait_vsync_event, 1);
206
207	/*
208	 * wait for FIMD to signal VSYNC interrupt or return after
209	 * timeout which is set to 50ms (refresh rate of 20).
210	 */
211	if (!wait_event_timeout(ctx->wait_vsync_queue,
212				!atomic_read(&ctx->wait_vsync_event),
213				HZ/20))
214		DRM_DEBUG_KMS("vblank wait timed out.\n");
215}
216
217static void fimd_clear_channel(struct exynos_drm_manager *mgr)
218{
219	struct fimd_context *ctx = mgr->ctx;
220	int win, ch_enabled = 0;
221
222	DRM_DEBUG_KMS("%s\n", __FILE__);
223
224	/* Check if any channel is enabled. */
225	for (win = 0; win < WINDOWS_NR; win++) {
226		u32 val = readl(ctx->regs + WINCON(win));
227
228		if (val & WINCONx_ENWIN) {
229			/* wincon */
230			val &= ~WINCONx_ENWIN;
231			writel(val, ctx->regs + WINCON(win));
232
233			/* unprotect windows */
234			if (ctx->driver_data->has_shadowcon) {
235				val = readl(ctx->regs + SHADOWCON);
236				val &= ~SHADOWCON_CHx_ENABLE(win);
237				writel(val, ctx->regs + SHADOWCON);
238			}
239			ch_enabled = 1;
240		}
241	}
242
243	/* Wait for vsync, as disable channel takes effect at next vsync */
244	if (ch_enabled) {
245		unsigned int state = ctx->suspended;
246
247		ctx->suspended = 0;
248		fimd_wait_for_vblank(mgr);
249		ctx->suspended = state;
250	}
251}
252
253static int fimd_mgr_initialize(struct exynos_drm_manager *mgr,
254			struct drm_device *drm_dev)
255{
256	struct fimd_context *ctx = mgr->ctx;
257	struct exynos_drm_private *priv;
258	priv = drm_dev->dev_private;
259
260	mgr->drm_dev = ctx->drm_dev = drm_dev;
261	mgr->pipe = ctx->pipe = priv->pipe++;
262
263	/* attach this sub driver to iommu mapping if supported. */
264	if (is_drm_iommu_supported(ctx->drm_dev)) {
265		/*
266		 * If any channel is already active, iommu will throw
267		 * a PAGE FAULT when enabled. So clear any channel if enabled.
268		 */
269		fimd_clear_channel(mgr);
270		drm_iommu_attach_device(ctx->drm_dev, ctx->dev);
271	}
272
273	return 0;
274}
275
276static void fimd_mgr_remove(struct exynos_drm_manager *mgr)
277{
278	struct fimd_context *ctx = mgr->ctx;
279
280	/* detach this sub driver from iommu mapping if supported. */
281	if (is_drm_iommu_supported(ctx->drm_dev))
282		drm_iommu_detach_device(ctx->drm_dev, ctx->dev);
283}
284
285static u32 fimd_calc_clkdiv(struct fimd_context *ctx,
286		const struct drm_display_mode *mode)
287{
288	unsigned long ideal_clk = mode->htotal * mode->vtotal * mode->vrefresh;
289	u32 clkdiv;
290
291	if (ctx->i80_if) {
292		/*
293		 * The frame done interrupt should be occurred prior to the
294		 * next TE signal.
295		 */
296		ideal_clk *= 2;
297	}
298
299	/* Find the clock divider value that gets us closest to ideal_clk */
300	clkdiv = DIV_ROUND_UP(clk_get_rate(ctx->lcd_clk), ideal_clk);
301
302	return (clkdiv < 0x100) ? clkdiv : 0xff;
303}
304
305static bool fimd_mode_fixup(struct exynos_drm_manager *mgr,
306		const struct drm_display_mode *mode,
307		struct drm_display_mode *adjusted_mode)
308{
309	if (adjusted_mode->vrefresh == 0)
310		adjusted_mode->vrefresh = FIMD_DEFAULT_FRAMERATE;
311
312	return true;
313}
314
315static void fimd_mode_set(struct exynos_drm_manager *mgr,
316		const struct drm_display_mode *in_mode)
317{
318	struct fimd_context *ctx = mgr->ctx;
319
320	drm_mode_copy(&ctx->mode, in_mode);
321}
322
323static void fimd_commit(struct exynos_drm_manager *mgr)
324{
325	struct fimd_context *ctx = mgr->ctx;
326	struct drm_display_mode *mode = &ctx->mode;
327	struct fimd_driver_data *driver_data = ctx->driver_data;
328	void *timing_base = ctx->regs + driver_data->timing_base;
329	u32 val, clkdiv;
330
331	if (ctx->suspended)
332		return;
333
334	/* nothing to do if we haven't set the mode yet */
335	if (mode->htotal == 0 || mode->vtotal == 0)
336		return;
337
338	if (ctx->i80_if) {
339		val = ctx->i80ifcon | I80IFEN_ENABLE;
340		writel(val, timing_base + I80IFCONFAx(0));
341
342		/* disable auto frame rate */
343		writel(0, timing_base + I80IFCONFBx(0));
344
345		/* set video type selection to I80 interface */
346		if (ctx->sysreg && regmap_update_bits(ctx->sysreg,
347					driver_data->lcdblk_offset,
348					0x3 << driver_data->lcdblk_vt_shift,
349					0x1 << driver_data->lcdblk_vt_shift)) {
350			DRM_ERROR("Failed to update sysreg for I80 i/f.\n");
351			return;
352		}
353	} else {
354		int vsync_len, vbpd, vfpd, hsync_len, hbpd, hfpd;
355		u32 vidcon1;
356
357		/* setup polarity values */
358		vidcon1 = ctx->vidcon1;
359		if (mode->flags & DRM_MODE_FLAG_NVSYNC)
360			vidcon1 |= VIDCON1_INV_VSYNC;
361		if (mode->flags & DRM_MODE_FLAG_NHSYNC)
362			vidcon1 |= VIDCON1_INV_HSYNC;
363		writel(vidcon1, ctx->regs + driver_data->timing_base + VIDCON1);
364
365		/* setup vertical timing values. */
366		vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
367		vbpd = mode->crtc_vtotal - mode->crtc_vsync_end;
368		vfpd = mode->crtc_vsync_start - mode->crtc_vdisplay;
369
370		val = VIDTCON0_VBPD(vbpd - 1) |
371			VIDTCON0_VFPD(vfpd - 1) |
372			VIDTCON0_VSPW(vsync_len - 1);
373		writel(val, ctx->regs + driver_data->timing_base + VIDTCON0);
374
375		/* setup horizontal timing values.  */
376		hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
377		hbpd = mode->crtc_htotal - mode->crtc_hsync_end;
378		hfpd = mode->crtc_hsync_start - mode->crtc_hdisplay;
379
380		val = VIDTCON1_HBPD(hbpd - 1) |
381			VIDTCON1_HFPD(hfpd - 1) |
382			VIDTCON1_HSPW(hsync_len - 1);
383		writel(val, ctx->regs + driver_data->timing_base + VIDTCON1);
384	}
385
386	if (driver_data->has_vidoutcon)
387		writel(ctx->vidout_con, timing_base + VIDOUT_CON);
388
389	/* set bypass selection */
390	if (ctx->sysreg && regmap_update_bits(ctx->sysreg,
391				driver_data->lcdblk_offset,
392				0x1 << driver_data->lcdblk_bypass_shift,
393				0x1 << driver_data->lcdblk_bypass_shift)) {
394		DRM_ERROR("Failed to update sysreg for bypass setting.\n");
395		return;
396	}
397
398	/* setup horizontal and vertical display size. */
399	val = VIDTCON2_LINEVAL(mode->vdisplay - 1) |
400	       VIDTCON2_HOZVAL(mode->hdisplay - 1) |
401	       VIDTCON2_LINEVAL_E(mode->vdisplay - 1) |
402	       VIDTCON2_HOZVAL_E(mode->hdisplay - 1);
403	writel(val, ctx->regs + driver_data->timing_base + VIDTCON2);
404
405	/*
406	 * fields of register with prefix '_F' would be updated
407	 * at vsync(same as dma start)
408	 */
409	val = ctx->vidcon0;
410	val |= VIDCON0_ENVID | VIDCON0_ENVID_F;
411
412	if (ctx->driver_data->has_clksel)
413		val |= VIDCON0_CLKSEL_LCD;
414
415	clkdiv = fimd_calc_clkdiv(ctx, mode);
416	if (clkdiv > 1)
417		val |= VIDCON0_CLKVAL_F(clkdiv - 1) | VIDCON0_CLKDIR;
418
419	writel(val, ctx->regs + VIDCON0);
420}
421
422static int fimd_enable_vblank(struct exynos_drm_manager *mgr)
423{
424	struct fimd_context *ctx = mgr->ctx;
425	u32 val;
426
427	if (ctx->suspended)
428		return -EPERM;
429
430	if (!test_and_set_bit(0, &ctx->irq_flags)) {
431		val = readl(ctx->regs + VIDINTCON0);
432
433		val |= VIDINTCON0_INT_ENABLE;
434		val |= VIDINTCON0_INT_FRAME;
435
436		val &= ~VIDINTCON0_FRAMESEL0_MASK;
437		val |= VIDINTCON0_FRAMESEL0_VSYNC;
438		val &= ~VIDINTCON0_FRAMESEL1_MASK;
439		val |= VIDINTCON0_FRAMESEL1_NONE;
440
441		writel(val, ctx->regs + VIDINTCON0);
442	}
443
444	return 0;
445}
446
447static void fimd_disable_vblank(struct exynos_drm_manager *mgr)
448{
449	struct fimd_context *ctx = mgr->ctx;
450	u32 val;
451
452	if (ctx->suspended)
453		return;
454
455	if (test_and_clear_bit(0, &ctx->irq_flags)) {
456		val = readl(ctx->regs + VIDINTCON0);
457
458		val &= ~VIDINTCON0_INT_FRAME;
459		val &= ~VIDINTCON0_INT_ENABLE;
460
461		writel(val, ctx->regs + VIDINTCON0);
462	}
463}
464
465static void fimd_win_mode_set(struct exynos_drm_manager *mgr,
466			struct exynos_drm_overlay *overlay)
467{
468	struct fimd_context *ctx = mgr->ctx;
469	struct fimd_win_data *win_data;
470	int win;
471	unsigned long offset;
472
473	if (!overlay) {
474		DRM_ERROR("overlay is NULL\n");
475		return;
476	}
477
478	win = overlay->zpos;
479	if (win == DEFAULT_ZPOS)
480		win = ctx->default_win;
481
482	if (win < 0 || win >= WINDOWS_NR)
483		return;
484
485	offset = overlay->fb_x * (overlay->bpp >> 3);
486	offset += overlay->fb_y * overlay->pitch;
487
488	DRM_DEBUG_KMS("offset = 0x%lx, pitch = %x\n", offset, overlay->pitch);
489
490	win_data = &ctx->win_data[win];
491
492	win_data->offset_x = overlay->crtc_x;
493	win_data->offset_y = overlay->crtc_y;
494	win_data->ovl_width = overlay->crtc_width;
495	win_data->ovl_height = overlay->crtc_height;
496	win_data->fb_width = overlay->fb_width;
497	win_data->fb_height = overlay->fb_height;
498	win_data->dma_addr = overlay->dma_addr[0] + offset;
499	win_data->bpp = overlay->bpp;
500	win_data->pixel_format = overlay->pixel_format;
501	win_data->buf_offsize = (overlay->fb_width - overlay->crtc_width) *
502				(overlay->bpp >> 3);
503	win_data->line_size = overlay->crtc_width * (overlay->bpp >> 3);
504
505	DRM_DEBUG_KMS("offset_x = %d, offset_y = %d\n",
506			win_data->offset_x, win_data->offset_y);
507	DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
508			win_data->ovl_width, win_data->ovl_height);
509	DRM_DEBUG_KMS("paddr = 0x%lx\n", (unsigned long)win_data->dma_addr);
510	DRM_DEBUG_KMS("fb_width = %d, crtc_width = %d\n",
511			overlay->fb_width, overlay->crtc_width);
512}
513
514static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win)
515{
516	struct fimd_win_data *win_data = &ctx->win_data[win];
517	unsigned long val;
518
519	val = WINCONx_ENWIN;
520
521	/*
522	 * In case of s3c64xx, window 0 doesn't support alpha channel.
523	 * So the request format is ARGB8888 then change it to XRGB8888.
524	 */
525	if (ctx->driver_data->has_limited_fmt && !win) {
526		if (win_data->pixel_format == DRM_FORMAT_ARGB8888)
527			win_data->pixel_format = DRM_FORMAT_XRGB8888;
528	}
529
530	switch (win_data->pixel_format) {
531	case DRM_FORMAT_C8:
532		val |= WINCON0_BPPMODE_8BPP_PALETTE;
533		val |= WINCONx_BURSTLEN_8WORD;
534		val |= WINCONx_BYTSWP;
535		break;
536	case DRM_FORMAT_XRGB1555:
537		val |= WINCON0_BPPMODE_16BPP_1555;
538		val |= WINCONx_HAWSWP;
539		val |= WINCONx_BURSTLEN_16WORD;
540		break;
541	case DRM_FORMAT_RGB565:
542		val |= WINCON0_BPPMODE_16BPP_565;
543		val |= WINCONx_HAWSWP;
544		val |= WINCONx_BURSTLEN_16WORD;
545		break;
546	case DRM_FORMAT_XRGB8888:
547		val |= WINCON0_BPPMODE_24BPP_888;
548		val |= WINCONx_WSWP;
549		val |= WINCONx_BURSTLEN_16WORD;
550		break;
551	case DRM_FORMAT_ARGB8888:
552		val |= WINCON1_BPPMODE_25BPP_A1888
553			| WINCON1_BLD_PIX | WINCON1_ALPHA_SEL;
554		val |= WINCONx_WSWP;
555		val |= WINCONx_BURSTLEN_16WORD;
556		break;
557	default:
558		DRM_DEBUG_KMS("invalid pixel size so using unpacked 24bpp.\n");
559
560		val |= WINCON0_BPPMODE_24BPP_888;
561		val |= WINCONx_WSWP;
562		val |= WINCONx_BURSTLEN_16WORD;
563		break;
564	}
565
566	DRM_DEBUG_KMS("bpp = %d\n", win_data->bpp);
567
568	/*
569	 * In case of exynos, setting dma-burst to 16Word causes permanent
570	 * tearing for very small buffers, e.g. cursor buffer. Burst Mode
571	 * switching which is based on overlay size is not recommended as
572	 * overlay size varies alot towards the end of the screen and rapid
573	 * movement causes unstable DMA which results into iommu crash/tear.
574	 */
575
576	if (win_data->fb_width < MIN_FB_WIDTH_FOR_16WORD_BURST) {
577		val &= ~WINCONx_BURSTLEN_MASK;
578		val |= WINCONx_BURSTLEN_4WORD;
579	}
580
581	writel(val, ctx->regs + WINCON(win));
582}
583
584static void fimd_win_set_colkey(struct fimd_context *ctx, unsigned int win)
585{
586	unsigned int keycon0 = 0, keycon1 = 0;
587
588	keycon0 = ~(WxKEYCON0_KEYBL_EN | WxKEYCON0_KEYEN_F |
589			WxKEYCON0_DIRCON) | WxKEYCON0_COMPKEY(0);
590
591	keycon1 = WxKEYCON1_COLVAL(0xffffffff);
592
593	writel(keycon0, ctx->regs + WKEYCON0_BASE(win));
594	writel(keycon1, ctx->regs + WKEYCON1_BASE(win));
595}
596
597/**
598 * shadow_protect_win() - disable updating values from shadow registers at vsync
599 *
600 * @win: window to protect registers for
601 * @protect: 1 to protect (disable updates)
602 */
603static void fimd_shadow_protect_win(struct fimd_context *ctx,
604							int win, bool protect)
605{
606	u32 reg, bits, val;
607
608	if (ctx->driver_data->has_shadowcon) {
609		reg = SHADOWCON;
610		bits = SHADOWCON_WINx_PROTECT(win);
611	} else {
612		reg = PRTCON;
613		bits = PRTCON_PROTECT;
614	}
615
616	val = readl(ctx->regs + reg);
617	if (protect)
618		val |= bits;
619	else
620		val &= ~bits;
621	writel(val, ctx->regs + reg);
622}
623
624static void fimd_win_commit(struct exynos_drm_manager *mgr, int zpos)
625{
626	struct fimd_context *ctx = mgr->ctx;
627	struct fimd_win_data *win_data;
628	int win = zpos;
629	unsigned long val, alpha, size;
630	unsigned int last_x;
631	unsigned int last_y;
632
633	if (ctx->suspended)
634		return;
635
636	if (win == DEFAULT_ZPOS)
637		win = ctx->default_win;
638
639	if (win < 0 || win >= WINDOWS_NR)
640		return;
641
642	win_data = &ctx->win_data[win];
643
644	/* If suspended, enable this on resume */
645	if (ctx->suspended) {
646		win_data->resume = true;
647		return;
648	}
649
650	/*
651	 * SHADOWCON/PRTCON register is used for enabling timing.
652	 *
653	 * for example, once only width value of a register is set,
654	 * if the dma is started then fimd hardware could malfunction so
655	 * with protect window setting, the register fields with prefix '_F'
656	 * wouldn't be updated at vsync also but updated once unprotect window
657	 * is set.
658	 */
659
660	/* protect windows */
661	fimd_shadow_protect_win(ctx, win, true);
662
663	/* buffer start address */
664	val = (unsigned long)win_data->dma_addr;
665	writel(val, ctx->regs + VIDWx_BUF_START(win, 0));
666
667	/* buffer end address */
668	size = win_data->fb_width * win_data->ovl_height * (win_data->bpp >> 3);
669	val = (unsigned long)(win_data->dma_addr + size);
670	writel(val, ctx->regs + VIDWx_BUF_END(win, 0));
671
672	DRM_DEBUG_KMS("start addr = 0x%lx, end addr = 0x%lx, size = 0x%lx\n",
673			(unsigned long)win_data->dma_addr, val, size);
674	DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
675			win_data->ovl_width, win_data->ovl_height);
676
677	/* buffer size */
678	val = VIDW_BUF_SIZE_OFFSET(win_data->buf_offsize) |
679		VIDW_BUF_SIZE_PAGEWIDTH(win_data->line_size) |
680		VIDW_BUF_SIZE_OFFSET_E(win_data->buf_offsize) |
681		VIDW_BUF_SIZE_PAGEWIDTH_E(win_data->line_size);
682	writel(val, ctx->regs + VIDWx_BUF_SIZE(win, 0));
683
684	/* OSD position */
685	val = VIDOSDxA_TOPLEFT_X(win_data->offset_x) |
686		VIDOSDxA_TOPLEFT_Y(win_data->offset_y) |
687		VIDOSDxA_TOPLEFT_X_E(win_data->offset_x) |
688		VIDOSDxA_TOPLEFT_Y_E(win_data->offset_y);
689	writel(val, ctx->regs + VIDOSD_A(win));
690
691	last_x = win_data->offset_x + win_data->ovl_width;
692	if (last_x)
693		last_x--;
694	last_y = win_data->offset_y + win_data->ovl_height;
695	if (last_y)
696		last_y--;
697
698	val = VIDOSDxB_BOTRIGHT_X(last_x) | VIDOSDxB_BOTRIGHT_Y(last_y) |
699		VIDOSDxB_BOTRIGHT_X_E(last_x) | VIDOSDxB_BOTRIGHT_Y_E(last_y);
700
701	writel(val, ctx->regs + VIDOSD_B(win));
702
703	DRM_DEBUG_KMS("osd pos: tx = %d, ty = %d, bx = %d, by = %d\n",
704			win_data->offset_x, win_data->offset_y, last_x, last_y);
705
706	/* hardware window 0 doesn't support alpha channel. */
707	if (win != 0) {
708		/* OSD alpha */
709		alpha = VIDISD14C_ALPHA1_R(0xf) |
710			VIDISD14C_ALPHA1_G(0xf) |
711			VIDISD14C_ALPHA1_B(0xf);
712
713		writel(alpha, ctx->regs + VIDOSD_C(win));
714	}
715
716	/* OSD size */
717	if (win != 3 && win != 4) {
718		u32 offset = VIDOSD_D(win);
719		if (win == 0)
720			offset = VIDOSD_C(win);
721		val = win_data->ovl_width * win_data->ovl_height;
722		writel(val, ctx->regs + offset);
723
724		DRM_DEBUG_KMS("osd size = 0x%x\n", (unsigned int)val);
725	}
726
727	fimd_win_set_pixfmt(ctx, win);
728
729	/* hardware window 0 doesn't support color key. */
730	if (win != 0)
731		fimd_win_set_colkey(ctx, win);
732
733	/* wincon */
734	val = readl(ctx->regs + WINCON(win));
735	val |= WINCONx_ENWIN;
736	writel(val, ctx->regs + WINCON(win));
737
738	/* Enable DMA channel and unprotect windows */
739	fimd_shadow_protect_win(ctx, win, false);
740
741	if (ctx->driver_data->has_shadowcon) {
742		val = readl(ctx->regs + SHADOWCON);
743		val |= SHADOWCON_CHx_ENABLE(win);
744		writel(val, ctx->regs + SHADOWCON);
745	}
746
747	win_data->enabled = true;
748
749	if (ctx->i80_if)
750		atomic_set(&ctx->win_updated, 1);
751}
752
753static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos)
754{
755	struct fimd_context *ctx = mgr->ctx;
756	struct fimd_win_data *win_data;
757	int win = zpos;
758	u32 val;
759
760	if (win == DEFAULT_ZPOS)
761		win = ctx->default_win;
762
763	if (win < 0 || win >= WINDOWS_NR)
764		return;
765
766	win_data = &ctx->win_data[win];
767
768	if (ctx->suspended) {
769		/* do not resume this window*/
770		win_data->resume = false;
771		return;
772	}
773
774	/* protect windows */
775	fimd_shadow_protect_win(ctx, win, true);
776
777	/* wincon */
778	val = readl(ctx->regs + WINCON(win));
779	val &= ~WINCONx_ENWIN;
780	writel(val, ctx->regs + WINCON(win));
781
782	/* unprotect windows */
783	if (ctx->driver_data->has_shadowcon) {
784		val = readl(ctx->regs + SHADOWCON);
785		val &= ~SHADOWCON_CHx_ENABLE(win);
786		writel(val, ctx->regs + SHADOWCON);
787	}
788
789	fimd_shadow_protect_win(ctx, win, false);
790
791	win_data->enabled = false;
792}
793
794static void fimd_window_suspend(struct exynos_drm_manager *mgr)
795{
796	struct fimd_context *ctx = mgr->ctx;
797	struct fimd_win_data *win_data;
798	int i;
799
800	for (i = 0; i < WINDOWS_NR; i++) {
801		win_data = &ctx->win_data[i];
802		win_data->resume = win_data->enabled;
803		if (win_data->enabled)
804			fimd_win_disable(mgr, i);
805	}
806	fimd_wait_for_vblank(mgr);
807}
808
809static void fimd_window_resume(struct exynos_drm_manager *mgr)
810{
811	struct fimd_context *ctx = mgr->ctx;
812	struct fimd_win_data *win_data;
813	int i;
814
815	for (i = 0; i < WINDOWS_NR; i++) {
816		win_data = &ctx->win_data[i];
817		win_data->enabled = win_data->resume;
818		win_data->resume = false;
819	}
820}
821
822static void fimd_apply(struct exynos_drm_manager *mgr)
823{
824	struct fimd_context *ctx = mgr->ctx;
825	struct fimd_win_data *win_data;
826	int i;
827
828	for (i = 0; i < WINDOWS_NR; i++) {
829		win_data = &ctx->win_data[i];
830		if (win_data->enabled)
831			fimd_win_commit(mgr, i);
832		else
833			fimd_win_disable(mgr, i);
834	}
835
836	fimd_commit(mgr);
837}
838
839static int fimd_poweron(struct exynos_drm_manager *mgr)
840{
841	struct fimd_context *ctx = mgr->ctx;
842	int ret;
843
844	if (!ctx->suspended)
845		return 0;
846
847	ctx->suspended = false;
848
849	pm_runtime_get_sync(ctx->dev);
850
851	ret = clk_prepare_enable(ctx->bus_clk);
852	if (ret < 0) {
853		DRM_ERROR("Failed to prepare_enable the bus clk [%d]\n", ret);
854		goto bus_clk_err;
855	}
856
857	ret = clk_prepare_enable(ctx->lcd_clk);
858	if  (ret < 0) {
859		DRM_ERROR("Failed to prepare_enable the lcd clk [%d]\n", ret);
860		goto lcd_clk_err;
861	}
862
863	/* if vblank was enabled status, enable it again. */
864	if (test_and_clear_bit(0, &ctx->irq_flags)) {
865		ret = fimd_enable_vblank(mgr);
866		if (ret) {
867			DRM_ERROR("Failed to re-enable vblank [%d]\n", ret);
868			goto enable_vblank_err;
869		}
870	}
871
872	fimd_window_resume(mgr);
873
874	fimd_apply(mgr);
875
876	return 0;
877
878enable_vblank_err:
879	clk_disable_unprepare(ctx->lcd_clk);
880lcd_clk_err:
881	clk_disable_unprepare(ctx->bus_clk);
882bus_clk_err:
883	ctx->suspended = true;
884	return ret;
885}
886
887static int fimd_poweroff(struct exynos_drm_manager *mgr)
888{
889	struct fimd_context *ctx = mgr->ctx;
890
891	if (ctx->suspended)
892		return 0;
893
894	/*
895	 * We need to make sure that all windows are disabled before we
896	 * suspend that connector. Otherwise we might try to scan from
897	 * a destroyed buffer later.
898	 */
899	fimd_window_suspend(mgr);
900
901	clk_disable_unprepare(ctx->lcd_clk);
902	clk_disable_unprepare(ctx->bus_clk);
903
904	pm_runtime_put_sync(ctx->dev);
905
906	ctx->suspended = true;
907	return 0;
908}
909
910static void fimd_dpms(struct exynos_drm_manager *mgr, int mode)
911{
912	DRM_DEBUG_KMS("%s, %d\n", __FILE__, mode);
913
914	switch (mode) {
915	case DRM_MODE_DPMS_ON:
916		fimd_poweron(mgr);
917		break;
918	case DRM_MODE_DPMS_STANDBY:
919	case DRM_MODE_DPMS_SUSPEND:
920	case DRM_MODE_DPMS_OFF:
921		fimd_poweroff(mgr);
922		break;
923	default:
924		DRM_DEBUG_KMS("unspecified mode %d\n", mode);
925		break;
926	}
927}
928
929static void fimd_trigger(struct device *dev)
930{
931	struct exynos_drm_manager *mgr = get_fimd_manager(dev);
932	struct fimd_context *ctx = mgr->ctx;
933	struct fimd_driver_data *driver_data = ctx->driver_data;
934	void *timing_base = ctx->regs + driver_data->timing_base;
935	u32 reg;
936
937	atomic_set(&ctx->triggering, 1);
938
939	reg = readl(ctx->regs + VIDINTCON0);
940	reg |= (VIDINTCON0_INT_ENABLE | VIDINTCON0_INT_I80IFDONE |
941						VIDINTCON0_INT_SYSMAINCON);
942	writel(reg, ctx->regs + VIDINTCON0);
943
944	reg = readl(timing_base + TRIGCON);
945	reg |= (TRGMODE_I80_RGB_ENABLE_I80 | SWTRGCMD_I80_RGB_ENABLE);
946	writel(reg, timing_base + TRIGCON);
947}
948
949static void fimd_te_handler(struct exynos_drm_manager *mgr)
950{
951	struct fimd_context *ctx = mgr->ctx;
952
953	/* Checks the crtc is detached already from encoder */
954	if (ctx->pipe < 0 || !ctx->drm_dev)
955		return;
956
957	 /*
958	 * Skips to trigger if in triggering state, because multiple triggering
959	 * requests can cause panel reset.
960	 */
961	if (atomic_read(&ctx->triggering))
962		return;
963
964	/*
965	 * If there is a page flip request, triggers and handles the page flip
966	 * event so that current fb can be updated into panel GRAM.
967	 */
968	if (atomic_add_unless(&ctx->win_updated, -1, 0))
969		fimd_trigger(ctx->dev);
970
971	/* Wakes up vsync event queue */
972	if (atomic_read(&ctx->wait_vsync_event)) {
973		atomic_set(&ctx->wait_vsync_event, 0);
974		wake_up(&ctx->wait_vsync_queue);
975
976		if (!atomic_read(&ctx->triggering))
977			drm_handle_vblank(ctx->drm_dev, ctx->pipe);
978	}
979}
980
981static struct exynos_drm_manager_ops fimd_manager_ops = {
982	.dpms = fimd_dpms,
983	.mode_fixup = fimd_mode_fixup,
984	.mode_set = fimd_mode_set,
985	.commit = fimd_commit,
986	.enable_vblank = fimd_enable_vblank,
987	.disable_vblank = fimd_disable_vblank,
988	.wait_for_vblank = fimd_wait_for_vblank,
989	.win_mode_set = fimd_win_mode_set,
990	.win_commit = fimd_win_commit,
991	.win_disable = fimd_win_disable,
992	.te_handler = fimd_te_handler,
993};
994
995static struct exynos_drm_manager fimd_manager = {
996	.type = EXYNOS_DISPLAY_TYPE_LCD,
997	.ops = &fimd_manager_ops,
998};
999
1000static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
1001{
1002	struct fimd_context *ctx = (struct fimd_context *)dev_id;
1003	u32 val, clear_bit;
1004
1005	val = readl(ctx->regs + VIDINTCON1);
1006
1007	clear_bit = ctx->i80_if ? VIDINTCON1_INT_I80 : VIDINTCON1_INT_FRAME;
1008	if (val & clear_bit)
1009		writel(clear_bit, ctx->regs + VIDINTCON1);
1010
1011	/* check the crtc is detached already from encoder */
1012	if (ctx->pipe < 0 || !ctx->drm_dev)
1013		goto out;
1014
1015	if (ctx->i80_if) {
1016		/* unset I80 frame done interrupt */
1017		val = readl(ctx->regs + VIDINTCON0);
1018		val &= ~(VIDINTCON0_INT_I80IFDONE | VIDINTCON0_INT_SYSMAINCON);
1019		writel(val, ctx->regs + VIDINTCON0);
1020
1021		/* exit triggering mode */
1022		atomic_set(&ctx->triggering, 0);
1023
1024		drm_handle_vblank(ctx->drm_dev, ctx->pipe);
1025		exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
1026	} else {
1027		drm_handle_vblank(ctx->drm_dev, ctx->pipe);
1028		exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
1029
1030		/* set wait vsync event to zero and wake up queue. */
1031		if (atomic_read(&ctx->wait_vsync_event)) {
1032			atomic_set(&ctx->wait_vsync_event, 0);
1033			wake_up(&ctx->wait_vsync_queue);
1034		}
1035	}
1036
1037out:
1038	return IRQ_HANDLED;
1039}
1040
1041static int fimd_bind(struct device *dev, struct device *master, void *data)
1042{
1043	struct fimd_context *ctx = fimd_manager.ctx;
1044	struct drm_device *drm_dev = data;
1045
1046	fimd_mgr_initialize(&fimd_manager, drm_dev);
1047	exynos_drm_crtc_create(&fimd_manager);
1048	if (ctx->display)
1049		exynos_drm_create_enc_conn(drm_dev, ctx->display);
1050
1051	return 0;
1052
1053}
1054
1055static void fimd_unbind(struct device *dev, struct device *master,
1056			void *data)
1057{
1058	struct exynos_drm_manager *mgr = dev_get_drvdata(dev);
1059	struct fimd_context *ctx = fimd_manager.ctx;
1060
1061	fimd_dpms(mgr, DRM_MODE_DPMS_OFF);
1062
1063	if (ctx->display)
1064		exynos_dpi_remove(dev);
1065
1066	fimd_mgr_remove(mgr);
1067}
1068
1069static const struct component_ops fimd_component_ops = {
1070	.bind	= fimd_bind,
1071	.unbind = fimd_unbind,
1072};
1073
1074static int fimd_probe(struct platform_device *pdev)
1075{
1076	struct device *dev = &pdev->dev;
1077	struct fimd_context *ctx;
1078	struct device_node *i80_if_timings;
1079	struct resource *res;
1080	int ret = -EINVAL;
1081
1082	ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC,
1083					fimd_manager.type);
1084	if (ret)
1085		return ret;
1086
1087	if (!dev->of_node) {
1088		ret = -ENODEV;
1089		goto err_del_component;
1090	}
1091
1092	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
1093	if (!ctx) {
1094		ret = -ENOMEM;
1095		goto err_del_component;
1096	}
1097
1098	ctx->dev = dev;
1099	ctx->suspended = true;
1100	ctx->driver_data = drm_fimd_get_driver_data(pdev);
1101
1102	if (of_property_read_bool(dev->of_node, "samsung,invert-vden"))
1103		ctx->vidcon1 |= VIDCON1_INV_VDEN;
1104	if (of_property_read_bool(dev->of_node, "samsung,invert-vclk"))
1105		ctx->vidcon1 |= VIDCON1_INV_VCLK;
1106
1107	i80_if_timings = of_get_child_by_name(dev->of_node, "i80-if-timings");
1108	if (i80_if_timings) {
1109		u32 val;
1110
1111		ctx->i80_if = true;
1112
1113		if (ctx->driver_data->has_vidoutcon)
1114			ctx->vidout_con |= VIDOUT_CON_F_I80_LDI0;
1115		else
1116			ctx->vidcon0 |= VIDCON0_VIDOUT_I80_LDI0;
1117		/*
1118		 * The user manual describes that this "DSI_EN" bit is required
1119		 * to enable I80 24-bit data interface.
1120		 */
1121		ctx->vidcon0 |= VIDCON0_DSI_EN;
1122
1123		if (of_property_read_u32(i80_if_timings, "cs-setup", &val))
1124			val = 0;
1125		ctx->i80ifcon = LCD_CS_SETUP(val);
1126		if (of_property_read_u32(i80_if_timings, "wr-setup", &val))
1127			val = 0;
1128		ctx->i80ifcon |= LCD_WR_SETUP(val);
1129		if (of_property_read_u32(i80_if_timings, "wr-active", &val))
1130			val = 1;
1131		ctx->i80ifcon |= LCD_WR_ACTIVE(val);
1132		if (of_property_read_u32(i80_if_timings, "wr-hold", &val))
1133			val = 0;
1134		ctx->i80ifcon |= LCD_WR_HOLD(val);
1135	}
1136	of_node_put(i80_if_timings);
1137
1138	ctx->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
1139							"samsung,sysreg");
1140	if (IS_ERR(ctx->sysreg)) {
1141		dev_warn(dev, "failed to get system register.\n");
1142		ctx->sysreg = NULL;
1143	}
1144
1145	ctx->bus_clk = devm_clk_get(dev, "fimd");
1146	if (IS_ERR(ctx->bus_clk)) {
1147		dev_err(dev, "failed to get bus clock\n");
1148		ret = PTR_ERR(ctx->bus_clk);
1149		goto err_del_component;
1150	}
1151
1152	ctx->lcd_clk = devm_clk_get(dev, "sclk_fimd");
1153	if (IS_ERR(ctx->lcd_clk)) {
1154		dev_err(dev, "failed to get lcd clock\n");
1155		ret = PTR_ERR(ctx->lcd_clk);
1156		goto err_del_component;
1157	}
1158
1159	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1160
1161	ctx->regs = devm_ioremap_resource(dev, res);
1162	if (IS_ERR(ctx->regs)) {
1163		ret = PTR_ERR(ctx->regs);
1164		goto err_del_component;
1165	}
1166
1167	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
1168					   ctx->i80_if ? "lcd_sys" : "vsync");
1169	if (!res) {
1170		dev_err(dev, "irq request failed.\n");
1171		ret = -ENXIO;
1172		goto err_del_component;
1173	}
1174
1175	ret = devm_request_irq(dev, res->start, fimd_irq_handler,
1176							0, "drm_fimd", ctx);
1177	if (ret) {
1178		dev_err(dev, "irq request failed.\n");
1179		goto err_del_component;
1180	}
1181
1182	init_waitqueue_head(&ctx->wait_vsync_queue);
1183	atomic_set(&ctx->wait_vsync_event, 0);
1184
1185	platform_set_drvdata(pdev, &fimd_manager);
1186
1187	fimd_manager.ctx = ctx;
1188
1189	ctx->display = exynos_dpi_probe(dev);
1190	if (IS_ERR(ctx->display))
1191		return PTR_ERR(ctx->display);
1192
1193	pm_runtime_enable(&pdev->dev);
1194
1195	ret = component_add(&pdev->dev, &fimd_component_ops);
1196	if (ret)
1197		goto err_disable_pm_runtime;
1198
1199	return ret;
1200
1201err_disable_pm_runtime:
1202	pm_runtime_disable(&pdev->dev);
1203
1204err_del_component:
1205	exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
1206	return ret;
1207}
1208
1209static int fimd_remove(struct platform_device *pdev)
1210{
1211	pm_runtime_disable(&pdev->dev);
1212
1213	component_del(&pdev->dev, &fimd_component_ops);
1214	exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
1215
1216	return 0;
1217}
1218
1219struct platform_driver fimd_driver = {
1220	.probe		= fimd_probe,
1221	.remove		= fimd_remove,
1222	.driver		= {
1223		.name	= "exynos4-fb",
1224		.owner	= THIS_MODULE,
1225		.of_match_table = fimd_driver_dt_match,
1226	},
1227};
1228