18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* exynos_drm_fimd.c
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2011 Samsung Electronics Co.Ltd
58c2ecf20Sopenharmony_ci * Authors:
68c2ecf20Sopenharmony_ci *	Joonyoung Shim <jy0922.shim@samsung.com>
78c2ecf20Sopenharmony_ci *	Inki Dae <inki.dae@samsung.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/clk.h>
118c2ecf20Sopenharmony_ci#include <linux/component.h>
128c2ecf20Sopenharmony_ci#include <linux/kernel.h>
138c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h>
148c2ecf20Sopenharmony_ci#include <linux/of.h>
158c2ecf20Sopenharmony_ci#include <linux/of_device.h>
168c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
178c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
188c2ecf20Sopenharmony_ci#include <linux/regmap.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include <video/of_display_timing.h>
218c2ecf20Sopenharmony_ci#include <video/of_videomode.h>
228c2ecf20Sopenharmony_ci#include <video/samsung_fimd.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h>
258c2ecf20Sopenharmony_ci#include <drm/drm_vblank.h>
268c2ecf20Sopenharmony_ci#include <drm/exynos_drm.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#include "exynos_drm_crtc.h"
298c2ecf20Sopenharmony_ci#include "exynos_drm_drv.h"
308c2ecf20Sopenharmony_ci#include "exynos_drm_fb.h"
318c2ecf20Sopenharmony_ci#include "exynos_drm_plane.h"
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci/*
348c2ecf20Sopenharmony_ci * FIMD stands for Fully Interactive Mobile Display and
358c2ecf20Sopenharmony_ci * as a display controller, it transfers contents drawn on memory
368c2ecf20Sopenharmony_ci * to a LCD Panel through Display Interfaces such as RGB or
378c2ecf20Sopenharmony_ci * CPU Interface.
388c2ecf20Sopenharmony_ci */
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#define MIN_FB_WIDTH_FOR_16WORD_BURST 128
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/* position control register for hardware window 0, 2 ~ 4.*/
438c2ecf20Sopenharmony_ci#define VIDOSD_A(win)		(VIDOSD_BASE + 0x00 + (win) * 16)
448c2ecf20Sopenharmony_ci#define VIDOSD_B(win)		(VIDOSD_BASE + 0x04 + (win) * 16)
458c2ecf20Sopenharmony_ci/*
468c2ecf20Sopenharmony_ci * size control register for hardware windows 0 and alpha control register
478c2ecf20Sopenharmony_ci * for hardware windows 1 ~ 4
488c2ecf20Sopenharmony_ci */
498c2ecf20Sopenharmony_ci#define VIDOSD_C(win)		(VIDOSD_BASE + 0x08 + (win) * 16)
508c2ecf20Sopenharmony_ci/* size control register for hardware windows 1 ~ 2. */
518c2ecf20Sopenharmony_ci#define VIDOSD_D(win)		(VIDOSD_BASE + 0x0C + (win) * 16)
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci#define VIDWnALPHA0(win)	(VIDW_ALPHA + 0x00 + (win) * 8)
548c2ecf20Sopenharmony_ci#define VIDWnALPHA1(win)	(VIDW_ALPHA + 0x04 + (win) * 8)
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci#define VIDWx_BUF_START(win, buf)	(VIDW_BUF_START(buf) + (win) * 8)
578c2ecf20Sopenharmony_ci#define VIDWx_BUF_START_S(win, buf)	(VIDW_BUF_START_S(buf) + (win) * 8)
588c2ecf20Sopenharmony_ci#define VIDWx_BUF_END(win, buf)		(VIDW_BUF_END(buf) + (win) * 8)
598c2ecf20Sopenharmony_ci#define VIDWx_BUF_SIZE(win, buf)	(VIDW_BUF_SIZE(buf) + (win) * 4)
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci/* color key control register for hardware window 1 ~ 4. */
628c2ecf20Sopenharmony_ci#define WKEYCON0_BASE(x)		((WKEYCON0 + 0x140) + ((x - 1) * 8))
638c2ecf20Sopenharmony_ci/* color key value register for hardware window 1 ~ 4. */
648c2ecf20Sopenharmony_ci#define WKEYCON1_BASE(x)		((WKEYCON1 + 0x140) + ((x - 1) * 8))
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci/* I80 trigger control register */
678c2ecf20Sopenharmony_ci#define TRIGCON				0x1A4
688c2ecf20Sopenharmony_ci#define TRGMODE_ENABLE			(1 << 0)
698c2ecf20Sopenharmony_ci#define SWTRGCMD_ENABLE			(1 << 1)
708c2ecf20Sopenharmony_ci/* Exynos3250, 3472, 5260 5410, 5420 and 5422 only supported. */
718c2ecf20Sopenharmony_ci#define HWTRGEN_ENABLE			(1 << 3)
728c2ecf20Sopenharmony_ci#define HWTRGMASK_ENABLE		(1 << 4)
738c2ecf20Sopenharmony_ci/* Exynos3250, 3472, 5260, 5420 and 5422 only supported. */
748c2ecf20Sopenharmony_ci#define HWTRIGEN_PER_ENABLE		(1 << 31)
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci/* display mode change control register except exynos4 */
778c2ecf20Sopenharmony_ci#define VIDOUT_CON			0x000
788c2ecf20Sopenharmony_ci#define VIDOUT_CON_F_I80_LDI0		(0x2 << 8)
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci/* I80 interface control for main LDI register */
818c2ecf20Sopenharmony_ci#define I80IFCONFAx(x)			(0x1B0 + (x) * 4)
828c2ecf20Sopenharmony_ci#define I80IFCONFBx(x)			(0x1B8 + (x) * 4)
838c2ecf20Sopenharmony_ci#define LCD_CS_SETUP(x)			((x) << 16)
848c2ecf20Sopenharmony_ci#define LCD_WR_SETUP(x)			((x) << 12)
858c2ecf20Sopenharmony_ci#define LCD_WR_ACTIVE(x)		((x) << 8)
868c2ecf20Sopenharmony_ci#define LCD_WR_HOLD(x)			((x) << 4)
878c2ecf20Sopenharmony_ci#define I80IFEN_ENABLE			(1 << 0)
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci/* FIMD has totally five hardware windows. */
908c2ecf20Sopenharmony_ci#define WINDOWS_NR	5
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci/* HW trigger flag on i80 panel. */
938c2ecf20Sopenharmony_ci#define I80_HW_TRG     (1 << 1)
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistruct fimd_driver_data {
968c2ecf20Sopenharmony_ci	unsigned int timing_base;
978c2ecf20Sopenharmony_ci	unsigned int lcdblk_offset;
988c2ecf20Sopenharmony_ci	unsigned int lcdblk_vt_shift;
998c2ecf20Sopenharmony_ci	unsigned int lcdblk_bypass_shift;
1008c2ecf20Sopenharmony_ci	unsigned int lcdblk_mic_bypass_shift;
1018c2ecf20Sopenharmony_ci	unsigned int trg_type;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	unsigned int has_shadowcon:1;
1048c2ecf20Sopenharmony_ci	unsigned int has_clksel:1;
1058c2ecf20Sopenharmony_ci	unsigned int has_limited_fmt:1;
1068c2ecf20Sopenharmony_ci	unsigned int has_vidoutcon:1;
1078c2ecf20Sopenharmony_ci	unsigned int has_vtsel:1;
1088c2ecf20Sopenharmony_ci	unsigned int has_mic_bypass:1;
1098c2ecf20Sopenharmony_ci	unsigned int has_dp_clk:1;
1108c2ecf20Sopenharmony_ci	unsigned int has_hw_trigger:1;
1118c2ecf20Sopenharmony_ci	unsigned int has_trigger_per_te:1;
1128c2ecf20Sopenharmony_ci};
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistatic struct fimd_driver_data s3c64xx_fimd_driver_data = {
1158c2ecf20Sopenharmony_ci	.timing_base = 0x0,
1168c2ecf20Sopenharmony_ci	.has_clksel = 1,
1178c2ecf20Sopenharmony_ci	.has_limited_fmt = 1,
1188c2ecf20Sopenharmony_ci};
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic struct fimd_driver_data s5pv210_fimd_driver_data = {
1218c2ecf20Sopenharmony_ci	.timing_base = 0x0,
1228c2ecf20Sopenharmony_ci	.has_shadowcon = 1,
1238c2ecf20Sopenharmony_ci	.has_clksel = 1,
1248c2ecf20Sopenharmony_ci};
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic struct fimd_driver_data exynos3_fimd_driver_data = {
1278c2ecf20Sopenharmony_ci	.timing_base = 0x20000,
1288c2ecf20Sopenharmony_ci	.lcdblk_offset = 0x210,
1298c2ecf20Sopenharmony_ci	.lcdblk_bypass_shift = 1,
1308c2ecf20Sopenharmony_ci	.has_shadowcon = 1,
1318c2ecf20Sopenharmony_ci	.has_vidoutcon = 1,
1328c2ecf20Sopenharmony_ci};
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic struct fimd_driver_data exynos4_fimd_driver_data = {
1358c2ecf20Sopenharmony_ci	.timing_base = 0x0,
1368c2ecf20Sopenharmony_ci	.lcdblk_offset = 0x210,
1378c2ecf20Sopenharmony_ci	.lcdblk_vt_shift = 10,
1388c2ecf20Sopenharmony_ci	.lcdblk_bypass_shift = 1,
1398c2ecf20Sopenharmony_ci	.has_shadowcon = 1,
1408c2ecf20Sopenharmony_ci	.has_vtsel = 1,
1418c2ecf20Sopenharmony_ci};
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic struct fimd_driver_data exynos5_fimd_driver_data = {
1448c2ecf20Sopenharmony_ci	.timing_base = 0x20000,
1458c2ecf20Sopenharmony_ci	.lcdblk_offset = 0x214,
1468c2ecf20Sopenharmony_ci	.lcdblk_vt_shift = 24,
1478c2ecf20Sopenharmony_ci	.lcdblk_bypass_shift = 15,
1488c2ecf20Sopenharmony_ci	.has_shadowcon = 1,
1498c2ecf20Sopenharmony_ci	.has_vidoutcon = 1,
1508c2ecf20Sopenharmony_ci	.has_vtsel = 1,
1518c2ecf20Sopenharmony_ci	.has_dp_clk = 1,
1528c2ecf20Sopenharmony_ci};
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistatic struct fimd_driver_data exynos5420_fimd_driver_data = {
1558c2ecf20Sopenharmony_ci	.timing_base = 0x20000,
1568c2ecf20Sopenharmony_ci	.lcdblk_offset = 0x214,
1578c2ecf20Sopenharmony_ci	.lcdblk_vt_shift = 24,
1588c2ecf20Sopenharmony_ci	.lcdblk_bypass_shift = 15,
1598c2ecf20Sopenharmony_ci	.lcdblk_mic_bypass_shift = 11,
1608c2ecf20Sopenharmony_ci	.has_shadowcon = 1,
1618c2ecf20Sopenharmony_ci	.has_vidoutcon = 1,
1628c2ecf20Sopenharmony_ci	.has_vtsel = 1,
1638c2ecf20Sopenharmony_ci	.has_mic_bypass = 1,
1648c2ecf20Sopenharmony_ci	.has_dp_clk = 1,
1658c2ecf20Sopenharmony_ci};
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistruct fimd_context {
1688c2ecf20Sopenharmony_ci	struct device			*dev;
1698c2ecf20Sopenharmony_ci	struct drm_device		*drm_dev;
1708c2ecf20Sopenharmony_ci	void				*dma_priv;
1718c2ecf20Sopenharmony_ci	struct exynos_drm_crtc		*crtc;
1728c2ecf20Sopenharmony_ci	struct exynos_drm_plane		planes[WINDOWS_NR];
1738c2ecf20Sopenharmony_ci	struct exynos_drm_plane_config	configs[WINDOWS_NR];
1748c2ecf20Sopenharmony_ci	struct clk			*bus_clk;
1758c2ecf20Sopenharmony_ci	struct clk			*lcd_clk;
1768c2ecf20Sopenharmony_ci	void __iomem			*regs;
1778c2ecf20Sopenharmony_ci	struct regmap			*sysreg;
1788c2ecf20Sopenharmony_ci	unsigned long			irq_flags;
1798c2ecf20Sopenharmony_ci	u32				vidcon0;
1808c2ecf20Sopenharmony_ci	u32				vidcon1;
1818c2ecf20Sopenharmony_ci	u32				vidout_con;
1828c2ecf20Sopenharmony_ci	u32				i80ifcon;
1838c2ecf20Sopenharmony_ci	bool				i80_if;
1848c2ecf20Sopenharmony_ci	bool				suspended;
1858c2ecf20Sopenharmony_ci	wait_queue_head_t		wait_vsync_queue;
1868c2ecf20Sopenharmony_ci	atomic_t			wait_vsync_event;
1878c2ecf20Sopenharmony_ci	atomic_t			win_updated;
1888c2ecf20Sopenharmony_ci	atomic_t			triggering;
1898c2ecf20Sopenharmony_ci	u32				clkdiv;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	const struct fimd_driver_data *driver_data;
1928c2ecf20Sopenharmony_ci	struct drm_encoder *encoder;
1938c2ecf20Sopenharmony_ci	struct exynos_drm_clk		dp_clk;
1948c2ecf20Sopenharmony_ci};
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_cistatic const struct of_device_id fimd_driver_dt_match[] = {
1978c2ecf20Sopenharmony_ci	{ .compatible = "samsung,s3c6400-fimd",
1988c2ecf20Sopenharmony_ci	  .data = &s3c64xx_fimd_driver_data },
1998c2ecf20Sopenharmony_ci	{ .compatible = "samsung,s5pv210-fimd",
2008c2ecf20Sopenharmony_ci	  .data = &s5pv210_fimd_driver_data },
2018c2ecf20Sopenharmony_ci	{ .compatible = "samsung,exynos3250-fimd",
2028c2ecf20Sopenharmony_ci	  .data = &exynos3_fimd_driver_data },
2038c2ecf20Sopenharmony_ci	{ .compatible = "samsung,exynos4210-fimd",
2048c2ecf20Sopenharmony_ci	  .data = &exynos4_fimd_driver_data },
2058c2ecf20Sopenharmony_ci	{ .compatible = "samsung,exynos5250-fimd",
2068c2ecf20Sopenharmony_ci	  .data = &exynos5_fimd_driver_data },
2078c2ecf20Sopenharmony_ci	{ .compatible = "samsung,exynos5420-fimd",
2088c2ecf20Sopenharmony_ci	  .data = &exynos5420_fimd_driver_data },
2098c2ecf20Sopenharmony_ci	{},
2108c2ecf20Sopenharmony_ci};
2118c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, fimd_driver_dt_match);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_cistatic const enum drm_plane_type fimd_win_types[WINDOWS_NR] = {
2148c2ecf20Sopenharmony_ci	DRM_PLANE_TYPE_PRIMARY,
2158c2ecf20Sopenharmony_ci	DRM_PLANE_TYPE_OVERLAY,
2168c2ecf20Sopenharmony_ci	DRM_PLANE_TYPE_OVERLAY,
2178c2ecf20Sopenharmony_ci	DRM_PLANE_TYPE_OVERLAY,
2188c2ecf20Sopenharmony_ci	DRM_PLANE_TYPE_CURSOR,
2198c2ecf20Sopenharmony_ci};
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_cistatic const uint32_t fimd_formats[] = {
2228c2ecf20Sopenharmony_ci	DRM_FORMAT_C8,
2238c2ecf20Sopenharmony_ci	DRM_FORMAT_XRGB1555,
2248c2ecf20Sopenharmony_ci	DRM_FORMAT_RGB565,
2258c2ecf20Sopenharmony_ci	DRM_FORMAT_XRGB8888,
2268c2ecf20Sopenharmony_ci	DRM_FORMAT_ARGB8888,
2278c2ecf20Sopenharmony_ci};
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic const unsigned int capabilities[WINDOWS_NR] = {
2308c2ecf20Sopenharmony_ci	0,
2318c2ecf20Sopenharmony_ci	EXYNOS_DRM_PLANE_CAP_WIN_BLEND | EXYNOS_DRM_PLANE_CAP_PIX_BLEND,
2328c2ecf20Sopenharmony_ci	EXYNOS_DRM_PLANE_CAP_WIN_BLEND | EXYNOS_DRM_PLANE_CAP_PIX_BLEND,
2338c2ecf20Sopenharmony_ci	EXYNOS_DRM_PLANE_CAP_WIN_BLEND | EXYNOS_DRM_PLANE_CAP_PIX_BLEND,
2348c2ecf20Sopenharmony_ci	EXYNOS_DRM_PLANE_CAP_WIN_BLEND | EXYNOS_DRM_PLANE_CAP_PIX_BLEND,
2358c2ecf20Sopenharmony_ci};
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_cistatic inline void fimd_set_bits(struct fimd_context *ctx, u32 reg, u32 mask,
2388c2ecf20Sopenharmony_ci				 u32 val)
2398c2ecf20Sopenharmony_ci{
2408c2ecf20Sopenharmony_ci	val = (val & mask) | (readl(ctx->regs + reg) & ~mask);
2418c2ecf20Sopenharmony_ci	writel(val, ctx->regs + reg);
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cistatic int fimd_enable_vblank(struct exynos_drm_crtc *crtc)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	struct fimd_context *ctx = crtc->ctx;
2478c2ecf20Sopenharmony_ci	u32 val;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	if (ctx->suspended)
2508c2ecf20Sopenharmony_ci		return -EPERM;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	if (!test_and_set_bit(0, &ctx->irq_flags)) {
2538c2ecf20Sopenharmony_ci		val = readl(ctx->regs + VIDINTCON0);
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci		val |= VIDINTCON0_INT_ENABLE;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci		if (ctx->i80_if) {
2588c2ecf20Sopenharmony_ci			val |= VIDINTCON0_INT_I80IFDONE;
2598c2ecf20Sopenharmony_ci			val |= VIDINTCON0_INT_SYSMAINCON;
2608c2ecf20Sopenharmony_ci			val &= ~VIDINTCON0_INT_SYSSUBCON;
2618c2ecf20Sopenharmony_ci		} else {
2628c2ecf20Sopenharmony_ci			val |= VIDINTCON0_INT_FRAME;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci			val &= ~VIDINTCON0_FRAMESEL0_MASK;
2658c2ecf20Sopenharmony_ci			val |= VIDINTCON0_FRAMESEL0_FRONTPORCH;
2668c2ecf20Sopenharmony_ci			val &= ~VIDINTCON0_FRAMESEL1_MASK;
2678c2ecf20Sopenharmony_ci			val |= VIDINTCON0_FRAMESEL1_NONE;
2688c2ecf20Sopenharmony_ci		}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci		writel(val, ctx->regs + VIDINTCON0);
2718c2ecf20Sopenharmony_ci	}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	return 0;
2748c2ecf20Sopenharmony_ci}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic void fimd_disable_vblank(struct exynos_drm_crtc *crtc)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	struct fimd_context *ctx = crtc->ctx;
2798c2ecf20Sopenharmony_ci	u32 val;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	if (ctx->suspended)
2828c2ecf20Sopenharmony_ci		return;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	if (test_and_clear_bit(0, &ctx->irq_flags)) {
2858c2ecf20Sopenharmony_ci		val = readl(ctx->regs + VIDINTCON0);
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci		val &= ~VIDINTCON0_INT_ENABLE;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci		if (ctx->i80_if) {
2908c2ecf20Sopenharmony_ci			val &= ~VIDINTCON0_INT_I80IFDONE;
2918c2ecf20Sopenharmony_ci			val &= ~VIDINTCON0_INT_SYSMAINCON;
2928c2ecf20Sopenharmony_ci			val &= ~VIDINTCON0_INT_SYSSUBCON;
2938c2ecf20Sopenharmony_ci		} else
2948c2ecf20Sopenharmony_ci			val &= ~VIDINTCON0_INT_FRAME;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci		writel(val, ctx->regs + VIDINTCON0);
2978c2ecf20Sopenharmony_ci	}
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_cistatic void fimd_wait_for_vblank(struct exynos_drm_crtc *crtc)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	struct fimd_context *ctx = crtc->ctx;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	if (ctx->suspended)
3058c2ecf20Sopenharmony_ci		return;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	atomic_set(&ctx->wait_vsync_event, 1);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	/*
3108c2ecf20Sopenharmony_ci	 * wait for FIMD to signal VSYNC interrupt or return after
3118c2ecf20Sopenharmony_ci	 * timeout which is set to 50ms (refresh rate of 20).
3128c2ecf20Sopenharmony_ci	 */
3138c2ecf20Sopenharmony_ci	if (!wait_event_timeout(ctx->wait_vsync_queue,
3148c2ecf20Sopenharmony_ci				!atomic_read(&ctx->wait_vsync_event),
3158c2ecf20Sopenharmony_ci				HZ/20))
3168c2ecf20Sopenharmony_ci		DRM_DEV_DEBUG_KMS(ctx->dev, "vblank wait timed out.\n");
3178c2ecf20Sopenharmony_ci}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_cistatic void fimd_enable_video_output(struct fimd_context *ctx, unsigned int win,
3208c2ecf20Sopenharmony_ci					bool enable)
3218c2ecf20Sopenharmony_ci{
3228c2ecf20Sopenharmony_ci	u32 val = readl(ctx->regs + WINCON(win));
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	if (enable)
3258c2ecf20Sopenharmony_ci		val |= WINCONx_ENWIN;
3268c2ecf20Sopenharmony_ci	else
3278c2ecf20Sopenharmony_ci		val &= ~WINCONx_ENWIN;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	writel(val, ctx->regs + WINCON(win));
3308c2ecf20Sopenharmony_ci}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_cistatic void fimd_enable_shadow_channel_path(struct fimd_context *ctx,
3338c2ecf20Sopenharmony_ci						unsigned int win,
3348c2ecf20Sopenharmony_ci						bool enable)
3358c2ecf20Sopenharmony_ci{
3368c2ecf20Sopenharmony_ci	u32 val = readl(ctx->regs + SHADOWCON);
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	if (enable)
3398c2ecf20Sopenharmony_ci		val |= SHADOWCON_CHx_ENABLE(win);
3408c2ecf20Sopenharmony_ci	else
3418c2ecf20Sopenharmony_ci		val &= ~SHADOWCON_CHx_ENABLE(win);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	writel(val, ctx->regs + SHADOWCON);
3448c2ecf20Sopenharmony_ci}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_cistatic void fimd_clear_channels(struct exynos_drm_crtc *crtc)
3478c2ecf20Sopenharmony_ci{
3488c2ecf20Sopenharmony_ci	struct fimd_context *ctx = crtc->ctx;
3498c2ecf20Sopenharmony_ci	unsigned int win, ch_enabled = 0;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	/* Hardware is in unknown state, so ensure it gets enabled properly */
3528c2ecf20Sopenharmony_ci	pm_runtime_get_sync(ctx->dev);
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	clk_prepare_enable(ctx->bus_clk);
3558c2ecf20Sopenharmony_ci	clk_prepare_enable(ctx->lcd_clk);
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	/* Check if any channel is enabled. */
3588c2ecf20Sopenharmony_ci	for (win = 0; win < WINDOWS_NR; win++) {
3598c2ecf20Sopenharmony_ci		u32 val = readl(ctx->regs + WINCON(win));
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci		if (val & WINCONx_ENWIN) {
3628c2ecf20Sopenharmony_ci			fimd_enable_video_output(ctx, win, false);
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci			if (ctx->driver_data->has_shadowcon)
3658c2ecf20Sopenharmony_ci				fimd_enable_shadow_channel_path(ctx, win,
3668c2ecf20Sopenharmony_ci								false);
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci			ch_enabled = 1;
3698c2ecf20Sopenharmony_ci		}
3708c2ecf20Sopenharmony_ci	}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	/* Wait for vsync, as disable channel takes effect at next vsync */
3738c2ecf20Sopenharmony_ci	if (ch_enabled) {
3748c2ecf20Sopenharmony_ci		ctx->suspended = false;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci		fimd_enable_vblank(ctx->crtc);
3778c2ecf20Sopenharmony_ci		fimd_wait_for_vblank(ctx->crtc);
3788c2ecf20Sopenharmony_ci		fimd_disable_vblank(ctx->crtc);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci		ctx->suspended = true;
3818c2ecf20Sopenharmony_ci	}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	clk_disable_unprepare(ctx->lcd_clk);
3848c2ecf20Sopenharmony_ci	clk_disable_unprepare(ctx->bus_clk);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	pm_runtime_put(ctx->dev);
3878c2ecf20Sopenharmony_ci}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_cistatic int fimd_atomic_check(struct exynos_drm_crtc *crtc,
3918c2ecf20Sopenharmony_ci		struct drm_crtc_state *state)
3928c2ecf20Sopenharmony_ci{
3938c2ecf20Sopenharmony_ci	struct drm_display_mode *mode = &state->adjusted_mode;
3948c2ecf20Sopenharmony_ci	struct fimd_context *ctx = crtc->ctx;
3958c2ecf20Sopenharmony_ci	unsigned long ideal_clk, lcd_rate;
3968c2ecf20Sopenharmony_ci	u32 clkdiv;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	if (mode->clock == 0) {
3998c2ecf20Sopenharmony_ci		DRM_DEV_ERROR(ctx->dev, "Mode has zero clock value.\n");
4008c2ecf20Sopenharmony_ci		return -EINVAL;
4018c2ecf20Sopenharmony_ci	}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	ideal_clk = mode->clock * 1000;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	if (ctx->i80_if) {
4068c2ecf20Sopenharmony_ci		/*
4078c2ecf20Sopenharmony_ci		 * The frame done interrupt should be occurred prior to the
4088c2ecf20Sopenharmony_ci		 * next TE signal.
4098c2ecf20Sopenharmony_ci		 */
4108c2ecf20Sopenharmony_ci		ideal_clk *= 2;
4118c2ecf20Sopenharmony_ci	}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	lcd_rate = clk_get_rate(ctx->lcd_clk);
4148c2ecf20Sopenharmony_ci	if (2 * lcd_rate < ideal_clk) {
4158c2ecf20Sopenharmony_ci		DRM_DEV_ERROR(ctx->dev,
4168c2ecf20Sopenharmony_ci			      "sclk_fimd clock too low(%lu) for requested pixel clock(%lu)\n",
4178c2ecf20Sopenharmony_ci			      lcd_rate, ideal_clk);
4188c2ecf20Sopenharmony_ci		return -EINVAL;
4198c2ecf20Sopenharmony_ci	}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	/* Find the clock divider value that gets us closest to ideal_clk */
4228c2ecf20Sopenharmony_ci	clkdiv = DIV_ROUND_CLOSEST(lcd_rate, ideal_clk);
4238c2ecf20Sopenharmony_ci	if (clkdiv >= 0x200) {
4248c2ecf20Sopenharmony_ci		DRM_DEV_ERROR(ctx->dev, "requested pixel clock(%lu) too low\n",
4258c2ecf20Sopenharmony_ci			      ideal_clk);
4268c2ecf20Sopenharmony_ci		return -EINVAL;
4278c2ecf20Sopenharmony_ci	}
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	ctx->clkdiv = (clkdiv < 0x100) ? clkdiv : 0xff;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	return 0;
4328c2ecf20Sopenharmony_ci}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_cistatic void fimd_setup_trigger(struct fimd_context *ctx)
4358c2ecf20Sopenharmony_ci{
4368c2ecf20Sopenharmony_ci	void __iomem *timing_base = ctx->regs + ctx->driver_data->timing_base;
4378c2ecf20Sopenharmony_ci	u32 trg_type = ctx->driver_data->trg_type;
4388c2ecf20Sopenharmony_ci	u32 val = readl(timing_base + TRIGCON);
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	val &= ~(TRGMODE_ENABLE);
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	if (trg_type == I80_HW_TRG) {
4438c2ecf20Sopenharmony_ci		if (ctx->driver_data->has_hw_trigger)
4448c2ecf20Sopenharmony_ci			val |= HWTRGEN_ENABLE | HWTRGMASK_ENABLE;
4458c2ecf20Sopenharmony_ci		if (ctx->driver_data->has_trigger_per_te)
4468c2ecf20Sopenharmony_ci			val |= HWTRIGEN_PER_ENABLE;
4478c2ecf20Sopenharmony_ci	} else {
4488c2ecf20Sopenharmony_ci		val |= TRGMODE_ENABLE;
4498c2ecf20Sopenharmony_ci	}
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	writel(val, timing_base + TRIGCON);
4528c2ecf20Sopenharmony_ci}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_cistatic void fimd_commit(struct exynos_drm_crtc *crtc)
4558c2ecf20Sopenharmony_ci{
4568c2ecf20Sopenharmony_ci	struct fimd_context *ctx = crtc->ctx;
4578c2ecf20Sopenharmony_ci	struct drm_display_mode *mode = &crtc->base.state->adjusted_mode;
4588c2ecf20Sopenharmony_ci	const struct fimd_driver_data *driver_data = ctx->driver_data;
4598c2ecf20Sopenharmony_ci	void *timing_base = ctx->regs + driver_data->timing_base;
4608c2ecf20Sopenharmony_ci	u32 val;
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	if (ctx->suspended)
4638c2ecf20Sopenharmony_ci		return;
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	/* nothing to do if we haven't set the mode yet */
4668c2ecf20Sopenharmony_ci	if (mode->htotal == 0 || mode->vtotal == 0)
4678c2ecf20Sopenharmony_ci		return;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	if (ctx->i80_if) {
4708c2ecf20Sopenharmony_ci		val = ctx->i80ifcon | I80IFEN_ENABLE;
4718c2ecf20Sopenharmony_ci		writel(val, timing_base + I80IFCONFAx(0));
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci		/* disable auto frame rate */
4748c2ecf20Sopenharmony_ci		writel(0, timing_base + I80IFCONFBx(0));
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci		/* set video type selection to I80 interface */
4778c2ecf20Sopenharmony_ci		if (driver_data->has_vtsel && ctx->sysreg &&
4788c2ecf20Sopenharmony_ci				regmap_update_bits(ctx->sysreg,
4798c2ecf20Sopenharmony_ci					driver_data->lcdblk_offset,
4808c2ecf20Sopenharmony_ci					0x3 << driver_data->lcdblk_vt_shift,
4818c2ecf20Sopenharmony_ci					0x1 << driver_data->lcdblk_vt_shift)) {
4828c2ecf20Sopenharmony_ci			DRM_DEV_ERROR(ctx->dev,
4838c2ecf20Sopenharmony_ci				      "Failed to update sysreg for I80 i/f.\n");
4848c2ecf20Sopenharmony_ci			return;
4858c2ecf20Sopenharmony_ci		}
4868c2ecf20Sopenharmony_ci	} else {
4878c2ecf20Sopenharmony_ci		int vsync_len, vbpd, vfpd, hsync_len, hbpd, hfpd;
4888c2ecf20Sopenharmony_ci		u32 vidcon1;
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci		/* setup polarity values */
4918c2ecf20Sopenharmony_ci		vidcon1 = ctx->vidcon1;
4928c2ecf20Sopenharmony_ci		if (mode->flags & DRM_MODE_FLAG_NVSYNC)
4938c2ecf20Sopenharmony_ci			vidcon1 |= VIDCON1_INV_VSYNC;
4948c2ecf20Sopenharmony_ci		if (mode->flags & DRM_MODE_FLAG_NHSYNC)
4958c2ecf20Sopenharmony_ci			vidcon1 |= VIDCON1_INV_HSYNC;
4968c2ecf20Sopenharmony_ci		writel(vidcon1, ctx->regs + driver_data->timing_base + VIDCON1);
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci		/* setup vertical timing values. */
4998c2ecf20Sopenharmony_ci		vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
5008c2ecf20Sopenharmony_ci		vbpd = mode->crtc_vtotal - mode->crtc_vsync_end;
5018c2ecf20Sopenharmony_ci		vfpd = mode->crtc_vsync_start - mode->crtc_vdisplay;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci		val = VIDTCON0_VBPD(vbpd - 1) |
5048c2ecf20Sopenharmony_ci			VIDTCON0_VFPD(vfpd - 1) |
5058c2ecf20Sopenharmony_ci			VIDTCON0_VSPW(vsync_len - 1);
5068c2ecf20Sopenharmony_ci		writel(val, ctx->regs + driver_data->timing_base + VIDTCON0);
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci		/* setup horizontal timing values.  */
5098c2ecf20Sopenharmony_ci		hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
5108c2ecf20Sopenharmony_ci		hbpd = mode->crtc_htotal - mode->crtc_hsync_end;
5118c2ecf20Sopenharmony_ci		hfpd = mode->crtc_hsync_start - mode->crtc_hdisplay;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci		val = VIDTCON1_HBPD(hbpd - 1) |
5148c2ecf20Sopenharmony_ci			VIDTCON1_HFPD(hfpd - 1) |
5158c2ecf20Sopenharmony_ci			VIDTCON1_HSPW(hsync_len - 1);
5168c2ecf20Sopenharmony_ci		writel(val, ctx->regs + driver_data->timing_base + VIDTCON1);
5178c2ecf20Sopenharmony_ci	}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	if (driver_data->has_vidoutcon)
5208c2ecf20Sopenharmony_ci		writel(ctx->vidout_con, timing_base + VIDOUT_CON);
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	/* set bypass selection */
5238c2ecf20Sopenharmony_ci	if (ctx->sysreg && regmap_update_bits(ctx->sysreg,
5248c2ecf20Sopenharmony_ci				driver_data->lcdblk_offset,
5258c2ecf20Sopenharmony_ci				0x1 << driver_data->lcdblk_bypass_shift,
5268c2ecf20Sopenharmony_ci				0x1 << driver_data->lcdblk_bypass_shift)) {
5278c2ecf20Sopenharmony_ci		DRM_DEV_ERROR(ctx->dev,
5288c2ecf20Sopenharmony_ci			      "Failed to update sysreg for bypass setting.\n");
5298c2ecf20Sopenharmony_ci		return;
5308c2ecf20Sopenharmony_ci	}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	/* TODO: When MIC is enabled for display path, the lcdblk_mic_bypass
5338c2ecf20Sopenharmony_ci	 * bit should be cleared.
5348c2ecf20Sopenharmony_ci	 */
5358c2ecf20Sopenharmony_ci	if (driver_data->has_mic_bypass && ctx->sysreg &&
5368c2ecf20Sopenharmony_ci	    regmap_update_bits(ctx->sysreg,
5378c2ecf20Sopenharmony_ci				driver_data->lcdblk_offset,
5388c2ecf20Sopenharmony_ci				0x1 << driver_data->lcdblk_mic_bypass_shift,
5398c2ecf20Sopenharmony_ci				0x1 << driver_data->lcdblk_mic_bypass_shift)) {
5408c2ecf20Sopenharmony_ci		DRM_DEV_ERROR(ctx->dev,
5418c2ecf20Sopenharmony_ci			      "Failed to update sysreg for bypass mic.\n");
5428c2ecf20Sopenharmony_ci		return;
5438c2ecf20Sopenharmony_ci	}
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	/* setup horizontal and vertical display size. */
5468c2ecf20Sopenharmony_ci	val = VIDTCON2_LINEVAL(mode->vdisplay - 1) |
5478c2ecf20Sopenharmony_ci	       VIDTCON2_HOZVAL(mode->hdisplay - 1) |
5488c2ecf20Sopenharmony_ci	       VIDTCON2_LINEVAL_E(mode->vdisplay - 1) |
5498c2ecf20Sopenharmony_ci	       VIDTCON2_HOZVAL_E(mode->hdisplay - 1);
5508c2ecf20Sopenharmony_ci	writel(val, ctx->regs + driver_data->timing_base + VIDTCON2);
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	fimd_setup_trigger(ctx);
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	/*
5558c2ecf20Sopenharmony_ci	 * fields of register with prefix '_F' would be updated
5568c2ecf20Sopenharmony_ci	 * at vsync(same as dma start)
5578c2ecf20Sopenharmony_ci	 */
5588c2ecf20Sopenharmony_ci	val = ctx->vidcon0;
5598c2ecf20Sopenharmony_ci	val |= VIDCON0_ENVID | VIDCON0_ENVID_F;
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	if (ctx->driver_data->has_clksel)
5628c2ecf20Sopenharmony_ci		val |= VIDCON0_CLKSEL_LCD;
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	if (ctx->clkdiv > 1)
5658c2ecf20Sopenharmony_ci		val |= VIDCON0_CLKVAL_F(ctx->clkdiv - 1) | VIDCON0_CLKDIR;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	writel(val, ctx->regs + VIDCON0);
5688c2ecf20Sopenharmony_ci}
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_cistatic void fimd_win_set_bldeq(struct fimd_context *ctx, unsigned int win,
5718c2ecf20Sopenharmony_ci			       unsigned int alpha, unsigned int pixel_alpha)
5728c2ecf20Sopenharmony_ci{
5738c2ecf20Sopenharmony_ci	u32 mask = BLENDEQ_A_FUNC_F(0xf) | BLENDEQ_B_FUNC_F(0xf);
5748c2ecf20Sopenharmony_ci	u32 val = 0;
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	switch (pixel_alpha) {
5778c2ecf20Sopenharmony_ci	case DRM_MODE_BLEND_PIXEL_NONE:
5788c2ecf20Sopenharmony_ci	case DRM_MODE_BLEND_COVERAGE:
5798c2ecf20Sopenharmony_ci		val |= BLENDEQ_A_FUNC_F(BLENDEQ_ALPHA_A);
5808c2ecf20Sopenharmony_ci		val |= BLENDEQ_B_FUNC_F(BLENDEQ_ONE_MINUS_ALPHA_A);
5818c2ecf20Sopenharmony_ci		break;
5828c2ecf20Sopenharmony_ci	case DRM_MODE_BLEND_PREMULTI:
5838c2ecf20Sopenharmony_ci	default:
5848c2ecf20Sopenharmony_ci		if (alpha != DRM_BLEND_ALPHA_OPAQUE) {
5858c2ecf20Sopenharmony_ci			val |= BLENDEQ_A_FUNC_F(BLENDEQ_ALPHA0);
5868c2ecf20Sopenharmony_ci			val |= BLENDEQ_B_FUNC_F(BLENDEQ_ONE_MINUS_ALPHA_A);
5878c2ecf20Sopenharmony_ci		} else {
5888c2ecf20Sopenharmony_ci			val |= BLENDEQ_A_FUNC_F(BLENDEQ_ONE);
5898c2ecf20Sopenharmony_ci			val |= BLENDEQ_B_FUNC_F(BLENDEQ_ONE_MINUS_ALPHA_A);
5908c2ecf20Sopenharmony_ci		}
5918c2ecf20Sopenharmony_ci		break;
5928c2ecf20Sopenharmony_ci	}
5938c2ecf20Sopenharmony_ci	fimd_set_bits(ctx, BLENDEQx(win), mask, val);
5948c2ecf20Sopenharmony_ci}
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_cistatic void fimd_win_set_bldmod(struct fimd_context *ctx, unsigned int win,
5978c2ecf20Sopenharmony_ci				unsigned int alpha, unsigned int pixel_alpha)
5988c2ecf20Sopenharmony_ci{
5998c2ecf20Sopenharmony_ci	u32 win_alpha_l = (alpha >> 8) & 0xf;
6008c2ecf20Sopenharmony_ci	u32 win_alpha_h = alpha >> 12;
6018c2ecf20Sopenharmony_ci	u32 val = 0;
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	switch (pixel_alpha) {
6048c2ecf20Sopenharmony_ci	case DRM_MODE_BLEND_PIXEL_NONE:
6058c2ecf20Sopenharmony_ci		break;
6068c2ecf20Sopenharmony_ci	case DRM_MODE_BLEND_COVERAGE:
6078c2ecf20Sopenharmony_ci	case DRM_MODE_BLEND_PREMULTI:
6088c2ecf20Sopenharmony_ci	default:
6098c2ecf20Sopenharmony_ci		val |= WINCON1_ALPHA_SEL;
6108c2ecf20Sopenharmony_ci		val |= WINCON1_BLD_PIX;
6118c2ecf20Sopenharmony_ci		val |= WINCON1_ALPHA_MUL;
6128c2ecf20Sopenharmony_ci		break;
6138c2ecf20Sopenharmony_ci	}
6148c2ecf20Sopenharmony_ci	fimd_set_bits(ctx, WINCON(win), WINCONx_BLEND_MODE_MASK, val);
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	/* OSD alpha */
6178c2ecf20Sopenharmony_ci	val = VIDISD14C_ALPHA0_R(win_alpha_h) |
6188c2ecf20Sopenharmony_ci		VIDISD14C_ALPHA0_G(win_alpha_h) |
6198c2ecf20Sopenharmony_ci		VIDISD14C_ALPHA0_B(win_alpha_h) |
6208c2ecf20Sopenharmony_ci		VIDISD14C_ALPHA1_R(0x0) |
6218c2ecf20Sopenharmony_ci		VIDISD14C_ALPHA1_G(0x0) |
6228c2ecf20Sopenharmony_ci		VIDISD14C_ALPHA1_B(0x0);
6238c2ecf20Sopenharmony_ci	writel(val, ctx->regs + VIDOSD_C(win));
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	val = VIDW_ALPHA_R(win_alpha_l) | VIDW_ALPHA_G(win_alpha_l) |
6268c2ecf20Sopenharmony_ci		VIDW_ALPHA_B(win_alpha_l);
6278c2ecf20Sopenharmony_ci	writel(val, ctx->regs + VIDWnALPHA0(win));
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	val = VIDW_ALPHA_R(0x0) | VIDW_ALPHA_G(0x0) |
6308c2ecf20Sopenharmony_ci		VIDW_ALPHA_B(0x0);
6318c2ecf20Sopenharmony_ci	writel(val, ctx->regs + VIDWnALPHA1(win));
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	fimd_set_bits(ctx, BLENDCON, BLENDCON_NEW_MASK,
6348c2ecf20Sopenharmony_ci			BLENDCON_NEW_8BIT_ALPHA_VALUE);
6358c2ecf20Sopenharmony_ci}
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_cistatic void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win,
6388c2ecf20Sopenharmony_ci				struct drm_framebuffer *fb, int width)
6398c2ecf20Sopenharmony_ci{
6408c2ecf20Sopenharmony_ci	struct exynos_drm_plane *plane = &ctx->planes[win];
6418c2ecf20Sopenharmony_ci	struct exynos_drm_plane_state *state =
6428c2ecf20Sopenharmony_ci		to_exynos_plane_state(plane->base.state);
6438c2ecf20Sopenharmony_ci	uint32_t pixel_format = fb->format->format;
6448c2ecf20Sopenharmony_ci	unsigned int alpha = state->base.alpha;
6458c2ecf20Sopenharmony_ci	u32 val = WINCONx_ENWIN;
6468c2ecf20Sopenharmony_ci	unsigned int pixel_alpha;
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	if (fb->format->has_alpha)
6498c2ecf20Sopenharmony_ci		pixel_alpha = state->base.pixel_blend_mode;
6508c2ecf20Sopenharmony_ci	else
6518c2ecf20Sopenharmony_ci		pixel_alpha = DRM_MODE_BLEND_PIXEL_NONE;
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	/*
6548c2ecf20Sopenharmony_ci	 * In case of s3c64xx, window 0 doesn't support alpha channel.
6558c2ecf20Sopenharmony_ci	 * So the request format is ARGB8888 then change it to XRGB8888.
6568c2ecf20Sopenharmony_ci	 */
6578c2ecf20Sopenharmony_ci	if (ctx->driver_data->has_limited_fmt && !win) {
6588c2ecf20Sopenharmony_ci		if (pixel_format == DRM_FORMAT_ARGB8888)
6598c2ecf20Sopenharmony_ci			pixel_format = DRM_FORMAT_XRGB8888;
6608c2ecf20Sopenharmony_ci	}
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	switch (pixel_format) {
6638c2ecf20Sopenharmony_ci	case DRM_FORMAT_C8:
6648c2ecf20Sopenharmony_ci		val |= WINCON0_BPPMODE_8BPP_PALETTE;
6658c2ecf20Sopenharmony_ci		val |= WINCONx_BURSTLEN_8WORD;
6668c2ecf20Sopenharmony_ci		val |= WINCONx_BYTSWP;
6678c2ecf20Sopenharmony_ci		break;
6688c2ecf20Sopenharmony_ci	case DRM_FORMAT_XRGB1555:
6698c2ecf20Sopenharmony_ci		val |= WINCON0_BPPMODE_16BPP_1555;
6708c2ecf20Sopenharmony_ci		val |= WINCONx_HAWSWP;
6718c2ecf20Sopenharmony_ci		val |= WINCONx_BURSTLEN_16WORD;
6728c2ecf20Sopenharmony_ci		break;
6738c2ecf20Sopenharmony_ci	case DRM_FORMAT_RGB565:
6748c2ecf20Sopenharmony_ci		val |= WINCON0_BPPMODE_16BPP_565;
6758c2ecf20Sopenharmony_ci		val |= WINCONx_HAWSWP;
6768c2ecf20Sopenharmony_ci		val |= WINCONx_BURSTLEN_16WORD;
6778c2ecf20Sopenharmony_ci		break;
6788c2ecf20Sopenharmony_ci	case DRM_FORMAT_XRGB8888:
6798c2ecf20Sopenharmony_ci		val |= WINCON0_BPPMODE_24BPP_888;
6808c2ecf20Sopenharmony_ci		val |= WINCONx_WSWP;
6818c2ecf20Sopenharmony_ci		val |= WINCONx_BURSTLEN_16WORD;
6828c2ecf20Sopenharmony_ci		break;
6838c2ecf20Sopenharmony_ci	case DRM_FORMAT_ARGB8888:
6848c2ecf20Sopenharmony_ci	default:
6858c2ecf20Sopenharmony_ci		val |= WINCON1_BPPMODE_25BPP_A1888;
6868c2ecf20Sopenharmony_ci		val |= WINCONx_WSWP;
6878c2ecf20Sopenharmony_ci		val |= WINCONx_BURSTLEN_16WORD;
6888c2ecf20Sopenharmony_ci		break;
6898c2ecf20Sopenharmony_ci	}
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	/*
6928c2ecf20Sopenharmony_ci	 * Setting dma-burst to 16Word causes permanent tearing for very small
6938c2ecf20Sopenharmony_ci	 * buffers, e.g. cursor buffer. Burst Mode switching which based on
6948c2ecf20Sopenharmony_ci	 * plane size is not recommended as plane size varies alot towards the
6958c2ecf20Sopenharmony_ci	 * end of the screen and rapid movement causes unstable DMA, but it is
6968c2ecf20Sopenharmony_ci	 * still better to change dma-burst than displaying garbage.
6978c2ecf20Sopenharmony_ci	 */
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	if (width < MIN_FB_WIDTH_FOR_16WORD_BURST) {
7008c2ecf20Sopenharmony_ci		val &= ~WINCONx_BURSTLEN_MASK;
7018c2ecf20Sopenharmony_ci		val |= WINCONx_BURSTLEN_4WORD;
7028c2ecf20Sopenharmony_ci	}
7038c2ecf20Sopenharmony_ci	fimd_set_bits(ctx, WINCON(win), ~WINCONx_BLEND_MODE_MASK, val);
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	/* hardware window 0 doesn't support alpha channel. */
7068c2ecf20Sopenharmony_ci	if (win != 0) {
7078c2ecf20Sopenharmony_ci		fimd_win_set_bldmod(ctx, win, alpha, pixel_alpha);
7088c2ecf20Sopenharmony_ci		fimd_win_set_bldeq(ctx, win, alpha, pixel_alpha);
7098c2ecf20Sopenharmony_ci	}
7108c2ecf20Sopenharmony_ci}
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_cistatic void fimd_win_set_colkey(struct fimd_context *ctx, unsigned int win)
7138c2ecf20Sopenharmony_ci{
7148c2ecf20Sopenharmony_ci	unsigned int keycon0 = 0, keycon1 = 0;
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	keycon0 = ~(WxKEYCON0_KEYBL_EN | WxKEYCON0_KEYEN_F |
7178c2ecf20Sopenharmony_ci			WxKEYCON0_DIRCON) | WxKEYCON0_COMPKEY(0);
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	keycon1 = WxKEYCON1_COLVAL(0xffffffff);
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	writel(keycon0, ctx->regs + WKEYCON0_BASE(win));
7228c2ecf20Sopenharmony_ci	writel(keycon1, ctx->regs + WKEYCON1_BASE(win));
7238c2ecf20Sopenharmony_ci}
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci/**
7268c2ecf20Sopenharmony_ci * shadow_protect_win() - disable updating values from shadow registers at vsync
7278c2ecf20Sopenharmony_ci *
7288c2ecf20Sopenharmony_ci * @win: window to protect registers for
7298c2ecf20Sopenharmony_ci * @protect: 1 to protect (disable updates)
7308c2ecf20Sopenharmony_ci */
7318c2ecf20Sopenharmony_cistatic void fimd_shadow_protect_win(struct fimd_context *ctx,
7328c2ecf20Sopenharmony_ci				    unsigned int win, bool protect)
7338c2ecf20Sopenharmony_ci{
7348c2ecf20Sopenharmony_ci	u32 reg, bits, val;
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	/*
7378c2ecf20Sopenharmony_ci	 * SHADOWCON/PRTCON register is used for enabling timing.
7388c2ecf20Sopenharmony_ci	 *
7398c2ecf20Sopenharmony_ci	 * for example, once only width value of a register is set,
7408c2ecf20Sopenharmony_ci	 * if the dma is started then fimd hardware could malfunction so
7418c2ecf20Sopenharmony_ci	 * with protect window setting, the register fields with prefix '_F'
7428c2ecf20Sopenharmony_ci	 * wouldn't be updated at vsync also but updated once unprotect window
7438c2ecf20Sopenharmony_ci	 * is set.
7448c2ecf20Sopenharmony_ci	 */
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	if (ctx->driver_data->has_shadowcon) {
7478c2ecf20Sopenharmony_ci		reg = SHADOWCON;
7488c2ecf20Sopenharmony_ci		bits = SHADOWCON_WINx_PROTECT(win);
7498c2ecf20Sopenharmony_ci	} else {
7508c2ecf20Sopenharmony_ci		reg = PRTCON;
7518c2ecf20Sopenharmony_ci		bits = PRTCON_PROTECT;
7528c2ecf20Sopenharmony_ci	}
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	val = readl(ctx->regs + reg);
7558c2ecf20Sopenharmony_ci	if (protect)
7568c2ecf20Sopenharmony_ci		val |= bits;
7578c2ecf20Sopenharmony_ci	else
7588c2ecf20Sopenharmony_ci		val &= ~bits;
7598c2ecf20Sopenharmony_ci	writel(val, ctx->regs + reg);
7608c2ecf20Sopenharmony_ci}
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_cistatic void fimd_atomic_begin(struct exynos_drm_crtc *crtc)
7638c2ecf20Sopenharmony_ci{
7648c2ecf20Sopenharmony_ci	struct fimd_context *ctx = crtc->ctx;
7658c2ecf20Sopenharmony_ci	int i;
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	if (ctx->suspended)
7688c2ecf20Sopenharmony_ci		return;
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	for (i = 0; i < WINDOWS_NR; i++)
7718c2ecf20Sopenharmony_ci		fimd_shadow_protect_win(ctx, i, true);
7728c2ecf20Sopenharmony_ci}
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_cistatic void fimd_atomic_flush(struct exynos_drm_crtc *crtc)
7758c2ecf20Sopenharmony_ci{
7768c2ecf20Sopenharmony_ci	struct fimd_context *ctx = crtc->ctx;
7778c2ecf20Sopenharmony_ci	int i;
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	if (ctx->suspended)
7808c2ecf20Sopenharmony_ci		return;
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	for (i = 0; i < WINDOWS_NR; i++)
7838c2ecf20Sopenharmony_ci		fimd_shadow_protect_win(ctx, i, false);
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	exynos_crtc_handle_event(crtc);
7868c2ecf20Sopenharmony_ci}
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_cistatic void fimd_update_plane(struct exynos_drm_crtc *crtc,
7898c2ecf20Sopenharmony_ci			      struct exynos_drm_plane *plane)
7908c2ecf20Sopenharmony_ci{
7918c2ecf20Sopenharmony_ci	struct exynos_drm_plane_state *state =
7928c2ecf20Sopenharmony_ci				to_exynos_plane_state(plane->base.state);
7938c2ecf20Sopenharmony_ci	struct fimd_context *ctx = crtc->ctx;
7948c2ecf20Sopenharmony_ci	struct drm_framebuffer *fb = state->base.fb;
7958c2ecf20Sopenharmony_ci	dma_addr_t dma_addr;
7968c2ecf20Sopenharmony_ci	unsigned long val, size, offset;
7978c2ecf20Sopenharmony_ci	unsigned int last_x, last_y, buf_offsize, line_size;
7988c2ecf20Sopenharmony_ci	unsigned int win = plane->index;
7998c2ecf20Sopenharmony_ci	unsigned int cpp = fb->format->cpp[0];
8008c2ecf20Sopenharmony_ci	unsigned int pitch = fb->pitches[0];
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	if (ctx->suspended)
8038c2ecf20Sopenharmony_ci		return;
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	offset = state->src.x * cpp;
8068c2ecf20Sopenharmony_ci	offset += state->src.y * pitch;
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	/* buffer start address */
8098c2ecf20Sopenharmony_ci	dma_addr = exynos_drm_fb_dma_addr(fb, 0) + offset;
8108c2ecf20Sopenharmony_ci	val = (unsigned long)dma_addr;
8118c2ecf20Sopenharmony_ci	writel(val, ctx->regs + VIDWx_BUF_START(win, 0));
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	/* buffer end address */
8148c2ecf20Sopenharmony_ci	size = pitch * state->crtc.h;
8158c2ecf20Sopenharmony_ci	val = (unsigned long)(dma_addr + size);
8168c2ecf20Sopenharmony_ci	writel(val, ctx->regs + VIDWx_BUF_END(win, 0));
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	DRM_DEV_DEBUG_KMS(ctx->dev,
8198c2ecf20Sopenharmony_ci			  "start addr = 0x%lx, end addr = 0x%lx, size = 0x%lx\n",
8208c2ecf20Sopenharmony_ci			  (unsigned long)dma_addr, val, size);
8218c2ecf20Sopenharmony_ci	DRM_DEV_DEBUG_KMS(ctx->dev, "ovl_width = %d, ovl_height = %d\n",
8228c2ecf20Sopenharmony_ci			  state->crtc.w, state->crtc.h);
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci	/* buffer size */
8258c2ecf20Sopenharmony_ci	buf_offsize = pitch - (state->crtc.w * cpp);
8268c2ecf20Sopenharmony_ci	line_size = state->crtc.w * cpp;
8278c2ecf20Sopenharmony_ci	val = VIDW_BUF_SIZE_OFFSET(buf_offsize) |
8288c2ecf20Sopenharmony_ci		VIDW_BUF_SIZE_PAGEWIDTH(line_size) |
8298c2ecf20Sopenharmony_ci		VIDW_BUF_SIZE_OFFSET_E(buf_offsize) |
8308c2ecf20Sopenharmony_ci		VIDW_BUF_SIZE_PAGEWIDTH_E(line_size);
8318c2ecf20Sopenharmony_ci	writel(val, ctx->regs + VIDWx_BUF_SIZE(win, 0));
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	/* OSD position */
8348c2ecf20Sopenharmony_ci	val = VIDOSDxA_TOPLEFT_X(state->crtc.x) |
8358c2ecf20Sopenharmony_ci		VIDOSDxA_TOPLEFT_Y(state->crtc.y) |
8368c2ecf20Sopenharmony_ci		VIDOSDxA_TOPLEFT_X_E(state->crtc.x) |
8378c2ecf20Sopenharmony_ci		VIDOSDxA_TOPLEFT_Y_E(state->crtc.y);
8388c2ecf20Sopenharmony_ci	writel(val, ctx->regs + VIDOSD_A(win));
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	last_x = state->crtc.x + state->crtc.w;
8418c2ecf20Sopenharmony_ci	if (last_x)
8428c2ecf20Sopenharmony_ci		last_x--;
8438c2ecf20Sopenharmony_ci	last_y = state->crtc.y + state->crtc.h;
8448c2ecf20Sopenharmony_ci	if (last_y)
8458c2ecf20Sopenharmony_ci		last_y--;
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	val = VIDOSDxB_BOTRIGHT_X(last_x) | VIDOSDxB_BOTRIGHT_Y(last_y) |
8488c2ecf20Sopenharmony_ci		VIDOSDxB_BOTRIGHT_X_E(last_x) | VIDOSDxB_BOTRIGHT_Y_E(last_y);
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	writel(val, ctx->regs + VIDOSD_B(win));
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	DRM_DEV_DEBUG_KMS(ctx->dev,
8538c2ecf20Sopenharmony_ci			  "osd pos: tx = %d, ty = %d, bx = %d, by = %d\n",
8548c2ecf20Sopenharmony_ci			  state->crtc.x, state->crtc.y, last_x, last_y);
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	/* OSD size */
8578c2ecf20Sopenharmony_ci	if (win != 3 && win != 4) {
8588c2ecf20Sopenharmony_ci		u32 offset = VIDOSD_D(win);
8598c2ecf20Sopenharmony_ci		if (win == 0)
8608c2ecf20Sopenharmony_ci			offset = VIDOSD_C(win);
8618c2ecf20Sopenharmony_ci		val = state->crtc.w * state->crtc.h;
8628c2ecf20Sopenharmony_ci		writel(val, ctx->regs + offset);
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci		DRM_DEV_DEBUG_KMS(ctx->dev, "osd size = 0x%x\n",
8658c2ecf20Sopenharmony_ci				  (unsigned int)val);
8668c2ecf20Sopenharmony_ci	}
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	fimd_win_set_pixfmt(ctx, win, fb, state->src.w);
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	/* hardware window 0 doesn't support color key. */
8718c2ecf20Sopenharmony_ci	if (win != 0)
8728c2ecf20Sopenharmony_ci		fimd_win_set_colkey(ctx, win);
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	fimd_enable_video_output(ctx, win, true);
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci	if (ctx->driver_data->has_shadowcon)
8778c2ecf20Sopenharmony_ci		fimd_enable_shadow_channel_path(ctx, win, true);
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci	if (ctx->i80_if)
8808c2ecf20Sopenharmony_ci		atomic_set(&ctx->win_updated, 1);
8818c2ecf20Sopenharmony_ci}
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_cistatic void fimd_disable_plane(struct exynos_drm_crtc *crtc,
8848c2ecf20Sopenharmony_ci			       struct exynos_drm_plane *plane)
8858c2ecf20Sopenharmony_ci{
8868c2ecf20Sopenharmony_ci	struct fimd_context *ctx = crtc->ctx;
8878c2ecf20Sopenharmony_ci	unsigned int win = plane->index;
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci	if (ctx->suspended)
8908c2ecf20Sopenharmony_ci		return;
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	fimd_enable_video_output(ctx, win, false);
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci	if (ctx->driver_data->has_shadowcon)
8958c2ecf20Sopenharmony_ci		fimd_enable_shadow_channel_path(ctx, win, false);
8968c2ecf20Sopenharmony_ci}
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_cistatic void fimd_atomic_enable(struct exynos_drm_crtc *crtc)
8998c2ecf20Sopenharmony_ci{
9008c2ecf20Sopenharmony_ci	struct fimd_context *ctx = crtc->ctx;
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci	if (!ctx->suspended)
9038c2ecf20Sopenharmony_ci		return;
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	ctx->suspended = false;
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci	pm_runtime_get_sync(ctx->dev);
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci	/* if vblank was enabled status, enable it again. */
9108c2ecf20Sopenharmony_ci	if (test_and_clear_bit(0, &ctx->irq_flags))
9118c2ecf20Sopenharmony_ci		fimd_enable_vblank(ctx->crtc);
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ci	fimd_commit(ctx->crtc);
9148c2ecf20Sopenharmony_ci}
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_cistatic void fimd_atomic_disable(struct exynos_drm_crtc *crtc)
9178c2ecf20Sopenharmony_ci{
9188c2ecf20Sopenharmony_ci	struct fimd_context *ctx = crtc->ctx;
9198c2ecf20Sopenharmony_ci	int i;
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci	if (ctx->suspended)
9228c2ecf20Sopenharmony_ci		return;
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	/*
9258c2ecf20Sopenharmony_ci	 * We need to make sure that all windows are disabled before we
9268c2ecf20Sopenharmony_ci	 * suspend that connector. Otherwise we might try to scan from
9278c2ecf20Sopenharmony_ci	 * a destroyed buffer later.
9288c2ecf20Sopenharmony_ci	 */
9298c2ecf20Sopenharmony_ci	for (i = 0; i < WINDOWS_NR; i++)
9308c2ecf20Sopenharmony_ci		fimd_disable_plane(crtc, &ctx->planes[i]);
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci	fimd_enable_vblank(crtc);
9338c2ecf20Sopenharmony_ci	fimd_wait_for_vblank(crtc);
9348c2ecf20Sopenharmony_ci	fimd_disable_vblank(crtc);
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci	writel(0, ctx->regs + VIDCON0);
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	pm_runtime_put_sync(ctx->dev);
9398c2ecf20Sopenharmony_ci	ctx->suspended = true;
9408c2ecf20Sopenharmony_ci}
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_cistatic void fimd_trigger(struct device *dev)
9438c2ecf20Sopenharmony_ci{
9448c2ecf20Sopenharmony_ci	struct fimd_context *ctx = dev_get_drvdata(dev);
9458c2ecf20Sopenharmony_ci	const struct fimd_driver_data *driver_data = ctx->driver_data;
9468c2ecf20Sopenharmony_ci	void *timing_base = ctx->regs + driver_data->timing_base;
9478c2ecf20Sopenharmony_ci	u32 reg;
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci	 /*
9508c2ecf20Sopenharmony_ci	  * Skips triggering if in triggering state, because multiple triggering
9518c2ecf20Sopenharmony_ci	  * requests can cause panel reset.
9528c2ecf20Sopenharmony_ci	  */
9538c2ecf20Sopenharmony_ci	if (atomic_read(&ctx->triggering))
9548c2ecf20Sopenharmony_ci		return;
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci	/* Enters triggering mode */
9578c2ecf20Sopenharmony_ci	atomic_set(&ctx->triggering, 1);
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci	reg = readl(timing_base + TRIGCON);
9608c2ecf20Sopenharmony_ci	reg |= (TRGMODE_ENABLE | SWTRGCMD_ENABLE);
9618c2ecf20Sopenharmony_ci	writel(reg, timing_base + TRIGCON);
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	/*
9648c2ecf20Sopenharmony_ci	 * Exits triggering mode if vblank is not enabled yet, because when the
9658c2ecf20Sopenharmony_ci	 * VIDINTCON0 register is not set, it can not exit from triggering mode.
9668c2ecf20Sopenharmony_ci	 */
9678c2ecf20Sopenharmony_ci	if (!test_bit(0, &ctx->irq_flags))
9688c2ecf20Sopenharmony_ci		atomic_set(&ctx->triggering, 0);
9698c2ecf20Sopenharmony_ci}
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_cistatic void fimd_te_handler(struct exynos_drm_crtc *crtc)
9728c2ecf20Sopenharmony_ci{
9738c2ecf20Sopenharmony_ci	struct fimd_context *ctx = crtc->ctx;
9748c2ecf20Sopenharmony_ci	u32 trg_type = ctx->driver_data->trg_type;
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci	/* Checks the crtc is detached already from encoder */
9778c2ecf20Sopenharmony_ci	if (!ctx->drm_dev)
9788c2ecf20Sopenharmony_ci		return;
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci	if (trg_type == I80_HW_TRG)
9818c2ecf20Sopenharmony_ci		goto out;
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	/*
9848c2ecf20Sopenharmony_ci	 * If there is a page flip request, triggers and handles the page flip
9858c2ecf20Sopenharmony_ci	 * event so that current fb can be updated into panel GRAM.
9868c2ecf20Sopenharmony_ci	 */
9878c2ecf20Sopenharmony_ci	if (atomic_add_unless(&ctx->win_updated, -1, 0))
9888c2ecf20Sopenharmony_ci		fimd_trigger(ctx->dev);
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ciout:
9918c2ecf20Sopenharmony_ci	/* Wakes up vsync event queue */
9928c2ecf20Sopenharmony_ci	if (atomic_read(&ctx->wait_vsync_event)) {
9938c2ecf20Sopenharmony_ci		atomic_set(&ctx->wait_vsync_event, 0);
9948c2ecf20Sopenharmony_ci		wake_up(&ctx->wait_vsync_queue);
9958c2ecf20Sopenharmony_ci	}
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	if (test_bit(0, &ctx->irq_flags))
9988c2ecf20Sopenharmony_ci		drm_crtc_handle_vblank(&ctx->crtc->base);
9998c2ecf20Sopenharmony_ci}
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_cistatic void fimd_dp_clock_enable(struct exynos_drm_clk *clk, bool enable)
10028c2ecf20Sopenharmony_ci{
10038c2ecf20Sopenharmony_ci	struct fimd_context *ctx = container_of(clk, struct fimd_context,
10048c2ecf20Sopenharmony_ci						dp_clk);
10058c2ecf20Sopenharmony_ci	u32 val = enable ? DP_MIE_CLK_DP_ENABLE : DP_MIE_CLK_DISABLE;
10068c2ecf20Sopenharmony_ci	writel(val, ctx->regs + DP_MIE_CLKCON);
10078c2ecf20Sopenharmony_ci}
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_cistatic const struct exynos_drm_crtc_ops fimd_crtc_ops = {
10108c2ecf20Sopenharmony_ci	.atomic_enable = fimd_atomic_enable,
10118c2ecf20Sopenharmony_ci	.atomic_disable = fimd_atomic_disable,
10128c2ecf20Sopenharmony_ci	.enable_vblank = fimd_enable_vblank,
10138c2ecf20Sopenharmony_ci	.disable_vblank = fimd_disable_vblank,
10148c2ecf20Sopenharmony_ci	.atomic_begin = fimd_atomic_begin,
10158c2ecf20Sopenharmony_ci	.update_plane = fimd_update_plane,
10168c2ecf20Sopenharmony_ci	.disable_plane = fimd_disable_plane,
10178c2ecf20Sopenharmony_ci	.atomic_flush = fimd_atomic_flush,
10188c2ecf20Sopenharmony_ci	.atomic_check = fimd_atomic_check,
10198c2ecf20Sopenharmony_ci	.te_handler = fimd_te_handler,
10208c2ecf20Sopenharmony_ci};
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_cistatic irqreturn_t fimd_irq_handler(int irq, void *dev_id)
10238c2ecf20Sopenharmony_ci{
10248c2ecf20Sopenharmony_ci	struct fimd_context *ctx = (struct fimd_context *)dev_id;
10258c2ecf20Sopenharmony_ci	u32 val, clear_bit;
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci	val = readl(ctx->regs + VIDINTCON1);
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_ci	clear_bit = ctx->i80_if ? VIDINTCON1_INT_I80 : VIDINTCON1_INT_FRAME;
10308c2ecf20Sopenharmony_ci	if (val & clear_bit)
10318c2ecf20Sopenharmony_ci		writel(clear_bit, ctx->regs + VIDINTCON1);
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci	/* check the crtc is detached already from encoder */
10348c2ecf20Sopenharmony_ci	if (!ctx->drm_dev)
10358c2ecf20Sopenharmony_ci		goto out;
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci	if (!ctx->i80_if)
10388c2ecf20Sopenharmony_ci		drm_crtc_handle_vblank(&ctx->crtc->base);
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci	if (ctx->i80_if) {
10418c2ecf20Sopenharmony_ci		/* Exits triggering mode */
10428c2ecf20Sopenharmony_ci		atomic_set(&ctx->triggering, 0);
10438c2ecf20Sopenharmony_ci	} else {
10448c2ecf20Sopenharmony_ci		/* set wait vsync event to zero and wake up queue. */
10458c2ecf20Sopenharmony_ci		if (atomic_read(&ctx->wait_vsync_event)) {
10468c2ecf20Sopenharmony_ci			atomic_set(&ctx->wait_vsync_event, 0);
10478c2ecf20Sopenharmony_ci			wake_up(&ctx->wait_vsync_queue);
10488c2ecf20Sopenharmony_ci		}
10498c2ecf20Sopenharmony_ci	}
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_ciout:
10528c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
10538c2ecf20Sopenharmony_ci}
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_cistatic int fimd_bind(struct device *dev, struct device *master, void *data)
10568c2ecf20Sopenharmony_ci{
10578c2ecf20Sopenharmony_ci	struct fimd_context *ctx = dev_get_drvdata(dev);
10588c2ecf20Sopenharmony_ci	struct drm_device *drm_dev = data;
10598c2ecf20Sopenharmony_ci	struct exynos_drm_plane *exynos_plane;
10608c2ecf20Sopenharmony_ci	unsigned int i;
10618c2ecf20Sopenharmony_ci	int ret;
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_ci	ctx->drm_dev = drm_dev;
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_ci	for (i = 0; i < WINDOWS_NR; i++) {
10668c2ecf20Sopenharmony_ci		ctx->configs[i].pixel_formats = fimd_formats;
10678c2ecf20Sopenharmony_ci		ctx->configs[i].num_pixel_formats = ARRAY_SIZE(fimd_formats);
10688c2ecf20Sopenharmony_ci		ctx->configs[i].zpos = i;
10698c2ecf20Sopenharmony_ci		ctx->configs[i].type = fimd_win_types[i];
10708c2ecf20Sopenharmony_ci		ctx->configs[i].capabilities = capabilities[i];
10718c2ecf20Sopenharmony_ci		ret = exynos_plane_init(drm_dev, &ctx->planes[i], i,
10728c2ecf20Sopenharmony_ci					&ctx->configs[i]);
10738c2ecf20Sopenharmony_ci		if (ret)
10748c2ecf20Sopenharmony_ci			return ret;
10758c2ecf20Sopenharmony_ci	}
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci	exynos_plane = &ctx->planes[DEFAULT_WIN];
10788c2ecf20Sopenharmony_ci	ctx->crtc = exynos_drm_crtc_create(drm_dev, &exynos_plane->base,
10798c2ecf20Sopenharmony_ci			EXYNOS_DISPLAY_TYPE_LCD, &fimd_crtc_ops, ctx);
10808c2ecf20Sopenharmony_ci	if (IS_ERR(ctx->crtc))
10818c2ecf20Sopenharmony_ci		return PTR_ERR(ctx->crtc);
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	if (ctx->driver_data->has_dp_clk) {
10848c2ecf20Sopenharmony_ci		ctx->dp_clk.enable = fimd_dp_clock_enable;
10858c2ecf20Sopenharmony_ci		ctx->crtc->pipe_clk = &ctx->dp_clk;
10868c2ecf20Sopenharmony_ci	}
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci	if (ctx->encoder)
10898c2ecf20Sopenharmony_ci		exynos_dpi_bind(drm_dev, ctx->encoder);
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci	if (is_drm_iommu_supported(drm_dev))
10928c2ecf20Sopenharmony_ci		fimd_clear_channels(ctx->crtc);
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ci	return exynos_drm_register_dma(drm_dev, dev, &ctx->dma_priv);
10958c2ecf20Sopenharmony_ci}
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_cistatic void fimd_unbind(struct device *dev, struct device *master,
10988c2ecf20Sopenharmony_ci			void *data)
10998c2ecf20Sopenharmony_ci{
11008c2ecf20Sopenharmony_ci	struct fimd_context *ctx = dev_get_drvdata(dev);
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_ci	fimd_atomic_disable(ctx->crtc);
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ci	exynos_drm_unregister_dma(ctx->drm_dev, ctx->dev, &ctx->dma_priv);
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci	if (ctx->encoder)
11078c2ecf20Sopenharmony_ci		exynos_dpi_remove(ctx->encoder);
11088c2ecf20Sopenharmony_ci}
11098c2ecf20Sopenharmony_ci
11108c2ecf20Sopenharmony_cistatic const struct component_ops fimd_component_ops = {
11118c2ecf20Sopenharmony_ci	.bind	= fimd_bind,
11128c2ecf20Sopenharmony_ci	.unbind = fimd_unbind,
11138c2ecf20Sopenharmony_ci};
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_cistatic int fimd_probe(struct platform_device *pdev)
11168c2ecf20Sopenharmony_ci{
11178c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
11188c2ecf20Sopenharmony_ci	struct fimd_context *ctx;
11198c2ecf20Sopenharmony_ci	struct device_node *i80_if_timings;
11208c2ecf20Sopenharmony_ci	struct resource *res;
11218c2ecf20Sopenharmony_ci	int ret;
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_ci	if (!dev->of_node)
11248c2ecf20Sopenharmony_ci		return -ENODEV;
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_ci	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
11278c2ecf20Sopenharmony_ci	if (!ctx)
11288c2ecf20Sopenharmony_ci		return -ENOMEM;
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_ci	ctx->dev = dev;
11318c2ecf20Sopenharmony_ci	ctx->suspended = true;
11328c2ecf20Sopenharmony_ci	ctx->driver_data = of_device_get_match_data(dev);
11338c2ecf20Sopenharmony_ci
11348c2ecf20Sopenharmony_ci	if (of_property_read_bool(dev->of_node, "samsung,invert-vden"))
11358c2ecf20Sopenharmony_ci		ctx->vidcon1 |= VIDCON1_INV_VDEN;
11368c2ecf20Sopenharmony_ci	if (of_property_read_bool(dev->of_node, "samsung,invert-vclk"))
11378c2ecf20Sopenharmony_ci		ctx->vidcon1 |= VIDCON1_INV_VCLK;
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci	i80_if_timings = of_get_child_by_name(dev->of_node, "i80-if-timings");
11408c2ecf20Sopenharmony_ci	if (i80_if_timings) {
11418c2ecf20Sopenharmony_ci		u32 val;
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ci		ctx->i80_if = true;
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_ci		if (ctx->driver_data->has_vidoutcon)
11468c2ecf20Sopenharmony_ci			ctx->vidout_con |= VIDOUT_CON_F_I80_LDI0;
11478c2ecf20Sopenharmony_ci		else
11488c2ecf20Sopenharmony_ci			ctx->vidcon0 |= VIDCON0_VIDOUT_I80_LDI0;
11498c2ecf20Sopenharmony_ci		/*
11508c2ecf20Sopenharmony_ci		 * The user manual describes that this "DSI_EN" bit is required
11518c2ecf20Sopenharmony_ci		 * to enable I80 24-bit data interface.
11528c2ecf20Sopenharmony_ci		 */
11538c2ecf20Sopenharmony_ci		ctx->vidcon0 |= VIDCON0_DSI_EN;
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci		if (of_property_read_u32(i80_if_timings, "cs-setup", &val))
11568c2ecf20Sopenharmony_ci			val = 0;
11578c2ecf20Sopenharmony_ci		ctx->i80ifcon = LCD_CS_SETUP(val);
11588c2ecf20Sopenharmony_ci		if (of_property_read_u32(i80_if_timings, "wr-setup", &val))
11598c2ecf20Sopenharmony_ci			val = 0;
11608c2ecf20Sopenharmony_ci		ctx->i80ifcon |= LCD_WR_SETUP(val);
11618c2ecf20Sopenharmony_ci		if (of_property_read_u32(i80_if_timings, "wr-active", &val))
11628c2ecf20Sopenharmony_ci			val = 1;
11638c2ecf20Sopenharmony_ci		ctx->i80ifcon |= LCD_WR_ACTIVE(val);
11648c2ecf20Sopenharmony_ci		if (of_property_read_u32(i80_if_timings, "wr-hold", &val))
11658c2ecf20Sopenharmony_ci			val = 0;
11668c2ecf20Sopenharmony_ci		ctx->i80ifcon |= LCD_WR_HOLD(val);
11678c2ecf20Sopenharmony_ci	}
11688c2ecf20Sopenharmony_ci	of_node_put(i80_if_timings);
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci	ctx->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
11718c2ecf20Sopenharmony_ci							"samsung,sysreg");
11728c2ecf20Sopenharmony_ci	if (IS_ERR(ctx->sysreg)) {
11738c2ecf20Sopenharmony_ci		dev_warn(dev, "failed to get system register.\n");
11748c2ecf20Sopenharmony_ci		ctx->sysreg = NULL;
11758c2ecf20Sopenharmony_ci	}
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci	ctx->bus_clk = devm_clk_get(dev, "fimd");
11788c2ecf20Sopenharmony_ci	if (IS_ERR(ctx->bus_clk)) {
11798c2ecf20Sopenharmony_ci		dev_err(dev, "failed to get bus clock\n");
11808c2ecf20Sopenharmony_ci		return PTR_ERR(ctx->bus_clk);
11818c2ecf20Sopenharmony_ci	}
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_ci	ctx->lcd_clk = devm_clk_get(dev, "sclk_fimd");
11848c2ecf20Sopenharmony_ci	if (IS_ERR(ctx->lcd_clk)) {
11858c2ecf20Sopenharmony_ci		dev_err(dev, "failed to get lcd clock\n");
11868c2ecf20Sopenharmony_ci		return PTR_ERR(ctx->lcd_clk);
11878c2ecf20Sopenharmony_ci	}
11888c2ecf20Sopenharmony_ci
11898c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_ci	ctx->regs = devm_ioremap_resource(dev, res);
11928c2ecf20Sopenharmony_ci	if (IS_ERR(ctx->regs))
11938c2ecf20Sopenharmony_ci		return PTR_ERR(ctx->regs);
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_ci	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
11968c2ecf20Sopenharmony_ci					   ctx->i80_if ? "lcd_sys" : "vsync");
11978c2ecf20Sopenharmony_ci	if (!res) {
11988c2ecf20Sopenharmony_ci		dev_err(dev, "irq request failed.\n");
11998c2ecf20Sopenharmony_ci		return -ENXIO;
12008c2ecf20Sopenharmony_ci	}
12018c2ecf20Sopenharmony_ci
12028c2ecf20Sopenharmony_ci	ret = devm_request_irq(dev, res->start, fimd_irq_handler,
12038c2ecf20Sopenharmony_ci							0, "drm_fimd", ctx);
12048c2ecf20Sopenharmony_ci	if (ret) {
12058c2ecf20Sopenharmony_ci		dev_err(dev, "irq request failed.\n");
12068c2ecf20Sopenharmony_ci		return ret;
12078c2ecf20Sopenharmony_ci	}
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_ci	init_waitqueue_head(&ctx->wait_vsync_queue);
12108c2ecf20Sopenharmony_ci	atomic_set(&ctx->wait_vsync_event, 0);
12118c2ecf20Sopenharmony_ci
12128c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, ctx);
12138c2ecf20Sopenharmony_ci
12148c2ecf20Sopenharmony_ci	ctx->encoder = exynos_dpi_probe(dev);
12158c2ecf20Sopenharmony_ci	if (IS_ERR(ctx->encoder))
12168c2ecf20Sopenharmony_ci		return PTR_ERR(ctx->encoder);
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_ci	pm_runtime_enable(dev);
12198c2ecf20Sopenharmony_ci
12208c2ecf20Sopenharmony_ci	ret = component_add(dev, &fimd_component_ops);
12218c2ecf20Sopenharmony_ci	if (ret)
12228c2ecf20Sopenharmony_ci		goto err_disable_pm_runtime;
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci	return ret;
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_cierr_disable_pm_runtime:
12278c2ecf20Sopenharmony_ci	pm_runtime_disable(dev);
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_ci	return ret;
12308c2ecf20Sopenharmony_ci}
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_cistatic int fimd_remove(struct platform_device *pdev)
12338c2ecf20Sopenharmony_ci{
12348c2ecf20Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_ci	component_del(&pdev->dev, &fimd_component_ops);
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci	return 0;
12398c2ecf20Sopenharmony_ci}
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
12428c2ecf20Sopenharmony_cistatic int exynos_fimd_suspend(struct device *dev)
12438c2ecf20Sopenharmony_ci{
12448c2ecf20Sopenharmony_ci	struct fimd_context *ctx = dev_get_drvdata(dev);
12458c2ecf20Sopenharmony_ci
12468c2ecf20Sopenharmony_ci	clk_disable_unprepare(ctx->lcd_clk);
12478c2ecf20Sopenharmony_ci	clk_disable_unprepare(ctx->bus_clk);
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci	return 0;
12508c2ecf20Sopenharmony_ci}
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_cistatic int exynos_fimd_resume(struct device *dev)
12538c2ecf20Sopenharmony_ci{
12548c2ecf20Sopenharmony_ci	struct fimd_context *ctx = dev_get_drvdata(dev);
12558c2ecf20Sopenharmony_ci	int ret;
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(ctx->bus_clk);
12588c2ecf20Sopenharmony_ci	if (ret < 0) {
12598c2ecf20Sopenharmony_ci		DRM_DEV_ERROR(dev,
12608c2ecf20Sopenharmony_ci			      "Failed to prepare_enable the bus clk [%d]\n",
12618c2ecf20Sopenharmony_ci			      ret);
12628c2ecf20Sopenharmony_ci		return ret;
12638c2ecf20Sopenharmony_ci	}
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(ctx->lcd_clk);
12668c2ecf20Sopenharmony_ci	if  (ret < 0) {
12678c2ecf20Sopenharmony_ci		DRM_DEV_ERROR(dev,
12688c2ecf20Sopenharmony_ci			      "Failed to prepare_enable the lcd clk [%d]\n",
12698c2ecf20Sopenharmony_ci			      ret);
12708c2ecf20Sopenharmony_ci		return ret;
12718c2ecf20Sopenharmony_ci	}
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_ci	return 0;
12748c2ecf20Sopenharmony_ci}
12758c2ecf20Sopenharmony_ci#endif
12768c2ecf20Sopenharmony_ci
12778c2ecf20Sopenharmony_cistatic const struct dev_pm_ops exynos_fimd_pm_ops = {
12788c2ecf20Sopenharmony_ci	SET_RUNTIME_PM_OPS(exynos_fimd_suspend, exynos_fimd_resume, NULL)
12798c2ecf20Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
12808c2ecf20Sopenharmony_ci				pm_runtime_force_resume)
12818c2ecf20Sopenharmony_ci};
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_cistruct platform_driver fimd_driver = {
12848c2ecf20Sopenharmony_ci	.probe		= fimd_probe,
12858c2ecf20Sopenharmony_ci	.remove		= fimd_remove,
12868c2ecf20Sopenharmony_ci	.driver		= {
12878c2ecf20Sopenharmony_ci		.name	= "exynos4-fb",
12888c2ecf20Sopenharmony_ci		.owner	= THIS_MODULE,
12898c2ecf20Sopenharmony_ci		.pm	= &exynos_fimd_pm_ops,
12908c2ecf20Sopenharmony_ci		.of_match_table = fimd_driver_dt_match,
12918c2ecf20Sopenharmony_ci	},
12928c2ecf20Sopenharmony_ci};
1293