162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2020 Unisoc Inc.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/component.h>
762306a36Sopenharmony_ci#include <linux/delay.h>
862306a36Sopenharmony_ci#include <linux/dma-buf.h>
962306a36Sopenharmony_ci#include <linux/io.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/of.h>
1262306a36Sopenharmony_ci#include <linux/of_graph.h>
1362306a36Sopenharmony_ci#include <linux/platform_device.h>
1462306a36Sopenharmony_ci#include <linux/wait.h>
1562306a36Sopenharmony_ci#include <linux/workqueue.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h>
1862306a36Sopenharmony_ci#include <drm/drm_blend.h>
1962306a36Sopenharmony_ci#include <drm/drm_fb_dma_helper.h>
2062306a36Sopenharmony_ci#include <drm/drm_framebuffer.h>
2162306a36Sopenharmony_ci#include <drm/drm_gem_dma_helper.h>
2262306a36Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include "sprd_drm.h"
2562306a36Sopenharmony_ci#include "sprd_dpu.h"
2662306a36Sopenharmony_ci#include "sprd_dsi.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/* Global control registers */
2962306a36Sopenharmony_ci#define REG_DPU_CTRL	0x04
3062306a36Sopenharmony_ci#define REG_DPU_CFG0	0x08
3162306a36Sopenharmony_ci#define REG_PANEL_SIZE	0x20
3262306a36Sopenharmony_ci#define REG_BLEND_SIZE	0x24
3362306a36Sopenharmony_ci#define REG_BG_COLOR	0x2C
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci/* Layer0 control registers */
3662306a36Sopenharmony_ci#define REG_LAY_BASE_ADDR0	0x30
3762306a36Sopenharmony_ci#define REG_LAY_BASE_ADDR1	0x34
3862306a36Sopenharmony_ci#define REG_LAY_BASE_ADDR2	0x38
3962306a36Sopenharmony_ci#define REG_LAY_CTRL		0x40
4062306a36Sopenharmony_ci#define REG_LAY_SIZE		0x44
4162306a36Sopenharmony_ci#define REG_LAY_PITCH		0x48
4262306a36Sopenharmony_ci#define REG_LAY_POS		0x4C
4362306a36Sopenharmony_ci#define REG_LAY_ALPHA		0x50
4462306a36Sopenharmony_ci#define REG_LAY_CROP_START	0x5C
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci/* Interrupt control registers */
4762306a36Sopenharmony_ci#define REG_DPU_INT_EN		0x1E0
4862306a36Sopenharmony_ci#define REG_DPU_INT_CLR		0x1E4
4962306a36Sopenharmony_ci#define REG_DPU_INT_STS		0x1E8
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/* DPI control registers */
5262306a36Sopenharmony_ci#define REG_DPI_CTRL		0x1F0
5362306a36Sopenharmony_ci#define REG_DPI_H_TIMING	0x1F4
5462306a36Sopenharmony_ci#define REG_DPI_V_TIMING	0x1F8
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci/* MMU control registers */
5762306a36Sopenharmony_ci#define REG_MMU_EN			0x800
5862306a36Sopenharmony_ci#define REG_MMU_VPN_RANGE		0x80C
5962306a36Sopenharmony_ci#define REG_MMU_PPN1			0x83C
6062306a36Sopenharmony_ci#define REG_MMU_RANGE1			0x840
6162306a36Sopenharmony_ci#define REG_MMU_PPN2			0x844
6262306a36Sopenharmony_ci#define REG_MMU_RANGE2			0x848
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci/* Global control bits */
6562306a36Sopenharmony_ci#define BIT_DPU_RUN			BIT(0)
6662306a36Sopenharmony_ci#define BIT_DPU_STOP			BIT(1)
6762306a36Sopenharmony_ci#define BIT_DPU_REG_UPDATE		BIT(2)
6862306a36Sopenharmony_ci#define BIT_DPU_IF_EDPI			BIT(0)
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci/* Layer control bits */
7162306a36Sopenharmony_ci#define BIT_DPU_LAY_EN				BIT(0)
7262306a36Sopenharmony_ci#define BIT_DPU_LAY_LAYER_ALPHA			(0x01 << 2)
7362306a36Sopenharmony_ci#define BIT_DPU_LAY_COMBO_ALPHA			(0x02 << 2)
7462306a36Sopenharmony_ci#define BIT_DPU_LAY_FORMAT_YUV422_2PLANE		(0x00 << 4)
7562306a36Sopenharmony_ci#define BIT_DPU_LAY_FORMAT_YUV420_2PLANE		(0x01 << 4)
7662306a36Sopenharmony_ci#define BIT_DPU_LAY_FORMAT_YUV420_3PLANE		(0x02 << 4)
7762306a36Sopenharmony_ci#define BIT_DPU_LAY_FORMAT_ARGB8888			(0x03 << 4)
7862306a36Sopenharmony_ci#define BIT_DPU_LAY_FORMAT_RGB565			(0x04 << 4)
7962306a36Sopenharmony_ci#define BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3		(0x00 << 8)
8062306a36Sopenharmony_ci#define BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0		(0x01 << 8)
8162306a36Sopenharmony_ci#define BIT_DPU_LAY_NO_SWITCH			(0x00 << 10)
8262306a36Sopenharmony_ci#define BIT_DPU_LAY_RB_OR_UV_SWITCH		(0x01 << 10)
8362306a36Sopenharmony_ci#define BIT_DPU_LAY_MODE_BLEND_NORMAL		(0x00 << 16)
8462306a36Sopenharmony_ci#define BIT_DPU_LAY_MODE_BLEND_PREMULT		(0x01 << 16)
8562306a36Sopenharmony_ci#define BIT_DPU_LAY_ROTATION_0		(0x00 << 20)
8662306a36Sopenharmony_ci#define BIT_DPU_LAY_ROTATION_90		(0x01 << 20)
8762306a36Sopenharmony_ci#define BIT_DPU_LAY_ROTATION_180	(0x02 << 20)
8862306a36Sopenharmony_ci#define BIT_DPU_LAY_ROTATION_270	(0x03 << 20)
8962306a36Sopenharmony_ci#define BIT_DPU_LAY_ROTATION_0_M	(0x04 << 20)
9062306a36Sopenharmony_ci#define BIT_DPU_LAY_ROTATION_90_M	(0x05 << 20)
9162306a36Sopenharmony_ci#define BIT_DPU_LAY_ROTATION_180_M	(0x06 << 20)
9262306a36Sopenharmony_ci#define BIT_DPU_LAY_ROTATION_270_M	(0x07 << 20)
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci/* Interrupt control & status bits */
9562306a36Sopenharmony_ci#define BIT_DPU_INT_DONE		BIT(0)
9662306a36Sopenharmony_ci#define BIT_DPU_INT_TE			BIT(1)
9762306a36Sopenharmony_ci#define BIT_DPU_INT_ERR			BIT(2)
9862306a36Sopenharmony_ci#define BIT_DPU_INT_UPDATE_DONE		BIT(4)
9962306a36Sopenharmony_ci#define BIT_DPU_INT_VSYNC		BIT(5)
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci/* DPI control bits */
10262306a36Sopenharmony_ci#define BIT_DPU_EDPI_TE_EN		BIT(8)
10362306a36Sopenharmony_ci#define BIT_DPU_EDPI_FROM_EXTERNAL_PAD	BIT(10)
10462306a36Sopenharmony_ci#define BIT_DPU_DPI_HALT_EN		BIT(16)
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic const u32 layer_fmts[] = {
10762306a36Sopenharmony_ci	DRM_FORMAT_XRGB8888,
10862306a36Sopenharmony_ci	DRM_FORMAT_XBGR8888,
10962306a36Sopenharmony_ci	DRM_FORMAT_ARGB8888,
11062306a36Sopenharmony_ci	DRM_FORMAT_ABGR8888,
11162306a36Sopenharmony_ci	DRM_FORMAT_RGBA8888,
11262306a36Sopenharmony_ci	DRM_FORMAT_BGRA8888,
11362306a36Sopenharmony_ci	DRM_FORMAT_RGBX8888,
11462306a36Sopenharmony_ci	DRM_FORMAT_RGB565,
11562306a36Sopenharmony_ci	DRM_FORMAT_BGR565,
11662306a36Sopenharmony_ci	DRM_FORMAT_NV12,
11762306a36Sopenharmony_ci	DRM_FORMAT_NV21,
11862306a36Sopenharmony_ci	DRM_FORMAT_NV16,
11962306a36Sopenharmony_ci	DRM_FORMAT_NV61,
12062306a36Sopenharmony_ci	DRM_FORMAT_YUV420,
12162306a36Sopenharmony_ci	DRM_FORMAT_YVU420,
12262306a36Sopenharmony_ci};
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistruct sprd_plane {
12562306a36Sopenharmony_ci	struct drm_plane base;
12662306a36Sopenharmony_ci};
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic int dpu_wait_stop_done(struct sprd_dpu *dpu)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	struct dpu_context *ctx = &dpu->ctx;
13162306a36Sopenharmony_ci	int rc;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	if (ctx->stopped)
13462306a36Sopenharmony_ci		return 0;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	rc = wait_event_interruptible_timeout(ctx->wait_queue, ctx->evt_stop,
13762306a36Sopenharmony_ci					      msecs_to_jiffies(500));
13862306a36Sopenharmony_ci	ctx->evt_stop = false;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	ctx->stopped = true;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	if (!rc) {
14362306a36Sopenharmony_ci		drm_err(dpu->drm, "dpu wait for stop done time out!\n");
14462306a36Sopenharmony_ci		return -ETIMEDOUT;
14562306a36Sopenharmony_ci	}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	return 0;
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic int dpu_wait_update_done(struct sprd_dpu *dpu)
15162306a36Sopenharmony_ci{
15262306a36Sopenharmony_ci	struct dpu_context *ctx = &dpu->ctx;
15362306a36Sopenharmony_ci	int rc;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	ctx->evt_update = false;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	rc = wait_event_interruptible_timeout(ctx->wait_queue, ctx->evt_update,
15862306a36Sopenharmony_ci					      msecs_to_jiffies(500));
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	if (!rc) {
16162306a36Sopenharmony_ci		drm_err(dpu->drm, "dpu wait for reg update done time out!\n");
16262306a36Sopenharmony_ci		return -ETIMEDOUT;
16362306a36Sopenharmony_ci	}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	return 0;
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic u32 drm_format_to_dpu(struct drm_framebuffer *fb)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	u32 format = 0;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	switch (fb->format->format) {
17362306a36Sopenharmony_ci	case DRM_FORMAT_BGRA8888:
17462306a36Sopenharmony_ci		/* BGRA8888 -> ARGB8888 */
17562306a36Sopenharmony_ci		format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
17662306a36Sopenharmony_ci		format |= BIT_DPU_LAY_FORMAT_ARGB8888;
17762306a36Sopenharmony_ci		break;
17862306a36Sopenharmony_ci	case DRM_FORMAT_RGBX8888:
17962306a36Sopenharmony_ci	case DRM_FORMAT_RGBA8888:
18062306a36Sopenharmony_ci		/* RGBA8888 -> ABGR8888 */
18162306a36Sopenharmony_ci		format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
18262306a36Sopenharmony_ci		fallthrough;
18362306a36Sopenharmony_ci	case DRM_FORMAT_ABGR8888:
18462306a36Sopenharmony_ci		/* RB switch */
18562306a36Sopenharmony_ci		format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
18662306a36Sopenharmony_ci		fallthrough;
18762306a36Sopenharmony_ci	case DRM_FORMAT_ARGB8888:
18862306a36Sopenharmony_ci		format |= BIT_DPU_LAY_FORMAT_ARGB8888;
18962306a36Sopenharmony_ci		break;
19062306a36Sopenharmony_ci	case DRM_FORMAT_XBGR8888:
19162306a36Sopenharmony_ci		/* RB switch */
19262306a36Sopenharmony_ci		format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
19362306a36Sopenharmony_ci		fallthrough;
19462306a36Sopenharmony_ci	case DRM_FORMAT_XRGB8888:
19562306a36Sopenharmony_ci		format |= BIT_DPU_LAY_FORMAT_ARGB8888;
19662306a36Sopenharmony_ci		break;
19762306a36Sopenharmony_ci	case DRM_FORMAT_BGR565:
19862306a36Sopenharmony_ci		/* RB switch */
19962306a36Sopenharmony_ci		format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
20062306a36Sopenharmony_ci		fallthrough;
20162306a36Sopenharmony_ci	case DRM_FORMAT_RGB565:
20262306a36Sopenharmony_ci		format |= BIT_DPU_LAY_FORMAT_RGB565;
20362306a36Sopenharmony_ci		break;
20462306a36Sopenharmony_ci	case DRM_FORMAT_NV12:
20562306a36Sopenharmony_ci		/* 2-Lane: Yuv420 */
20662306a36Sopenharmony_ci		format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE;
20762306a36Sopenharmony_ci		/* Y endian */
20862306a36Sopenharmony_ci		format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
20962306a36Sopenharmony_ci		/* UV endian */
21062306a36Sopenharmony_ci		format |= BIT_DPU_LAY_NO_SWITCH;
21162306a36Sopenharmony_ci		break;
21262306a36Sopenharmony_ci	case DRM_FORMAT_NV21:
21362306a36Sopenharmony_ci		/* 2-Lane: Yuv420 */
21462306a36Sopenharmony_ci		format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE;
21562306a36Sopenharmony_ci		/* Y endian */
21662306a36Sopenharmony_ci		format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
21762306a36Sopenharmony_ci		/* UV endian */
21862306a36Sopenharmony_ci		format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
21962306a36Sopenharmony_ci		break;
22062306a36Sopenharmony_ci	case DRM_FORMAT_NV16:
22162306a36Sopenharmony_ci		/* 2-Lane: Yuv422 */
22262306a36Sopenharmony_ci		format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE;
22362306a36Sopenharmony_ci		/* Y endian */
22462306a36Sopenharmony_ci		format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
22562306a36Sopenharmony_ci		/* UV endian */
22662306a36Sopenharmony_ci		format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
22762306a36Sopenharmony_ci		break;
22862306a36Sopenharmony_ci	case DRM_FORMAT_NV61:
22962306a36Sopenharmony_ci		/* 2-Lane: Yuv422 */
23062306a36Sopenharmony_ci		format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE;
23162306a36Sopenharmony_ci		/* Y endian */
23262306a36Sopenharmony_ci		format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
23362306a36Sopenharmony_ci		/* UV endian */
23462306a36Sopenharmony_ci		format |= BIT_DPU_LAY_NO_SWITCH;
23562306a36Sopenharmony_ci		break;
23662306a36Sopenharmony_ci	case DRM_FORMAT_YUV420:
23762306a36Sopenharmony_ci		format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE;
23862306a36Sopenharmony_ci		/* Y endian */
23962306a36Sopenharmony_ci		format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
24062306a36Sopenharmony_ci		/* UV endian */
24162306a36Sopenharmony_ci		format |= BIT_DPU_LAY_NO_SWITCH;
24262306a36Sopenharmony_ci		break;
24362306a36Sopenharmony_ci	case DRM_FORMAT_YVU420:
24462306a36Sopenharmony_ci		format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE;
24562306a36Sopenharmony_ci		/* Y endian */
24662306a36Sopenharmony_ci		format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
24762306a36Sopenharmony_ci		/* UV endian */
24862306a36Sopenharmony_ci		format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
24962306a36Sopenharmony_ci		break;
25062306a36Sopenharmony_ci	default:
25162306a36Sopenharmony_ci		break;
25262306a36Sopenharmony_ci	}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	return format;
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cistatic u32 drm_rotation_to_dpu(struct drm_plane_state *state)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	u32 rotation = 0;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	switch (state->rotation) {
26262306a36Sopenharmony_ci	default:
26362306a36Sopenharmony_ci	case DRM_MODE_ROTATE_0:
26462306a36Sopenharmony_ci		rotation = BIT_DPU_LAY_ROTATION_0;
26562306a36Sopenharmony_ci		break;
26662306a36Sopenharmony_ci	case DRM_MODE_ROTATE_90:
26762306a36Sopenharmony_ci		rotation = BIT_DPU_LAY_ROTATION_90;
26862306a36Sopenharmony_ci		break;
26962306a36Sopenharmony_ci	case DRM_MODE_ROTATE_180:
27062306a36Sopenharmony_ci		rotation = BIT_DPU_LAY_ROTATION_180;
27162306a36Sopenharmony_ci		break;
27262306a36Sopenharmony_ci	case DRM_MODE_ROTATE_270:
27362306a36Sopenharmony_ci		rotation = BIT_DPU_LAY_ROTATION_270;
27462306a36Sopenharmony_ci		break;
27562306a36Sopenharmony_ci	case DRM_MODE_REFLECT_Y:
27662306a36Sopenharmony_ci		rotation = BIT_DPU_LAY_ROTATION_180_M;
27762306a36Sopenharmony_ci		break;
27862306a36Sopenharmony_ci	case (DRM_MODE_REFLECT_Y | DRM_MODE_ROTATE_90):
27962306a36Sopenharmony_ci		rotation = BIT_DPU_LAY_ROTATION_90_M;
28062306a36Sopenharmony_ci		break;
28162306a36Sopenharmony_ci	case DRM_MODE_REFLECT_X:
28262306a36Sopenharmony_ci		rotation = BIT_DPU_LAY_ROTATION_0_M;
28362306a36Sopenharmony_ci		break;
28462306a36Sopenharmony_ci	case (DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90):
28562306a36Sopenharmony_ci		rotation = BIT_DPU_LAY_ROTATION_270_M;
28662306a36Sopenharmony_ci		break;
28762306a36Sopenharmony_ci	}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	return rotation;
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_cistatic u32 drm_blend_to_dpu(struct drm_plane_state *state)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	u32 blend = 0;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	switch (state->pixel_blend_mode) {
29762306a36Sopenharmony_ci	case DRM_MODE_BLEND_COVERAGE:
29862306a36Sopenharmony_ci		/* alpha mode select - combo alpha */
29962306a36Sopenharmony_ci		blend |= BIT_DPU_LAY_COMBO_ALPHA;
30062306a36Sopenharmony_ci		/* Normal mode */
30162306a36Sopenharmony_ci		blend |= BIT_DPU_LAY_MODE_BLEND_NORMAL;
30262306a36Sopenharmony_ci		break;
30362306a36Sopenharmony_ci	case DRM_MODE_BLEND_PREMULTI:
30462306a36Sopenharmony_ci		/* alpha mode select - combo alpha */
30562306a36Sopenharmony_ci		blend |= BIT_DPU_LAY_COMBO_ALPHA;
30662306a36Sopenharmony_ci		/* Pre-mult mode */
30762306a36Sopenharmony_ci		blend |= BIT_DPU_LAY_MODE_BLEND_PREMULT;
30862306a36Sopenharmony_ci		break;
30962306a36Sopenharmony_ci	case DRM_MODE_BLEND_PIXEL_NONE:
31062306a36Sopenharmony_ci	default:
31162306a36Sopenharmony_ci		/* don't do blending, maybe RGBX */
31262306a36Sopenharmony_ci		/* alpha mode select - layer alpha */
31362306a36Sopenharmony_ci		blend |= BIT_DPU_LAY_LAYER_ALPHA;
31462306a36Sopenharmony_ci		break;
31562306a36Sopenharmony_ci	}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	return blend;
31862306a36Sopenharmony_ci}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cistatic void sprd_dpu_layer(struct sprd_dpu *dpu, struct drm_plane_state *state)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	struct dpu_context *ctx = &dpu->ctx;
32362306a36Sopenharmony_ci	struct drm_gem_dma_object *dma_obj;
32462306a36Sopenharmony_ci	struct drm_framebuffer *fb = state->fb;
32562306a36Sopenharmony_ci	u32 addr, size, offset, pitch, blend, format, rotation;
32662306a36Sopenharmony_ci	u32 src_x = state->src_x >> 16;
32762306a36Sopenharmony_ci	u32 src_y = state->src_y >> 16;
32862306a36Sopenharmony_ci	u32 src_w = state->src_w >> 16;
32962306a36Sopenharmony_ci	u32 src_h = state->src_h >> 16;
33062306a36Sopenharmony_ci	u32 dst_x = state->crtc_x;
33162306a36Sopenharmony_ci	u32 dst_y = state->crtc_y;
33262306a36Sopenharmony_ci	u32 alpha = state->alpha;
33362306a36Sopenharmony_ci	u32 index = state->zpos;
33462306a36Sopenharmony_ci	int i;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	offset = (dst_x & 0xffff) | (dst_y << 16);
33762306a36Sopenharmony_ci	size = (src_w & 0xffff) | (src_h << 16);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	for (i = 0; i < fb->format->num_planes; i++) {
34062306a36Sopenharmony_ci		dma_obj = drm_fb_dma_get_gem_obj(fb, i);
34162306a36Sopenharmony_ci		addr = dma_obj->dma_addr + fb->offsets[i];
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci		if (i == 0)
34462306a36Sopenharmony_ci			layer_reg_wr(ctx, REG_LAY_BASE_ADDR0, addr, index);
34562306a36Sopenharmony_ci		else if (i == 1)
34662306a36Sopenharmony_ci			layer_reg_wr(ctx, REG_LAY_BASE_ADDR1, addr, index);
34762306a36Sopenharmony_ci		else
34862306a36Sopenharmony_ci			layer_reg_wr(ctx, REG_LAY_BASE_ADDR2, addr, index);
34962306a36Sopenharmony_ci	}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	if (fb->format->num_planes == 3) {
35262306a36Sopenharmony_ci		/* UV pitch is 1/2 of Y pitch */
35362306a36Sopenharmony_ci		pitch = (fb->pitches[0] / fb->format->cpp[0]) |
35462306a36Sopenharmony_ci				(fb->pitches[0] / fb->format->cpp[0] << 15);
35562306a36Sopenharmony_ci	} else {
35662306a36Sopenharmony_ci		pitch = fb->pitches[0] / fb->format->cpp[0];
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	layer_reg_wr(ctx, REG_LAY_POS, offset, index);
36062306a36Sopenharmony_ci	layer_reg_wr(ctx, REG_LAY_SIZE, size, index);
36162306a36Sopenharmony_ci	layer_reg_wr(ctx, REG_LAY_CROP_START,
36262306a36Sopenharmony_ci		     src_y << 16 | src_x, index);
36362306a36Sopenharmony_ci	layer_reg_wr(ctx, REG_LAY_ALPHA, alpha, index);
36462306a36Sopenharmony_ci	layer_reg_wr(ctx, REG_LAY_PITCH, pitch, index);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	format = drm_format_to_dpu(fb);
36762306a36Sopenharmony_ci	blend = drm_blend_to_dpu(state);
36862306a36Sopenharmony_ci	rotation = drm_rotation_to_dpu(state);
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	layer_reg_wr(ctx, REG_LAY_CTRL, BIT_DPU_LAY_EN |
37162306a36Sopenharmony_ci				format |
37262306a36Sopenharmony_ci				blend |
37362306a36Sopenharmony_ci				rotation,
37462306a36Sopenharmony_ci				index);
37562306a36Sopenharmony_ci}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_cistatic void sprd_dpu_flip(struct sprd_dpu *dpu)
37862306a36Sopenharmony_ci{
37962306a36Sopenharmony_ci	struct dpu_context *ctx = &dpu->ctx;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	/*
38262306a36Sopenharmony_ci	 * Make sure the dpu is in stop status. DPU has no shadow
38362306a36Sopenharmony_ci	 * registers in EDPI mode. So the config registers can only be
38462306a36Sopenharmony_ci	 * updated in the rising edge of DPU_RUN bit.
38562306a36Sopenharmony_ci	 */
38662306a36Sopenharmony_ci	if (ctx->if_type == SPRD_DPU_IF_EDPI)
38762306a36Sopenharmony_ci		dpu_wait_stop_done(dpu);
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	/* update trigger and wait */
39062306a36Sopenharmony_ci	if (ctx->if_type == SPRD_DPU_IF_DPI) {
39162306a36Sopenharmony_ci		if (!ctx->stopped) {
39262306a36Sopenharmony_ci			dpu_reg_set(ctx, REG_DPU_CTRL, BIT_DPU_REG_UPDATE);
39362306a36Sopenharmony_ci			dpu_wait_update_done(dpu);
39462306a36Sopenharmony_ci		}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci		dpu_reg_set(ctx, REG_DPU_INT_EN, BIT_DPU_INT_ERR);
39762306a36Sopenharmony_ci	} else if (ctx->if_type == SPRD_DPU_IF_EDPI) {
39862306a36Sopenharmony_ci		dpu_reg_set(ctx, REG_DPU_CTRL, BIT_DPU_RUN);
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci		ctx->stopped = false;
40162306a36Sopenharmony_ci	}
40262306a36Sopenharmony_ci}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_cistatic void sprd_dpu_init(struct sprd_dpu *dpu)
40562306a36Sopenharmony_ci{
40662306a36Sopenharmony_ci	struct dpu_context *ctx = &dpu->ctx;
40762306a36Sopenharmony_ci	u32 int_mask = 0;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	writel(0x00, ctx->base + REG_BG_COLOR);
41062306a36Sopenharmony_ci	writel(0x00, ctx->base + REG_MMU_EN);
41162306a36Sopenharmony_ci	writel(0x00, ctx->base + REG_MMU_PPN1);
41262306a36Sopenharmony_ci	writel(0xffff, ctx->base + REG_MMU_RANGE1);
41362306a36Sopenharmony_ci	writel(0x00, ctx->base + REG_MMU_PPN2);
41462306a36Sopenharmony_ci	writel(0xffff, ctx->base + REG_MMU_RANGE2);
41562306a36Sopenharmony_ci	writel(0x1ffff, ctx->base + REG_MMU_VPN_RANGE);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	if (ctx->if_type == SPRD_DPU_IF_DPI) {
41862306a36Sopenharmony_ci		/* use dpi as interface */
41962306a36Sopenharmony_ci		dpu_reg_clr(ctx, REG_DPU_CFG0, BIT_DPU_IF_EDPI);
42062306a36Sopenharmony_ci		/* disable Halt function for SPRD DSI */
42162306a36Sopenharmony_ci		dpu_reg_clr(ctx, REG_DPI_CTRL, BIT_DPU_DPI_HALT_EN);
42262306a36Sopenharmony_ci		/* select te from external pad */
42362306a36Sopenharmony_ci		dpu_reg_set(ctx, REG_DPI_CTRL, BIT_DPU_EDPI_FROM_EXTERNAL_PAD);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci		/* enable dpu update done INT */
42662306a36Sopenharmony_ci		int_mask |= BIT_DPU_INT_UPDATE_DONE;
42762306a36Sopenharmony_ci		/* enable dpu done INT */
42862306a36Sopenharmony_ci		int_mask |= BIT_DPU_INT_DONE;
42962306a36Sopenharmony_ci		/* enable dpu dpi vsync */
43062306a36Sopenharmony_ci		int_mask |= BIT_DPU_INT_VSYNC;
43162306a36Sopenharmony_ci		/* enable dpu TE INT */
43262306a36Sopenharmony_ci		int_mask |= BIT_DPU_INT_TE;
43362306a36Sopenharmony_ci		/* enable underflow err INT */
43462306a36Sopenharmony_ci		int_mask |= BIT_DPU_INT_ERR;
43562306a36Sopenharmony_ci	} else if (ctx->if_type == SPRD_DPU_IF_EDPI) {
43662306a36Sopenharmony_ci		/* use edpi as interface */
43762306a36Sopenharmony_ci		dpu_reg_set(ctx, REG_DPU_CFG0, BIT_DPU_IF_EDPI);
43862306a36Sopenharmony_ci		/* use external te */
43962306a36Sopenharmony_ci		dpu_reg_set(ctx, REG_DPI_CTRL, BIT_DPU_EDPI_FROM_EXTERNAL_PAD);
44062306a36Sopenharmony_ci		/* enable te */
44162306a36Sopenharmony_ci		dpu_reg_set(ctx, REG_DPI_CTRL, BIT_DPU_EDPI_TE_EN);
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci		/* enable stop done INT */
44462306a36Sopenharmony_ci		int_mask |= BIT_DPU_INT_DONE;
44562306a36Sopenharmony_ci		/* enable TE INT */
44662306a36Sopenharmony_ci		int_mask |= BIT_DPU_INT_TE;
44762306a36Sopenharmony_ci	}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	writel(int_mask, ctx->base + REG_DPU_INT_EN);
45062306a36Sopenharmony_ci}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_cistatic void sprd_dpu_fini(struct sprd_dpu *dpu)
45362306a36Sopenharmony_ci{
45462306a36Sopenharmony_ci	struct dpu_context *ctx = &dpu->ctx;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	writel(0x00, ctx->base + REG_DPU_INT_EN);
45762306a36Sopenharmony_ci	writel(0xff, ctx->base + REG_DPU_INT_CLR);
45862306a36Sopenharmony_ci}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_cistatic void sprd_dpi_init(struct sprd_dpu *dpu)
46162306a36Sopenharmony_ci{
46262306a36Sopenharmony_ci	struct dpu_context *ctx = &dpu->ctx;
46362306a36Sopenharmony_ci	u32 reg_val;
46462306a36Sopenharmony_ci	u32 size;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	size = (ctx->vm.vactive << 16) | ctx->vm.hactive;
46762306a36Sopenharmony_ci	writel(size, ctx->base + REG_PANEL_SIZE);
46862306a36Sopenharmony_ci	writel(size, ctx->base + REG_BLEND_SIZE);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	if (ctx->if_type == SPRD_DPU_IF_DPI) {
47162306a36Sopenharmony_ci		/* set dpi timing */
47262306a36Sopenharmony_ci		reg_val = ctx->vm.hsync_len << 0 |
47362306a36Sopenharmony_ci			  ctx->vm.hback_porch << 8 |
47462306a36Sopenharmony_ci			  ctx->vm.hfront_porch << 20;
47562306a36Sopenharmony_ci		writel(reg_val, ctx->base + REG_DPI_H_TIMING);
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci		reg_val = ctx->vm.vsync_len << 0 |
47862306a36Sopenharmony_ci			  ctx->vm.vback_porch << 8 |
47962306a36Sopenharmony_ci			  ctx->vm.vfront_porch << 20;
48062306a36Sopenharmony_ci		writel(reg_val, ctx->base + REG_DPI_V_TIMING);
48162306a36Sopenharmony_ci	}
48262306a36Sopenharmony_ci}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_civoid sprd_dpu_run(struct sprd_dpu *dpu)
48562306a36Sopenharmony_ci{
48662306a36Sopenharmony_ci	struct dpu_context *ctx = &dpu->ctx;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	dpu_reg_set(ctx, REG_DPU_CTRL, BIT_DPU_RUN);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	ctx->stopped = false;
49162306a36Sopenharmony_ci}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_civoid sprd_dpu_stop(struct sprd_dpu *dpu)
49462306a36Sopenharmony_ci{
49562306a36Sopenharmony_ci	struct dpu_context *ctx = &dpu->ctx;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	if (ctx->if_type == SPRD_DPU_IF_DPI)
49862306a36Sopenharmony_ci		dpu_reg_set(ctx, REG_DPU_CTRL, BIT_DPU_STOP);
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	dpu_wait_stop_done(dpu);
50162306a36Sopenharmony_ci}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_cistatic int sprd_plane_atomic_check(struct drm_plane *plane,
50462306a36Sopenharmony_ci				   struct drm_atomic_state *state)
50562306a36Sopenharmony_ci{
50662306a36Sopenharmony_ci	struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state,
50762306a36Sopenharmony_ci									     plane);
50862306a36Sopenharmony_ci	struct drm_crtc_state *crtc_state;
50962306a36Sopenharmony_ci	u32 fmt;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	if (!plane_state->fb || !plane_state->crtc)
51262306a36Sopenharmony_ci		return 0;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	fmt = drm_format_to_dpu(plane_state->fb);
51562306a36Sopenharmony_ci	if (!fmt)
51662306a36Sopenharmony_ci		return -EINVAL;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	crtc_state = drm_atomic_get_crtc_state(plane_state->state, plane_state->crtc);
51962306a36Sopenharmony_ci	if (IS_ERR(crtc_state))
52062306a36Sopenharmony_ci		return PTR_ERR(crtc_state);
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	return drm_atomic_helper_check_plane_state(plane_state, crtc_state,
52362306a36Sopenharmony_ci						  DRM_PLANE_NO_SCALING,
52462306a36Sopenharmony_ci						  DRM_PLANE_NO_SCALING,
52562306a36Sopenharmony_ci						  true, true);
52662306a36Sopenharmony_ci}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_cistatic void sprd_plane_atomic_update(struct drm_plane *drm_plane,
52962306a36Sopenharmony_ci				     struct drm_atomic_state *state)
53062306a36Sopenharmony_ci{
53162306a36Sopenharmony_ci	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
53262306a36Sopenharmony_ci									   drm_plane);
53362306a36Sopenharmony_ci	struct sprd_dpu *dpu = to_sprd_crtc(new_state->crtc);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	/* start configure dpu layers */
53662306a36Sopenharmony_ci	sprd_dpu_layer(dpu, new_state);
53762306a36Sopenharmony_ci}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_cistatic void sprd_plane_atomic_disable(struct drm_plane *drm_plane,
54062306a36Sopenharmony_ci				      struct drm_atomic_state *state)
54162306a36Sopenharmony_ci{
54262306a36Sopenharmony_ci	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
54362306a36Sopenharmony_ci									   drm_plane);
54462306a36Sopenharmony_ci	struct sprd_dpu *dpu = to_sprd_crtc(old_state->crtc);
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	layer_reg_wr(&dpu->ctx, REG_LAY_CTRL, 0x00, old_state->zpos);
54762306a36Sopenharmony_ci}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_cistatic void sprd_plane_create_properties(struct sprd_plane *plane, int index)
55062306a36Sopenharmony_ci{
55162306a36Sopenharmony_ci	unsigned int supported_modes = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
55262306a36Sopenharmony_ci				       BIT(DRM_MODE_BLEND_PREMULTI) |
55362306a36Sopenharmony_ci				       BIT(DRM_MODE_BLEND_COVERAGE);
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	/* create rotation property */
55662306a36Sopenharmony_ci	drm_plane_create_rotation_property(&plane->base,
55762306a36Sopenharmony_ci					   DRM_MODE_ROTATE_0,
55862306a36Sopenharmony_ci					   DRM_MODE_ROTATE_MASK |
55962306a36Sopenharmony_ci					   DRM_MODE_REFLECT_MASK);
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	/* create alpha property */
56262306a36Sopenharmony_ci	drm_plane_create_alpha_property(&plane->base);
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	/* create blend mode property */
56562306a36Sopenharmony_ci	drm_plane_create_blend_mode_property(&plane->base, supported_modes);
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	/* create zpos property */
56862306a36Sopenharmony_ci	drm_plane_create_zpos_immutable_property(&plane->base, index);
56962306a36Sopenharmony_ci}
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_cistatic const struct drm_plane_helper_funcs sprd_plane_helper_funcs = {
57262306a36Sopenharmony_ci	.atomic_check = sprd_plane_atomic_check,
57362306a36Sopenharmony_ci	.atomic_update = sprd_plane_atomic_update,
57462306a36Sopenharmony_ci	.atomic_disable = sprd_plane_atomic_disable,
57562306a36Sopenharmony_ci};
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_cistatic const struct drm_plane_funcs sprd_plane_funcs = {
57862306a36Sopenharmony_ci	.update_plane = drm_atomic_helper_update_plane,
57962306a36Sopenharmony_ci	.disable_plane	= drm_atomic_helper_disable_plane,
58062306a36Sopenharmony_ci	.destroy = drm_plane_cleanup,
58162306a36Sopenharmony_ci	.reset = drm_atomic_helper_plane_reset,
58262306a36Sopenharmony_ci	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
58362306a36Sopenharmony_ci	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
58462306a36Sopenharmony_ci};
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_cistatic struct sprd_plane *sprd_planes_init(struct drm_device *drm)
58762306a36Sopenharmony_ci{
58862306a36Sopenharmony_ci	struct sprd_plane *plane, *primary;
58962306a36Sopenharmony_ci	enum drm_plane_type plane_type;
59062306a36Sopenharmony_ci	int i;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	for (i = 0; i < 6; i++) {
59362306a36Sopenharmony_ci		plane_type = (i == 0) ? DRM_PLANE_TYPE_PRIMARY :
59462306a36Sopenharmony_ci					DRM_PLANE_TYPE_OVERLAY;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci		plane = drmm_universal_plane_alloc(drm, struct sprd_plane, base,
59762306a36Sopenharmony_ci						   1, &sprd_plane_funcs,
59862306a36Sopenharmony_ci						   layer_fmts, ARRAY_SIZE(layer_fmts),
59962306a36Sopenharmony_ci						   NULL, plane_type, NULL);
60062306a36Sopenharmony_ci		if (IS_ERR(plane)) {
60162306a36Sopenharmony_ci			drm_err(drm, "failed to init drm plane: %d\n", i);
60262306a36Sopenharmony_ci			return plane;
60362306a36Sopenharmony_ci		}
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci		drm_plane_helper_add(&plane->base, &sprd_plane_helper_funcs);
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci		sprd_plane_create_properties(plane, i);
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci		if (i == 0)
61062306a36Sopenharmony_ci			primary = plane;
61162306a36Sopenharmony_ci	}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	return primary;
61462306a36Sopenharmony_ci}
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_cistatic void sprd_crtc_mode_set_nofb(struct drm_crtc *crtc)
61762306a36Sopenharmony_ci{
61862306a36Sopenharmony_ci	struct sprd_dpu *dpu = to_sprd_crtc(crtc);
61962306a36Sopenharmony_ci	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
62062306a36Sopenharmony_ci	struct drm_encoder *encoder;
62162306a36Sopenharmony_ci	struct sprd_dsi *dsi;
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	drm_display_mode_to_videomode(mode, &dpu->ctx.vm);
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	drm_for_each_encoder_mask(encoder, crtc->dev,
62662306a36Sopenharmony_ci				  crtc->state->encoder_mask) {
62762306a36Sopenharmony_ci		dsi = encoder_to_dsi(encoder);
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci		if (dsi->slave->mode_flags & MIPI_DSI_MODE_VIDEO)
63062306a36Sopenharmony_ci			dpu->ctx.if_type = SPRD_DPU_IF_DPI;
63162306a36Sopenharmony_ci		else
63262306a36Sopenharmony_ci			dpu->ctx.if_type = SPRD_DPU_IF_EDPI;
63362306a36Sopenharmony_ci	}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	sprd_dpi_init(dpu);
63662306a36Sopenharmony_ci}
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_cistatic void sprd_crtc_atomic_enable(struct drm_crtc *crtc,
63962306a36Sopenharmony_ci				    struct drm_atomic_state *state)
64062306a36Sopenharmony_ci{
64162306a36Sopenharmony_ci	struct sprd_dpu *dpu = to_sprd_crtc(crtc);
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	sprd_dpu_init(dpu);
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	drm_crtc_vblank_on(&dpu->base);
64662306a36Sopenharmony_ci}
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_cistatic void sprd_crtc_atomic_disable(struct drm_crtc *crtc,
64962306a36Sopenharmony_ci				     struct drm_atomic_state *state)
65062306a36Sopenharmony_ci{
65162306a36Sopenharmony_ci	struct sprd_dpu *dpu = to_sprd_crtc(crtc);
65262306a36Sopenharmony_ci	struct drm_device *drm = dpu->base.dev;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	drm_crtc_vblank_off(&dpu->base);
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	sprd_dpu_fini(dpu);
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	spin_lock_irq(&drm->event_lock);
65962306a36Sopenharmony_ci	if (crtc->state->event) {
66062306a36Sopenharmony_ci		drm_crtc_send_vblank_event(crtc, crtc->state->event);
66162306a36Sopenharmony_ci		crtc->state->event = NULL;
66262306a36Sopenharmony_ci	}
66362306a36Sopenharmony_ci	spin_unlock_irq(&drm->event_lock);
66462306a36Sopenharmony_ci}
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_cistatic void sprd_crtc_atomic_flush(struct drm_crtc *crtc,
66762306a36Sopenharmony_ci				   struct drm_atomic_state *state)
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci{
67062306a36Sopenharmony_ci	struct sprd_dpu *dpu = to_sprd_crtc(crtc);
67162306a36Sopenharmony_ci	struct drm_device *drm = dpu->base.dev;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	sprd_dpu_flip(dpu);
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	spin_lock_irq(&drm->event_lock);
67662306a36Sopenharmony_ci	if (crtc->state->event) {
67762306a36Sopenharmony_ci		drm_crtc_send_vblank_event(crtc, crtc->state->event);
67862306a36Sopenharmony_ci		crtc->state->event = NULL;
67962306a36Sopenharmony_ci	}
68062306a36Sopenharmony_ci	spin_unlock_irq(&drm->event_lock);
68162306a36Sopenharmony_ci}
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_cistatic int sprd_crtc_enable_vblank(struct drm_crtc *crtc)
68462306a36Sopenharmony_ci{
68562306a36Sopenharmony_ci	struct sprd_dpu *dpu = to_sprd_crtc(crtc);
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	dpu_reg_set(&dpu->ctx, REG_DPU_INT_EN, BIT_DPU_INT_VSYNC);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	return 0;
69062306a36Sopenharmony_ci}
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_cistatic void sprd_crtc_disable_vblank(struct drm_crtc *crtc)
69362306a36Sopenharmony_ci{
69462306a36Sopenharmony_ci	struct sprd_dpu *dpu = to_sprd_crtc(crtc);
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	dpu_reg_clr(&dpu->ctx, REG_DPU_INT_EN, BIT_DPU_INT_VSYNC);
69762306a36Sopenharmony_ci}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_cistatic const struct drm_crtc_helper_funcs sprd_crtc_helper_funcs = {
70062306a36Sopenharmony_ci	.mode_set_nofb	= sprd_crtc_mode_set_nofb,
70162306a36Sopenharmony_ci	.atomic_flush	= sprd_crtc_atomic_flush,
70262306a36Sopenharmony_ci	.atomic_enable	= sprd_crtc_atomic_enable,
70362306a36Sopenharmony_ci	.atomic_disable	= sprd_crtc_atomic_disable,
70462306a36Sopenharmony_ci};
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_cistatic const struct drm_crtc_funcs sprd_crtc_funcs = {
70762306a36Sopenharmony_ci	.destroy	= drm_crtc_cleanup,
70862306a36Sopenharmony_ci	.set_config	= drm_atomic_helper_set_config,
70962306a36Sopenharmony_ci	.page_flip	= drm_atomic_helper_page_flip,
71062306a36Sopenharmony_ci	.reset		= drm_atomic_helper_crtc_reset,
71162306a36Sopenharmony_ci	.atomic_duplicate_state	= drm_atomic_helper_crtc_duplicate_state,
71262306a36Sopenharmony_ci	.atomic_destroy_state	= drm_atomic_helper_crtc_destroy_state,
71362306a36Sopenharmony_ci	.enable_vblank	= sprd_crtc_enable_vblank,
71462306a36Sopenharmony_ci	.disable_vblank	= sprd_crtc_disable_vblank,
71562306a36Sopenharmony_ci};
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_cistatic struct sprd_dpu *sprd_crtc_init(struct drm_device *drm,
71862306a36Sopenharmony_ci				       struct drm_plane *primary, struct device *dev)
71962306a36Sopenharmony_ci{
72062306a36Sopenharmony_ci	struct device_node *port;
72162306a36Sopenharmony_ci	struct sprd_dpu *dpu;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	dpu = drmm_crtc_alloc_with_planes(drm, struct sprd_dpu, base,
72462306a36Sopenharmony_ci					  primary, NULL,
72562306a36Sopenharmony_ci					&sprd_crtc_funcs, NULL);
72662306a36Sopenharmony_ci	if (IS_ERR(dpu)) {
72762306a36Sopenharmony_ci		drm_err(drm, "failed to init crtc\n");
72862306a36Sopenharmony_ci		return dpu;
72962306a36Sopenharmony_ci	}
73062306a36Sopenharmony_ci	drm_crtc_helper_add(&dpu->base, &sprd_crtc_helper_funcs);
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	/*
73362306a36Sopenharmony_ci	 * set crtc port so that drm_of_find_possible_crtcs call works
73462306a36Sopenharmony_ci	 */
73562306a36Sopenharmony_ci	port = of_graph_get_port_by_id(dev->of_node, 0);
73662306a36Sopenharmony_ci	if (!port) {
73762306a36Sopenharmony_ci		drm_err(drm, "failed to found crtc output port for %s\n",
73862306a36Sopenharmony_ci			dev->of_node->full_name);
73962306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
74062306a36Sopenharmony_ci	}
74162306a36Sopenharmony_ci	dpu->base.port = port;
74262306a36Sopenharmony_ci	of_node_put(port);
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	return dpu;
74562306a36Sopenharmony_ci}
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_cistatic irqreturn_t sprd_dpu_isr(int irq, void *data)
74862306a36Sopenharmony_ci{
74962306a36Sopenharmony_ci	struct sprd_dpu *dpu = data;
75062306a36Sopenharmony_ci	struct dpu_context *ctx = &dpu->ctx;
75162306a36Sopenharmony_ci	u32 reg_val, int_mask = 0;
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	reg_val = readl(ctx->base + REG_DPU_INT_STS);
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	/* disable err interrupt */
75662306a36Sopenharmony_ci	if (reg_val & BIT_DPU_INT_ERR) {
75762306a36Sopenharmony_ci		int_mask |= BIT_DPU_INT_ERR;
75862306a36Sopenharmony_ci		drm_warn(dpu->drm, "Warning: dpu underflow!\n");
75962306a36Sopenharmony_ci	}
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	/* dpu update done isr */
76262306a36Sopenharmony_ci	if (reg_val & BIT_DPU_INT_UPDATE_DONE) {
76362306a36Sopenharmony_ci		ctx->evt_update = true;
76462306a36Sopenharmony_ci		wake_up_interruptible_all(&ctx->wait_queue);
76562306a36Sopenharmony_ci	}
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	/* dpu stop done isr */
76862306a36Sopenharmony_ci	if (reg_val & BIT_DPU_INT_DONE) {
76962306a36Sopenharmony_ci		ctx->evt_stop = true;
77062306a36Sopenharmony_ci		wake_up_interruptible_all(&ctx->wait_queue);
77162306a36Sopenharmony_ci	}
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	if (reg_val & BIT_DPU_INT_VSYNC)
77462306a36Sopenharmony_ci		drm_crtc_handle_vblank(&dpu->base);
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	writel(reg_val, ctx->base + REG_DPU_INT_CLR);
77762306a36Sopenharmony_ci	dpu_reg_clr(ctx, REG_DPU_INT_EN, int_mask);
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	return IRQ_HANDLED;
78062306a36Sopenharmony_ci}
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_cistatic int sprd_dpu_context_init(struct sprd_dpu *dpu,
78362306a36Sopenharmony_ci				 struct device *dev)
78462306a36Sopenharmony_ci{
78562306a36Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
78662306a36Sopenharmony_ci	struct dpu_context *ctx = &dpu->ctx;
78762306a36Sopenharmony_ci	struct resource *res;
78862306a36Sopenharmony_ci	int ret;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
79162306a36Sopenharmony_ci	if (!res) {
79262306a36Sopenharmony_ci		dev_err(dev, "failed to get I/O resource\n");
79362306a36Sopenharmony_ci		return -EINVAL;
79462306a36Sopenharmony_ci	}
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	ctx->base = devm_ioremap(dev, res->start, resource_size(res));
79762306a36Sopenharmony_ci	if (!ctx->base) {
79862306a36Sopenharmony_ci		dev_err(dev, "failed to map dpu registers\n");
79962306a36Sopenharmony_ci		return -EFAULT;
80062306a36Sopenharmony_ci	}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	ctx->irq = platform_get_irq(pdev, 0);
80362306a36Sopenharmony_ci	if (ctx->irq < 0)
80462306a36Sopenharmony_ci		return ctx->irq;
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	/* disable and clear interrupts before register dpu IRQ. */
80762306a36Sopenharmony_ci	writel(0x00, ctx->base + REG_DPU_INT_EN);
80862306a36Sopenharmony_ci	writel(0xff, ctx->base + REG_DPU_INT_CLR);
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	ret = devm_request_irq(dev, ctx->irq, sprd_dpu_isr,
81162306a36Sopenharmony_ci			       IRQF_TRIGGER_NONE, "DPU", dpu);
81262306a36Sopenharmony_ci	if (ret) {
81362306a36Sopenharmony_ci		dev_err(dev, "failed to register dpu irq handler\n");
81462306a36Sopenharmony_ci		return ret;
81562306a36Sopenharmony_ci	}
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	init_waitqueue_head(&ctx->wait_queue);
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	return 0;
82062306a36Sopenharmony_ci}
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_cistatic int sprd_dpu_bind(struct device *dev, struct device *master, void *data)
82362306a36Sopenharmony_ci{
82462306a36Sopenharmony_ci	struct drm_device *drm = data;
82562306a36Sopenharmony_ci	struct sprd_dpu *dpu;
82662306a36Sopenharmony_ci	struct sprd_plane *plane;
82762306a36Sopenharmony_ci	int ret;
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	plane = sprd_planes_init(drm);
83062306a36Sopenharmony_ci	if (IS_ERR(plane))
83162306a36Sopenharmony_ci		return PTR_ERR(plane);
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	dpu = sprd_crtc_init(drm, &plane->base, dev);
83462306a36Sopenharmony_ci	if (IS_ERR(dpu))
83562306a36Sopenharmony_ci		return PTR_ERR(dpu);
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	dpu->drm = drm;
83862306a36Sopenharmony_ci	dev_set_drvdata(dev, dpu);
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	ret = sprd_dpu_context_init(dpu, dev);
84162306a36Sopenharmony_ci	if (ret)
84262306a36Sopenharmony_ci		return ret;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	return 0;
84562306a36Sopenharmony_ci}
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_cistatic const struct component_ops dpu_component_ops = {
84862306a36Sopenharmony_ci	.bind = sprd_dpu_bind,
84962306a36Sopenharmony_ci};
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_cistatic const struct of_device_id dpu_match_table[] = {
85262306a36Sopenharmony_ci	{ .compatible = "sprd,sharkl3-dpu" },
85362306a36Sopenharmony_ci	{ /* sentinel */ },
85462306a36Sopenharmony_ci};
85562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, dpu_match_table);
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_cistatic int sprd_dpu_probe(struct platform_device *pdev)
85862306a36Sopenharmony_ci{
85962306a36Sopenharmony_ci	return component_add(&pdev->dev, &dpu_component_ops);
86062306a36Sopenharmony_ci}
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_cistatic int sprd_dpu_remove(struct platform_device *pdev)
86362306a36Sopenharmony_ci{
86462306a36Sopenharmony_ci	component_del(&pdev->dev, &dpu_component_ops);
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	return 0;
86762306a36Sopenharmony_ci}
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_cistruct platform_driver sprd_dpu_driver = {
87062306a36Sopenharmony_ci	.probe = sprd_dpu_probe,
87162306a36Sopenharmony_ci	.remove = sprd_dpu_remove,
87262306a36Sopenharmony_ci	.driver = {
87362306a36Sopenharmony_ci		.name = "sprd-dpu-drv",
87462306a36Sopenharmony_ci		.of_match_table = dpu_match_table,
87562306a36Sopenharmony_ci	},
87662306a36Sopenharmony_ci};
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ciMODULE_AUTHOR("Leon He <leon.he@unisoc.com>");
87962306a36Sopenharmony_ciMODULE_AUTHOR("Kevin Tang <kevin.tang@unisoc.com>");
88062306a36Sopenharmony_ciMODULE_DESCRIPTION("Unisoc Display Controller Driver");
88162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
882