162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* exynos_drm_fimd.c
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2011 Samsung Electronics Co.Ltd
562306a36Sopenharmony_ci * Authors:
662306a36Sopenharmony_ci *	Joonyoung Shim <jy0922.shim@samsung.com>
762306a36Sopenharmony_ci *	Inki Dae <inki.dae@samsung.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/clk.h>
1162306a36Sopenharmony_ci#include <linux/component.h>
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/mfd/syscon.h>
1462306a36Sopenharmony_ci#include <linux/of.h>
1562306a36Sopenharmony_ci#include <linux/platform_device.h>
1662306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1762306a36Sopenharmony_ci#include <linux/regmap.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <video/of_display_timing.h>
2062306a36Sopenharmony_ci#include <video/of_videomode.h>
2162306a36Sopenharmony_ci#include <video/samsung_fimd.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include <drm/drm_blend.h>
2462306a36Sopenharmony_ci#include <drm/drm_fourcc.h>
2562306a36Sopenharmony_ci#include <drm/drm_framebuffer.h>
2662306a36Sopenharmony_ci#include <drm/drm_vblank.h>
2762306a36Sopenharmony_ci#include <drm/exynos_drm.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#include "exynos_drm_crtc.h"
3062306a36Sopenharmony_ci#include "exynos_drm_drv.h"
3162306a36Sopenharmony_ci#include "exynos_drm_fb.h"
3262306a36Sopenharmony_ci#include "exynos_drm_plane.h"
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/*
3562306a36Sopenharmony_ci * FIMD stands for Fully Interactive Mobile Display and
3662306a36Sopenharmony_ci * as a display controller, it transfers contents drawn on memory
3762306a36Sopenharmony_ci * to a LCD Panel through Display Interfaces such as RGB or
3862306a36Sopenharmony_ci * CPU Interface.
3962306a36Sopenharmony_ci */
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#define MIN_FB_WIDTH_FOR_16WORD_BURST 128
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci/* position control register for hardware window 0, 2 ~ 4.*/
4462306a36Sopenharmony_ci#define VIDOSD_A(win)		(VIDOSD_BASE + 0x00 + (win) * 16)
4562306a36Sopenharmony_ci#define VIDOSD_B(win)		(VIDOSD_BASE + 0x04 + (win) * 16)
4662306a36Sopenharmony_ci/*
4762306a36Sopenharmony_ci * size control register for hardware windows 0 and alpha control register
4862306a36Sopenharmony_ci * for hardware windows 1 ~ 4
4962306a36Sopenharmony_ci */
5062306a36Sopenharmony_ci#define VIDOSD_C(win)		(VIDOSD_BASE + 0x08 + (win) * 16)
5162306a36Sopenharmony_ci/* size control register for hardware windows 1 ~ 2. */
5262306a36Sopenharmony_ci#define VIDOSD_D(win)		(VIDOSD_BASE + 0x0C + (win) * 16)
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci#define VIDWnALPHA0(win)	(VIDW_ALPHA + 0x00 + (win) * 8)
5562306a36Sopenharmony_ci#define VIDWnALPHA1(win)	(VIDW_ALPHA + 0x04 + (win) * 8)
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci#define VIDWx_BUF_START(win, buf)	(VIDW_BUF_START(buf) + (win) * 8)
5862306a36Sopenharmony_ci#define VIDWx_BUF_START_S(win, buf)	(VIDW_BUF_START_S(buf) + (win) * 8)
5962306a36Sopenharmony_ci#define VIDWx_BUF_END(win, buf)		(VIDW_BUF_END(buf) + (win) * 8)
6062306a36Sopenharmony_ci#define VIDWx_BUF_SIZE(win, buf)	(VIDW_BUF_SIZE(buf) + (win) * 4)
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/* color key control register for hardware window 1 ~ 4. */
6362306a36Sopenharmony_ci#define WKEYCON0_BASE(x)		((WKEYCON0 + 0x140) + ((x - 1) * 8))
6462306a36Sopenharmony_ci/* color key value register for hardware window 1 ~ 4. */
6562306a36Sopenharmony_ci#define WKEYCON1_BASE(x)		((WKEYCON1 + 0x140) + ((x - 1) * 8))
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci/* I80 trigger control register */
6862306a36Sopenharmony_ci#define TRIGCON				0x1A4
6962306a36Sopenharmony_ci#define TRGMODE_ENABLE			(1 << 0)
7062306a36Sopenharmony_ci#define SWTRGCMD_ENABLE			(1 << 1)
7162306a36Sopenharmony_ci/* Exynos3250, 3472, 5260 5410, 5420 and 5422 only supported. */
7262306a36Sopenharmony_ci#define HWTRGEN_ENABLE			(1 << 3)
7362306a36Sopenharmony_ci#define HWTRGMASK_ENABLE		(1 << 4)
7462306a36Sopenharmony_ci/* Exynos3250, 3472, 5260, 5420 and 5422 only supported. */
7562306a36Sopenharmony_ci#define HWTRIGEN_PER_ENABLE		(1 << 31)
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci/* display mode change control register except exynos4 */
7862306a36Sopenharmony_ci#define VIDOUT_CON			0x000
7962306a36Sopenharmony_ci#define VIDOUT_CON_F_I80_LDI0		(0x2 << 8)
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci/* I80 interface control for main LDI register */
8262306a36Sopenharmony_ci#define I80IFCONFAx(x)			(0x1B0 + (x) * 4)
8362306a36Sopenharmony_ci#define I80IFCONFBx(x)			(0x1B8 + (x) * 4)
8462306a36Sopenharmony_ci#define LCD_CS_SETUP(x)			((x) << 16)
8562306a36Sopenharmony_ci#define LCD_WR_SETUP(x)			((x) << 12)
8662306a36Sopenharmony_ci#define LCD_WR_ACTIVE(x)		((x) << 8)
8762306a36Sopenharmony_ci#define LCD_WR_HOLD(x)			((x) << 4)
8862306a36Sopenharmony_ci#define I80IFEN_ENABLE			(1 << 0)
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci/* FIMD has totally five hardware windows. */
9162306a36Sopenharmony_ci#define WINDOWS_NR	5
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci/* HW trigger flag on i80 panel. */
9462306a36Sopenharmony_ci#define I80_HW_TRG     (1 << 1)
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistruct fimd_driver_data {
9762306a36Sopenharmony_ci	unsigned int timing_base;
9862306a36Sopenharmony_ci	unsigned int lcdblk_offset;
9962306a36Sopenharmony_ci	unsigned int lcdblk_vt_shift;
10062306a36Sopenharmony_ci	unsigned int lcdblk_bypass_shift;
10162306a36Sopenharmony_ci	unsigned int lcdblk_mic_bypass_shift;
10262306a36Sopenharmony_ci	unsigned int trg_type;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	unsigned int has_shadowcon:1;
10562306a36Sopenharmony_ci	unsigned int has_clksel:1;
10662306a36Sopenharmony_ci	unsigned int has_limited_fmt:1;
10762306a36Sopenharmony_ci	unsigned int has_vidoutcon:1;
10862306a36Sopenharmony_ci	unsigned int has_vtsel:1;
10962306a36Sopenharmony_ci	unsigned int has_mic_bypass:1;
11062306a36Sopenharmony_ci	unsigned int has_dp_clk:1;
11162306a36Sopenharmony_ci	unsigned int has_hw_trigger:1;
11262306a36Sopenharmony_ci	unsigned int has_trigger_per_te:1;
11362306a36Sopenharmony_ci	unsigned int has_bgr_support:1;
11462306a36Sopenharmony_ci};
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic struct fimd_driver_data s3c64xx_fimd_driver_data = {
11762306a36Sopenharmony_ci	.timing_base = 0x0,
11862306a36Sopenharmony_ci	.has_clksel = 1,
11962306a36Sopenharmony_ci	.has_limited_fmt = 1,
12062306a36Sopenharmony_ci};
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic struct fimd_driver_data s5pv210_fimd_driver_data = {
12362306a36Sopenharmony_ci	.timing_base = 0x0,
12462306a36Sopenharmony_ci	.has_shadowcon = 1,
12562306a36Sopenharmony_ci	.has_clksel = 1,
12662306a36Sopenharmony_ci};
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic struct fimd_driver_data exynos3_fimd_driver_data = {
12962306a36Sopenharmony_ci	.timing_base = 0x20000,
13062306a36Sopenharmony_ci	.lcdblk_offset = 0x210,
13162306a36Sopenharmony_ci	.lcdblk_bypass_shift = 1,
13262306a36Sopenharmony_ci	.has_shadowcon = 1,
13362306a36Sopenharmony_ci	.has_vidoutcon = 1,
13462306a36Sopenharmony_ci};
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic struct fimd_driver_data exynos4_fimd_driver_data = {
13762306a36Sopenharmony_ci	.timing_base = 0x0,
13862306a36Sopenharmony_ci	.lcdblk_offset = 0x210,
13962306a36Sopenharmony_ci	.lcdblk_vt_shift = 10,
14062306a36Sopenharmony_ci	.lcdblk_bypass_shift = 1,
14162306a36Sopenharmony_ci	.has_shadowcon = 1,
14262306a36Sopenharmony_ci	.has_vtsel = 1,
14362306a36Sopenharmony_ci	.has_bgr_support = 1,
14462306a36Sopenharmony_ci};
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic struct fimd_driver_data exynos5_fimd_driver_data = {
14762306a36Sopenharmony_ci	.timing_base = 0x20000,
14862306a36Sopenharmony_ci	.lcdblk_offset = 0x214,
14962306a36Sopenharmony_ci	.lcdblk_vt_shift = 24,
15062306a36Sopenharmony_ci	.lcdblk_bypass_shift = 15,
15162306a36Sopenharmony_ci	.has_shadowcon = 1,
15262306a36Sopenharmony_ci	.has_vidoutcon = 1,
15362306a36Sopenharmony_ci	.has_vtsel = 1,
15462306a36Sopenharmony_ci	.has_dp_clk = 1,
15562306a36Sopenharmony_ci	.has_bgr_support = 1,
15662306a36Sopenharmony_ci};
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistatic struct fimd_driver_data exynos5420_fimd_driver_data = {
15962306a36Sopenharmony_ci	.timing_base = 0x20000,
16062306a36Sopenharmony_ci	.lcdblk_offset = 0x214,
16162306a36Sopenharmony_ci	.lcdblk_vt_shift = 24,
16262306a36Sopenharmony_ci	.lcdblk_bypass_shift = 15,
16362306a36Sopenharmony_ci	.lcdblk_mic_bypass_shift = 11,
16462306a36Sopenharmony_ci	.has_shadowcon = 1,
16562306a36Sopenharmony_ci	.has_vidoutcon = 1,
16662306a36Sopenharmony_ci	.has_vtsel = 1,
16762306a36Sopenharmony_ci	.has_mic_bypass = 1,
16862306a36Sopenharmony_ci	.has_dp_clk = 1,
16962306a36Sopenharmony_ci	.has_bgr_support = 1,
17062306a36Sopenharmony_ci};
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistruct fimd_context {
17362306a36Sopenharmony_ci	struct device			*dev;
17462306a36Sopenharmony_ci	struct drm_device		*drm_dev;
17562306a36Sopenharmony_ci	void				*dma_priv;
17662306a36Sopenharmony_ci	struct exynos_drm_crtc		*crtc;
17762306a36Sopenharmony_ci	struct exynos_drm_plane		planes[WINDOWS_NR];
17862306a36Sopenharmony_ci	struct exynos_drm_plane_config	configs[WINDOWS_NR];
17962306a36Sopenharmony_ci	struct clk			*bus_clk;
18062306a36Sopenharmony_ci	struct clk			*lcd_clk;
18162306a36Sopenharmony_ci	void __iomem			*regs;
18262306a36Sopenharmony_ci	struct regmap			*sysreg;
18362306a36Sopenharmony_ci	unsigned long			irq_flags;
18462306a36Sopenharmony_ci	u32				vidcon0;
18562306a36Sopenharmony_ci	u32				vidcon1;
18662306a36Sopenharmony_ci	u32				vidout_con;
18762306a36Sopenharmony_ci	u32				i80ifcon;
18862306a36Sopenharmony_ci	bool				i80_if;
18962306a36Sopenharmony_ci	bool				suspended;
19062306a36Sopenharmony_ci	wait_queue_head_t		wait_vsync_queue;
19162306a36Sopenharmony_ci	atomic_t			wait_vsync_event;
19262306a36Sopenharmony_ci	atomic_t			win_updated;
19362306a36Sopenharmony_ci	atomic_t			triggering;
19462306a36Sopenharmony_ci	u32				clkdiv;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	const struct fimd_driver_data *driver_data;
19762306a36Sopenharmony_ci	struct drm_encoder *encoder;
19862306a36Sopenharmony_ci	struct exynos_drm_clk		dp_clk;
19962306a36Sopenharmony_ci};
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic const struct of_device_id fimd_driver_dt_match[] = {
20262306a36Sopenharmony_ci	{ .compatible = "samsung,s3c6400-fimd",
20362306a36Sopenharmony_ci	  .data = &s3c64xx_fimd_driver_data },
20462306a36Sopenharmony_ci	{ .compatible = "samsung,s5pv210-fimd",
20562306a36Sopenharmony_ci	  .data = &s5pv210_fimd_driver_data },
20662306a36Sopenharmony_ci	{ .compatible = "samsung,exynos3250-fimd",
20762306a36Sopenharmony_ci	  .data = &exynos3_fimd_driver_data },
20862306a36Sopenharmony_ci	{ .compatible = "samsung,exynos4210-fimd",
20962306a36Sopenharmony_ci	  .data = &exynos4_fimd_driver_data },
21062306a36Sopenharmony_ci	{ .compatible = "samsung,exynos5250-fimd",
21162306a36Sopenharmony_ci	  .data = &exynos5_fimd_driver_data },
21262306a36Sopenharmony_ci	{ .compatible = "samsung,exynos5420-fimd",
21362306a36Sopenharmony_ci	  .data = &exynos5420_fimd_driver_data },
21462306a36Sopenharmony_ci	{},
21562306a36Sopenharmony_ci};
21662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, fimd_driver_dt_match);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic const enum drm_plane_type fimd_win_types[WINDOWS_NR] = {
21962306a36Sopenharmony_ci	DRM_PLANE_TYPE_PRIMARY,
22062306a36Sopenharmony_ci	DRM_PLANE_TYPE_OVERLAY,
22162306a36Sopenharmony_ci	DRM_PLANE_TYPE_OVERLAY,
22262306a36Sopenharmony_ci	DRM_PLANE_TYPE_OVERLAY,
22362306a36Sopenharmony_ci	DRM_PLANE_TYPE_CURSOR,
22462306a36Sopenharmony_ci};
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic const uint32_t fimd_formats[] = {
22762306a36Sopenharmony_ci	DRM_FORMAT_C8,
22862306a36Sopenharmony_ci	DRM_FORMAT_XRGB1555,
22962306a36Sopenharmony_ci	DRM_FORMAT_RGB565,
23062306a36Sopenharmony_ci	DRM_FORMAT_XRGB8888,
23162306a36Sopenharmony_ci	DRM_FORMAT_ARGB8888,
23262306a36Sopenharmony_ci};
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_cistatic const uint32_t fimd_extended_formats[] = {
23562306a36Sopenharmony_ci	DRM_FORMAT_C8,
23662306a36Sopenharmony_ci	DRM_FORMAT_XRGB1555,
23762306a36Sopenharmony_ci	DRM_FORMAT_XBGR1555,
23862306a36Sopenharmony_ci	DRM_FORMAT_RGB565,
23962306a36Sopenharmony_ci	DRM_FORMAT_BGR565,
24062306a36Sopenharmony_ci	DRM_FORMAT_XRGB8888,
24162306a36Sopenharmony_ci	DRM_FORMAT_XBGR8888,
24262306a36Sopenharmony_ci	DRM_FORMAT_ARGB8888,
24362306a36Sopenharmony_ci	DRM_FORMAT_ABGR8888,
24462306a36Sopenharmony_ci};
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_cistatic const unsigned int capabilities[WINDOWS_NR] = {
24762306a36Sopenharmony_ci	0,
24862306a36Sopenharmony_ci	EXYNOS_DRM_PLANE_CAP_WIN_BLEND | EXYNOS_DRM_PLANE_CAP_PIX_BLEND,
24962306a36Sopenharmony_ci	EXYNOS_DRM_PLANE_CAP_WIN_BLEND | EXYNOS_DRM_PLANE_CAP_PIX_BLEND,
25062306a36Sopenharmony_ci	EXYNOS_DRM_PLANE_CAP_WIN_BLEND | EXYNOS_DRM_PLANE_CAP_PIX_BLEND,
25162306a36Sopenharmony_ci	EXYNOS_DRM_PLANE_CAP_WIN_BLEND | EXYNOS_DRM_PLANE_CAP_PIX_BLEND,
25262306a36Sopenharmony_ci};
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cistatic inline void fimd_set_bits(struct fimd_context *ctx, u32 reg, u32 mask,
25562306a36Sopenharmony_ci				 u32 val)
25662306a36Sopenharmony_ci{
25762306a36Sopenharmony_ci	val = (val & mask) | (readl(ctx->regs + reg) & ~mask);
25862306a36Sopenharmony_ci	writel(val, ctx->regs + reg);
25962306a36Sopenharmony_ci}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_cistatic int fimd_enable_vblank(struct exynos_drm_crtc *crtc)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	struct fimd_context *ctx = crtc->ctx;
26462306a36Sopenharmony_ci	u32 val;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	if (ctx->suspended)
26762306a36Sopenharmony_ci		return -EPERM;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	if (!test_and_set_bit(0, &ctx->irq_flags)) {
27062306a36Sopenharmony_ci		val = readl(ctx->regs + VIDINTCON0);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci		val |= VIDINTCON0_INT_ENABLE;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci		if (ctx->i80_if) {
27562306a36Sopenharmony_ci			val |= VIDINTCON0_INT_I80IFDONE;
27662306a36Sopenharmony_ci			val |= VIDINTCON0_INT_SYSMAINCON;
27762306a36Sopenharmony_ci			val &= ~VIDINTCON0_INT_SYSSUBCON;
27862306a36Sopenharmony_ci		} else {
27962306a36Sopenharmony_ci			val |= VIDINTCON0_INT_FRAME;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci			val &= ~VIDINTCON0_FRAMESEL0_MASK;
28262306a36Sopenharmony_ci			val |= VIDINTCON0_FRAMESEL0_FRONTPORCH;
28362306a36Sopenharmony_ci			val &= ~VIDINTCON0_FRAMESEL1_MASK;
28462306a36Sopenharmony_ci			val |= VIDINTCON0_FRAMESEL1_NONE;
28562306a36Sopenharmony_ci		}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci		writel(val, ctx->regs + VIDINTCON0);
28862306a36Sopenharmony_ci	}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	return 0;
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_cistatic void fimd_disable_vblank(struct exynos_drm_crtc *crtc)
29462306a36Sopenharmony_ci{
29562306a36Sopenharmony_ci	struct fimd_context *ctx = crtc->ctx;
29662306a36Sopenharmony_ci	u32 val;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	if (ctx->suspended)
29962306a36Sopenharmony_ci		return;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	if (test_and_clear_bit(0, &ctx->irq_flags)) {
30262306a36Sopenharmony_ci		val = readl(ctx->regs + VIDINTCON0);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci		val &= ~VIDINTCON0_INT_ENABLE;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci		if (ctx->i80_if) {
30762306a36Sopenharmony_ci			val &= ~VIDINTCON0_INT_I80IFDONE;
30862306a36Sopenharmony_ci			val &= ~VIDINTCON0_INT_SYSMAINCON;
30962306a36Sopenharmony_ci			val &= ~VIDINTCON0_INT_SYSSUBCON;
31062306a36Sopenharmony_ci		} else
31162306a36Sopenharmony_ci			val &= ~VIDINTCON0_INT_FRAME;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci		writel(val, ctx->regs + VIDINTCON0);
31462306a36Sopenharmony_ci	}
31562306a36Sopenharmony_ci}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_cistatic void fimd_wait_for_vblank(struct exynos_drm_crtc *crtc)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	struct fimd_context *ctx = crtc->ctx;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	if (ctx->suspended)
32262306a36Sopenharmony_ci		return;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	atomic_set(&ctx->wait_vsync_event, 1);
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	/*
32762306a36Sopenharmony_ci	 * wait for FIMD to signal VSYNC interrupt or return after
32862306a36Sopenharmony_ci	 * timeout which is set to 50ms (refresh rate of 20).
32962306a36Sopenharmony_ci	 */
33062306a36Sopenharmony_ci	if (!wait_event_timeout(ctx->wait_vsync_queue,
33162306a36Sopenharmony_ci				!atomic_read(&ctx->wait_vsync_event),
33262306a36Sopenharmony_ci				HZ/20))
33362306a36Sopenharmony_ci		DRM_DEV_DEBUG_KMS(ctx->dev, "vblank wait timed out.\n");
33462306a36Sopenharmony_ci}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_cistatic void fimd_enable_video_output(struct fimd_context *ctx, unsigned int win,
33762306a36Sopenharmony_ci					bool enable)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	u32 val = readl(ctx->regs + WINCON(win));
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	if (enable)
34262306a36Sopenharmony_ci		val |= WINCONx_ENWIN;
34362306a36Sopenharmony_ci	else
34462306a36Sopenharmony_ci		val &= ~WINCONx_ENWIN;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	writel(val, ctx->regs + WINCON(win));
34762306a36Sopenharmony_ci}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_cistatic void fimd_enable_shadow_channel_path(struct fimd_context *ctx,
35062306a36Sopenharmony_ci						unsigned int win,
35162306a36Sopenharmony_ci						bool enable)
35262306a36Sopenharmony_ci{
35362306a36Sopenharmony_ci	u32 val = readl(ctx->regs + SHADOWCON);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	if (enable)
35662306a36Sopenharmony_ci		val |= SHADOWCON_CHx_ENABLE(win);
35762306a36Sopenharmony_ci	else
35862306a36Sopenharmony_ci		val &= ~SHADOWCON_CHx_ENABLE(win);
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	writel(val, ctx->regs + SHADOWCON);
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_cistatic int fimd_clear_channels(struct exynos_drm_crtc *crtc)
36462306a36Sopenharmony_ci{
36562306a36Sopenharmony_ci	struct fimd_context *ctx = crtc->ctx;
36662306a36Sopenharmony_ci	unsigned int win, ch_enabled = 0;
36762306a36Sopenharmony_ci	int ret;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	/* Hardware is in unknown state, so ensure it gets enabled properly */
37062306a36Sopenharmony_ci	ret = pm_runtime_resume_and_get(ctx->dev);
37162306a36Sopenharmony_ci	if (ret < 0) {
37262306a36Sopenharmony_ci		dev_err(ctx->dev, "failed to enable FIMD device.\n");
37362306a36Sopenharmony_ci		return ret;
37462306a36Sopenharmony_ci	}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	clk_prepare_enable(ctx->bus_clk);
37762306a36Sopenharmony_ci	clk_prepare_enable(ctx->lcd_clk);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	/* Check if any channel is enabled. */
38062306a36Sopenharmony_ci	for (win = 0; win < WINDOWS_NR; win++) {
38162306a36Sopenharmony_ci		u32 val = readl(ctx->regs + WINCON(win));
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci		if (val & WINCONx_ENWIN) {
38462306a36Sopenharmony_ci			fimd_enable_video_output(ctx, win, false);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci			if (ctx->driver_data->has_shadowcon)
38762306a36Sopenharmony_ci				fimd_enable_shadow_channel_path(ctx, win,
38862306a36Sopenharmony_ci								false);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci			ch_enabled = 1;
39162306a36Sopenharmony_ci		}
39262306a36Sopenharmony_ci	}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	/* Wait for vsync, as disable channel takes effect at next vsync */
39562306a36Sopenharmony_ci	if (ch_enabled) {
39662306a36Sopenharmony_ci		ctx->suspended = false;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci		fimd_enable_vblank(ctx->crtc);
39962306a36Sopenharmony_ci		fimd_wait_for_vblank(ctx->crtc);
40062306a36Sopenharmony_ci		fimd_disable_vblank(ctx->crtc);
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci		ctx->suspended = true;
40362306a36Sopenharmony_ci	}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	clk_disable_unprepare(ctx->lcd_clk);
40662306a36Sopenharmony_ci	clk_disable_unprepare(ctx->bus_clk);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	pm_runtime_put(ctx->dev);
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	return 0;
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_cistatic int fimd_atomic_check(struct exynos_drm_crtc *crtc,
41562306a36Sopenharmony_ci		struct drm_crtc_state *state)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	struct drm_display_mode *mode = &state->adjusted_mode;
41862306a36Sopenharmony_ci	struct fimd_context *ctx = crtc->ctx;
41962306a36Sopenharmony_ci	unsigned long ideal_clk, lcd_rate;
42062306a36Sopenharmony_ci	u32 clkdiv;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	if (mode->clock == 0) {
42362306a36Sopenharmony_ci		DRM_DEV_ERROR(ctx->dev, "Mode has zero clock value.\n");
42462306a36Sopenharmony_ci		return -EINVAL;
42562306a36Sopenharmony_ci	}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	ideal_clk = mode->clock * 1000;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	if (ctx->i80_if) {
43062306a36Sopenharmony_ci		/*
43162306a36Sopenharmony_ci		 * The frame done interrupt should be occurred prior to the
43262306a36Sopenharmony_ci		 * next TE signal.
43362306a36Sopenharmony_ci		 */
43462306a36Sopenharmony_ci		ideal_clk *= 2;
43562306a36Sopenharmony_ci	}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	lcd_rate = clk_get_rate(ctx->lcd_clk);
43862306a36Sopenharmony_ci	if (2 * lcd_rate < ideal_clk) {
43962306a36Sopenharmony_ci		DRM_DEV_ERROR(ctx->dev,
44062306a36Sopenharmony_ci			      "sclk_fimd clock too low(%lu) for requested pixel clock(%lu)\n",
44162306a36Sopenharmony_ci			      lcd_rate, ideal_clk);
44262306a36Sopenharmony_ci		return -EINVAL;
44362306a36Sopenharmony_ci	}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	/* Find the clock divider value that gets us closest to ideal_clk */
44662306a36Sopenharmony_ci	clkdiv = DIV_ROUND_CLOSEST(lcd_rate, ideal_clk);
44762306a36Sopenharmony_ci	if (clkdiv >= 0x200) {
44862306a36Sopenharmony_ci		DRM_DEV_ERROR(ctx->dev, "requested pixel clock(%lu) too low\n",
44962306a36Sopenharmony_ci			      ideal_clk);
45062306a36Sopenharmony_ci		return -EINVAL;
45162306a36Sopenharmony_ci	}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	ctx->clkdiv = (clkdiv < 0x100) ? clkdiv : 0xff;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	return 0;
45662306a36Sopenharmony_ci}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_cistatic void fimd_setup_trigger(struct fimd_context *ctx)
45962306a36Sopenharmony_ci{
46062306a36Sopenharmony_ci	void __iomem *timing_base = ctx->regs + ctx->driver_data->timing_base;
46162306a36Sopenharmony_ci	u32 trg_type = ctx->driver_data->trg_type;
46262306a36Sopenharmony_ci	u32 val = readl(timing_base + TRIGCON);
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	val &= ~(TRGMODE_ENABLE);
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	if (trg_type == I80_HW_TRG) {
46762306a36Sopenharmony_ci		if (ctx->driver_data->has_hw_trigger)
46862306a36Sopenharmony_ci			val |= HWTRGEN_ENABLE | HWTRGMASK_ENABLE;
46962306a36Sopenharmony_ci		if (ctx->driver_data->has_trigger_per_te)
47062306a36Sopenharmony_ci			val |= HWTRIGEN_PER_ENABLE;
47162306a36Sopenharmony_ci	} else {
47262306a36Sopenharmony_ci		val |= TRGMODE_ENABLE;
47362306a36Sopenharmony_ci	}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	writel(val, timing_base + TRIGCON);
47662306a36Sopenharmony_ci}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_cistatic void fimd_commit(struct exynos_drm_crtc *crtc)
47962306a36Sopenharmony_ci{
48062306a36Sopenharmony_ci	struct fimd_context *ctx = crtc->ctx;
48162306a36Sopenharmony_ci	struct drm_display_mode *mode = &crtc->base.state->adjusted_mode;
48262306a36Sopenharmony_ci	const struct fimd_driver_data *driver_data = ctx->driver_data;
48362306a36Sopenharmony_ci	void *timing_base = ctx->regs + driver_data->timing_base;
48462306a36Sopenharmony_ci	u32 val;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	if (ctx->suspended)
48762306a36Sopenharmony_ci		return;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	/* nothing to do if we haven't set the mode yet */
49062306a36Sopenharmony_ci	if (mode->htotal == 0 || mode->vtotal == 0)
49162306a36Sopenharmony_ci		return;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	if (ctx->i80_if) {
49462306a36Sopenharmony_ci		val = ctx->i80ifcon | I80IFEN_ENABLE;
49562306a36Sopenharmony_ci		writel(val, timing_base + I80IFCONFAx(0));
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci		/* disable auto frame rate */
49862306a36Sopenharmony_ci		writel(0, timing_base + I80IFCONFBx(0));
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci		/* set video type selection to I80 interface */
50162306a36Sopenharmony_ci		if (driver_data->has_vtsel && ctx->sysreg &&
50262306a36Sopenharmony_ci				regmap_update_bits(ctx->sysreg,
50362306a36Sopenharmony_ci					driver_data->lcdblk_offset,
50462306a36Sopenharmony_ci					0x3 << driver_data->lcdblk_vt_shift,
50562306a36Sopenharmony_ci					0x1 << driver_data->lcdblk_vt_shift)) {
50662306a36Sopenharmony_ci			DRM_DEV_ERROR(ctx->dev,
50762306a36Sopenharmony_ci				      "Failed to update sysreg for I80 i/f.\n");
50862306a36Sopenharmony_ci			return;
50962306a36Sopenharmony_ci		}
51062306a36Sopenharmony_ci	} else {
51162306a36Sopenharmony_ci		int vsync_len, vbpd, vfpd, hsync_len, hbpd, hfpd;
51262306a36Sopenharmony_ci		u32 vidcon1;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci		/* setup polarity values */
51562306a36Sopenharmony_ci		vidcon1 = ctx->vidcon1;
51662306a36Sopenharmony_ci		if (mode->flags & DRM_MODE_FLAG_NVSYNC)
51762306a36Sopenharmony_ci			vidcon1 |= VIDCON1_INV_VSYNC;
51862306a36Sopenharmony_ci		if (mode->flags & DRM_MODE_FLAG_NHSYNC)
51962306a36Sopenharmony_ci			vidcon1 |= VIDCON1_INV_HSYNC;
52062306a36Sopenharmony_ci		writel(vidcon1, ctx->regs + driver_data->timing_base + VIDCON1);
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci		/* setup vertical timing values. */
52362306a36Sopenharmony_ci		vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
52462306a36Sopenharmony_ci		vbpd = mode->crtc_vtotal - mode->crtc_vsync_end;
52562306a36Sopenharmony_ci		vfpd = mode->crtc_vsync_start - mode->crtc_vdisplay;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci		val = VIDTCON0_VBPD(vbpd - 1) |
52862306a36Sopenharmony_ci			VIDTCON0_VFPD(vfpd - 1) |
52962306a36Sopenharmony_ci			VIDTCON0_VSPW(vsync_len - 1);
53062306a36Sopenharmony_ci		writel(val, ctx->regs + driver_data->timing_base + VIDTCON0);
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci		/* setup horizontal timing values.  */
53362306a36Sopenharmony_ci		hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
53462306a36Sopenharmony_ci		hbpd = mode->crtc_htotal - mode->crtc_hsync_end;
53562306a36Sopenharmony_ci		hfpd = mode->crtc_hsync_start - mode->crtc_hdisplay;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci		val = VIDTCON1_HBPD(hbpd - 1) |
53862306a36Sopenharmony_ci			VIDTCON1_HFPD(hfpd - 1) |
53962306a36Sopenharmony_ci			VIDTCON1_HSPW(hsync_len - 1);
54062306a36Sopenharmony_ci		writel(val, ctx->regs + driver_data->timing_base + VIDTCON1);
54162306a36Sopenharmony_ci	}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	if (driver_data->has_vidoutcon)
54462306a36Sopenharmony_ci		writel(ctx->vidout_con, timing_base + VIDOUT_CON);
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	/* set bypass selection */
54762306a36Sopenharmony_ci	if (ctx->sysreg && regmap_update_bits(ctx->sysreg,
54862306a36Sopenharmony_ci				driver_data->lcdblk_offset,
54962306a36Sopenharmony_ci				0x1 << driver_data->lcdblk_bypass_shift,
55062306a36Sopenharmony_ci				0x1 << driver_data->lcdblk_bypass_shift)) {
55162306a36Sopenharmony_ci		DRM_DEV_ERROR(ctx->dev,
55262306a36Sopenharmony_ci			      "Failed to update sysreg for bypass setting.\n");
55362306a36Sopenharmony_ci		return;
55462306a36Sopenharmony_ci	}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	/* TODO: When MIC is enabled for display path, the lcdblk_mic_bypass
55762306a36Sopenharmony_ci	 * bit should be cleared.
55862306a36Sopenharmony_ci	 */
55962306a36Sopenharmony_ci	if (driver_data->has_mic_bypass && ctx->sysreg &&
56062306a36Sopenharmony_ci	    regmap_update_bits(ctx->sysreg,
56162306a36Sopenharmony_ci				driver_data->lcdblk_offset,
56262306a36Sopenharmony_ci				0x1 << driver_data->lcdblk_mic_bypass_shift,
56362306a36Sopenharmony_ci				0x1 << driver_data->lcdblk_mic_bypass_shift)) {
56462306a36Sopenharmony_ci		DRM_DEV_ERROR(ctx->dev,
56562306a36Sopenharmony_ci			      "Failed to update sysreg for bypass mic.\n");
56662306a36Sopenharmony_ci		return;
56762306a36Sopenharmony_ci	}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	/* setup horizontal and vertical display size. */
57062306a36Sopenharmony_ci	val = VIDTCON2_LINEVAL(mode->vdisplay - 1) |
57162306a36Sopenharmony_ci	       VIDTCON2_HOZVAL(mode->hdisplay - 1) |
57262306a36Sopenharmony_ci	       VIDTCON2_LINEVAL_E(mode->vdisplay - 1) |
57362306a36Sopenharmony_ci	       VIDTCON2_HOZVAL_E(mode->hdisplay - 1);
57462306a36Sopenharmony_ci	writel(val, ctx->regs + driver_data->timing_base + VIDTCON2);
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	fimd_setup_trigger(ctx);
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	/*
57962306a36Sopenharmony_ci	 * fields of register with prefix '_F' would be updated
58062306a36Sopenharmony_ci	 * at vsync(same as dma start)
58162306a36Sopenharmony_ci	 */
58262306a36Sopenharmony_ci	val = ctx->vidcon0;
58362306a36Sopenharmony_ci	val |= VIDCON0_ENVID | VIDCON0_ENVID_F;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	if (ctx->driver_data->has_clksel)
58662306a36Sopenharmony_ci		val |= VIDCON0_CLKSEL_LCD;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	if (ctx->clkdiv > 1)
58962306a36Sopenharmony_ci		val |= VIDCON0_CLKVAL_F(ctx->clkdiv - 1) | VIDCON0_CLKDIR;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	writel(val, ctx->regs + VIDCON0);
59262306a36Sopenharmony_ci}
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_cistatic void fimd_win_set_bldeq(struct fimd_context *ctx, unsigned int win,
59562306a36Sopenharmony_ci			       unsigned int alpha, unsigned int pixel_alpha)
59662306a36Sopenharmony_ci{
59762306a36Sopenharmony_ci	u32 mask = BLENDEQ_A_FUNC_F(0xf) | BLENDEQ_B_FUNC_F(0xf);
59862306a36Sopenharmony_ci	u32 val = 0;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	switch (pixel_alpha) {
60162306a36Sopenharmony_ci	case DRM_MODE_BLEND_PIXEL_NONE:
60262306a36Sopenharmony_ci	case DRM_MODE_BLEND_COVERAGE:
60362306a36Sopenharmony_ci		val |= BLENDEQ_A_FUNC_F(BLENDEQ_ALPHA_A);
60462306a36Sopenharmony_ci		val |= BLENDEQ_B_FUNC_F(BLENDEQ_ONE_MINUS_ALPHA_A);
60562306a36Sopenharmony_ci		break;
60662306a36Sopenharmony_ci	case DRM_MODE_BLEND_PREMULTI:
60762306a36Sopenharmony_ci	default:
60862306a36Sopenharmony_ci		if (alpha != DRM_BLEND_ALPHA_OPAQUE) {
60962306a36Sopenharmony_ci			val |= BLENDEQ_A_FUNC_F(BLENDEQ_ALPHA0);
61062306a36Sopenharmony_ci			val |= BLENDEQ_B_FUNC_F(BLENDEQ_ONE_MINUS_ALPHA_A);
61162306a36Sopenharmony_ci		} else {
61262306a36Sopenharmony_ci			val |= BLENDEQ_A_FUNC_F(BLENDEQ_ONE);
61362306a36Sopenharmony_ci			val |= BLENDEQ_B_FUNC_F(BLENDEQ_ONE_MINUS_ALPHA_A);
61462306a36Sopenharmony_ci		}
61562306a36Sopenharmony_ci		break;
61662306a36Sopenharmony_ci	}
61762306a36Sopenharmony_ci	fimd_set_bits(ctx, BLENDEQx(win), mask, val);
61862306a36Sopenharmony_ci}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_cistatic void fimd_win_set_bldmod(struct fimd_context *ctx, unsigned int win,
62162306a36Sopenharmony_ci				unsigned int alpha, unsigned int pixel_alpha)
62262306a36Sopenharmony_ci{
62362306a36Sopenharmony_ci	u32 win_alpha_l = (alpha >> 8) & 0xf;
62462306a36Sopenharmony_ci	u32 win_alpha_h = alpha >> 12;
62562306a36Sopenharmony_ci	u32 val = 0;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	switch (pixel_alpha) {
62862306a36Sopenharmony_ci	case DRM_MODE_BLEND_PIXEL_NONE:
62962306a36Sopenharmony_ci		break;
63062306a36Sopenharmony_ci	case DRM_MODE_BLEND_COVERAGE:
63162306a36Sopenharmony_ci	case DRM_MODE_BLEND_PREMULTI:
63262306a36Sopenharmony_ci	default:
63362306a36Sopenharmony_ci		val |= WINCON1_ALPHA_SEL;
63462306a36Sopenharmony_ci		val |= WINCON1_BLD_PIX;
63562306a36Sopenharmony_ci		val |= WINCON1_ALPHA_MUL;
63662306a36Sopenharmony_ci		break;
63762306a36Sopenharmony_ci	}
63862306a36Sopenharmony_ci	fimd_set_bits(ctx, WINCON(win), WINCONx_BLEND_MODE_MASK, val);
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	/* OSD alpha */
64162306a36Sopenharmony_ci	val = VIDISD14C_ALPHA0_R(win_alpha_h) |
64262306a36Sopenharmony_ci		VIDISD14C_ALPHA0_G(win_alpha_h) |
64362306a36Sopenharmony_ci		VIDISD14C_ALPHA0_B(win_alpha_h) |
64462306a36Sopenharmony_ci		VIDISD14C_ALPHA1_R(0x0) |
64562306a36Sopenharmony_ci		VIDISD14C_ALPHA1_G(0x0) |
64662306a36Sopenharmony_ci		VIDISD14C_ALPHA1_B(0x0);
64762306a36Sopenharmony_ci	writel(val, ctx->regs + VIDOSD_C(win));
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	val = VIDW_ALPHA_R(win_alpha_l) | VIDW_ALPHA_G(win_alpha_l) |
65062306a36Sopenharmony_ci		VIDW_ALPHA_B(win_alpha_l);
65162306a36Sopenharmony_ci	writel(val, ctx->regs + VIDWnALPHA0(win));
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	val = VIDW_ALPHA_R(0x0) | VIDW_ALPHA_G(0x0) |
65462306a36Sopenharmony_ci		VIDW_ALPHA_B(0x0);
65562306a36Sopenharmony_ci	writel(val, ctx->regs + VIDWnALPHA1(win));
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	fimd_set_bits(ctx, BLENDCON, BLENDCON_NEW_MASK,
65862306a36Sopenharmony_ci			BLENDCON_NEW_8BIT_ALPHA_VALUE);
65962306a36Sopenharmony_ci}
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_cistatic void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win,
66262306a36Sopenharmony_ci				struct drm_framebuffer *fb, int width)
66362306a36Sopenharmony_ci{
66462306a36Sopenharmony_ci	struct exynos_drm_plane *plane = &ctx->planes[win];
66562306a36Sopenharmony_ci	struct exynos_drm_plane_state *state =
66662306a36Sopenharmony_ci		to_exynos_plane_state(plane->base.state);
66762306a36Sopenharmony_ci	uint32_t pixel_format = fb->format->format;
66862306a36Sopenharmony_ci	unsigned int alpha = state->base.alpha;
66962306a36Sopenharmony_ci	u32 val = WINCONx_ENWIN;
67062306a36Sopenharmony_ci	unsigned int pixel_alpha;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	if (fb->format->has_alpha)
67362306a36Sopenharmony_ci		pixel_alpha = state->base.pixel_blend_mode;
67462306a36Sopenharmony_ci	else
67562306a36Sopenharmony_ci		pixel_alpha = DRM_MODE_BLEND_PIXEL_NONE;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	/*
67862306a36Sopenharmony_ci	 * In case of s3c64xx, window 0 doesn't support alpha channel.
67962306a36Sopenharmony_ci	 * So the request format is ARGB8888 then change it to XRGB8888.
68062306a36Sopenharmony_ci	 */
68162306a36Sopenharmony_ci	if (ctx->driver_data->has_limited_fmt && !win) {
68262306a36Sopenharmony_ci		if (pixel_format == DRM_FORMAT_ARGB8888)
68362306a36Sopenharmony_ci			pixel_format = DRM_FORMAT_XRGB8888;
68462306a36Sopenharmony_ci	}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	switch (pixel_format) {
68762306a36Sopenharmony_ci	case DRM_FORMAT_C8:
68862306a36Sopenharmony_ci		val |= WINCON0_BPPMODE_8BPP_PALETTE;
68962306a36Sopenharmony_ci		val |= WINCONx_BURSTLEN_8WORD;
69062306a36Sopenharmony_ci		val |= WINCONx_BYTSWP;
69162306a36Sopenharmony_ci		break;
69262306a36Sopenharmony_ci	case DRM_FORMAT_XRGB1555:
69362306a36Sopenharmony_ci	case DRM_FORMAT_XBGR1555:
69462306a36Sopenharmony_ci		val |= WINCON0_BPPMODE_16BPP_1555;
69562306a36Sopenharmony_ci		val |= WINCONx_HAWSWP;
69662306a36Sopenharmony_ci		val |= WINCONx_BURSTLEN_16WORD;
69762306a36Sopenharmony_ci		break;
69862306a36Sopenharmony_ci	case DRM_FORMAT_RGB565:
69962306a36Sopenharmony_ci	case DRM_FORMAT_BGR565:
70062306a36Sopenharmony_ci		val |= WINCON0_BPPMODE_16BPP_565;
70162306a36Sopenharmony_ci		val |= WINCONx_HAWSWP;
70262306a36Sopenharmony_ci		val |= WINCONx_BURSTLEN_16WORD;
70362306a36Sopenharmony_ci		break;
70462306a36Sopenharmony_ci	case DRM_FORMAT_XRGB8888:
70562306a36Sopenharmony_ci	case DRM_FORMAT_XBGR8888:
70662306a36Sopenharmony_ci		val |= WINCON0_BPPMODE_24BPP_888;
70762306a36Sopenharmony_ci		val |= WINCONx_WSWP;
70862306a36Sopenharmony_ci		val |= WINCONx_BURSTLEN_16WORD;
70962306a36Sopenharmony_ci		break;
71062306a36Sopenharmony_ci	case DRM_FORMAT_ARGB8888:
71162306a36Sopenharmony_ci	case DRM_FORMAT_ABGR8888:
71262306a36Sopenharmony_ci	default:
71362306a36Sopenharmony_ci		val |= WINCON1_BPPMODE_25BPP_A1888;
71462306a36Sopenharmony_ci		val |= WINCONx_WSWP;
71562306a36Sopenharmony_ci		val |= WINCONx_BURSTLEN_16WORD;
71662306a36Sopenharmony_ci		break;
71762306a36Sopenharmony_ci	}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	switch (pixel_format) {
72062306a36Sopenharmony_ci	case DRM_FORMAT_XBGR1555:
72162306a36Sopenharmony_ci	case DRM_FORMAT_XBGR8888:
72262306a36Sopenharmony_ci	case DRM_FORMAT_ABGR8888:
72362306a36Sopenharmony_ci	case DRM_FORMAT_BGR565:
72462306a36Sopenharmony_ci		writel(WIN_RGB_ORDER_REVERSE, ctx->regs + WIN_RGB_ORDER(win));
72562306a36Sopenharmony_ci		break;
72662306a36Sopenharmony_ci	default:
72762306a36Sopenharmony_ci		writel(WIN_RGB_ORDER_FORWARD, ctx->regs + WIN_RGB_ORDER(win));
72862306a36Sopenharmony_ci		break;
72962306a36Sopenharmony_ci	}
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	/*
73262306a36Sopenharmony_ci	 * Setting dma-burst to 16Word causes permanent tearing for very small
73362306a36Sopenharmony_ci	 * buffers, e.g. cursor buffer. Burst Mode switching which based on
73462306a36Sopenharmony_ci	 * plane size is not recommended as plane size varies alot towards the
73562306a36Sopenharmony_ci	 * end of the screen and rapid movement causes unstable DMA, but it is
73662306a36Sopenharmony_ci	 * still better to change dma-burst than displaying garbage.
73762306a36Sopenharmony_ci	 */
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	if (width < MIN_FB_WIDTH_FOR_16WORD_BURST) {
74062306a36Sopenharmony_ci		val &= ~WINCONx_BURSTLEN_MASK;
74162306a36Sopenharmony_ci		val |= WINCONx_BURSTLEN_4WORD;
74262306a36Sopenharmony_ci	}
74362306a36Sopenharmony_ci	fimd_set_bits(ctx, WINCON(win), ~WINCONx_BLEND_MODE_MASK, val);
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	/* hardware window 0 doesn't support alpha channel. */
74662306a36Sopenharmony_ci	if (win != 0) {
74762306a36Sopenharmony_ci		fimd_win_set_bldmod(ctx, win, alpha, pixel_alpha);
74862306a36Sopenharmony_ci		fimd_win_set_bldeq(ctx, win, alpha, pixel_alpha);
74962306a36Sopenharmony_ci	}
75062306a36Sopenharmony_ci}
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_cistatic void fimd_win_set_colkey(struct fimd_context *ctx, unsigned int win)
75362306a36Sopenharmony_ci{
75462306a36Sopenharmony_ci	unsigned int keycon0 = 0, keycon1 = 0;
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	keycon0 = ~(WxKEYCON0_KEYBL_EN | WxKEYCON0_KEYEN_F |
75762306a36Sopenharmony_ci			WxKEYCON0_DIRCON) | WxKEYCON0_COMPKEY(0);
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	keycon1 = WxKEYCON1_COLVAL(0xffffffff);
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	writel(keycon0, ctx->regs + WKEYCON0_BASE(win));
76262306a36Sopenharmony_ci	writel(keycon1, ctx->regs + WKEYCON1_BASE(win));
76362306a36Sopenharmony_ci}
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci/**
76662306a36Sopenharmony_ci * fimd_shadow_protect_win() - disable updating values from shadow registers at vsync
76762306a36Sopenharmony_ci *
76862306a36Sopenharmony_ci * @ctx: local driver data
76962306a36Sopenharmony_ci * @win: window to protect registers for
77062306a36Sopenharmony_ci * @protect: 1 to protect (disable updates)
77162306a36Sopenharmony_ci */
77262306a36Sopenharmony_cistatic void fimd_shadow_protect_win(struct fimd_context *ctx,
77362306a36Sopenharmony_ci				    unsigned int win, bool protect)
77462306a36Sopenharmony_ci{
77562306a36Sopenharmony_ci	u32 reg, bits, val;
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	/*
77862306a36Sopenharmony_ci	 * SHADOWCON/PRTCON register is used for enabling timing.
77962306a36Sopenharmony_ci	 *
78062306a36Sopenharmony_ci	 * for example, once only width value of a register is set,
78162306a36Sopenharmony_ci	 * if the dma is started then fimd hardware could malfunction so
78262306a36Sopenharmony_ci	 * with protect window setting, the register fields with prefix '_F'
78362306a36Sopenharmony_ci	 * wouldn't be updated at vsync also but updated once unprotect window
78462306a36Sopenharmony_ci	 * is set.
78562306a36Sopenharmony_ci	 */
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	if (ctx->driver_data->has_shadowcon) {
78862306a36Sopenharmony_ci		reg = SHADOWCON;
78962306a36Sopenharmony_ci		bits = SHADOWCON_WINx_PROTECT(win);
79062306a36Sopenharmony_ci	} else {
79162306a36Sopenharmony_ci		reg = PRTCON;
79262306a36Sopenharmony_ci		bits = PRTCON_PROTECT;
79362306a36Sopenharmony_ci	}
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	val = readl(ctx->regs + reg);
79662306a36Sopenharmony_ci	if (protect)
79762306a36Sopenharmony_ci		val |= bits;
79862306a36Sopenharmony_ci	else
79962306a36Sopenharmony_ci		val &= ~bits;
80062306a36Sopenharmony_ci	writel(val, ctx->regs + reg);
80162306a36Sopenharmony_ci}
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_cistatic void fimd_atomic_begin(struct exynos_drm_crtc *crtc)
80462306a36Sopenharmony_ci{
80562306a36Sopenharmony_ci	struct fimd_context *ctx = crtc->ctx;
80662306a36Sopenharmony_ci	int i;
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	if (ctx->suspended)
80962306a36Sopenharmony_ci		return;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	for (i = 0; i < WINDOWS_NR; i++)
81262306a36Sopenharmony_ci		fimd_shadow_protect_win(ctx, i, true);
81362306a36Sopenharmony_ci}
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_cistatic void fimd_atomic_flush(struct exynos_drm_crtc *crtc)
81662306a36Sopenharmony_ci{
81762306a36Sopenharmony_ci	struct fimd_context *ctx = crtc->ctx;
81862306a36Sopenharmony_ci	int i;
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	if (ctx->suspended)
82162306a36Sopenharmony_ci		return;
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	for (i = 0; i < WINDOWS_NR; i++)
82462306a36Sopenharmony_ci		fimd_shadow_protect_win(ctx, i, false);
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	exynos_crtc_handle_event(crtc);
82762306a36Sopenharmony_ci}
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_cistatic void fimd_update_plane(struct exynos_drm_crtc *crtc,
83062306a36Sopenharmony_ci			      struct exynos_drm_plane *plane)
83162306a36Sopenharmony_ci{
83262306a36Sopenharmony_ci	struct exynos_drm_plane_state *state =
83362306a36Sopenharmony_ci				to_exynos_plane_state(plane->base.state);
83462306a36Sopenharmony_ci	struct fimd_context *ctx = crtc->ctx;
83562306a36Sopenharmony_ci	struct drm_framebuffer *fb = state->base.fb;
83662306a36Sopenharmony_ci	dma_addr_t dma_addr;
83762306a36Sopenharmony_ci	unsigned long val, size, offset;
83862306a36Sopenharmony_ci	unsigned int last_x, last_y, buf_offsize, line_size;
83962306a36Sopenharmony_ci	unsigned int win = plane->index;
84062306a36Sopenharmony_ci	unsigned int cpp = fb->format->cpp[0];
84162306a36Sopenharmony_ci	unsigned int pitch = fb->pitches[0];
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	if (ctx->suspended)
84462306a36Sopenharmony_ci		return;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	offset = state->src.x * cpp;
84762306a36Sopenharmony_ci	offset += state->src.y * pitch;
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci	/* buffer start address */
85062306a36Sopenharmony_ci	dma_addr = exynos_drm_fb_dma_addr(fb, 0) + offset;
85162306a36Sopenharmony_ci	val = (unsigned long)dma_addr;
85262306a36Sopenharmony_ci	writel(val, ctx->regs + VIDWx_BUF_START(win, 0));
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	/* buffer end address */
85562306a36Sopenharmony_ci	size = pitch * state->crtc.h;
85662306a36Sopenharmony_ci	val = (unsigned long)(dma_addr + size);
85762306a36Sopenharmony_ci	writel(val, ctx->regs + VIDWx_BUF_END(win, 0));
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	DRM_DEV_DEBUG_KMS(ctx->dev,
86062306a36Sopenharmony_ci			  "start addr = 0x%lx, end addr = 0x%lx, size = 0x%lx\n",
86162306a36Sopenharmony_ci			  (unsigned long)dma_addr, val, size);
86262306a36Sopenharmony_ci	DRM_DEV_DEBUG_KMS(ctx->dev, "ovl_width = %d, ovl_height = %d\n",
86362306a36Sopenharmony_ci			  state->crtc.w, state->crtc.h);
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	/* buffer size */
86662306a36Sopenharmony_ci	buf_offsize = pitch - (state->crtc.w * cpp);
86762306a36Sopenharmony_ci	line_size = state->crtc.w * cpp;
86862306a36Sopenharmony_ci	val = VIDW_BUF_SIZE_OFFSET(buf_offsize) |
86962306a36Sopenharmony_ci		VIDW_BUF_SIZE_PAGEWIDTH(line_size) |
87062306a36Sopenharmony_ci		VIDW_BUF_SIZE_OFFSET_E(buf_offsize) |
87162306a36Sopenharmony_ci		VIDW_BUF_SIZE_PAGEWIDTH_E(line_size);
87262306a36Sopenharmony_ci	writel(val, ctx->regs + VIDWx_BUF_SIZE(win, 0));
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	/* OSD position */
87562306a36Sopenharmony_ci	val = VIDOSDxA_TOPLEFT_X(state->crtc.x) |
87662306a36Sopenharmony_ci		VIDOSDxA_TOPLEFT_Y(state->crtc.y) |
87762306a36Sopenharmony_ci		VIDOSDxA_TOPLEFT_X_E(state->crtc.x) |
87862306a36Sopenharmony_ci		VIDOSDxA_TOPLEFT_Y_E(state->crtc.y);
87962306a36Sopenharmony_ci	writel(val, ctx->regs + VIDOSD_A(win));
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	last_x = state->crtc.x + state->crtc.w;
88262306a36Sopenharmony_ci	if (last_x)
88362306a36Sopenharmony_ci		last_x--;
88462306a36Sopenharmony_ci	last_y = state->crtc.y + state->crtc.h;
88562306a36Sopenharmony_ci	if (last_y)
88662306a36Sopenharmony_ci		last_y--;
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	val = VIDOSDxB_BOTRIGHT_X(last_x) | VIDOSDxB_BOTRIGHT_Y(last_y) |
88962306a36Sopenharmony_ci		VIDOSDxB_BOTRIGHT_X_E(last_x) | VIDOSDxB_BOTRIGHT_Y_E(last_y);
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	writel(val, ctx->regs + VIDOSD_B(win));
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	DRM_DEV_DEBUG_KMS(ctx->dev,
89462306a36Sopenharmony_ci			  "osd pos: tx = %d, ty = %d, bx = %d, by = %d\n",
89562306a36Sopenharmony_ci			  state->crtc.x, state->crtc.y, last_x, last_y);
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	/* OSD size */
89862306a36Sopenharmony_ci	if (win != 3 && win != 4) {
89962306a36Sopenharmony_ci		u32 offset = VIDOSD_D(win);
90062306a36Sopenharmony_ci		if (win == 0)
90162306a36Sopenharmony_ci			offset = VIDOSD_C(win);
90262306a36Sopenharmony_ci		val = state->crtc.w * state->crtc.h;
90362306a36Sopenharmony_ci		writel(val, ctx->regs + offset);
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci		DRM_DEV_DEBUG_KMS(ctx->dev, "osd size = 0x%x\n",
90662306a36Sopenharmony_ci				  (unsigned int)val);
90762306a36Sopenharmony_ci	}
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	fimd_win_set_pixfmt(ctx, win, fb, state->src.w);
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	/* hardware window 0 doesn't support color key. */
91262306a36Sopenharmony_ci	if (win != 0)
91362306a36Sopenharmony_ci		fimd_win_set_colkey(ctx, win);
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	fimd_enable_video_output(ctx, win, true);
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	if (ctx->driver_data->has_shadowcon)
91862306a36Sopenharmony_ci		fimd_enable_shadow_channel_path(ctx, win, true);
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	if (ctx->i80_if)
92162306a36Sopenharmony_ci		atomic_set(&ctx->win_updated, 1);
92262306a36Sopenharmony_ci}
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_cistatic void fimd_disable_plane(struct exynos_drm_crtc *crtc,
92562306a36Sopenharmony_ci			       struct exynos_drm_plane *plane)
92662306a36Sopenharmony_ci{
92762306a36Sopenharmony_ci	struct fimd_context *ctx = crtc->ctx;
92862306a36Sopenharmony_ci	unsigned int win = plane->index;
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	if (ctx->suspended)
93162306a36Sopenharmony_ci		return;
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	fimd_enable_video_output(ctx, win, false);
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	if (ctx->driver_data->has_shadowcon)
93662306a36Sopenharmony_ci		fimd_enable_shadow_channel_path(ctx, win, false);
93762306a36Sopenharmony_ci}
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_cistatic void fimd_atomic_enable(struct exynos_drm_crtc *crtc)
94062306a36Sopenharmony_ci{
94162306a36Sopenharmony_ci	struct fimd_context *ctx = crtc->ctx;
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	if (!ctx->suspended)
94462306a36Sopenharmony_ci		return;
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	ctx->suspended = false;
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	if (pm_runtime_resume_and_get(ctx->dev) < 0) {
94962306a36Sopenharmony_ci		dev_warn(ctx->dev, "failed to enable FIMD device.\n");
95062306a36Sopenharmony_ci		return;
95162306a36Sopenharmony_ci	}
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	/* if vblank was enabled status, enable it again. */
95462306a36Sopenharmony_ci	if (test_and_clear_bit(0, &ctx->irq_flags))
95562306a36Sopenharmony_ci		fimd_enable_vblank(ctx->crtc);
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	fimd_commit(ctx->crtc);
95862306a36Sopenharmony_ci}
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_cistatic void fimd_atomic_disable(struct exynos_drm_crtc *crtc)
96162306a36Sopenharmony_ci{
96262306a36Sopenharmony_ci	struct fimd_context *ctx = crtc->ctx;
96362306a36Sopenharmony_ci	int i;
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	if (ctx->suspended)
96662306a36Sopenharmony_ci		return;
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	/*
96962306a36Sopenharmony_ci	 * We need to make sure that all windows are disabled before we
97062306a36Sopenharmony_ci	 * suspend that connector. Otherwise we might try to scan from
97162306a36Sopenharmony_ci	 * a destroyed buffer later.
97262306a36Sopenharmony_ci	 */
97362306a36Sopenharmony_ci	for (i = 0; i < WINDOWS_NR; i++)
97462306a36Sopenharmony_ci		fimd_disable_plane(crtc, &ctx->planes[i]);
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci	fimd_enable_vblank(crtc);
97762306a36Sopenharmony_ci	fimd_wait_for_vblank(crtc);
97862306a36Sopenharmony_ci	fimd_disable_vblank(crtc);
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	writel(0, ctx->regs + VIDCON0);
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	pm_runtime_put_sync(ctx->dev);
98362306a36Sopenharmony_ci	ctx->suspended = true;
98462306a36Sopenharmony_ci}
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_cistatic void fimd_trigger(struct device *dev)
98762306a36Sopenharmony_ci{
98862306a36Sopenharmony_ci	struct fimd_context *ctx = dev_get_drvdata(dev);
98962306a36Sopenharmony_ci	const struct fimd_driver_data *driver_data = ctx->driver_data;
99062306a36Sopenharmony_ci	void *timing_base = ctx->regs + driver_data->timing_base;
99162306a36Sopenharmony_ci	u32 reg;
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	 /*
99462306a36Sopenharmony_ci	  * Skips triggering if in triggering state, because multiple triggering
99562306a36Sopenharmony_ci	  * requests can cause panel reset.
99662306a36Sopenharmony_ci	  */
99762306a36Sopenharmony_ci	if (atomic_read(&ctx->triggering))
99862306a36Sopenharmony_ci		return;
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	/* Enters triggering mode */
100162306a36Sopenharmony_ci	atomic_set(&ctx->triggering, 1);
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	reg = readl(timing_base + TRIGCON);
100462306a36Sopenharmony_ci	reg |= (TRGMODE_ENABLE | SWTRGCMD_ENABLE);
100562306a36Sopenharmony_ci	writel(reg, timing_base + TRIGCON);
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	/*
100862306a36Sopenharmony_ci	 * Exits triggering mode if vblank is not enabled yet, because when the
100962306a36Sopenharmony_ci	 * VIDINTCON0 register is not set, it can not exit from triggering mode.
101062306a36Sopenharmony_ci	 */
101162306a36Sopenharmony_ci	if (!test_bit(0, &ctx->irq_flags))
101262306a36Sopenharmony_ci		atomic_set(&ctx->triggering, 0);
101362306a36Sopenharmony_ci}
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_cistatic void fimd_te_handler(struct exynos_drm_crtc *crtc)
101662306a36Sopenharmony_ci{
101762306a36Sopenharmony_ci	struct fimd_context *ctx = crtc->ctx;
101862306a36Sopenharmony_ci	u32 trg_type = ctx->driver_data->trg_type;
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	/* Checks the crtc is detached already from encoder */
102162306a36Sopenharmony_ci	if (!ctx->drm_dev)
102262306a36Sopenharmony_ci		return;
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	if (trg_type == I80_HW_TRG)
102562306a36Sopenharmony_ci		goto out;
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	/*
102862306a36Sopenharmony_ci	 * If there is a page flip request, triggers and handles the page flip
102962306a36Sopenharmony_ci	 * event so that current fb can be updated into panel GRAM.
103062306a36Sopenharmony_ci	 */
103162306a36Sopenharmony_ci	if (atomic_add_unless(&ctx->win_updated, -1, 0))
103262306a36Sopenharmony_ci		fimd_trigger(ctx->dev);
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ciout:
103562306a36Sopenharmony_ci	/* Wakes up vsync event queue */
103662306a36Sopenharmony_ci	if (atomic_read(&ctx->wait_vsync_event)) {
103762306a36Sopenharmony_ci		atomic_set(&ctx->wait_vsync_event, 0);
103862306a36Sopenharmony_ci		wake_up(&ctx->wait_vsync_queue);
103962306a36Sopenharmony_ci	}
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	if (test_bit(0, &ctx->irq_flags))
104262306a36Sopenharmony_ci		drm_crtc_handle_vblank(&ctx->crtc->base);
104362306a36Sopenharmony_ci}
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_cistatic void fimd_dp_clock_enable(struct exynos_drm_clk *clk, bool enable)
104662306a36Sopenharmony_ci{
104762306a36Sopenharmony_ci	struct fimd_context *ctx = container_of(clk, struct fimd_context,
104862306a36Sopenharmony_ci						dp_clk);
104962306a36Sopenharmony_ci	u32 val = enable ? DP_MIE_CLK_DP_ENABLE : DP_MIE_CLK_DISABLE;
105062306a36Sopenharmony_ci	writel(val, ctx->regs + DP_MIE_CLKCON);
105162306a36Sopenharmony_ci}
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_cistatic const struct exynos_drm_crtc_ops fimd_crtc_ops = {
105462306a36Sopenharmony_ci	.atomic_enable = fimd_atomic_enable,
105562306a36Sopenharmony_ci	.atomic_disable = fimd_atomic_disable,
105662306a36Sopenharmony_ci	.enable_vblank = fimd_enable_vblank,
105762306a36Sopenharmony_ci	.disable_vblank = fimd_disable_vblank,
105862306a36Sopenharmony_ci	.atomic_begin = fimd_atomic_begin,
105962306a36Sopenharmony_ci	.update_plane = fimd_update_plane,
106062306a36Sopenharmony_ci	.disable_plane = fimd_disable_plane,
106162306a36Sopenharmony_ci	.atomic_flush = fimd_atomic_flush,
106262306a36Sopenharmony_ci	.atomic_check = fimd_atomic_check,
106362306a36Sopenharmony_ci	.te_handler = fimd_te_handler,
106462306a36Sopenharmony_ci};
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_cistatic irqreturn_t fimd_irq_handler(int irq, void *dev_id)
106762306a36Sopenharmony_ci{
106862306a36Sopenharmony_ci	struct fimd_context *ctx = (struct fimd_context *)dev_id;
106962306a36Sopenharmony_ci	u32 val, clear_bit;
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	val = readl(ctx->regs + VIDINTCON1);
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	clear_bit = ctx->i80_if ? VIDINTCON1_INT_I80 : VIDINTCON1_INT_FRAME;
107462306a36Sopenharmony_ci	if (val & clear_bit)
107562306a36Sopenharmony_ci		writel(clear_bit, ctx->regs + VIDINTCON1);
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	/* check the crtc is detached already from encoder */
107862306a36Sopenharmony_ci	if (!ctx->drm_dev)
107962306a36Sopenharmony_ci		goto out;
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	if (!ctx->i80_if)
108262306a36Sopenharmony_ci		drm_crtc_handle_vblank(&ctx->crtc->base);
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	if (ctx->i80_if) {
108562306a36Sopenharmony_ci		/* Exits triggering mode */
108662306a36Sopenharmony_ci		atomic_set(&ctx->triggering, 0);
108762306a36Sopenharmony_ci	} else {
108862306a36Sopenharmony_ci		/* set wait vsync event to zero and wake up queue. */
108962306a36Sopenharmony_ci		if (atomic_read(&ctx->wait_vsync_event)) {
109062306a36Sopenharmony_ci			atomic_set(&ctx->wait_vsync_event, 0);
109162306a36Sopenharmony_ci			wake_up(&ctx->wait_vsync_queue);
109262306a36Sopenharmony_ci		}
109362306a36Sopenharmony_ci	}
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ciout:
109662306a36Sopenharmony_ci	return IRQ_HANDLED;
109762306a36Sopenharmony_ci}
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_cistatic int fimd_bind(struct device *dev, struct device *master, void *data)
110062306a36Sopenharmony_ci{
110162306a36Sopenharmony_ci	struct fimd_context *ctx = dev_get_drvdata(dev);
110262306a36Sopenharmony_ci	struct drm_device *drm_dev = data;
110362306a36Sopenharmony_ci	struct exynos_drm_plane *exynos_plane;
110462306a36Sopenharmony_ci	unsigned int i;
110562306a36Sopenharmony_ci	int ret;
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	ctx->drm_dev = drm_dev;
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	for (i = 0; i < WINDOWS_NR; i++) {
111062306a36Sopenharmony_ci		if (ctx->driver_data->has_bgr_support) {
111162306a36Sopenharmony_ci			ctx->configs[i].pixel_formats = fimd_extended_formats;
111262306a36Sopenharmony_ci			ctx->configs[i].num_pixel_formats = ARRAY_SIZE(fimd_extended_formats);
111362306a36Sopenharmony_ci		} else {
111462306a36Sopenharmony_ci			ctx->configs[i].pixel_formats = fimd_formats;
111562306a36Sopenharmony_ci			ctx->configs[i].num_pixel_formats = ARRAY_SIZE(fimd_formats);
111662306a36Sopenharmony_ci		}
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci		ctx->configs[i].zpos = i;
111962306a36Sopenharmony_ci		ctx->configs[i].type = fimd_win_types[i];
112062306a36Sopenharmony_ci		ctx->configs[i].capabilities = capabilities[i];
112162306a36Sopenharmony_ci		ret = exynos_plane_init(drm_dev, &ctx->planes[i], i,
112262306a36Sopenharmony_ci					&ctx->configs[i]);
112362306a36Sopenharmony_ci		if (ret)
112462306a36Sopenharmony_ci			return ret;
112562306a36Sopenharmony_ci	}
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	exynos_plane = &ctx->planes[DEFAULT_WIN];
112862306a36Sopenharmony_ci	ctx->crtc = exynos_drm_crtc_create(drm_dev, &exynos_plane->base,
112962306a36Sopenharmony_ci			EXYNOS_DISPLAY_TYPE_LCD, &fimd_crtc_ops, ctx);
113062306a36Sopenharmony_ci	if (IS_ERR(ctx->crtc))
113162306a36Sopenharmony_ci		return PTR_ERR(ctx->crtc);
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	if (ctx->driver_data->has_dp_clk) {
113462306a36Sopenharmony_ci		ctx->dp_clk.enable = fimd_dp_clock_enable;
113562306a36Sopenharmony_ci		ctx->crtc->pipe_clk = &ctx->dp_clk;
113662306a36Sopenharmony_ci	}
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	if (ctx->encoder)
113962306a36Sopenharmony_ci		exynos_dpi_bind(drm_dev, ctx->encoder);
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	if (is_drm_iommu_supported(drm_dev)) {
114262306a36Sopenharmony_ci		int ret;
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci		ret = fimd_clear_channels(ctx->crtc);
114562306a36Sopenharmony_ci		if (ret < 0)
114662306a36Sopenharmony_ci			return ret;
114762306a36Sopenharmony_ci	}
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci	return exynos_drm_register_dma(drm_dev, dev, &ctx->dma_priv);
115062306a36Sopenharmony_ci}
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_cistatic void fimd_unbind(struct device *dev, struct device *master,
115362306a36Sopenharmony_ci			void *data)
115462306a36Sopenharmony_ci{
115562306a36Sopenharmony_ci	struct fimd_context *ctx = dev_get_drvdata(dev);
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	fimd_atomic_disable(ctx->crtc);
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	exynos_drm_unregister_dma(ctx->drm_dev, ctx->dev, &ctx->dma_priv);
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	if (ctx->encoder)
116262306a36Sopenharmony_ci		exynos_dpi_remove(ctx->encoder);
116362306a36Sopenharmony_ci}
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_cistatic const struct component_ops fimd_component_ops = {
116662306a36Sopenharmony_ci	.bind	= fimd_bind,
116762306a36Sopenharmony_ci	.unbind = fimd_unbind,
116862306a36Sopenharmony_ci};
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_cistatic int fimd_probe(struct platform_device *pdev)
117162306a36Sopenharmony_ci{
117262306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
117362306a36Sopenharmony_ci	struct fimd_context *ctx;
117462306a36Sopenharmony_ci	struct device_node *i80_if_timings;
117562306a36Sopenharmony_ci	int ret;
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	if (!dev->of_node)
117862306a36Sopenharmony_ci		return -ENODEV;
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
118162306a36Sopenharmony_ci	if (!ctx)
118262306a36Sopenharmony_ci		return -ENOMEM;
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	ctx->dev = dev;
118562306a36Sopenharmony_ci	ctx->suspended = true;
118662306a36Sopenharmony_ci	ctx->driver_data = of_device_get_match_data(dev);
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	if (of_property_read_bool(dev->of_node, "samsung,invert-vden"))
118962306a36Sopenharmony_ci		ctx->vidcon1 |= VIDCON1_INV_VDEN;
119062306a36Sopenharmony_ci	if (of_property_read_bool(dev->of_node, "samsung,invert-vclk"))
119162306a36Sopenharmony_ci		ctx->vidcon1 |= VIDCON1_INV_VCLK;
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	i80_if_timings = of_get_child_by_name(dev->of_node, "i80-if-timings");
119462306a36Sopenharmony_ci	if (i80_if_timings) {
119562306a36Sopenharmony_ci		u32 val;
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci		ctx->i80_if = true;
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci		if (ctx->driver_data->has_vidoutcon)
120062306a36Sopenharmony_ci			ctx->vidout_con |= VIDOUT_CON_F_I80_LDI0;
120162306a36Sopenharmony_ci		else
120262306a36Sopenharmony_ci			ctx->vidcon0 |= VIDCON0_VIDOUT_I80_LDI0;
120362306a36Sopenharmony_ci		/*
120462306a36Sopenharmony_ci		 * The user manual describes that this "DSI_EN" bit is required
120562306a36Sopenharmony_ci		 * to enable I80 24-bit data interface.
120662306a36Sopenharmony_ci		 */
120762306a36Sopenharmony_ci		ctx->vidcon0 |= VIDCON0_DSI_EN;
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci		if (of_property_read_u32(i80_if_timings, "cs-setup", &val))
121062306a36Sopenharmony_ci			val = 0;
121162306a36Sopenharmony_ci		ctx->i80ifcon = LCD_CS_SETUP(val);
121262306a36Sopenharmony_ci		if (of_property_read_u32(i80_if_timings, "wr-setup", &val))
121362306a36Sopenharmony_ci			val = 0;
121462306a36Sopenharmony_ci		ctx->i80ifcon |= LCD_WR_SETUP(val);
121562306a36Sopenharmony_ci		if (of_property_read_u32(i80_if_timings, "wr-active", &val))
121662306a36Sopenharmony_ci			val = 1;
121762306a36Sopenharmony_ci		ctx->i80ifcon |= LCD_WR_ACTIVE(val);
121862306a36Sopenharmony_ci		if (of_property_read_u32(i80_if_timings, "wr-hold", &val))
121962306a36Sopenharmony_ci			val = 0;
122062306a36Sopenharmony_ci		ctx->i80ifcon |= LCD_WR_HOLD(val);
122162306a36Sopenharmony_ci	}
122262306a36Sopenharmony_ci	of_node_put(i80_if_timings);
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	ctx->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
122562306a36Sopenharmony_ci							"samsung,sysreg");
122662306a36Sopenharmony_ci	if (IS_ERR(ctx->sysreg)) {
122762306a36Sopenharmony_ci		dev_warn(dev, "failed to get system register.\n");
122862306a36Sopenharmony_ci		ctx->sysreg = NULL;
122962306a36Sopenharmony_ci	}
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci	ctx->bus_clk = devm_clk_get(dev, "fimd");
123262306a36Sopenharmony_ci	if (IS_ERR(ctx->bus_clk)) {
123362306a36Sopenharmony_ci		dev_err(dev, "failed to get bus clock\n");
123462306a36Sopenharmony_ci		return PTR_ERR(ctx->bus_clk);
123562306a36Sopenharmony_ci	}
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	ctx->lcd_clk = devm_clk_get(dev, "sclk_fimd");
123862306a36Sopenharmony_ci	if (IS_ERR(ctx->lcd_clk)) {
123962306a36Sopenharmony_ci		dev_err(dev, "failed to get lcd clock\n");
124062306a36Sopenharmony_ci		return PTR_ERR(ctx->lcd_clk);
124162306a36Sopenharmony_ci	}
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	ctx->regs = devm_platform_ioremap_resource(pdev, 0);
124462306a36Sopenharmony_ci	if (IS_ERR(ctx->regs))
124562306a36Sopenharmony_ci		return PTR_ERR(ctx->regs);
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	ret = platform_get_irq_byname(pdev, ctx->i80_if ? "lcd_sys" : "vsync");
124862306a36Sopenharmony_ci	if (ret < 0)
124962306a36Sopenharmony_ci		return ret;
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci	ret = devm_request_irq(dev, ret, fimd_irq_handler, 0, "drm_fimd", ctx);
125262306a36Sopenharmony_ci	if (ret) {
125362306a36Sopenharmony_ci		dev_err(dev, "irq request failed.\n");
125462306a36Sopenharmony_ci		return ret;
125562306a36Sopenharmony_ci	}
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	init_waitqueue_head(&ctx->wait_vsync_queue);
125862306a36Sopenharmony_ci	atomic_set(&ctx->wait_vsync_event, 0);
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	platform_set_drvdata(pdev, ctx);
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	ctx->encoder = exynos_dpi_probe(dev);
126362306a36Sopenharmony_ci	if (IS_ERR(ctx->encoder))
126462306a36Sopenharmony_ci		return PTR_ERR(ctx->encoder);
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci	pm_runtime_enable(dev);
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	ret = component_add(dev, &fimd_component_ops);
126962306a36Sopenharmony_ci	if (ret)
127062306a36Sopenharmony_ci		goto err_disable_pm_runtime;
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	return ret;
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_cierr_disable_pm_runtime:
127562306a36Sopenharmony_ci	pm_runtime_disable(dev);
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci	return ret;
127862306a36Sopenharmony_ci}
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_cistatic int fimd_remove(struct platform_device *pdev)
128162306a36Sopenharmony_ci{
128262306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	component_del(&pdev->dev, &fimd_component_ops);
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	return 0;
128762306a36Sopenharmony_ci}
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_cistatic int exynos_fimd_suspend(struct device *dev)
129062306a36Sopenharmony_ci{
129162306a36Sopenharmony_ci	struct fimd_context *ctx = dev_get_drvdata(dev);
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	clk_disable_unprepare(ctx->lcd_clk);
129462306a36Sopenharmony_ci	clk_disable_unprepare(ctx->bus_clk);
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	return 0;
129762306a36Sopenharmony_ci}
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_cistatic int exynos_fimd_resume(struct device *dev)
130062306a36Sopenharmony_ci{
130162306a36Sopenharmony_ci	struct fimd_context *ctx = dev_get_drvdata(dev);
130262306a36Sopenharmony_ci	int ret;
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci	ret = clk_prepare_enable(ctx->bus_clk);
130562306a36Sopenharmony_ci	if (ret < 0) {
130662306a36Sopenharmony_ci		DRM_DEV_ERROR(dev,
130762306a36Sopenharmony_ci			      "Failed to prepare_enable the bus clk [%d]\n",
130862306a36Sopenharmony_ci			      ret);
130962306a36Sopenharmony_ci		return ret;
131062306a36Sopenharmony_ci	}
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci	ret = clk_prepare_enable(ctx->lcd_clk);
131362306a36Sopenharmony_ci	if  (ret < 0) {
131462306a36Sopenharmony_ci		DRM_DEV_ERROR(dev,
131562306a36Sopenharmony_ci			      "Failed to prepare_enable the lcd clk [%d]\n",
131662306a36Sopenharmony_ci			      ret);
131762306a36Sopenharmony_ci		return ret;
131862306a36Sopenharmony_ci	}
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	return 0;
132162306a36Sopenharmony_ci}
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_cistatic DEFINE_RUNTIME_DEV_PM_OPS(exynos_fimd_pm_ops, exynos_fimd_suspend,
132462306a36Sopenharmony_ci				 exynos_fimd_resume, NULL);
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_cistruct platform_driver fimd_driver = {
132762306a36Sopenharmony_ci	.probe		= fimd_probe,
132862306a36Sopenharmony_ci	.remove		= fimd_remove,
132962306a36Sopenharmony_ci	.driver		= {
133062306a36Sopenharmony_ci		.name	= "exynos4-fb",
133162306a36Sopenharmony_ci		.owner	= THIS_MODULE,
133262306a36Sopenharmony_ci		.pm	= pm_ptr(&exynos_fimd_pm_ops),
133362306a36Sopenharmony_ci		.of_match_table = fimd_driver_dt_match,
133462306a36Sopenharmony_ci	},
133562306a36Sopenharmony_ci};
1336