11c248b7dSInki Dae /* exynos_drm_fimd.c
21c248b7dSInki Dae  *
31c248b7dSInki Dae  * Copyright (C) 2011 Samsung Electronics Co.Ltd
41c248b7dSInki Dae  * Authors:
51c248b7dSInki Dae  *	Joonyoung Shim <jy0922.shim@samsung.com>
61c248b7dSInki Dae  *	Inki Dae <inki.dae@samsung.com>
71c248b7dSInki Dae  *
81c248b7dSInki Dae  * This program is free software; you can redistribute  it and/or modify it
91c248b7dSInki Dae  * under  the terms of  the GNU General  Public License as published by the
101c248b7dSInki Dae  * Free Software Foundation;  either version 2 of the  License, or (at your
111c248b7dSInki Dae  * option) any later version.
121c248b7dSInki Dae  *
131c248b7dSInki Dae  */
14760285e7SDavid Howells #include <drm/drmP.h>
151c248b7dSInki Dae 
161c248b7dSInki Dae #include <linux/kernel.h>
171c248b7dSInki Dae #include <linux/platform_device.h>
181c248b7dSInki Dae #include <linux/clk.h>
193f1c781dSSachin Kamat #include <linux/of.h>
20d636ead8SJoonyoung Shim #include <linux/of_device.h>
21cb91f6a0SJoonyoung Shim #include <linux/pm_runtime.h>
22f37cd5e8SInki Dae #include <linux/component.h>
233854fab2SYoungJun Cho #include <linux/mfd/syscon.h>
243854fab2SYoungJun Cho #include <linux/regmap.h>
251c248b7dSInki Dae 
267f4596f4SVikas Sajjan #include <video/of_display_timing.h>
27111e6055SAndrzej Hajda #include <video/of_videomode.h>
285a213a55SLeela Krishna Amudala #include <video/samsung_fimd.h>
291c248b7dSInki Dae #include <drm/exynos_drm.h>
301c248b7dSInki Dae 
311c248b7dSInki Dae #include "exynos_drm_drv.h"
321c248b7dSInki Dae #include "exynos_drm_fbdev.h"
331c248b7dSInki Dae #include "exynos_drm_crtc.h"
34bcc5cd1cSInki Dae #include "exynos_drm_iommu.h"
351c248b7dSInki Dae 
361c248b7dSInki Dae /*
37b8654b37SSachin Kamat  * FIMD stands for Fully Interactive Mobile Display and
381c248b7dSInki Dae  * as a display controller, it transfers contents drawn on memory
391c248b7dSInki Dae  * to a LCD Panel through Display Interfaces such as RGB or
401c248b7dSInki Dae  * CPU Interface.
411c248b7dSInki Dae  */
421c248b7dSInki Dae 
43111e6055SAndrzej Hajda #define FIMD_DEFAULT_FRAMERATE 60
4466367461SRahul Sharma #define MIN_FB_WIDTH_FOR_16WORD_BURST 128
45111e6055SAndrzej Hajda 
461c248b7dSInki Dae /* position control register for hardware window 0, 2 ~ 4.*/
471c248b7dSInki Dae #define VIDOSD_A(win)		(VIDOSD_BASE + 0x00 + (win) * 16)
481c248b7dSInki Dae #define VIDOSD_B(win)		(VIDOSD_BASE + 0x04 + (win) * 16)
490f10cf14SLeela Krishna Amudala /*
500f10cf14SLeela Krishna Amudala  * size control register for hardware windows 0 and alpha control register
510f10cf14SLeela Krishna Amudala  * for hardware windows 1 ~ 4
520f10cf14SLeela Krishna Amudala  */
530f10cf14SLeela Krishna Amudala #define VIDOSD_C(win)		(VIDOSD_BASE + 0x08 + (win) * 16)
540f10cf14SLeela Krishna Amudala /* size control register for hardware windows 1 ~ 2. */
551c248b7dSInki Dae #define VIDOSD_D(win)		(VIDOSD_BASE + 0x0C + (win) * 16)
561c248b7dSInki Dae 
571c248b7dSInki Dae #define VIDWx_BUF_START(win, buf)	(VIDW_BUF_START(buf) + (win) * 8)
581c248b7dSInki Dae #define VIDWx_BUF_END(win, buf)		(VIDW_BUF_END(buf) + (win) * 8)
591c248b7dSInki Dae #define VIDWx_BUF_SIZE(win, buf)	(VIDW_BUF_SIZE(buf) + (win) * 4)
601c248b7dSInki Dae 
611c248b7dSInki Dae /* color key control register for hardware window 1 ~ 4. */
620f10cf14SLeela Krishna Amudala #define WKEYCON0_BASE(x)		((WKEYCON0 + 0x140) + ((x - 1) * 8))
631c248b7dSInki Dae /* color key value register for hardware window 1 ~ 4. */
640f10cf14SLeela Krishna Amudala #define WKEYCON1_BASE(x)		((WKEYCON1 + 0x140) + ((x - 1) * 8))
651c248b7dSInki Dae 
663854fab2SYoungJun Cho /* I80 / RGB trigger control register */
673854fab2SYoungJun Cho #define TRIGCON				0x1A4
683854fab2SYoungJun Cho #define TRGMODE_I80_RGB_ENABLE_I80	(1 << 0)
693854fab2SYoungJun Cho #define SWTRGCMD_I80_RGB_ENABLE		(1 << 1)
703854fab2SYoungJun Cho 
713854fab2SYoungJun Cho /* display mode change control register except exynos4 */
723854fab2SYoungJun Cho #define VIDOUT_CON			0x000
733854fab2SYoungJun Cho #define VIDOUT_CON_F_I80_LDI0		(0x2 << 8)
743854fab2SYoungJun Cho 
753854fab2SYoungJun Cho /* I80 interface control for main LDI register */
763854fab2SYoungJun Cho #define I80IFCONFAx(x)			(0x1B0 + (x) * 4)
773854fab2SYoungJun Cho #define I80IFCONFBx(x)			(0x1B8 + (x) * 4)
783854fab2SYoungJun Cho #define LCD_CS_SETUP(x)			((x) << 16)
793854fab2SYoungJun Cho #define LCD_WR_SETUP(x)			((x) << 12)
803854fab2SYoungJun Cho #define LCD_WR_ACTIVE(x)		((x) << 8)
813854fab2SYoungJun Cho #define LCD_WR_HOLD(x)			((x) << 4)
823854fab2SYoungJun Cho #define I80IFEN_ENABLE			(1 << 0)
833854fab2SYoungJun Cho 
841c248b7dSInki Dae /* FIMD has totally five hardware windows. */
851c248b7dSInki Dae #define WINDOWS_NR	5
861c248b7dSInki Dae 
87e2e13389SLeela Krishna Amudala struct fimd_driver_data {
88e2e13389SLeela Krishna Amudala 	unsigned int timing_base;
893854fab2SYoungJun Cho 	unsigned int lcdblk_offset;
903854fab2SYoungJun Cho 	unsigned int lcdblk_vt_shift;
913854fab2SYoungJun Cho 	unsigned int lcdblk_bypass_shift;
92de7af100STomasz Figa 
93de7af100STomasz Figa 	unsigned int has_shadowcon:1;
94411d9ed4STomasz Figa 	unsigned int has_clksel:1;
955cc4621aSInki Dae 	unsigned int has_limited_fmt:1;
963854fab2SYoungJun Cho 	unsigned int has_vidoutcon:1;
973c3c9c1dSJoonyoung Shim 	unsigned int has_vtsel:1;
98e2e13389SLeela Krishna Amudala };
99e2e13389SLeela Krishna Amudala 
100725ddeadSTomasz Figa static struct fimd_driver_data s3c64xx_fimd_driver_data = {
101725ddeadSTomasz Figa 	.timing_base = 0x0,
102725ddeadSTomasz Figa 	.has_clksel = 1,
1035cc4621aSInki Dae 	.has_limited_fmt = 1,
104725ddeadSTomasz Figa };
105725ddeadSTomasz Figa 
106d6ce7b58SInki Dae static struct fimd_driver_data exynos3_fimd_driver_data = {
107d6ce7b58SInki Dae 	.timing_base = 0x20000,
108d6ce7b58SInki Dae 	.lcdblk_offset = 0x210,
109d6ce7b58SInki Dae 	.lcdblk_bypass_shift = 1,
110d6ce7b58SInki Dae 	.has_shadowcon = 1,
111d6ce7b58SInki Dae 	.has_vidoutcon = 1,
112d6ce7b58SInki Dae };
113d6ce7b58SInki Dae 
1146ecf18f9SSachin Kamat static struct fimd_driver_data exynos4_fimd_driver_data = {
115e2e13389SLeela Krishna Amudala 	.timing_base = 0x0,
1163854fab2SYoungJun Cho 	.lcdblk_offset = 0x210,
1173854fab2SYoungJun Cho 	.lcdblk_vt_shift = 10,
1183854fab2SYoungJun Cho 	.lcdblk_bypass_shift = 1,
119de7af100STomasz Figa 	.has_shadowcon = 1,
1203c3c9c1dSJoonyoung Shim 	.has_vtsel = 1,
121e2e13389SLeela Krishna Amudala };
122e2e13389SLeela Krishna Amudala 
123dcb622aaSYoungJun Cho static struct fimd_driver_data exynos4415_fimd_driver_data = {
124dcb622aaSYoungJun Cho 	.timing_base = 0x20000,
125dcb622aaSYoungJun Cho 	.lcdblk_offset = 0x210,
126dcb622aaSYoungJun Cho 	.lcdblk_vt_shift = 10,
127dcb622aaSYoungJun Cho 	.lcdblk_bypass_shift = 1,
128dcb622aaSYoungJun Cho 	.has_shadowcon = 1,
129dcb622aaSYoungJun Cho 	.has_vidoutcon = 1,
1303c3c9c1dSJoonyoung Shim 	.has_vtsel = 1,
131dcb622aaSYoungJun Cho };
132dcb622aaSYoungJun Cho 
1336ecf18f9SSachin Kamat static struct fimd_driver_data exynos5_fimd_driver_data = {
134e2e13389SLeela Krishna Amudala 	.timing_base = 0x20000,
1353854fab2SYoungJun Cho 	.lcdblk_offset = 0x214,
1363854fab2SYoungJun Cho 	.lcdblk_vt_shift = 24,
1373854fab2SYoungJun Cho 	.lcdblk_bypass_shift = 15,
138de7af100STomasz Figa 	.has_shadowcon = 1,
1393854fab2SYoungJun Cho 	.has_vidoutcon = 1,
1403c3c9c1dSJoonyoung Shim 	.has_vtsel = 1,
141e2e13389SLeela Krishna Amudala };
142e2e13389SLeela Krishna Amudala 
1431c248b7dSInki Dae struct fimd_win_data {
1441c248b7dSInki Dae 	unsigned int		offset_x;
1451c248b7dSInki Dae 	unsigned int		offset_y;
14619c8b834SInki Dae 	unsigned int		ovl_width;
14719c8b834SInki Dae 	unsigned int		ovl_height;
14819c8b834SInki Dae 	unsigned int		fb_width;
14919c8b834SInki Dae 	unsigned int		fb_height;
1501c248b7dSInki Dae 	unsigned int		bpp;
151a4f38a80SInki Dae 	unsigned int		pixel_format;
1522c871127SInki Dae 	dma_addr_t		dma_addr;
1531c248b7dSInki Dae 	unsigned int		buf_offsize;
1541c248b7dSInki Dae 	unsigned int		line_size;	/* bytes */
155ec05da95SInki Dae 	bool			enabled;
156db7e55aeSPrathyush K 	bool			resume;
1571c248b7dSInki Dae };
1581c248b7dSInki Dae 
1591c248b7dSInki Dae struct fimd_context {
160bb7704d6SSean Paul 	struct device			*dev;
16140c8ab4bSSean Paul 	struct drm_device		*drm_dev;
16293bca243SGustavo Padovan 	struct exynos_drm_crtc		*crtc;
1631c248b7dSInki Dae 	struct clk			*bus_clk;
1641c248b7dSInki Dae 	struct clk			*lcd_clk;
1651c248b7dSInki Dae 	void __iomem			*regs;
1663854fab2SYoungJun Cho 	struct regmap			*sysreg;
1671c248b7dSInki Dae 	struct fimd_win_data		win_data[WINDOWS_NR];
1681c248b7dSInki Dae 	unsigned int			default_win;
1691c248b7dSInki Dae 	unsigned long			irq_flags;
1703854fab2SYoungJun Cho 	u32				vidcon0;
1711c248b7dSInki Dae 	u32				vidcon1;
1723854fab2SYoungJun Cho 	u32				vidout_con;
1733854fab2SYoungJun Cho 	u32				i80ifcon;
1743854fab2SYoungJun Cho 	bool				i80_if;
175cb91f6a0SJoonyoung Shim 	bool				suspended;
176080be03dSSean Paul 	int				pipe;
17701ce113cSPrathyush K 	wait_queue_head_t		wait_vsync_queue;
17801ce113cSPrathyush K 	atomic_t			wait_vsync_event;
1793854fab2SYoungJun Cho 	atomic_t			win_updated;
1803854fab2SYoungJun Cho 	atomic_t			triggering;
1811c248b7dSInki Dae 
182562ad9f4SAndrzej Hajda 	struct exynos_drm_panel_info panel;
18318873465STomasz Figa 	struct fimd_driver_data *driver_data;
184000cc920SAndrzej Hajda 	struct exynos_drm_display *display;
1851c248b7dSInki Dae };
1861c248b7dSInki Dae 
187d636ead8SJoonyoung Shim static const struct of_device_id fimd_driver_dt_match[] = {
188725ddeadSTomasz Figa 	{ .compatible = "samsung,s3c6400-fimd",
189725ddeadSTomasz Figa 	  .data = &s3c64xx_fimd_driver_data },
190d6ce7b58SInki Dae 	{ .compatible = "samsung,exynos3250-fimd",
191d6ce7b58SInki Dae 	  .data = &exynos3_fimd_driver_data },
1925830daf8SVikas Sajjan 	{ .compatible = "samsung,exynos4210-fimd",
193d636ead8SJoonyoung Shim 	  .data = &exynos4_fimd_driver_data },
194dcb622aaSYoungJun Cho 	{ .compatible = "samsung,exynos4415-fimd",
195dcb622aaSYoungJun Cho 	  .data = &exynos4415_fimd_driver_data },
1965830daf8SVikas Sajjan 	{ .compatible = "samsung,exynos5250-fimd",
197d636ead8SJoonyoung Shim 	  .data = &exynos5_fimd_driver_data },
198d636ead8SJoonyoung Shim 	{},
199d636ead8SJoonyoung Shim };
2000262ceebSSjoerd Simons MODULE_DEVICE_TABLE(of, fimd_driver_dt_match);
201d636ead8SJoonyoung Shim 
202e2e13389SLeela Krishna Amudala static inline struct fimd_driver_data *drm_fimd_get_driver_data(
203e2e13389SLeela Krishna Amudala 	struct platform_device *pdev)
204e2e13389SLeela Krishna Amudala {
205d636ead8SJoonyoung Shim 	const struct of_device_id *of_id =
206d636ead8SJoonyoung Shim 			of_match_device(fimd_driver_dt_match, &pdev->dev);
207d636ead8SJoonyoung Shim 
208d636ead8SJoonyoung Shim 	return (struct fimd_driver_data *)of_id->data;
209e2e13389SLeela Krishna Amudala }
210e2e13389SLeela Krishna Amudala 
21193bca243SGustavo Padovan static void fimd_wait_for_vblank(struct exynos_drm_crtc *crtc)
212f13bdbd1SAkshu Agrawal {
21393bca243SGustavo Padovan 	struct fimd_context *ctx = crtc->ctx;
214f13bdbd1SAkshu Agrawal 
215f13bdbd1SAkshu Agrawal 	if (ctx->suspended)
216f13bdbd1SAkshu Agrawal 		return;
217f13bdbd1SAkshu Agrawal 
218f13bdbd1SAkshu Agrawal 	atomic_set(&ctx->wait_vsync_event, 1);
219f13bdbd1SAkshu Agrawal 
220f13bdbd1SAkshu Agrawal 	/*
221f13bdbd1SAkshu Agrawal 	 * wait for FIMD to signal VSYNC interrupt or return after
222f13bdbd1SAkshu Agrawal 	 * timeout which is set to 50ms (refresh rate of 20).
223f13bdbd1SAkshu Agrawal 	 */
224f13bdbd1SAkshu Agrawal 	if (!wait_event_timeout(ctx->wait_vsync_queue,
225f13bdbd1SAkshu Agrawal 				!atomic_read(&ctx->wait_vsync_event),
226f13bdbd1SAkshu Agrawal 				HZ/20))
227f13bdbd1SAkshu Agrawal 		DRM_DEBUG_KMS("vblank wait timed out.\n");
228f13bdbd1SAkshu Agrawal }
229f13bdbd1SAkshu Agrawal 
230f181a543SYoungJun Cho static void fimd_enable_video_output(struct fimd_context *ctx, int win,
231f181a543SYoungJun Cho 					bool enable)
232f181a543SYoungJun Cho {
233f181a543SYoungJun Cho 	u32 val = readl(ctx->regs + WINCON(win));
234f181a543SYoungJun Cho 
235f181a543SYoungJun Cho 	if (enable)
236f181a543SYoungJun Cho 		val |= WINCONx_ENWIN;
237f181a543SYoungJun Cho 	else
238f181a543SYoungJun Cho 		val &= ~WINCONx_ENWIN;
239f181a543SYoungJun Cho 
240f181a543SYoungJun Cho 	writel(val, ctx->regs + WINCON(win));
241f181a543SYoungJun Cho }
242f181a543SYoungJun Cho 
243999d8b31SYoungJun Cho static void fimd_enable_shadow_channel_path(struct fimd_context *ctx, int win,
244999d8b31SYoungJun Cho 						bool enable)
245999d8b31SYoungJun Cho {
246999d8b31SYoungJun Cho 	u32 val = readl(ctx->regs + SHADOWCON);
247999d8b31SYoungJun Cho 
248999d8b31SYoungJun Cho 	if (enable)
249999d8b31SYoungJun Cho 		val |= SHADOWCON_CHx_ENABLE(win);
250999d8b31SYoungJun Cho 	else
251999d8b31SYoungJun Cho 		val &= ~SHADOWCON_CHx_ENABLE(win);
252999d8b31SYoungJun Cho 
253999d8b31SYoungJun Cho 	writel(val, ctx->regs + SHADOWCON);
254999d8b31SYoungJun Cho }
255999d8b31SYoungJun Cho 
25693bca243SGustavo Padovan static void fimd_clear_channel(struct exynos_drm_crtc *crtc)
257f13bdbd1SAkshu Agrawal {
25893bca243SGustavo Padovan 	struct fimd_context *ctx = crtc->ctx;
259f13bdbd1SAkshu Agrawal 	int win, ch_enabled = 0;
260f13bdbd1SAkshu Agrawal 
261f13bdbd1SAkshu Agrawal 	DRM_DEBUG_KMS("%s\n", __FILE__);
262f13bdbd1SAkshu Agrawal 
263f13bdbd1SAkshu Agrawal 	/* Check if any channel is enabled. */
264f13bdbd1SAkshu Agrawal 	for (win = 0; win < WINDOWS_NR; win++) {
265eb8a3bf7SMarek Szyprowski 		u32 val = readl(ctx->regs + WINCON(win));
266eb8a3bf7SMarek Szyprowski 
267eb8a3bf7SMarek Szyprowski 		if (val & WINCONx_ENWIN) {
268f181a543SYoungJun Cho 			fimd_enable_video_output(ctx, win, false);
269eb8a3bf7SMarek Szyprowski 
270999d8b31SYoungJun Cho 			if (ctx->driver_data->has_shadowcon)
271999d8b31SYoungJun Cho 				fimd_enable_shadow_channel_path(ctx, win,
272999d8b31SYoungJun Cho 								false);
273999d8b31SYoungJun Cho 
274f13bdbd1SAkshu Agrawal 			ch_enabled = 1;
275f13bdbd1SAkshu Agrawal 		}
276f13bdbd1SAkshu Agrawal 	}
277f13bdbd1SAkshu Agrawal 
278f13bdbd1SAkshu Agrawal 	/* Wait for vsync, as disable channel takes effect at next vsync */
279eb8a3bf7SMarek Szyprowski 	if (ch_enabled) {
280eb8a3bf7SMarek Szyprowski 		unsigned int state = ctx->suspended;
281eb8a3bf7SMarek Szyprowski 
282eb8a3bf7SMarek Szyprowski 		ctx->suspended = 0;
28393bca243SGustavo Padovan 		fimd_wait_for_vblank(crtc);
284eb8a3bf7SMarek Szyprowski 		ctx->suspended = state;
285eb8a3bf7SMarek Szyprowski 	}
286f13bdbd1SAkshu Agrawal }
287f13bdbd1SAkshu Agrawal 
28893bca243SGustavo Padovan static int fimd_ctx_initialize(struct fimd_context *ctx,
289f37cd5e8SInki Dae 			struct drm_device *drm_dev)
29040c8ab4bSSean Paul {
291f37cd5e8SInki Dae 	struct exynos_drm_private *priv;
292f37cd5e8SInki Dae 	priv = drm_dev->dev_private;
29340c8ab4bSSean Paul 
294eb88e422SGustavo Padovan 	ctx->drm_dev = drm_dev;
2958a326eddSGustavo Padovan 	ctx->pipe = priv->pipe++;
296080be03dSSean Paul 
297080be03dSSean Paul 	/* attach this sub driver to iommu mapping if supported. */
298f13bdbd1SAkshu Agrawal 	if (is_drm_iommu_supported(ctx->drm_dev)) {
299f13bdbd1SAkshu Agrawal 		/*
300f13bdbd1SAkshu Agrawal 		 * If any channel is already active, iommu will throw
301f13bdbd1SAkshu Agrawal 		 * a PAGE FAULT when enabled. So clear any channel if enabled.
302f13bdbd1SAkshu Agrawal 		 */
30393bca243SGustavo Padovan 		fimd_clear_channel(ctx->crtc);
304080be03dSSean Paul 		drm_iommu_attach_device(ctx->drm_dev, ctx->dev);
305f13bdbd1SAkshu Agrawal 	}
30640c8ab4bSSean Paul 
30740c8ab4bSSean Paul 	return 0;
30840c8ab4bSSean Paul }
30940c8ab4bSSean Paul 
31093bca243SGustavo Padovan static void fimd_ctx_remove(struct fimd_context *ctx)
311ec05da95SInki Dae {
312080be03dSSean Paul 	/* detach this sub driver from iommu mapping if supported. */
313080be03dSSean Paul 	if (is_drm_iommu_supported(ctx->drm_dev))
314080be03dSSean Paul 		drm_iommu_detach_device(ctx->drm_dev, ctx->dev);
315ec05da95SInki Dae }
316ec05da95SInki Dae 
317a968e727SSean Paul static u32 fimd_calc_clkdiv(struct fimd_context *ctx,
318a968e727SSean Paul 		const struct drm_display_mode *mode)
319a968e727SSean Paul {
320a968e727SSean Paul 	unsigned long ideal_clk = mode->htotal * mode->vtotal * mode->vrefresh;
321a968e727SSean Paul 	u32 clkdiv;
322a968e727SSean Paul 
3233854fab2SYoungJun Cho 	if (ctx->i80_if) {
3243854fab2SYoungJun Cho 		/*
3253854fab2SYoungJun Cho 		 * The frame done interrupt should be occurred prior to the
3263854fab2SYoungJun Cho 		 * next TE signal.
3273854fab2SYoungJun Cho 		 */
3283854fab2SYoungJun Cho 		ideal_clk *= 2;
3293854fab2SYoungJun Cho 	}
3303854fab2SYoungJun Cho 
331a968e727SSean Paul 	/* Find the clock divider value that gets us closest to ideal_clk */
332a968e727SSean Paul 	clkdiv = DIV_ROUND_UP(clk_get_rate(ctx->lcd_clk), ideal_clk);
333a968e727SSean Paul 
334a968e727SSean Paul 	return (clkdiv < 0x100) ? clkdiv : 0xff;
335a968e727SSean Paul }
336a968e727SSean Paul 
33793bca243SGustavo Padovan static bool fimd_mode_fixup(struct exynos_drm_crtc *crtc,
338a968e727SSean Paul 		const struct drm_display_mode *mode,
339a968e727SSean Paul 		struct drm_display_mode *adjusted_mode)
340a968e727SSean Paul {
341a968e727SSean Paul 	if (adjusted_mode->vrefresh == 0)
342a968e727SSean Paul 		adjusted_mode->vrefresh = FIMD_DEFAULT_FRAMERATE;
343a968e727SSean Paul 
344a968e727SSean Paul 	return true;
345a968e727SSean Paul }
346a968e727SSean Paul 
34793bca243SGustavo Padovan static void fimd_commit(struct exynos_drm_crtc *crtc)
3481c248b7dSInki Dae {
34993bca243SGustavo Padovan 	struct fimd_context *ctx = crtc->ctx;
350a8dc5ed6SGustavo Padovan 	struct drm_display_mode *mode = &crtc->base.mode;
3513854fab2SYoungJun Cho 	struct fimd_driver_data *driver_data = ctx->driver_data;
3523854fab2SYoungJun Cho 	void *timing_base = ctx->regs + driver_data->timing_base;
3533854fab2SYoungJun Cho 	u32 val, clkdiv;
3541c248b7dSInki Dae 
355e30d4bcfSInki Dae 	if (ctx->suspended)
356e30d4bcfSInki Dae 		return;
357e30d4bcfSInki Dae 
358a968e727SSean Paul 	/* nothing to do if we haven't set the mode yet */
359a968e727SSean Paul 	if (mode->htotal == 0 || mode->vtotal == 0)
360a968e727SSean Paul 		return;
361a968e727SSean Paul 
3623854fab2SYoungJun Cho 	if (ctx->i80_if) {
3633854fab2SYoungJun Cho 		val = ctx->i80ifcon | I80IFEN_ENABLE;
3643854fab2SYoungJun Cho 		writel(val, timing_base + I80IFCONFAx(0));
3653854fab2SYoungJun Cho 
3663854fab2SYoungJun Cho 		/* disable auto frame rate */
3673854fab2SYoungJun Cho 		writel(0, timing_base + I80IFCONFBx(0));
3683854fab2SYoungJun Cho 
3693854fab2SYoungJun Cho 		/* set video type selection to I80 interface */
3703c3c9c1dSJoonyoung Shim 		if (driver_data->has_vtsel && ctx->sysreg &&
3713c3c9c1dSJoonyoung Shim 				regmap_update_bits(ctx->sysreg,
3723854fab2SYoungJun Cho 					driver_data->lcdblk_offset,
3733854fab2SYoungJun Cho 					0x3 << driver_data->lcdblk_vt_shift,
3743854fab2SYoungJun Cho 					0x1 << driver_data->lcdblk_vt_shift)) {
3753854fab2SYoungJun Cho 			DRM_ERROR("Failed to update sysreg for I80 i/f.\n");
3763854fab2SYoungJun Cho 			return;
3773854fab2SYoungJun Cho 		}
3783854fab2SYoungJun Cho 	} else {
3793854fab2SYoungJun Cho 		int vsync_len, vbpd, vfpd, hsync_len, hbpd, hfpd;
3803854fab2SYoungJun Cho 		u32 vidcon1;
3813854fab2SYoungJun Cho 
3821417f109SSean Paul 		/* setup polarity values */
3831417f109SSean Paul 		vidcon1 = ctx->vidcon1;
3841417f109SSean Paul 		if (mode->flags & DRM_MODE_FLAG_NVSYNC)
3851417f109SSean Paul 			vidcon1 |= VIDCON1_INV_VSYNC;
3861417f109SSean Paul 		if (mode->flags & DRM_MODE_FLAG_NHSYNC)
3871417f109SSean Paul 			vidcon1 |= VIDCON1_INV_HSYNC;
3881417f109SSean Paul 		writel(vidcon1, ctx->regs + driver_data->timing_base + VIDCON1);
3891c248b7dSInki Dae 
3901c248b7dSInki Dae 		/* setup vertical timing values. */
391a968e727SSean Paul 		vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
3928b4cad23SAndrzej Hajda 		vbpd = mode->crtc_vtotal - mode->crtc_vsync_end;
3938b4cad23SAndrzej Hajda 		vfpd = mode->crtc_vsync_start - mode->crtc_vdisplay;
394a968e727SSean Paul 
395a968e727SSean Paul 		val = VIDTCON0_VBPD(vbpd - 1) |
396a968e727SSean Paul 			VIDTCON0_VFPD(vfpd - 1) |
397a968e727SSean Paul 			VIDTCON0_VSPW(vsync_len - 1);
398e2e13389SLeela Krishna Amudala 		writel(val, ctx->regs + driver_data->timing_base + VIDTCON0);
3991c248b7dSInki Dae 
4001c248b7dSInki Dae 		/* setup horizontal timing values.  */
401a968e727SSean Paul 		hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
4028b4cad23SAndrzej Hajda 		hbpd = mode->crtc_htotal - mode->crtc_hsync_end;
4038b4cad23SAndrzej Hajda 		hfpd = mode->crtc_hsync_start - mode->crtc_hdisplay;
404a968e727SSean Paul 
405a968e727SSean Paul 		val = VIDTCON1_HBPD(hbpd - 1) |
406a968e727SSean Paul 			VIDTCON1_HFPD(hfpd - 1) |
407a968e727SSean Paul 			VIDTCON1_HSPW(hsync_len - 1);
408e2e13389SLeela Krishna Amudala 		writel(val, ctx->regs + driver_data->timing_base + VIDTCON1);
4093854fab2SYoungJun Cho 	}
4103854fab2SYoungJun Cho 
4113854fab2SYoungJun Cho 	if (driver_data->has_vidoutcon)
4123854fab2SYoungJun Cho 		writel(ctx->vidout_con, timing_base + VIDOUT_CON);
4133854fab2SYoungJun Cho 
4143854fab2SYoungJun Cho 	/* set bypass selection */
4153854fab2SYoungJun Cho 	if (ctx->sysreg && regmap_update_bits(ctx->sysreg,
4163854fab2SYoungJun Cho 				driver_data->lcdblk_offset,
4173854fab2SYoungJun Cho 				0x1 << driver_data->lcdblk_bypass_shift,
4183854fab2SYoungJun Cho 				0x1 << driver_data->lcdblk_bypass_shift)) {
4193854fab2SYoungJun Cho 		DRM_ERROR("Failed to update sysreg for bypass setting.\n");
4203854fab2SYoungJun Cho 		return;
4213854fab2SYoungJun Cho 	}
4221c248b7dSInki Dae 
4231c248b7dSInki Dae 	/* setup horizontal and vertical display size. */
424a968e727SSean Paul 	val = VIDTCON2_LINEVAL(mode->vdisplay - 1) |
425a968e727SSean Paul 	       VIDTCON2_HOZVAL(mode->hdisplay - 1) |
426a968e727SSean Paul 	       VIDTCON2_LINEVAL_E(mode->vdisplay - 1) |
427a968e727SSean Paul 	       VIDTCON2_HOZVAL_E(mode->hdisplay - 1);
428e2e13389SLeela Krishna Amudala 	writel(val, ctx->regs + driver_data->timing_base + VIDTCON2);
4291c248b7dSInki Dae 
4301c248b7dSInki Dae 	/*
4311c248b7dSInki Dae 	 * fields of register with prefix '_F' would be updated
4321c248b7dSInki Dae 	 * at vsync(same as dma start)
4331c248b7dSInki Dae 	 */
4343854fab2SYoungJun Cho 	val = ctx->vidcon0;
4353854fab2SYoungJun Cho 	val |= VIDCON0_ENVID | VIDCON0_ENVID_F;
4361d531062SAndrzej Hajda 
4371d531062SAndrzej Hajda 	if (ctx->driver_data->has_clksel)
4381d531062SAndrzej Hajda 		val |= VIDCON0_CLKSEL_LCD;
4391d531062SAndrzej Hajda 
4401d531062SAndrzej Hajda 	clkdiv = fimd_calc_clkdiv(ctx, mode);
4411d531062SAndrzej Hajda 	if (clkdiv > 1)
4421d531062SAndrzej Hajda 		val |= VIDCON0_CLKVAL_F(clkdiv - 1) | VIDCON0_CLKDIR;
4431d531062SAndrzej Hajda 
4441c248b7dSInki Dae 	writel(val, ctx->regs + VIDCON0);
4451c248b7dSInki Dae }
4461c248b7dSInki Dae 
44793bca243SGustavo Padovan static int fimd_enable_vblank(struct exynos_drm_crtc *crtc)
4481c248b7dSInki Dae {
44993bca243SGustavo Padovan 	struct fimd_context *ctx = crtc->ctx;
4501c248b7dSInki Dae 	u32 val;
4511c248b7dSInki Dae 
452cb91f6a0SJoonyoung Shim 	if (ctx->suspended)
453cb91f6a0SJoonyoung Shim 		return -EPERM;
454cb91f6a0SJoonyoung Shim 
4551c248b7dSInki Dae 	if (!test_and_set_bit(0, &ctx->irq_flags)) {
4561c248b7dSInki Dae 		val = readl(ctx->regs + VIDINTCON0);
4571c248b7dSInki Dae 
4581c248b7dSInki Dae 		val |= VIDINTCON0_INT_ENABLE;
4591c905d95SYoungJun Cho 
4601c905d95SYoungJun Cho 		if (ctx->i80_if) {
4611c905d95SYoungJun Cho 			val |= VIDINTCON0_INT_I80IFDONE;
4621c905d95SYoungJun Cho 			val |= VIDINTCON0_INT_SYSMAINCON;
4631c905d95SYoungJun Cho 			val &= ~VIDINTCON0_INT_SYSSUBCON;
4641c905d95SYoungJun Cho 		} else {
4651c248b7dSInki Dae 			val |= VIDINTCON0_INT_FRAME;
4661c248b7dSInki Dae 
4671c248b7dSInki Dae 			val &= ~VIDINTCON0_FRAMESEL0_MASK;
4681c248b7dSInki Dae 			val |= VIDINTCON0_FRAMESEL0_VSYNC;
4691c248b7dSInki Dae 			val &= ~VIDINTCON0_FRAMESEL1_MASK;
4701c248b7dSInki Dae 			val |= VIDINTCON0_FRAMESEL1_NONE;
4711c905d95SYoungJun Cho 		}
4721c248b7dSInki Dae 
4731c248b7dSInki Dae 		writel(val, ctx->regs + VIDINTCON0);
4741c248b7dSInki Dae 	}
4751c248b7dSInki Dae 
4761c248b7dSInki Dae 	return 0;
4771c248b7dSInki Dae }
4781c248b7dSInki Dae 
47993bca243SGustavo Padovan static void fimd_disable_vblank(struct exynos_drm_crtc *crtc)
4801c248b7dSInki Dae {
48193bca243SGustavo Padovan 	struct fimd_context *ctx = crtc->ctx;
4821c248b7dSInki Dae 	u32 val;
4831c248b7dSInki Dae 
484cb91f6a0SJoonyoung Shim 	if (ctx->suspended)
485cb91f6a0SJoonyoung Shim 		return;
486cb91f6a0SJoonyoung Shim 
4871c248b7dSInki Dae 	if (test_and_clear_bit(0, &ctx->irq_flags)) {
4881c248b7dSInki Dae 		val = readl(ctx->regs + VIDINTCON0);
4891c248b7dSInki Dae 
4901c248b7dSInki Dae 		val &= ~VIDINTCON0_INT_ENABLE;
4911c248b7dSInki Dae 
4921c905d95SYoungJun Cho 		if (ctx->i80_if) {
4931c905d95SYoungJun Cho 			val &= ~VIDINTCON0_INT_I80IFDONE;
4941c905d95SYoungJun Cho 			val &= ~VIDINTCON0_INT_SYSMAINCON;
4951c905d95SYoungJun Cho 			val &= ~VIDINTCON0_INT_SYSSUBCON;
4961c905d95SYoungJun Cho 		} else
4971c905d95SYoungJun Cho 			val &= ~VIDINTCON0_INT_FRAME;
4981c905d95SYoungJun Cho 
4991c248b7dSInki Dae 		writel(val, ctx->regs + VIDINTCON0);
5001c248b7dSInki Dae 	}
5011c248b7dSInki Dae }
5021c248b7dSInki Dae 
50393bca243SGustavo Padovan static void fimd_win_mode_set(struct exynos_drm_crtc *crtc,
5048837deeaSGustavo Padovan 			struct exynos_drm_plane *plane)
5051c248b7dSInki Dae {
50693bca243SGustavo Padovan 	struct fimd_context *ctx = crtc->ctx;
5071c248b7dSInki Dae 	struct fimd_win_data *win_data;
508864ee9e6SJoonyoung Shim 	int win;
50919c8b834SInki Dae 	unsigned long offset;
5101c248b7dSInki Dae 
5118837deeaSGustavo Padovan 	if (!plane) {
5128837deeaSGustavo Padovan 		DRM_ERROR("plane is NULL\n");
5131c248b7dSInki Dae 		return;
5141c248b7dSInki Dae 	}
5151c248b7dSInki Dae 
5168837deeaSGustavo Padovan 	win = plane->zpos;
517864ee9e6SJoonyoung Shim 	if (win == DEFAULT_ZPOS)
518864ee9e6SJoonyoung Shim 		win = ctx->default_win;
519864ee9e6SJoonyoung Shim 
52037b006e8SKrzysztof Kozlowski 	if (win < 0 || win >= WINDOWS_NR)
521864ee9e6SJoonyoung Shim 		return;
522864ee9e6SJoonyoung Shim 
5238837deeaSGustavo Padovan 	offset = plane->fb_x * (plane->bpp >> 3);
5248837deeaSGustavo Padovan 	offset += plane->fb_y * plane->pitch;
52519c8b834SInki Dae 
5268837deeaSGustavo Padovan 	DRM_DEBUG_KMS("offset = 0x%lx, pitch = %x\n", offset, plane->pitch);
52719c8b834SInki Dae 
528864ee9e6SJoonyoung Shim 	win_data = &ctx->win_data[win];
5291c248b7dSInki Dae 
5308837deeaSGustavo Padovan 	win_data->offset_x = plane->crtc_x;
5318837deeaSGustavo Padovan 	win_data->offset_y = plane->crtc_y;
5328837deeaSGustavo Padovan 	win_data->ovl_width = plane->crtc_width;
5338837deeaSGustavo Padovan 	win_data->ovl_height = plane->crtc_height;
5348837deeaSGustavo Padovan 	win_data->fb_width = plane->fb_width;
5358837deeaSGustavo Padovan 	win_data->fb_height = plane->fb_height;
5368837deeaSGustavo Padovan 	win_data->dma_addr = plane->dma_addr[0] + offset;
5378837deeaSGustavo Padovan 	win_data->bpp = plane->bpp;
5388837deeaSGustavo Padovan 	win_data->pixel_format = plane->pixel_format;
5398837deeaSGustavo Padovan 	win_data->buf_offsize = (plane->fb_width - plane->crtc_width) *
5408837deeaSGustavo Padovan 				(plane->bpp >> 3);
5418837deeaSGustavo Padovan 	win_data->line_size = plane->crtc_width * (plane->bpp >> 3);
54219c8b834SInki Dae 
54319c8b834SInki Dae 	DRM_DEBUG_KMS("offset_x = %d, offset_y = %d\n",
54419c8b834SInki Dae 			win_data->offset_x, win_data->offset_y);
54519c8b834SInki Dae 	DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
54619c8b834SInki Dae 			win_data->ovl_width, win_data->ovl_height);
547ddd8e959SYoungJun Cho 	DRM_DEBUG_KMS("paddr = 0x%lx\n", (unsigned long)win_data->dma_addr);
54819c8b834SInki Dae 	DRM_DEBUG_KMS("fb_width = %d, crtc_width = %d\n",
5498837deeaSGustavo Padovan 			plane->fb_width, plane->crtc_width);
5501c248b7dSInki Dae }
5511c248b7dSInki Dae 
552bb7704d6SSean Paul static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win)
5531c248b7dSInki Dae {
5541c248b7dSInki Dae 	struct fimd_win_data *win_data = &ctx->win_data[win];
5551c248b7dSInki Dae 	unsigned long val;
5561c248b7dSInki Dae 
5571c248b7dSInki Dae 	val = WINCONx_ENWIN;
5581c248b7dSInki Dae 
5595cc4621aSInki Dae 	/*
5605cc4621aSInki Dae 	 * In case of s3c64xx, window 0 doesn't support alpha channel.
5615cc4621aSInki Dae 	 * So the request format is ARGB8888 then change it to XRGB8888.
5625cc4621aSInki Dae 	 */
5635cc4621aSInki Dae 	if (ctx->driver_data->has_limited_fmt && !win) {
5645cc4621aSInki Dae 		if (win_data->pixel_format == DRM_FORMAT_ARGB8888)
5655cc4621aSInki Dae 			win_data->pixel_format = DRM_FORMAT_XRGB8888;
5665cc4621aSInki Dae 	}
5675cc4621aSInki Dae 
568a4f38a80SInki Dae 	switch (win_data->pixel_format) {
569a4f38a80SInki Dae 	case DRM_FORMAT_C8:
5701c248b7dSInki Dae 		val |= WINCON0_BPPMODE_8BPP_PALETTE;
5711c248b7dSInki Dae 		val |= WINCONx_BURSTLEN_8WORD;
5721c248b7dSInki Dae 		val |= WINCONx_BYTSWP;
5731c248b7dSInki Dae 		break;
574a4f38a80SInki Dae 	case DRM_FORMAT_XRGB1555:
575a4f38a80SInki Dae 		val |= WINCON0_BPPMODE_16BPP_1555;
576a4f38a80SInki Dae 		val |= WINCONx_HAWSWP;
577a4f38a80SInki Dae 		val |= WINCONx_BURSTLEN_16WORD;
578a4f38a80SInki Dae 		break;
579a4f38a80SInki Dae 	case DRM_FORMAT_RGB565:
5801c248b7dSInki Dae 		val |= WINCON0_BPPMODE_16BPP_565;
5811c248b7dSInki Dae 		val |= WINCONx_HAWSWP;
5821c248b7dSInki Dae 		val |= WINCONx_BURSTLEN_16WORD;
5831c248b7dSInki Dae 		break;
584a4f38a80SInki Dae 	case DRM_FORMAT_XRGB8888:
5851c248b7dSInki Dae 		val |= WINCON0_BPPMODE_24BPP_888;
5861c248b7dSInki Dae 		val |= WINCONx_WSWP;
5871c248b7dSInki Dae 		val |= WINCONx_BURSTLEN_16WORD;
5881c248b7dSInki Dae 		break;
589a4f38a80SInki Dae 	case DRM_FORMAT_ARGB8888:
590a4f38a80SInki Dae 		val |= WINCON1_BPPMODE_25BPP_A1888
5911c248b7dSInki Dae 			| WINCON1_BLD_PIX | WINCON1_ALPHA_SEL;
5921c248b7dSInki Dae 		val |= WINCONx_WSWP;
5931c248b7dSInki Dae 		val |= WINCONx_BURSTLEN_16WORD;
5941c248b7dSInki Dae 		break;
5951c248b7dSInki Dae 	default:
5961c248b7dSInki Dae 		DRM_DEBUG_KMS("invalid pixel size so using unpacked 24bpp.\n");
5971c248b7dSInki Dae 
5981c248b7dSInki Dae 		val |= WINCON0_BPPMODE_24BPP_888;
5991c248b7dSInki Dae 		val |= WINCONx_WSWP;
6001c248b7dSInki Dae 		val |= WINCONx_BURSTLEN_16WORD;
6011c248b7dSInki Dae 		break;
6021c248b7dSInki Dae 	}
6031c248b7dSInki Dae 
6041c248b7dSInki Dae 	DRM_DEBUG_KMS("bpp = %d\n", win_data->bpp);
6051c248b7dSInki Dae 
60666367461SRahul Sharma 	/*
60766367461SRahul Sharma 	 * In case of exynos, setting dma-burst to 16Word causes permanent
60866367461SRahul Sharma 	 * tearing for very small buffers, e.g. cursor buffer. Burst Mode
6098837deeaSGustavo Padovan 	 * switching which is based on plane size is not recommended as
6108837deeaSGustavo Padovan 	 * plane size varies alot towards the end of the screen and rapid
61166367461SRahul Sharma 	 * movement causes unstable DMA which results into iommu crash/tear.
61266367461SRahul Sharma 	 */
61366367461SRahul Sharma 
61466367461SRahul Sharma 	if (win_data->fb_width < MIN_FB_WIDTH_FOR_16WORD_BURST) {
61566367461SRahul Sharma 		val &= ~WINCONx_BURSTLEN_MASK;
61666367461SRahul Sharma 		val |= WINCONx_BURSTLEN_4WORD;
61766367461SRahul Sharma 	}
61866367461SRahul Sharma 
6191c248b7dSInki Dae 	writel(val, ctx->regs + WINCON(win));
6201c248b7dSInki Dae }
6211c248b7dSInki Dae 
622bb7704d6SSean Paul static void fimd_win_set_colkey(struct fimd_context *ctx, unsigned int win)
6231c248b7dSInki Dae {
6241c248b7dSInki Dae 	unsigned int keycon0 = 0, keycon1 = 0;
6251c248b7dSInki Dae 
6261c248b7dSInki Dae 	keycon0 = ~(WxKEYCON0_KEYBL_EN | WxKEYCON0_KEYEN_F |
6271c248b7dSInki Dae 			WxKEYCON0_DIRCON) | WxKEYCON0_COMPKEY(0);
6281c248b7dSInki Dae 
6291c248b7dSInki Dae 	keycon1 = WxKEYCON1_COLVAL(0xffffffff);
6301c248b7dSInki Dae 
6311c248b7dSInki Dae 	writel(keycon0, ctx->regs + WKEYCON0_BASE(win));
6321c248b7dSInki Dae 	writel(keycon1, ctx->regs + WKEYCON1_BASE(win));
6331c248b7dSInki Dae }
6341c248b7dSInki Dae 
635de7af100STomasz Figa /**
636de7af100STomasz Figa  * shadow_protect_win() - disable updating values from shadow registers at vsync
637de7af100STomasz Figa  *
638de7af100STomasz Figa  * @win: window to protect registers for
639de7af100STomasz Figa  * @protect: 1 to protect (disable updates)
640de7af100STomasz Figa  */
641de7af100STomasz Figa static void fimd_shadow_protect_win(struct fimd_context *ctx,
642de7af100STomasz Figa 							int win, bool protect)
643de7af100STomasz Figa {
644de7af100STomasz Figa 	u32 reg, bits, val;
645de7af100STomasz Figa 
646de7af100STomasz Figa 	if (ctx->driver_data->has_shadowcon) {
647de7af100STomasz Figa 		reg = SHADOWCON;
648de7af100STomasz Figa 		bits = SHADOWCON_WINx_PROTECT(win);
649de7af100STomasz Figa 	} else {
650de7af100STomasz Figa 		reg = PRTCON;
651de7af100STomasz Figa 		bits = PRTCON_PROTECT;
652de7af100STomasz Figa 	}
653de7af100STomasz Figa 
654de7af100STomasz Figa 	val = readl(ctx->regs + reg);
655de7af100STomasz Figa 	if (protect)
656de7af100STomasz Figa 		val |= bits;
657de7af100STomasz Figa 	else
658de7af100STomasz Figa 		val &= ~bits;
659de7af100STomasz Figa 	writel(val, ctx->regs + reg);
660de7af100STomasz Figa }
661de7af100STomasz Figa 
66293bca243SGustavo Padovan static void fimd_win_commit(struct exynos_drm_crtc *crtc, int zpos)
6631c248b7dSInki Dae {
66493bca243SGustavo Padovan 	struct fimd_context *ctx = crtc->ctx;
6651c248b7dSInki Dae 	struct fimd_win_data *win_data;
666864ee9e6SJoonyoung Shim 	int win = zpos;
6671c248b7dSInki Dae 	unsigned long val, alpha, size;
668f56aad3aSJoonyoung Shim 	unsigned int last_x;
669f56aad3aSJoonyoung Shim 	unsigned int last_y;
6701c248b7dSInki Dae 
671e30d4bcfSInki Dae 	if (ctx->suspended)
672e30d4bcfSInki Dae 		return;
673e30d4bcfSInki Dae 
674864ee9e6SJoonyoung Shim 	if (win == DEFAULT_ZPOS)
675864ee9e6SJoonyoung Shim 		win = ctx->default_win;
676864ee9e6SJoonyoung Shim 
67737b006e8SKrzysztof Kozlowski 	if (win < 0 || win >= WINDOWS_NR)
6781c248b7dSInki Dae 		return;
6791c248b7dSInki Dae 
6801c248b7dSInki Dae 	win_data = &ctx->win_data[win];
6811c248b7dSInki Dae 
682a43b933bSSean Paul 	/* If suspended, enable this on resume */
683a43b933bSSean Paul 	if (ctx->suspended) {
684a43b933bSSean Paul 		win_data->resume = true;
685a43b933bSSean Paul 		return;
686a43b933bSSean Paul 	}
687a43b933bSSean Paul 
6881c248b7dSInki Dae 	/*
689de7af100STomasz Figa 	 * SHADOWCON/PRTCON register is used for enabling timing.
6901c248b7dSInki Dae 	 *
6911c248b7dSInki Dae 	 * for example, once only width value of a register is set,
6921c248b7dSInki Dae 	 * if the dma is started then fimd hardware could malfunction so
6931c248b7dSInki Dae 	 * with protect window setting, the register fields with prefix '_F'
6941c248b7dSInki Dae 	 * wouldn't be updated at vsync also but updated once unprotect window
6951c248b7dSInki Dae 	 * is set.
6961c248b7dSInki Dae 	 */
6971c248b7dSInki Dae 
6981c248b7dSInki Dae 	/* protect windows */
699de7af100STomasz Figa 	fimd_shadow_protect_win(ctx, win, true);
7001c248b7dSInki Dae 
7011c248b7dSInki Dae 	/* buffer start address */
7022c871127SInki Dae 	val = (unsigned long)win_data->dma_addr;
7031c248b7dSInki Dae 	writel(val, ctx->regs + VIDWx_BUF_START(win, 0));
7041c248b7dSInki Dae 
7051c248b7dSInki Dae 	/* buffer end address */
70619c8b834SInki Dae 	size = win_data->fb_width * win_data->ovl_height * (win_data->bpp >> 3);
7072c871127SInki Dae 	val = (unsigned long)(win_data->dma_addr + size);
7081c248b7dSInki Dae 	writel(val, ctx->regs + VIDWx_BUF_END(win, 0));
7091c248b7dSInki Dae 
7101c248b7dSInki Dae 	DRM_DEBUG_KMS("start addr = 0x%lx, end addr = 0x%lx, size = 0x%lx\n",
7112c871127SInki Dae 			(unsigned long)win_data->dma_addr, val, size);
71219c8b834SInki Dae 	DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
71319c8b834SInki Dae 			win_data->ovl_width, win_data->ovl_height);
7141c248b7dSInki Dae 
7151c248b7dSInki Dae 	/* buffer size */
7161c248b7dSInki Dae 	val = VIDW_BUF_SIZE_OFFSET(win_data->buf_offsize) |
717ca555e5aSJoonyoung Shim 		VIDW_BUF_SIZE_PAGEWIDTH(win_data->line_size) |
718ca555e5aSJoonyoung Shim 		VIDW_BUF_SIZE_OFFSET_E(win_data->buf_offsize) |
719ca555e5aSJoonyoung Shim 		VIDW_BUF_SIZE_PAGEWIDTH_E(win_data->line_size);
7201c248b7dSInki Dae 	writel(val, ctx->regs + VIDWx_BUF_SIZE(win, 0));
7211c248b7dSInki Dae 
7221c248b7dSInki Dae 	/* OSD position */
7231c248b7dSInki Dae 	val = VIDOSDxA_TOPLEFT_X(win_data->offset_x) |
724ca555e5aSJoonyoung Shim 		VIDOSDxA_TOPLEFT_Y(win_data->offset_y) |
725ca555e5aSJoonyoung Shim 		VIDOSDxA_TOPLEFT_X_E(win_data->offset_x) |
726ca555e5aSJoonyoung Shim 		VIDOSDxA_TOPLEFT_Y_E(win_data->offset_y);
7271c248b7dSInki Dae 	writel(val, ctx->regs + VIDOSD_A(win));
7281c248b7dSInki Dae 
729f56aad3aSJoonyoung Shim 	last_x = win_data->offset_x + win_data->ovl_width;
730f56aad3aSJoonyoung Shim 	if (last_x)
731f56aad3aSJoonyoung Shim 		last_x--;
732f56aad3aSJoonyoung Shim 	last_y = win_data->offset_y + win_data->ovl_height;
733f56aad3aSJoonyoung Shim 	if (last_y)
734f56aad3aSJoonyoung Shim 		last_y--;
735f56aad3aSJoonyoung Shim 
736ca555e5aSJoonyoung Shim 	val = VIDOSDxB_BOTRIGHT_X(last_x) | VIDOSDxB_BOTRIGHT_Y(last_y) |
737ca555e5aSJoonyoung Shim 		VIDOSDxB_BOTRIGHT_X_E(last_x) | VIDOSDxB_BOTRIGHT_Y_E(last_y);
738ca555e5aSJoonyoung Shim 
7391c248b7dSInki Dae 	writel(val, ctx->regs + VIDOSD_B(win));
7401c248b7dSInki Dae 
74119c8b834SInki Dae 	DRM_DEBUG_KMS("osd pos: tx = %d, ty = %d, bx = %d, by = %d\n",
742f56aad3aSJoonyoung Shim 			win_data->offset_x, win_data->offset_y, last_x, last_y);
7431c248b7dSInki Dae 
7441c248b7dSInki Dae 	/* hardware window 0 doesn't support alpha channel. */
7451c248b7dSInki Dae 	if (win != 0) {
7461c248b7dSInki Dae 		/* OSD alpha */
7471c248b7dSInki Dae 		alpha = VIDISD14C_ALPHA1_R(0xf) |
7481c248b7dSInki Dae 			VIDISD14C_ALPHA1_G(0xf) |
7491c248b7dSInki Dae 			VIDISD14C_ALPHA1_B(0xf);
7501c248b7dSInki Dae 
7511c248b7dSInki Dae 		writel(alpha, ctx->regs + VIDOSD_C(win));
7521c248b7dSInki Dae 	}
7531c248b7dSInki Dae 
7541c248b7dSInki Dae 	/* OSD size */
7551c248b7dSInki Dae 	if (win != 3 && win != 4) {
7561c248b7dSInki Dae 		u32 offset = VIDOSD_D(win);
7571c248b7dSInki Dae 		if (win == 0)
7580f10cf14SLeela Krishna Amudala 			offset = VIDOSD_C(win);
75919c8b834SInki Dae 		val = win_data->ovl_width * win_data->ovl_height;
7601c248b7dSInki Dae 		writel(val, ctx->regs + offset);
7611c248b7dSInki Dae 
7621c248b7dSInki Dae 		DRM_DEBUG_KMS("osd size = 0x%x\n", (unsigned int)val);
7631c248b7dSInki Dae 	}
7641c248b7dSInki Dae 
765bb7704d6SSean Paul 	fimd_win_set_pixfmt(ctx, win);
7661c248b7dSInki Dae 
7671c248b7dSInki Dae 	/* hardware window 0 doesn't support color key. */
7681c248b7dSInki Dae 	if (win != 0)
769bb7704d6SSean Paul 		fimd_win_set_colkey(ctx, win);
7701c248b7dSInki Dae 
771f181a543SYoungJun Cho 	fimd_enable_video_output(ctx, win, true);
772ec05da95SInki Dae 
773999d8b31SYoungJun Cho 	if (ctx->driver_data->has_shadowcon)
774999d8b31SYoungJun Cho 		fimd_enable_shadow_channel_path(ctx, win, true);
775ec05da95SInki Dae 
77674944a58SYoungJun Cho 	/* Enable DMA channel and unprotect windows */
77774944a58SYoungJun Cho 	fimd_shadow_protect_win(ctx, win, false);
77874944a58SYoungJun Cho 
779ec05da95SInki Dae 	win_data->enabled = true;
7803854fab2SYoungJun Cho 
7813854fab2SYoungJun Cho 	if (ctx->i80_if)
7823854fab2SYoungJun Cho 		atomic_set(&ctx->win_updated, 1);
7831c248b7dSInki Dae }
7841c248b7dSInki Dae 
78593bca243SGustavo Padovan static void fimd_win_disable(struct exynos_drm_crtc *crtc, int zpos)
7861c248b7dSInki Dae {
78793bca243SGustavo Padovan 	struct fimd_context *ctx = crtc->ctx;
788ec05da95SInki Dae 	struct fimd_win_data *win_data;
789864ee9e6SJoonyoung Shim 	int win = zpos;
7901c248b7dSInki Dae 
791864ee9e6SJoonyoung Shim 	if (win == DEFAULT_ZPOS)
792864ee9e6SJoonyoung Shim 		win = ctx->default_win;
793864ee9e6SJoonyoung Shim 
79437b006e8SKrzysztof Kozlowski 	if (win < 0 || win >= WINDOWS_NR)
7951c248b7dSInki Dae 		return;
7961c248b7dSInki Dae 
797ec05da95SInki Dae 	win_data = &ctx->win_data[win];
798ec05da95SInki Dae 
799db7e55aeSPrathyush K 	if (ctx->suspended) {
800db7e55aeSPrathyush K 		/* do not resume this window*/
801db7e55aeSPrathyush K 		win_data->resume = false;
802db7e55aeSPrathyush K 		return;
803db7e55aeSPrathyush K 	}
804db7e55aeSPrathyush K 
8051c248b7dSInki Dae 	/* protect windows */
806de7af100STomasz Figa 	fimd_shadow_protect_win(ctx, win, true);
8071c248b7dSInki Dae 
808f181a543SYoungJun Cho 	fimd_enable_video_output(ctx, win, false);
8091c248b7dSInki Dae 
810999d8b31SYoungJun Cho 	if (ctx->driver_data->has_shadowcon)
811999d8b31SYoungJun Cho 		fimd_enable_shadow_channel_path(ctx, win, false);
812de7af100STomasz Figa 
813999d8b31SYoungJun Cho 	/* unprotect windows */
814de7af100STomasz Figa 	fimd_shadow_protect_win(ctx, win, false);
815ec05da95SInki Dae 
816ec05da95SInki Dae 	win_data->enabled = false;
8171c248b7dSInki Dae }
8181c248b7dSInki Dae 
81993bca243SGustavo Padovan static void fimd_window_suspend(struct exynos_drm_crtc *crtc)
820a43b933bSSean Paul {
82193bca243SGustavo Padovan 	struct fimd_context *ctx = crtc->ctx;
822a43b933bSSean Paul 	struct fimd_win_data *win_data;
823a43b933bSSean Paul 	int i;
824a43b933bSSean Paul 
825a43b933bSSean Paul 	for (i = 0; i < WINDOWS_NR; i++) {
826a43b933bSSean Paul 		win_data = &ctx->win_data[i];
827a43b933bSSean Paul 		win_data->resume = win_data->enabled;
828a43b933bSSean Paul 		if (win_data->enabled)
82993bca243SGustavo Padovan 			fimd_win_disable(crtc, i);
830a43b933bSSean Paul 	}
831a43b933bSSean Paul }
832a43b933bSSean Paul 
83393bca243SGustavo Padovan static void fimd_window_resume(struct exynos_drm_crtc *crtc)
834a43b933bSSean Paul {
83593bca243SGustavo Padovan 	struct fimd_context *ctx = crtc->ctx;
836a43b933bSSean Paul 	struct fimd_win_data *win_data;
837a43b933bSSean Paul 	int i;
838a43b933bSSean Paul 
839a43b933bSSean Paul 	for (i = 0; i < WINDOWS_NR; i++) {
840a43b933bSSean Paul 		win_data = &ctx->win_data[i];
841a43b933bSSean Paul 		win_data->enabled = win_data->resume;
842a43b933bSSean Paul 		win_data->resume = false;
843a43b933bSSean Paul 	}
844a43b933bSSean Paul }
845a43b933bSSean Paul 
84693bca243SGustavo Padovan static void fimd_apply(struct exynos_drm_crtc *crtc)
847a43b933bSSean Paul {
84893bca243SGustavo Padovan 	struct fimd_context *ctx = crtc->ctx;
849a43b933bSSean Paul 	struct fimd_win_data *win_data;
850a43b933bSSean Paul 	int i;
851a43b933bSSean Paul 
852a43b933bSSean Paul 	for (i = 0; i < WINDOWS_NR; i++) {
853a43b933bSSean Paul 		win_data = &ctx->win_data[i];
854a43b933bSSean Paul 		if (win_data->enabled)
85593bca243SGustavo Padovan 			fimd_win_commit(crtc, i);
856d9b68d89SAndrzej Hajda 		else
85793bca243SGustavo Padovan 			fimd_win_disable(crtc, i);
858a43b933bSSean Paul 	}
859a43b933bSSean Paul 
86093bca243SGustavo Padovan 	fimd_commit(crtc);
861a43b933bSSean Paul }
862a43b933bSSean Paul 
86393bca243SGustavo Padovan static int fimd_poweron(struct exynos_drm_crtc *crtc)
864a43b933bSSean Paul {
86593bca243SGustavo Padovan 	struct fimd_context *ctx = crtc->ctx;
866a43b933bSSean Paul 	int ret;
867a43b933bSSean Paul 
868a43b933bSSean Paul 	if (!ctx->suspended)
869a43b933bSSean Paul 		return 0;
870a43b933bSSean Paul 
871a43b933bSSean Paul 	ctx->suspended = false;
872a43b933bSSean Paul 
873af65c804SSean Paul 	pm_runtime_get_sync(ctx->dev);
874af65c804SSean Paul 
875a43b933bSSean Paul 	ret = clk_prepare_enable(ctx->bus_clk);
876a43b933bSSean Paul 	if (ret < 0) {
877a43b933bSSean Paul 		DRM_ERROR("Failed to prepare_enable the bus clk [%d]\n", ret);
878a43b933bSSean Paul 		goto bus_clk_err;
879a43b933bSSean Paul 	}
880a43b933bSSean Paul 
881a43b933bSSean Paul 	ret = clk_prepare_enable(ctx->lcd_clk);
882a43b933bSSean Paul 	if  (ret < 0) {
883a43b933bSSean Paul 		DRM_ERROR("Failed to prepare_enable the lcd clk [%d]\n", ret);
884a43b933bSSean Paul 		goto lcd_clk_err;
885a43b933bSSean Paul 	}
886a43b933bSSean Paul 
887a43b933bSSean Paul 	/* if vblank was enabled status, enable it again. */
888a43b933bSSean Paul 	if (test_and_clear_bit(0, &ctx->irq_flags)) {
88993bca243SGustavo Padovan 		ret = fimd_enable_vblank(crtc);
890a43b933bSSean Paul 		if (ret) {
891a43b933bSSean Paul 			DRM_ERROR("Failed to re-enable vblank [%d]\n", ret);
892a43b933bSSean Paul 			goto enable_vblank_err;
893a43b933bSSean Paul 		}
894a43b933bSSean Paul 	}
895a43b933bSSean Paul 
89693bca243SGustavo Padovan 	fimd_window_resume(crtc);
897a43b933bSSean Paul 
89893bca243SGustavo Padovan 	fimd_apply(crtc);
899a43b933bSSean Paul 
900a43b933bSSean Paul 	return 0;
901a43b933bSSean Paul 
902a43b933bSSean Paul enable_vblank_err:
903a43b933bSSean Paul 	clk_disable_unprepare(ctx->lcd_clk);
904a43b933bSSean Paul lcd_clk_err:
905a43b933bSSean Paul 	clk_disable_unprepare(ctx->bus_clk);
906a43b933bSSean Paul bus_clk_err:
907a43b933bSSean Paul 	ctx->suspended = true;
908a43b933bSSean Paul 	return ret;
909a43b933bSSean Paul }
910a43b933bSSean Paul 
91193bca243SGustavo Padovan static int fimd_poweroff(struct exynos_drm_crtc *crtc)
912a43b933bSSean Paul {
91393bca243SGustavo Padovan 	struct fimd_context *ctx = crtc->ctx;
914a43b933bSSean Paul 
915a43b933bSSean Paul 	if (ctx->suspended)
916a43b933bSSean Paul 		return 0;
917a43b933bSSean Paul 
918a43b933bSSean Paul 	/*
919a43b933bSSean Paul 	 * We need to make sure that all windows are disabled before we
920a43b933bSSean Paul 	 * suspend that connector. Otherwise we might try to scan from
921a43b933bSSean Paul 	 * a destroyed buffer later.
922a43b933bSSean Paul 	 */
92393bca243SGustavo Padovan 	fimd_window_suspend(crtc);
924a43b933bSSean Paul 
925a43b933bSSean Paul 	clk_disable_unprepare(ctx->lcd_clk);
926a43b933bSSean Paul 	clk_disable_unprepare(ctx->bus_clk);
927a43b933bSSean Paul 
928af65c804SSean Paul 	pm_runtime_put_sync(ctx->dev);
929af65c804SSean Paul 
930a43b933bSSean Paul 	ctx->suspended = true;
931a43b933bSSean Paul 	return 0;
932a43b933bSSean Paul }
933a43b933bSSean Paul 
93493bca243SGustavo Padovan static void fimd_dpms(struct exynos_drm_crtc *crtc, int mode)
935080be03dSSean Paul {
936af65c804SSean Paul 	DRM_DEBUG_KMS("%s, %d\n", __FILE__, mode);
937080be03dSSean Paul 
938080be03dSSean Paul 	switch (mode) {
939080be03dSSean Paul 	case DRM_MODE_DPMS_ON:
94093bca243SGustavo Padovan 		fimd_poweron(crtc);
941080be03dSSean Paul 		break;
942080be03dSSean Paul 	case DRM_MODE_DPMS_STANDBY:
943080be03dSSean Paul 	case DRM_MODE_DPMS_SUSPEND:
944080be03dSSean Paul 	case DRM_MODE_DPMS_OFF:
94593bca243SGustavo Padovan 		fimd_poweroff(crtc);
946080be03dSSean Paul 		break;
947080be03dSSean Paul 	default:
948080be03dSSean Paul 		DRM_DEBUG_KMS("unspecified mode %d\n", mode);
949080be03dSSean Paul 		break;
950080be03dSSean Paul 	}
951080be03dSSean Paul }
952080be03dSSean Paul 
9533854fab2SYoungJun Cho static void fimd_trigger(struct device *dev)
9543854fab2SYoungJun Cho {
955e152dbd7SAndrzej Hajda 	struct fimd_context *ctx = dev_get_drvdata(dev);
9563854fab2SYoungJun Cho 	struct fimd_driver_data *driver_data = ctx->driver_data;
9573854fab2SYoungJun Cho 	void *timing_base = ctx->regs + driver_data->timing_base;
9583854fab2SYoungJun Cho 	u32 reg;
9593854fab2SYoungJun Cho 
9609b67eb73SJoonyoung Shim 	 /*
9611c905d95SYoungJun Cho 	  * Skips triggering if in triggering state, because multiple triggering
9629b67eb73SJoonyoung Shim 	  * requests can cause panel reset.
9639b67eb73SJoonyoung Shim 	  */
9649b67eb73SJoonyoung Shim 	if (atomic_read(&ctx->triggering))
9659b67eb73SJoonyoung Shim 		return;
9669b67eb73SJoonyoung Shim 
9671c905d95SYoungJun Cho 	/* Enters triggering mode */
9683854fab2SYoungJun Cho 	atomic_set(&ctx->triggering, 1);
9693854fab2SYoungJun Cho 
9703854fab2SYoungJun Cho 	reg = readl(timing_base + TRIGCON);
9713854fab2SYoungJun Cho 	reg |= (TRGMODE_I80_RGB_ENABLE_I80 | SWTRGCMD_I80_RGB_ENABLE);
9723854fab2SYoungJun Cho 	writel(reg, timing_base + TRIGCON);
97387ab85b3SYoungJun Cho 
97487ab85b3SYoungJun Cho 	/*
97587ab85b3SYoungJun Cho 	 * Exits triggering mode if vblank is not enabled yet, because when the
97687ab85b3SYoungJun Cho 	 * VIDINTCON0 register is not set, it can not exit from triggering mode.
97787ab85b3SYoungJun Cho 	 */
97887ab85b3SYoungJun Cho 	if (!test_bit(0, &ctx->irq_flags))
97987ab85b3SYoungJun Cho 		atomic_set(&ctx->triggering, 0);
9803854fab2SYoungJun Cho }
9813854fab2SYoungJun Cho 
98293bca243SGustavo Padovan static void fimd_te_handler(struct exynos_drm_crtc *crtc)
9833854fab2SYoungJun Cho {
98493bca243SGustavo Padovan 	struct fimd_context *ctx = crtc->ctx;
9853854fab2SYoungJun Cho 
9863854fab2SYoungJun Cho 	/* Checks the crtc is detached already from encoder */
9873854fab2SYoungJun Cho 	if (ctx->pipe < 0 || !ctx->drm_dev)
9883854fab2SYoungJun Cho 		return;
9893854fab2SYoungJun Cho 
9903854fab2SYoungJun Cho 	/*
9913854fab2SYoungJun Cho 	 * If there is a page flip request, triggers and handles the page flip
9923854fab2SYoungJun Cho 	 * event so that current fb can be updated into panel GRAM.
9933854fab2SYoungJun Cho 	 */
9943854fab2SYoungJun Cho 	if (atomic_add_unless(&ctx->win_updated, -1, 0))
9953854fab2SYoungJun Cho 		fimd_trigger(ctx->dev);
9963854fab2SYoungJun Cho 
9973854fab2SYoungJun Cho 	/* Wakes up vsync event queue */
9983854fab2SYoungJun Cho 	if (atomic_read(&ctx->wait_vsync_event)) {
9993854fab2SYoungJun Cho 		atomic_set(&ctx->wait_vsync_event, 0);
10003854fab2SYoungJun Cho 		wake_up(&ctx->wait_vsync_queue);
1001b301ae24SYoungJun Cho 	}
10023854fab2SYoungJun Cho 
1003adf67abfSJoonyoung Shim 	if (test_bit(0, &ctx->irq_flags))
10043854fab2SYoungJun Cho 		drm_handle_vblank(ctx->drm_dev, ctx->pipe);
10053854fab2SYoungJun Cho }
10063854fab2SYoungJun Cho 
100793bca243SGustavo Padovan static struct exynos_drm_crtc_ops fimd_crtc_ops = {
10081c6244c3SSean Paul 	.dpms = fimd_dpms,
1009a968e727SSean Paul 	.mode_fixup = fimd_mode_fixup,
10101c6244c3SSean Paul 	.commit = fimd_commit,
10111c6244c3SSean Paul 	.enable_vblank = fimd_enable_vblank,
10121c6244c3SSean Paul 	.disable_vblank = fimd_disable_vblank,
10131c6244c3SSean Paul 	.wait_for_vblank = fimd_wait_for_vblank,
10141c6244c3SSean Paul 	.win_mode_set = fimd_win_mode_set,
10151c6244c3SSean Paul 	.win_commit = fimd_win_commit,
10161c6244c3SSean Paul 	.win_disable = fimd_win_disable,
10173854fab2SYoungJun Cho 	.te_handler = fimd_te_handler,
10181c248b7dSInki Dae };
10191c248b7dSInki Dae 
10201c248b7dSInki Dae static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
10211c248b7dSInki Dae {
10221c248b7dSInki Dae 	struct fimd_context *ctx = (struct fimd_context *)dev_id;
10233854fab2SYoungJun Cho 	u32 val, clear_bit;
10241c248b7dSInki Dae 
10251c248b7dSInki Dae 	val = readl(ctx->regs + VIDINTCON1);
10261c248b7dSInki Dae 
10273854fab2SYoungJun Cho 	clear_bit = ctx->i80_if ? VIDINTCON1_INT_I80 : VIDINTCON1_INT_FRAME;
10283854fab2SYoungJun Cho 	if (val & clear_bit)
10293854fab2SYoungJun Cho 		writel(clear_bit, ctx->regs + VIDINTCON1);
10301c248b7dSInki Dae 
1031ec05da95SInki Dae 	/* check the crtc is detached already from encoder */
1032080be03dSSean Paul 	if (ctx->pipe < 0 || !ctx->drm_dev)
1033ec05da95SInki Dae 		goto out;
1034483b88f8SInki Dae 
1035adf67abfSJoonyoung Shim 	if (ctx->i80_if) {
10361c905d95SYoungJun Cho 		exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
10371c905d95SYoungJun Cho 
10381c905d95SYoungJun Cho 		/* Exits triggering mode */
10393854fab2SYoungJun Cho 		atomic_set(&ctx->triggering, 0);
10403854fab2SYoungJun Cho 	} else {
1041adf67abfSJoonyoung Shim 		drm_handle_vblank(ctx->drm_dev, ctx->pipe);
1042adf67abfSJoonyoung Shim 		exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
1043adf67abfSJoonyoung Shim 
104401ce113cSPrathyush K 		/* set wait vsync event to zero and wake up queue. */
104501ce113cSPrathyush K 		if (atomic_read(&ctx->wait_vsync_event)) {
104601ce113cSPrathyush K 			atomic_set(&ctx->wait_vsync_event, 0);
10478dd9ad5dSSeung-Woo Kim 			wake_up(&ctx->wait_vsync_queue);
104801ce113cSPrathyush K 		}
10493854fab2SYoungJun Cho 	}
10503854fab2SYoungJun Cho 
1051ec05da95SInki Dae out:
10521c248b7dSInki Dae 	return IRQ_HANDLED;
10531c248b7dSInki Dae }
10541c248b7dSInki Dae 
1055f37cd5e8SInki Dae static int fimd_bind(struct device *dev, struct device *master, void *data)
1056562ad9f4SAndrzej Hajda {
1057e152dbd7SAndrzej Hajda 	struct fimd_context *ctx = dev_get_drvdata(dev);
1058f37cd5e8SInki Dae 	struct drm_device *drm_dev = data;
1059000cc920SAndrzej Hajda 
106093bca243SGustavo Padovan 	ctx->crtc = exynos_drm_crtc_create(drm_dev, ctx->pipe,
106193bca243SGustavo Padovan 					   EXYNOS_DISPLAY_TYPE_LCD,
106293bca243SGustavo Padovan 					   &fimd_crtc_ops, ctx);
106393bca243SGustavo Padovan 	if (IS_ERR(ctx->crtc))
106493bca243SGustavo Padovan 		return PTR_ERR(ctx->crtc);
106593bca243SGustavo Padovan 
106693bca243SGustavo Padovan 	fimd_ctx_initialize(ctx, drm_dev);
106793bca243SGustavo Padovan 
1068000cc920SAndrzej Hajda 	if (ctx->display)
1069000cc920SAndrzej Hajda 		exynos_drm_create_enc_conn(drm_dev, ctx->display);
1070000cc920SAndrzej Hajda 
1071000cc920SAndrzej Hajda 	return 0;
1072000cc920SAndrzej Hajda 
1073000cc920SAndrzej Hajda }
1074000cc920SAndrzej Hajda 
1075000cc920SAndrzej Hajda static void fimd_unbind(struct device *dev, struct device *master,
1076000cc920SAndrzej Hajda 			void *data)
1077000cc920SAndrzej Hajda {
1078e152dbd7SAndrzej Hajda 	struct fimd_context *ctx = dev_get_drvdata(dev);
1079000cc920SAndrzej Hajda 
108093bca243SGustavo Padovan 	fimd_dpms(ctx->crtc, DRM_MODE_DPMS_OFF);
1081000cc920SAndrzej Hajda 
1082000cc920SAndrzej Hajda 	if (ctx->display)
10834cfde1f2SAndrzej Hajda 		exynos_dpi_remove(ctx->display);
1084000cc920SAndrzej Hajda 
108593bca243SGustavo Padovan 	fimd_ctx_remove(ctx);
1086000cc920SAndrzej Hajda }
1087000cc920SAndrzej Hajda 
1088000cc920SAndrzej Hajda static const struct component_ops fimd_component_ops = {
1089000cc920SAndrzej Hajda 	.bind	= fimd_bind,
1090000cc920SAndrzej Hajda 	.unbind = fimd_unbind,
1091000cc920SAndrzej Hajda };
1092000cc920SAndrzej Hajda 
1093000cc920SAndrzej Hajda static int fimd_probe(struct platform_device *pdev)
1094000cc920SAndrzej Hajda {
1095000cc920SAndrzej Hajda 	struct device *dev = &pdev->dev;
1096000cc920SAndrzej Hajda 	struct fimd_context *ctx;
10973854fab2SYoungJun Cho 	struct device_node *i80_if_timings;
1098000cc920SAndrzej Hajda 	struct resource *res;
1099fe42cfb4SGustavo Padovan 	int ret;
1100562ad9f4SAndrzej Hajda 
1101e152dbd7SAndrzej Hajda 	if (!dev->of_node)
1102e152dbd7SAndrzej Hajda 		return -ENODEV;
11032d3f173cSSachin Kamat 
1104d873ab99SSeung-Woo Kim 	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
1105e152dbd7SAndrzej Hajda 	if (!ctx)
1106e152dbd7SAndrzej Hajda 		return -ENOMEM;
1107e152dbd7SAndrzej Hajda 
1108e152dbd7SAndrzej Hajda 	ret = exynos_drm_component_add(dev, EXYNOS_DEVICE_TYPE_CRTC,
11095d1741adSGustavo Padovan 				       EXYNOS_DISPLAY_TYPE_LCD);
1110e152dbd7SAndrzej Hajda 	if (ret)
1111e152dbd7SAndrzej Hajda 		return ret;
11121c248b7dSInki Dae 
1113bb7704d6SSean Paul 	ctx->dev = dev;
1114a43b933bSSean Paul 	ctx->suspended = true;
11153854fab2SYoungJun Cho 	ctx->driver_data = drm_fimd_get_driver_data(pdev);
1116bb7704d6SSean Paul 
11171417f109SSean Paul 	if (of_property_read_bool(dev->of_node, "samsung,invert-vden"))
11181417f109SSean Paul 		ctx->vidcon1 |= VIDCON1_INV_VDEN;
11191417f109SSean Paul 	if (of_property_read_bool(dev->of_node, "samsung,invert-vclk"))
11201417f109SSean Paul 		ctx->vidcon1 |= VIDCON1_INV_VCLK;
1121562ad9f4SAndrzej Hajda 
11223854fab2SYoungJun Cho 	i80_if_timings = of_get_child_by_name(dev->of_node, "i80-if-timings");
11233854fab2SYoungJun Cho 	if (i80_if_timings) {
11243854fab2SYoungJun Cho 		u32 val;
11253854fab2SYoungJun Cho 
11263854fab2SYoungJun Cho 		ctx->i80_if = true;
11273854fab2SYoungJun Cho 
11283854fab2SYoungJun Cho 		if (ctx->driver_data->has_vidoutcon)
11293854fab2SYoungJun Cho 			ctx->vidout_con |= VIDOUT_CON_F_I80_LDI0;
11303854fab2SYoungJun Cho 		else
11313854fab2SYoungJun Cho 			ctx->vidcon0 |= VIDCON0_VIDOUT_I80_LDI0;
11323854fab2SYoungJun Cho 		/*
11333854fab2SYoungJun Cho 		 * The user manual describes that this "DSI_EN" bit is required
11343854fab2SYoungJun Cho 		 * to enable I80 24-bit data interface.
11353854fab2SYoungJun Cho 		 */
11363854fab2SYoungJun Cho 		ctx->vidcon0 |= VIDCON0_DSI_EN;
11373854fab2SYoungJun Cho 
11383854fab2SYoungJun Cho 		if (of_property_read_u32(i80_if_timings, "cs-setup", &val))
11393854fab2SYoungJun Cho 			val = 0;
11403854fab2SYoungJun Cho 		ctx->i80ifcon = LCD_CS_SETUP(val);
11413854fab2SYoungJun Cho 		if (of_property_read_u32(i80_if_timings, "wr-setup", &val))
11423854fab2SYoungJun Cho 			val = 0;
11433854fab2SYoungJun Cho 		ctx->i80ifcon |= LCD_WR_SETUP(val);
11443854fab2SYoungJun Cho 		if (of_property_read_u32(i80_if_timings, "wr-active", &val))
11453854fab2SYoungJun Cho 			val = 1;
11463854fab2SYoungJun Cho 		ctx->i80ifcon |= LCD_WR_ACTIVE(val);
11473854fab2SYoungJun Cho 		if (of_property_read_u32(i80_if_timings, "wr-hold", &val))
11483854fab2SYoungJun Cho 			val = 0;
11493854fab2SYoungJun Cho 		ctx->i80ifcon |= LCD_WR_HOLD(val);
11503854fab2SYoungJun Cho 	}
11513854fab2SYoungJun Cho 	of_node_put(i80_if_timings);
11523854fab2SYoungJun Cho 
11533854fab2SYoungJun Cho 	ctx->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
11543854fab2SYoungJun Cho 							"samsung,sysreg");
11553854fab2SYoungJun Cho 	if (IS_ERR(ctx->sysreg)) {
11563854fab2SYoungJun Cho 		dev_warn(dev, "failed to get system register.\n");
11573854fab2SYoungJun Cho 		ctx->sysreg = NULL;
11583854fab2SYoungJun Cho 	}
11593854fab2SYoungJun Cho 
1160a968e727SSean Paul 	ctx->bus_clk = devm_clk_get(dev, "fimd");
1161a968e727SSean Paul 	if (IS_ERR(ctx->bus_clk)) {
1162a968e727SSean Paul 		dev_err(dev, "failed to get bus clock\n");
1163df5225bcSInki Dae 		ret = PTR_ERR(ctx->bus_clk);
1164df5225bcSInki Dae 		goto err_del_component;
1165a968e727SSean Paul 	}
1166a968e727SSean Paul 
1167a968e727SSean Paul 	ctx->lcd_clk = devm_clk_get(dev, "sclk_fimd");
1168a968e727SSean Paul 	if (IS_ERR(ctx->lcd_clk)) {
1169a968e727SSean Paul 		dev_err(dev, "failed to get lcd clock\n");
1170df5225bcSInki Dae 		ret = PTR_ERR(ctx->lcd_clk);
1171df5225bcSInki Dae 		goto err_del_component;
1172a968e727SSean Paul 	}
11731c248b7dSInki Dae 
11741c248b7dSInki Dae 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
11751c248b7dSInki Dae 
1176d873ab99SSeung-Woo Kim 	ctx->regs = devm_ioremap_resource(dev, res);
1177df5225bcSInki Dae 	if (IS_ERR(ctx->regs)) {
1178df5225bcSInki Dae 		ret = PTR_ERR(ctx->regs);
1179df5225bcSInki Dae 		goto err_del_component;
1180df5225bcSInki Dae 	}
11811c248b7dSInki Dae 
11823854fab2SYoungJun Cho 	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
11833854fab2SYoungJun Cho 					   ctx->i80_if ? "lcd_sys" : "vsync");
11841c248b7dSInki Dae 	if (!res) {
11851c248b7dSInki Dae 		dev_err(dev, "irq request failed.\n");
1186df5225bcSInki Dae 		ret = -ENXIO;
1187df5225bcSInki Dae 		goto err_del_component;
11881c248b7dSInki Dae 	}
11891c248b7dSInki Dae 
1190055e0c06SSean Paul 	ret = devm_request_irq(dev, res->start, fimd_irq_handler,
1191edc57266SSachin Kamat 							0, "drm_fimd", ctx);
1192edc57266SSachin Kamat 	if (ret) {
11931c248b7dSInki Dae 		dev_err(dev, "irq request failed.\n");
1194df5225bcSInki Dae 		goto err_del_component;
11951c248b7dSInki Dae 	}
11961c248b7dSInki Dae 
119757ed0f7bSDaniel Vetter 	init_waitqueue_head(&ctx->wait_vsync_queue);
119801ce113cSPrathyush K 	atomic_set(&ctx->wait_vsync_event, 0);
11991c248b7dSInki Dae 
1200e152dbd7SAndrzej Hajda 	platform_set_drvdata(pdev, ctx);
1201080be03dSSean Paul 
1202000cc920SAndrzej Hajda 	ctx->display = exynos_dpi_probe(dev);
12035baf5d44SGustavo Padovan 	if (IS_ERR(ctx->display)) {
12045baf5d44SGustavo Padovan 		ret = PTR_ERR(ctx->display);
12055baf5d44SGustavo Padovan 		goto err_del_component;
12065baf5d44SGustavo Padovan 	}
1207f37cd5e8SInki Dae 
1208e152dbd7SAndrzej Hajda 	pm_runtime_enable(dev);
1209f37cd5e8SInki Dae 
1210e152dbd7SAndrzej Hajda 	ret = component_add(dev, &fimd_component_ops);
1211df5225bcSInki Dae 	if (ret)
1212df5225bcSInki Dae 		goto err_disable_pm_runtime;
1213df5225bcSInki Dae 
1214df5225bcSInki Dae 	return ret;
1215df5225bcSInki Dae 
1216df5225bcSInki Dae err_disable_pm_runtime:
1217e152dbd7SAndrzej Hajda 	pm_runtime_disable(dev);
1218df5225bcSInki Dae 
1219df5225bcSInki Dae err_del_component:
1220e152dbd7SAndrzej Hajda 	exynos_drm_component_del(dev, EXYNOS_DEVICE_TYPE_CRTC);
1221df5225bcSInki Dae 	return ret;
1222f37cd5e8SInki Dae }
1223f37cd5e8SInki Dae 
1224f37cd5e8SInki Dae static int fimd_remove(struct platform_device *pdev)
1225f37cd5e8SInki Dae {
1226af65c804SSean Paul 	pm_runtime_disable(&pdev->dev);
1227cb91f6a0SJoonyoung Shim 
1228df5225bcSInki Dae 	component_del(&pdev->dev, &fimd_component_ops);
1229df5225bcSInki Dae 	exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
1230df5225bcSInki Dae 
12311c248b7dSInki Dae 	return 0;
12321c248b7dSInki Dae }
12331c248b7dSInki Dae 
1234132a5b91SJoonyoung Shim struct platform_driver fimd_driver = {
12351c248b7dSInki Dae 	.probe		= fimd_probe,
123656550d94SGreg Kroah-Hartman 	.remove		= fimd_remove,
12371c248b7dSInki Dae 	.driver		= {
12381c248b7dSInki Dae 		.name	= "exynos4-fb",
12391c248b7dSInki Dae 		.owner	= THIS_MODULE,
12402d3f173cSSachin Kamat 		.of_match_table = fimd_driver_dt_match,
12411c248b7dSInki Dae 	},
12421c248b7dSInki Dae };
1243