162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2022 Marek Vasut <marex@denx.de>
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * This code is based on drivers/gpu/drm/mxsfb/mxsfb*
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/bitfield.h>
962306a36Sopenharmony_ci#include <linux/clk.h>
1062306a36Sopenharmony_ci#include <linux/io.h>
1162306a36Sopenharmony_ci#include <linux/iopoll.h>
1262306a36Sopenharmony_ci#include <linux/media-bus-format.h>
1362306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1462306a36Sopenharmony_ci#include <linux/spinlock.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <drm/drm_atomic.h>
1762306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h>
1862306a36Sopenharmony_ci#include <drm/drm_bridge.h>
1962306a36Sopenharmony_ci#include <drm/drm_color_mgmt.h>
2062306a36Sopenharmony_ci#include <drm/drm_connector.h>
2162306a36Sopenharmony_ci#include <drm/drm_crtc.h>
2262306a36Sopenharmony_ci#include <drm/drm_encoder.h>
2362306a36Sopenharmony_ci#include <drm/drm_fb_dma_helper.h>
2462306a36Sopenharmony_ci#include <drm/drm_fourcc.h>
2562306a36Sopenharmony_ci#include <drm/drm_framebuffer.h>
2662306a36Sopenharmony_ci#include <drm/drm_gem_atomic_helper.h>
2762306a36Sopenharmony_ci#include <drm/drm_gem_dma_helper.h>
2862306a36Sopenharmony_ci#include <drm/drm_plane.h>
2962306a36Sopenharmony_ci#include <drm/drm_vblank.h>
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#include "lcdif_drv.h"
3262306a36Sopenharmony_ci#include "lcdif_regs.h"
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistruct lcdif_crtc_state {
3562306a36Sopenharmony_ci	struct drm_crtc_state	base;	/* always be the first member */
3662306a36Sopenharmony_ci	u32			bus_format;
3762306a36Sopenharmony_ci	u32			bus_flags;
3862306a36Sopenharmony_ci};
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic inline struct lcdif_crtc_state *
4162306a36Sopenharmony_cito_lcdif_crtc_state(struct drm_crtc_state *s)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	return container_of(s, struct lcdif_crtc_state, base);
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci/* -----------------------------------------------------------------------------
4762306a36Sopenharmony_ci * CRTC
4862306a36Sopenharmony_ci */
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci/*
5162306a36Sopenharmony_ci * For conversion from YCbCr to RGB, the CSC operates as follows:
5262306a36Sopenharmony_ci *
5362306a36Sopenharmony_ci * |R|   |A1 A2 A3|   |Y  + D1|
5462306a36Sopenharmony_ci * |G| = |B1 B2 B3| * |Cb + D2|
5562306a36Sopenharmony_ci * |B|   |C1 C2 C3|   |Cr + D3|
5662306a36Sopenharmony_ci *
5762306a36Sopenharmony_ci * The A, B and C coefficients are expressed as Q2.8 fixed point values, and
5862306a36Sopenharmony_ci * the D coefficients as Q0.8. Despite the reference manual stating the
5962306a36Sopenharmony_ci * opposite, the D1, D2 and D3 offset values are added to Y, Cb and Cr, not
6062306a36Sopenharmony_ci * subtracted. They must thus be programmed with negative values.
6162306a36Sopenharmony_ci */
6262306a36Sopenharmony_cistatic const u32 lcdif_yuv2rgb_coeffs[3][2][6] = {
6362306a36Sopenharmony_ci	[DRM_COLOR_YCBCR_BT601] = {
6462306a36Sopenharmony_ci		[DRM_COLOR_YCBCR_LIMITED_RANGE] = {
6562306a36Sopenharmony_ci			/*
6662306a36Sopenharmony_ci			 * BT.601 limited range:
6762306a36Sopenharmony_ci			 *
6862306a36Sopenharmony_ci			 * |R|   |1.1644  0.0000  1.5960|   |Y  - 16 |
6962306a36Sopenharmony_ci			 * |G| = |1.1644 -0.3917 -0.8129| * |Cb - 128|
7062306a36Sopenharmony_ci			 * |B|   |1.1644  2.0172  0.0000|   |Cr - 128|
7162306a36Sopenharmony_ci			 */
7262306a36Sopenharmony_ci			CSC0_COEF0_A1(0x12a) | CSC0_COEF0_A2(0x000),
7362306a36Sopenharmony_ci			CSC0_COEF1_A3(0x199) | CSC0_COEF1_B1(0x12a),
7462306a36Sopenharmony_ci			CSC0_COEF2_B2(0x79c) | CSC0_COEF2_B3(0x730),
7562306a36Sopenharmony_ci			CSC0_COEF3_C1(0x12a) | CSC0_COEF3_C2(0x204),
7662306a36Sopenharmony_ci			CSC0_COEF4_C3(0x000) | CSC0_COEF4_D1(0x1f0),
7762306a36Sopenharmony_ci			CSC0_COEF5_D2(0x180) | CSC0_COEF5_D3(0x180),
7862306a36Sopenharmony_ci		},
7962306a36Sopenharmony_ci		[DRM_COLOR_YCBCR_FULL_RANGE] = {
8062306a36Sopenharmony_ci			/*
8162306a36Sopenharmony_ci			 * BT.601 full range:
8262306a36Sopenharmony_ci			 *
8362306a36Sopenharmony_ci			 * |R|   |1.0000  0.0000  1.4020|   |Y  - 0  |
8462306a36Sopenharmony_ci			 * |G| = |1.0000 -0.3441 -0.7141| * |Cb - 128|
8562306a36Sopenharmony_ci			 * |B|   |1.0000  1.7720  0.0000|   |Cr - 128|
8662306a36Sopenharmony_ci			 */
8762306a36Sopenharmony_ci			CSC0_COEF0_A1(0x100) | CSC0_COEF0_A2(0x000),
8862306a36Sopenharmony_ci			CSC0_COEF1_A3(0x167) | CSC0_COEF1_B1(0x100),
8962306a36Sopenharmony_ci			CSC0_COEF2_B2(0x7a8) | CSC0_COEF2_B3(0x749),
9062306a36Sopenharmony_ci			CSC0_COEF3_C1(0x100) | CSC0_COEF3_C2(0x1c6),
9162306a36Sopenharmony_ci			CSC0_COEF4_C3(0x000) | CSC0_COEF4_D1(0x000),
9262306a36Sopenharmony_ci			CSC0_COEF5_D2(0x180) | CSC0_COEF5_D3(0x180),
9362306a36Sopenharmony_ci		},
9462306a36Sopenharmony_ci	},
9562306a36Sopenharmony_ci	[DRM_COLOR_YCBCR_BT709] = {
9662306a36Sopenharmony_ci		[DRM_COLOR_YCBCR_LIMITED_RANGE] = {
9762306a36Sopenharmony_ci			/*
9862306a36Sopenharmony_ci			 * Rec.709 limited range:
9962306a36Sopenharmony_ci			 *
10062306a36Sopenharmony_ci			 * |R|   |1.1644  0.0000  1.7927|   |Y  - 16 |
10162306a36Sopenharmony_ci			 * |G| = |1.1644 -0.2132 -0.5329| * |Cb - 128|
10262306a36Sopenharmony_ci			 * |B|   |1.1644  2.1124  0.0000|   |Cr - 128|
10362306a36Sopenharmony_ci			 */
10462306a36Sopenharmony_ci			CSC0_COEF0_A1(0x12a) | CSC0_COEF0_A2(0x000),
10562306a36Sopenharmony_ci			CSC0_COEF1_A3(0x1cb) | CSC0_COEF1_B1(0x12a),
10662306a36Sopenharmony_ci			CSC0_COEF2_B2(0x7c9) | CSC0_COEF2_B3(0x778),
10762306a36Sopenharmony_ci			CSC0_COEF3_C1(0x12a) | CSC0_COEF3_C2(0x21d),
10862306a36Sopenharmony_ci			CSC0_COEF4_C3(0x000) | CSC0_COEF4_D1(0x1f0),
10962306a36Sopenharmony_ci			CSC0_COEF5_D2(0x180) | CSC0_COEF5_D3(0x180),
11062306a36Sopenharmony_ci		},
11162306a36Sopenharmony_ci		[DRM_COLOR_YCBCR_FULL_RANGE] = {
11262306a36Sopenharmony_ci			/*
11362306a36Sopenharmony_ci			 * Rec.709 full range:
11462306a36Sopenharmony_ci			 *
11562306a36Sopenharmony_ci			 * |R|   |1.0000  0.0000  1.5748|   |Y  - 0  |
11662306a36Sopenharmony_ci			 * |G| = |1.0000 -0.1873 -0.4681| * |Cb - 128|
11762306a36Sopenharmony_ci			 * |B|   |1.0000  1.8556  0.0000|   |Cr - 128|
11862306a36Sopenharmony_ci			 */
11962306a36Sopenharmony_ci			CSC0_COEF0_A1(0x100) | CSC0_COEF0_A2(0x000),
12062306a36Sopenharmony_ci			CSC0_COEF1_A3(0x193) | CSC0_COEF1_B1(0x100),
12162306a36Sopenharmony_ci			CSC0_COEF2_B2(0x7d0) | CSC0_COEF2_B3(0x788),
12262306a36Sopenharmony_ci			CSC0_COEF3_C1(0x100) | CSC0_COEF3_C2(0x1db),
12362306a36Sopenharmony_ci			CSC0_COEF4_C3(0x000) | CSC0_COEF4_D1(0x000),
12462306a36Sopenharmony_ci			CSC0_COEF5_D2(0x180) | CSC0_COEF5_D3(0x180),
12562306a36Sopenharmony_ci		},
12662306a36Sopenharmony_ci	},
12762306a36Sopenharmony_ci	[DRM_COLOR_YCBCR_BT2020] = {
12862306a36Sopenharmony_ci		[DRM_COLOR_YCBCR_LIMITED_RANGE] = {
12962306a36Sopenharmony_ci			/*
13062306a36Sopenharmony_ci			 * BT.2020 limited range:
13162306a36Sopenharmony_ci			 *
13262306a36Sopenharmony_ci			 * |R|   |1.1644  0.0000  1.6787|   |Y  - 16 |
13362306a36Sopenharmony_ci			 * |G| = |1.1644 -0.1874 -0.6505| * |Cb - 128|
13462306a36Sopenharmony_ci			 * |B|   |1.1644  2.1418  0.0000|   |Cr - 128|
13562306a36Sopenharmony_ci			 */
13662306a36Sopenharmony_ci			CSC0_COEF0_A1(0x12a) | CSC0_COEF0_A2(0x000),
13762306a36Sopenharmony_ci			CSC0_COEF1_A3(0x1ae) | CSC0_COEF1_B1(0x12a),
13862306a36Sopenharmony_ci			CSC0_COEF2_B2(0x7d0) | CSC0_COEF2_B3(0x759),
13962306a36Sopenharmony_ci			CSC0_COEF3_C1(0x12a) | CSC0_COEF3_C2(0x224),
14062306a36Sopenharmony_ci			CSC0_COEF4_C3(0x000) | CSC0_COEF4_D1(0x1f0),
14162306a36Sopenharmony_ci			CSC0_COEF5_D2(0x180) | CSC0_COEF5_D3(0x180),
14262306a36Sopenharmony_ci		},
14362306a36Sopenharmony_ci		[DRM_COLOR_YCBCR_FULL_RANGE] = {
14462306a36Sopenharmony_ci			/*
14562306a36Sopenharmony_ci			 * BT.2020 full range:
14662306a36Sopenharmony_ci			 *
14762306a36Sopenharmony_ci			 * |R|   |1.0000  0.0000  1.4746|   |Y  - 0  |
14862306a36Sopenharmony_ci			 * |G| = |1.0000 -0.1646 -0.5714| * |Cb - 128|
14962306a36Sopenharmony_ci			 * |B|   |1.0000  1.8814  0.0000|   |Cr - 128|
15062306a36Sopenharmony_ci			 */
15162306a36Sopenharmony_ci			CSC0_COEF0_A1(0x100) | CSC0_COEF0_A2(0x000),
15262306a36Sopenharmony_ci			CSC0_COEF1_A3(0x179) | CSC0_COEF1_B1(0x100),
15362306a36Sopenharmony_ci			CSC0_COEF2_B2(0x7d6) | CSC0_COEF2_B3(0x76e),
15462306a36Sopenharmony_ci			CSC0_COEF3_C1(0x100) | CSC0_COEF3_C2(0x1e2),
15562306a36Sopenharmony_ci			CSC0_COEF4_C3(0x000) | CSC0_COEF4_D1(0x000),
15662306a36Sopenharmony_ci			CSC0_COEF5_D2(0x180) | CSC0_COEF5_D3(0x180),
15762306a36Sopenharmony_ci		},
15862306a36Sopenharmony_ci	},
15962306a36Sopenharmony_ci};
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic void lcdif_set_formats(struct lcdif_drm_private *lcdif,
16262306a36Sopenharmony_ci			      struct drm_plane_state *plane_state,
16362306a36Sopenharmony_ci			      const u32 bus_format)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	struct drm_device *drm = lcdif->drm;
16662306a36Sopenharmony_ci	const u32 format = plane_state->fb->format->format;
16762306a36Sopenharmony_ci	bool in_yuv = false;
16862306a36Sopenharmony_ci	bool out_yuv = false;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	switch (bus_format) {
17162306a36Sopenharmony_ci	case MEDIA_BUS_FMT_RGB565_1X16:
17262306a36Sopenharmony_ci		writel(DISP_PARA_LINE_PATTERN_RGB565,
17362306a36Sopenharmony_ci		       lcdif->base + LCDC_V8_DISP_PARA);
17462306a36Sopenharmony_ci		break;
17562306a36Sopenharmony_ci	case MEDIA_BUS_FMT_RGB888_1X24:
17662306a36Sopenharmony_ci		writel(DISP_PARA_LINE_PATTERN_RGB888,
17762306a36Sopenharmony_ci		       lcdif->base + LCDC_V8_DISP_PARA);
17862306a36Sopenharmony_ci		break;
17962306a36Sopenharmony_ci	case MEDIA_BUS_FMT_UYVY8_1X16:
18062306a36Sopenharmony_ci		writel(DISP_PARA_LINE_PATTERN_UYVY_H,
18162306a36Sopenharmony_ci		       lcdif->base + LCDC_V8_DISP_PARA);
18262306a36Sopenharmony_ci		out_yuv = true;
18362306a36Sopenharmony_ci		break;
18462306a36Sopenharmony_ci	default:
18562306a36Sopenharmony_ci		dev_err(drm->dev, "Unknown media bus format 0x%x\n", bus_format);
18662306a36Sopenharmony_ci		break;
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	switch (format) {
19062306a36Sopenharmony_ci	/* RGB Formats */
19162306a36Sopenharmony_ci	case DRM_FORMAT_RGB565:
19262306a36Sopenharmony_ci		writel(CTRLDESCL0_5_BPP_16_RGB565,
19362306a36Sopenharmony_ci		       lcdif->base + LCDC_V8_CTRLDESCL0_5);
19462306a36Sopenharmony_ci		break;
19562306a36Sopenharmony_ci	case DRM_FORMAT_RGB888:
19662306a36Sopenharmony_ci		writel(CTRLDESCL0_5_BPP_24_RGB888,
19762306a36Sopenharmony_ci		       lcdif->base + LCDC_V8_CTRLDESCL0_5);
19862306a36Sopenharmony_ci		break;
19962306a36Sopenharmony_ci	case DRM_FORMAT_XRGB1555:
20062306a36Sopenharmony_ci		writel(CTRLDESCL0_5_BPP_16_ARGB1555,
20162306a36Sopenharmony_ci		       lcdif->base + LCDC_V8_CTRLDESCL0_5);
20262306a36Sopenharmony_ci		break;
20362306a36Sopenharmony_ci	case DRM_FORMAT_XRGB4444:
20462306a36Sopenharmony_ci		writel(CTRLDESCL0_5_BPP_16_ARGB4444,
20562306a36Sopenharmony_ci		       lcdif->base + LCDC_V8_CTRLDESCL0_5);
20662306a36Sopenharmony_ci		break;
20762306a36Sopenharmony_ci	case DRM_FORMAT_XBGR8888:
20862306a36Sopenharmony_ci		writel(CTRLDESCL0_5_BPP_32_ABGR8888,
20962306a36Sopenharmony_ci		       lcdif->base + LCDC_V8_CTRLDESCL0_5);
21062306a36Sopenharmony_ci		break;
21162306a36Sopenharmony_ci	case DRM_FORMAT_XRGB8888:
21262306a36Sopenharmony_ci		writel(CTRLDESCL0_5_BPP_32_ARGB8888,
21362306a36Sopenharmony_ci		       lcdif->base + LCDC_V8_CTRLDESCL0_5);
21462306a36Sopenharmony_ci		break;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	/* YUV Formats */
21762306a36Sopenharmony_ci	case DRM_FORMAT_YUYV:
21862306a36Sopenharmony_ci		writel(CTRLDESCL0_5_BPP_YCbCr422 | CTRLDESCL0_5_YUV_FORMAT_VY2UY1,
21962306a36Sopenharmony_ci		       lcdif->base + LCDC_V8_CTRLDESCL0_5);
22062306a36Sopenharmony_ci		in_yuv = true;
22162306a36Sopenharmony_ci		break;
22262306a36Sopenharmony_ci	case DRM_FORMAT_YVYU:
22362306a36Sopenharmony_ci		writel(CTRLDESCL0_5_BPP_YCbCr422 | CTRLDESCL0_5_YUV_FORMAT_UY2VY1,
22462306a36Sopenharmony_ci		       lcdif->base + LCDC_V8_CTRLDESCL0_5);
22562306a36Sopenharmony_ci		in_yuv = true;
22662306a36Sopenharmony_ci		break;
22762306a36Sopenharmony_ci	case DRM_FORMAT_UYVY:
22862306a36Sopenharmony_ci		writel(CTRLDESCL0_5_BPP_YCbCr422 | CTRLDESCL0_5_YUV_FORMAT_Y2VY1U,
22962306a36Sopenharmony_ci		       lcdif->base + LCDC_V8_CTRLDESCL0_5);
23062306a36Sopenharmony_ci		in_yuv = true;
23162306a36Sopenharmony_ci		break;
23262306a36Sopenharmony_ci	case DRM_FORMAT_VYUY:
23362306a36Sopenharmony_ci		writel(CTRLDESCL0_5_BPP_YCbCr422 | CTRLDESCL0_5_YUV_FORMAT_Y2UY1V,
23462306a36Sopenharmony_ci		       lcdif->base + LCDC_V8_CTRLDESCL0_5);
23562306a36Sopenharmony_ci		in_yuv = true;
23662306a36Sopenharmony_ci		break;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	default:
23962306a36Sopenharmony_ci		dev_err(drm->dev, "Unknown pixel format 0x%x\n", format);
24062306a36Sopenharmony_ci		break;
24162306a36Sopenharmony_ci	}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	/*
24462306a36Sopenharmony_ci	 * The CSC differentiates between "YCbCr" and "YUV", but the reference
24562306a36Sopenharmony_ci	 * manual doesn't detail how they differ. Experiments showed that the
24662306a36Sopenharmony_ci	 * luminance value is unaffected, only the calculations involving chroma
24762306a36Sopenharmony_ci	 * values differ. The YCbCr mode behaves as expected, with chroma values
24862306a36Sopenharmony_ci	 * being offset by 128. The YUV mode isn't fully understood.
24962306a36Sopenharmony_ci	 */
25062306a36Sopenharmony_ci	if (!in_yuv && out_yuv) {
25162306a36Sopenharmony_ci		/* RGB -> YCbCr */
25262306a36Sopenharmony_ci		writel(CSC0_CTRL_CSC_MODE_RGB2YCbCr,
25362306a36Sopenharmony_ci		       lcdif->base + LCDC_V8_CSC0_CTRL);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci		/*
25662306a36Sopenharmony_ci		 * CSC: BT.601 Limited Range RGB to YCbCr coefficients.
25762306a36Sopenharmony_ci		 *
25862306a36Sopenharmony_ci		 * |Y |   | 0.2568  0.5041  0.0979|   |R|   |16 |
25962306a36Sopenharmony_ci		 * |Cb| = |-0.1482 -0.2910  0.4392| * |G| + |128|
26062306a36Sopenharmony_ci		 * |Cr|   | 0.4392  0.4392 -0.3678|   |B|   |128|
26162306a36Sopenharmony_ci		 */
26262306a36Sopenharmony_ci		writel(CSC0_COEF0_A2(0x081) | CSC0_COEF0_A1(0x041),
26362306a36Sopenharmony_ci		       lcdif->base + LCDC_V8_CSC0_COEF0);
26462306a36Sopenharmony_ci		writel(CSC0_COEF1_B1(0x7db) | CSC0_COEF1_A3(0x019),
26562306a36Sopenharmony_ci		       lcdif->base + LCDC_V8_CSC0_COEF1);
26662306a36Sopenharmony_ci		writel(CSC0_COEF2_B3(0x070) | CSC0_COEF2_B2(0x7b6),
26762306a36Sopenharmony_ci		       lcdif->base + LCDC_V8_CSC0_COEF2);
26862306a36Sopenharmony_ci		writel(CSC0_COEF3_C2(0x7a2) | CSC0_COEF3_C1(0x070),
26962306a36Sopenharmony_ci		       lcdif->base + LCDC_V8_CSC0_COEF3);
27062306a36Sopenharmony_ci		writel(CSC0_COEF4_D1(0x010) | CSC0_COEF4_C3(0x7ee),
27162306a36Sopenharmony_ci		       lcdif->base + LCDC_V8_CSC0_COEF4);
27262306a36Sopenharmony_ci		writel(CSC0_COEF5_D3(0x080) | CSC0_COEF5_D2(0x080),
27362306a36Sopenharmony_ci		       lcdif->base + LCDC_V8_CSC0_COEF5);
27462306a36Sopenharmony_ci	} else if (in_yuv && !out_yuv) {
27562306a36Sopenharmony_ci		/* YCbCr -> RGB */
27662306a36Sopenharmony_ci		const u32 *coeffs =
27762306a36Sopenharmony_ci			lcdif_yuv2rgb_coeffs[plane_state->color_encoding]
27862306a36Sopenharmony_ci					    [plane_state->color_range];
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci		writel(CSC0_CTRL_CSC_MODE_YCbCr2RGB,
28162306a36Sopenharmony_ci		       lcdif->base + LCDC_V8_CSC0_CTRL);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci		writel(coeffs[0], lcdif->base + LCDC_V8_CSC0_COEF0);
28462306a36Sopenharmony_ci		writel(coeffs[1], lcdif->base + LCDC_V8_CSC0_COEF1);
28562306a36Sopenharmony_ci		writel(coeffs[2], lcdif->base + LCDC_V8_CSC0_COEF2);
28662306a36Sopenharmony_ci		writel(coeffs[3], lcdif->base + LCDC_V8_CSC0_COEF3);
28762306a36Sopenharmony_ci		writel(coeffs[4], lcdif->base + LCDC_V8_CSC0_COEF4);
28862306a36Sopenharmony_ci		writel(coeffs[5], lcdif->base + LCDC_V8_CSC0_COEF5);
28962306a36Sopenharmony_ci	} else {
29062306a36Sopenharmony_ci		/* RGB -> RGB, YCbCr -> YCbCr: bypass colorspace converter. */
29162306a36Sopenharmony_ci		writel(CSC0_CTRL_BYPASS, lcdif->base + LCDC_V8_CSC0_CTRL);
29262306a36Sopenharmony_ci	}
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_cistatic void lcdif_set_mode(struct lcdif_drm_private *lcdif, u32 bus_flags)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	struct drm_display_mode *m = &lcdif->crtc.state->adjusted_mode;
29862306a36Sopenharmony_ci	u32 ctrl = 0;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	if (m->flags & DRM_MODE_FLAG_NHSYNC)
30162306a36Sopenharmony_ci		ctrl |= CTRL_INV_HS;
30262306a36Sopenharmony_ci	if (m->flags & DRM_MODE_FLAG_NVSYNC)
30362306a36Sopenharmony_ci		ctrl |= CTRL_INV_VS;
30462306a36Sopenharmony_ci	if (bus_flags & DRM_BUS_FLAG_DE_LOW)
30562306a36Sopenharmony_ci		ctrl |= CTRL_INV_DE;
30662306a36Sopenharmony_ci	if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
30762306a36Sopenharmony_ci		ctrl |= CTRL_INV_PXCK;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	writel(ctrl, lcdif->base + LCDC_V8_CTRL);
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	writel(DISP_SIZE_DELTA_Y(m->vdisplay) |
31262306a36Sopenharmony_ci	       DISP_SIZE_DELTA_X(m->hdisplay),
31362306a36Sopenharmony_ci	       lcdif->base + LCDC_V8_DISP_SIZE);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	writel(HSYN_PARA_BP_H(m->htotal - m->hsync_end) |
31662306a36Sopenharmony_ci	       HSYN_PARA_FP_H(m->hsync_start - m->hdisplay),
31762306a36Sopenharmony_ci	       lcdif->base + LCDC_V8_HSYN_PARA);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	writel(VSYN_PARA_BP_V(m->vtotal - m->vsync_end) |
32062306a36Sopenharmony_ci	       VSYN_PARA_FP_V(m->vsync_start - m->vdisplay),
32162306a36Sopenharmony_ci	       lcdif->base + LCDC_V8_VSYN_PARA);
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	writel(VSYN_HSYN_WIDTH_PW_V(m->vsync_end - m->vsync_start) |
32462306a36Sopenharmony_ci	       VSYN_HSYN_WIDTH_PW_H(m->hsync_end - m->hsync_start),
32562306a36Sopenharmony_ci	       lcdif->base + LCDC_V8_VSYN_HSYN_WIDTH);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	writel(CTRLDESCL0_1_HEIGHT(m->vdisplay) |
32862306a36Sopenharmony_ci	       CTRLDESCL0_1_WIDTH(m->hdisplay),
32962306a36Sopenharmony_ci	       lcdif->base + LCDC_V8_CTRLDESCL0_1);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	/*
33262306a36Sopenharmony_ci	 * Undocumented P_SIZE and T_SIZE register but those written in the
33362306a36Sopenharmony_ci	 * downstream kernel those registers control the AXI burst size. As of
33462306a36Sopenharmony_ci	 * now there are two known values:
33562306a36Sopenharmony_ci	 *  1 - 128Byte
33662306a36Sopenharmony_ci	 *  2 - 256Byte
33762306a36Sopenharmony_ci	 * Downstream set it to 256B burst size to improve the memory
33862306a36Sopenharmony_ci	 * efficiency so set it here too.
33962306a36Sopenharmony_ci	 */
34062306a36Sopenharmony_ci	ctrl = CTRLDESCL0_3_P_SIZE(2) | CTRLDESCL0_3_T_SIZE(2) |
34162306a36Sopenharmony_ci	       CTRLDESCL0_3_PITCH(lcdif->crtc.primary->state->fb->pitches[0]);
34262306a36Sopenharmony_ci	writel(ctrl, lcdif->base + LCDC_V8_CTRLDESCL0_3);
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_cistatic void lcdif_enable_controller(struct lcdif_drm_private *lcdif)
34662306a36Sopenharmony_ci{
34762306a36Sopenharmony_ci	u32 reg;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	/* Set FIFO Panic watermarks, low 1/3, high 2/3 . */
35062306a36Sopenharmony_ci	writel(FIELD_PREP(PANIC0_THRES_LOW_MASK, 1 * PANIC0_THRES_MAX / 3) |
35162306a36Sopenharmony_ci	       FIELD_PREP(PANIC0_THRES_HIGH_MASK, 2 * PANIC0_THRES_MAX / 3),
35262306a36Sopenharmony_ci	       lcdif->base + LCDC_V8_PANIC0_THRES);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	/*
35562306a36Sopenharmony_ci	 * Enable FIFO Panic, this does not generate interrupt, but
35662306a36Sopenharmony_ci	 * boosts NoC priority based on FIFO Panic watermarks.
35762306a36Sopenharmony_ci	 */
35862306a36Sopenharmony_ci	writel(INT_ENABLE_D1_PLANE_PANIC_EN,
35962306a36Sopenharmony_ci	       lcdif->base + LCDC_V8_INT_ENABLE_D1);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	reg = readl(lcdif->base + LCDC_V8_DISP_PARA);
36262306a36Sopenharmony_ci	reg |= DISP_PARA_DISP_ON;
36362306a36Sopenharmony_ci	writel(reg, lcdif->base + LCDC_V8_DISP_PARA);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	reg = readl(lcdif->base + LCDC_V8_CTRLDESCL0_5);
36662306a36Sopenharmony_ci	reg |= CTRLDESCL0_5_EN;
36762306a36Sopenharmony_ci	writel(reg, lcdif->base + LCDC_V8_CTRLDESCL0_5);
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cistatic void lcdif_disable_controller(struct lcdif_drm_private *lcdif)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	u32 reg;
37362306a36Sopenharmony_ci	int ret;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	reg = readl(lcdif->base + LCDC_V8_CTRLDESCL0_5);
37662306a36Sopenharmony_ci	reg &= ~CTRLDESCL0_5_EN;
37762306a36Sopenharmony_ci	writel(reg, lcdif->base + LCDC_V8_CTRLDESCL0_5);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	ret = readl_poll_timeout(lcdif->base + LCDC_V8_CTRLDESCL0_5,
38062306a36Sopenharmony_ci				 reg, !(reg & CTRLDESCL0_5_EN),
38162306a36Sopenharmony_ci				 0, 36000);	/* Wait ~2 frame times max */
38262306a36Sopenharmony_ci	if (ret)
38362306a36Sopenharmony_ci		drm_err(lcdif->drm, "Failed to disable controller!\n");
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	reg = readl(lcdif->base + LCDC_V8_DISP_PARA);
38662306a36Sopenharmony_ci	reg &= ~DISP_PARA_DISP_ON;
38762306a36Sopenharmony_ci	writel(reg, lcdif->base + LCDC_V8_DISP_PARA);
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	/* Disable FIFO Panic NoC priority booster. */
39062306a36Sopenharmony_ci	writel(0, lcdif->base + LCDC_V8_INT_ENABLE_D1);
39162306a36Sopenharmony_ci}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_cistatic void lcdif_reset_block(struct lcdif_drm_private *lcdif)
39462306a36Sopenharmony_ci{
39562306a36Sopenharmony_ci	writel(CTRL_SW_RESET, lcdif->base + LCDC_V8_CTRL + REG_SET);
39662306a36Sopenharmony_ci	readl(lcdif->base + LCDC_V8_CTRL);
39762306a36Sopenharmony_ci	writel(CTRL_SW_RESET, lcdif->base + LCDC_V8_CTRL + REG_CLR);
39862306a36Sopenharmony_ci	readl(lcdif->base + LCDC_V8_CTRL);
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_cistatic void lcdif_crtc_mode_set_nofb(struct drm_crtc_state *crtc_state,
40262306a36Sopenharmony_ci				     struct drm_plane_state *plane_state)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	struct lcdif_crtc_state *lcdif_crtc_state = to_lcdif_crtc_state(crtc_state);
40562306a36Sopenharmony_ci	struct drm_device *drm = crtc_state->crtc->dev;
40662306a36Sopenharmony_ci	struct lcdif_drm_private *lcdif = to_lcdif_drm_private(drm);
40762306a36Sopenharmony_ci	struct drm_display_mode *m = &crtc_state->adjusted_mode;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	DRM_DEV_DEBUG_DRIVER(drm->dev, "Pixel clock: %dkHz (actual: %dkHz)\n",
41062306a36Sopenharmony_ci			     m->crtc_clock,
41162306a36Sopenharmony_ci			     (int)(clk_get_rate(lcdif->clk) / 1000));
41262306a36Sopenharmony_ci	DRM_DEV_DEBUG_DRIVER(drm->dev, "Bridge bus_flags: 0x%08X\n",
41362306a36Sopenharmony_ci			     lcdif_crtc_state->bus_flags);
41462306a36Sopenharmony_ci	DRM_DEV_DEBUG_DRIVER(drm->dev, "Mode flags: 0x%08X\n", m->flags);
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	/* Mandatory eLCDIF reset as per the Reference Manual */
41762306a36Sopenharmony_ci	lcdif_reset_block(lcdif);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	lcdif_set_formats(lcdif, plane_state, lcdif_crtc_state->bus_format);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	lcdif_set_mode(lcdif, lcdif_crtc_state->bus_flags);
42262306a36Sopenharmony_ci}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cistatic int lcdif_crtc_atomic_check(struct drm_crtc *crtc,
42562306a36Sopenharmony_ci				   struct drm_atomic_state *state)
42662306a36Sopenharmony_ci{
42762306a36Sopenharmony_ci	struct drm_device *drm = crtc->dev;
42862306a36Sopenharmony_ci	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
42962306a36Sopenharmony_ci									  crtc);
43062306a36Sopenharmony_ci	struct lcdif_crtc_state *lcdif_crtc_state = to_lcdif_crtc_state(crtc_state);
43162306a36Sopenharmony_ci	bool has_primary = crtc_state->plane_mask &
43262306a36Sopenharmony_ci			   drm_plane_mask(crtc->primary);
43362306a36Sopenharmony_ci	struct drm_connector_state *connector_state;
43462306a36Sopenharmony_ci	struct drm_connector *connector;
43562306a36Sopenharmony_ci	struct drm_encoder *encoder;
43662306a36Sopenharmony_ci	struct drm_bridge_state *bridge_state;
43762306a36Sopenharmony_ci	struct drm_bridge *bridge;
43862306a36Sopenharmony_ci	u32 bus_format, bus_flags;
43962306a36Sopenharmony_ci	bool format_set = false, flags_set = false;
44062306a36Sopenharmony_ci	int ret, i;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	/* The primary plane has to be enabled when the CRTC is active. */
44362306a36Sopenharmony_ci	if (crtc_state->active && !has_primary)
44462306a36Sopenharmony_ci		return -EINVAL;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	ret = drm_atomic_add_affected_planes(state, crtc);
44762306a36Sopenharmony_ci	if (ret)
44862306a36Sopenharmony_ci		return ret;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	/* Try to find consistent bus format and flags across first bridges. */
45162306a36Sopenharmony_ci	for_each_new_connector_in_state(state, connector, connector_state, i) {
45262306a36Sopenharmony_ci		if (!connector_state->crtc)
45362306a36Sopenharmony_ci			continue;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci		encoder = connector_state->best_encoder;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci		bridge = drm_bridge_chain_get_first_bridge(encoder);
45862306a36Sopenharmony_ci		if (!bridge)
45962306a36Sopenharmony_ci			continue;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci		bridge_state = drm_atomic_get_new_bridge_state(state, bridge);
46262306a36Sopenharmony_ci		if (!bridge_state)
46362306a36Sopenharmony_ci			bus_format = MEDIA_BUS_FMT_FIXED;
46462306a36Sopenharmony_ci		else
46562306a36Sopenharmony_ci			bus_format = bridge_state->input_bus_cfg.format;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci		if (bus_format == MEDIA_BUS_FMT_FIXED) {
46862306a36Sopenharmony_ci			dev_warn(drm->dev,
46962306a36Sopenharmony_ci				 "[ENCODER:%d:%s]'s bridge does not provide bus format, assuming MEDIA_BUS_FMT_RGB888_1X24.\n"
47062306a36Sopenharmony_ci				 "Please fix bridge driver by handling atomic_get_input_bus_fmts.\n",
47162306a36Sopenharmony_ci				 encoder->base.id, encoder->name);
47262306a36Sopenharmony_ci			bus_format = MEDIA_BUS_FMT_RGB888_1X24;
47362306a36Sopenharmony_ci		} else if (!bus_format) {
47462306a36Sopenharmony_ci			/* If all else fails, default to RGB888_1X24 */
47562306a36Sopenharmony_ci			bus_format = MEDIA_BUS_FMT_RGB888_1X24;
47662306a36Sopenharmony_ci		}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci		if (!format_set) {
47962306a36Sopenharmony_ci			lcdif_crtc_state->bus_format = bus_format;
48062306a36Sopenharmony_ci			format_set = true;
48162306a36Sopenharmony_ci		} else if (lcdif_crtc_state->bus_format != bus_format) {
48262306a36Sopenharmony_ci			DRM_DEV_DEBUG_DRIVER(drm->dev, "inconsistent bus format\n");
48362306a36Sopenharmony_ci			return -EINVAL;
48462306a36Sopenharmony_ci		}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci		if (bridge->timings)
48762306a36Sopenharmony_ci			bus_flags = bridge->timings->input_bus_flags;
48862306a36Sopenharmony_ci		else if (bridge_state)
48962306a36Sopenharmony_ci			bus_flags = bridge_state->input_bus_cfg.flags;
49062306a36Sopenharmony_ci		else
49162306a36Sopenharmony_ci			bus_flags = 0;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci		if (!flags_set) {
49462306a36Sopenharmony_ci			lcdif_crtc_state->bus_flags = bus_flags;
49562306a36Sopenharmony_ci			flags_set = true;
49662306a36Sopenharmony_ci		} else if (lcdif_crtc_state->bus_flags != bus_flags) {
49762306a36Sopenharmony_ci			DRM_DEV_DEBUG_DRIVER(drm->dev, "inconsistent bus flags\n");
49862306a36Sopenharmony_ci			return -EINVAL;
49962306a36Sopenharmony_ci		}
50062306a36Sopenharmony_ci	}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	return 0;
50362306a36Sopenharmony_ci}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_cistatic void lcdif_crtc_atomic_flush(struct drm_crtc *crtc,
50662306a36Sopenharmony_ci				    struct drm_atomic_state *state)
50762306a36Sopenharmony_ci{
50862306a36Sopenharmony_ci	struct lcdif_drm_private *lcdif = to_lcdif_drm_private(crtc->dev);
50962306a36Sopenharmony_ci	struct drm_pending_vblank_event *event;
51062306a36Sopenharmony_ci	u32 reg;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	reg = readl(lcdif->base + LCDC_V8_CTRLDESCL0_5);
51362306a36Sopenharmony_ci	reg |= CTRLDESCL0_5_SHADOW_LOAD_EN;
51462306a36Sopenharmony_ci	writel(reg, lcdif->base + LCDC_V8_CTRLDESCL0_5);
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	event = crtc->state->event;
51762306a36Sopenharmony_ci	crtc->state->event = NULL;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	if (!event)
52062306a36Sopenharmony_ci		return;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	spin_lock_irq(&crtc->dev->event_lock);
52362306a36Sopenharmony_ci	if (drm_crtc_vblank_get(crtc) == 0)
52462306a36Sopenharmony_ci		drm_crtc_arm_vblank_event(crtc, event);
52562306a36Sopenharmony_ci	else
52662306a36Sopenharmony_ci		drm_crtc_send_vblank_event(crtc, event);
52762306a36Sopenharmony_ci	spin_unlock_irq(&crtc->dev->event_lock);
52862306a36Sopenharmony_ci}
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_cistatic void lcdif_crtc_atomic_enable(struct drm_crtc *crtc,
53162306a36Sopenharmony_ci				     struct drm_atomic_state *state)
53262306a36Sopenharmony_ci{
53362306a36Sopenharmony_ci	struct lcdif_drm_private *lcdif = to_lcdif_drm_private(crtc->dev);
53462306a36Sopenharmony_ci	struct drm_crtc_state *new_cstate = drm_atomic_get_new_crtc_state(state, crtc);
53562306a36Sopenharmony_ci	struct drm_plane_state *new_pstate = drm_atomic_get_new_plane_state(state,
53662306a36Sopenharmony_ci									    crtc->primary);
53762306a36Sopenharmony_ci	struct drm_display_mode *m = &lcdif->crtc.state->adjusted_mode;
53862306a36Sopenharmony_ci	struct drm_device *drm = lcdif->drm;
53962306a36Sopenharmony_ci	dma_addr_t paddr;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	clk_set_rate(lcdif->clk, m->crtc_clock * 1000);
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	pm_runtime_get_sync(drm->dev);
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	lcdif_crtc_mode_set_nofb(new_cstate, new_pstate);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	/* Write cur_buf as well to avoid an initial corrupt frame */
54862306a36Sopenharmony_ci	paddr = drm_fb_dma_get_gem_addr(new_pstate->fb, new_pstate, 0);
54962306a36Sopenharmony_ci	if (paddr) {
55062306a36Sopenharmony_ci		writel(lower_32_bits(paddr),
55162306a36Sopenharmony_ci		       lcdif->base + LCDC_V8_CTRLDESCL_LOW0_4);
55262306a36Sopenharmony_ci		writel(CTRLDESCL_HIGH0_4_ADDR_HIGH(upper_32_bits(paddr)),
55362306a36Sopenharmony_ci		       lcdif->base + LCDC_V8_CTRLDESCL_HIGH0_4);
55462306a36Sopenharmony_ci	}
55562306a36Sopenharmony_ci	lcdif_enable_controller(lcdif);
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	drm_crtc_vblank_on(crtc);
55862306a36Sopenharmony_ci}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_cistatic void lcdif_crtc_atomic_disable(struct drm_crtc *crtc,
56162306a36Sopenharmony_ci				      struct drm_atomic_state *state)
56262306a36Sopenharmony_ci{
56362306a36Sopenharmony_ci	struct lcdif_drm_private *lcdif = to_lcdif_drm_private(crtc->dev);
56462306a36Sopenharmony_ci	struct drm_device *drm = lcdif->drm;
56562306a36Sopenharmony_ci	struct drm_pending_vblank_event *event;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	drm_crtc_vblank_off(crtc);
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	lcdif_disable_controller(lcdif);
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	spin_lock_irq(&drm->event_lock);
57262306a36Sopenharmony_ci	event = crtc->state->event;
57362306a36Sopenharmony_ci	if (event) {
57462306a36Sopenharmony_ci		crtc->state->event = NULL;
57562306a36Sopenharmony_ci		drm_crtc_send_vblank_event(crtc, event);
57662306a36Sopenharmony_ci	}
57762306a36Sopenharmony_ci	spin_unlock_irq(&drm->event_lock);
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	pm_runtime_put_sync(drm->dev);
58062306a36Sopenharmony_ci}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_cistatic void lcdif_crtc_atomic_destroy_state(struct drm_crtc *crtc,
58362306a36Sopenharmony_ci					    struct drm_crtc_state *state)
58462306a36Sopenharmony_ci{
58562306a36Sopenharmony_ci	__drm_atomic_helper_crtc_destroy_state(state);
58662306a36Sopenharmony_ci	kfree(to_lcdif_crtc_state(state));
58762306a36Sopenharmony_ci}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_cistatic void lcdif_crtc_reset(struct drm_crtc *crtc)
59062306a36Sopenharmony_ci{
59162306a36Sopenharmony_ci	struct lcdif_crtc_state *state;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	if (crtc->state)
59462306a36Sopenharmony_ci		lcdif_crtc_atomic_destroy_state(crtc, crtc->state);
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	crtc->state = NULL;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	state = kzalloc(sizeof(*state), GFP_KERNEL);
59962306a36Sopenharmony_ci	if (state)
60062306a36Sopenharmony_ci		__drm_atomic_helper_crtc_reset(crtc, &state->base);
60162306a36Sopenharmony_ci}
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_cistatic struct drm_crtc_state *
60462306a36Sopenharmony_cilcdif_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
60562306a36Sopenharmony_ci{
60662306a36Sopenharmony_ci	struct lcdif_crtc_state *old = to_lcdif_crtc_state(crtc->state);
60762306a36Sopenharmony_ci	struct lcdif_crtc_state *new;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	if (WARN_ON(!crtc->state))
61062306a36Sopenharmony_ci		return NULL;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	new = kzalloc(sizeof(*new), GFP_KERNEL);
61362306a36Sopenharmony_ci	if (!new)
61462306a36Sopenharmony_ci		return NULL;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	__drm_atomic_helper_crtc_duplicate_state(crtc, &new->base);
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	new->bus_format = old->bus_format;
61962306a36Sopenharmony_ci	new->bus_flags = old->bus_flags;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	return &new->base;
62262306a36Sopenharmony_ci}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_cistatic int lcdif_crtc_enable_vblank(struct drm_crtc *crtc)
62562306a36Sopenharmony_ci{
62662306a36Sopenharmony_ci	struct lcdif_drm_private *lcdif = to_lcdif_drm_private(crtc->dev);
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	/* Clear and enable VBLANK IRQ */
62962306a36Sopenharmony_ci	writel(INT_STATUS_D0_VS_BLANK, lcdif->base + LCDC_V8_INT_STATUS_D0);
63062306a36Sopenharmony_ci	writel(INT_ENABLE_D0_VS_BLANK_EN, lcdif->base + LCDC_V8_INT_ENABLE_D0);
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	return 0;
63362306a36Sopenharmony_ci}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_cistatic void lcdif_crtc_disable_vblank(struct drm_crtc *crtc)
63662306a36Sopenharmony_ci{
63762306a36Sopenharmony_ci	struct lcdif_drm_private *lcdif = to_lcdif_drm_private(crtc->dev);
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	/* Disable and clear VBLANK IRQ */
64062306a36Sopenharmony_ci	writel(0, lcdif->base + LCDC_V8_INT_ENABLE_D0);
64162306a36Sopenharmony_ci	writel(INT_STATUS_D0_VS_BLANK, lcdif->base + LCDC_V8_INT_STATUS_D0);
64262306a36Sopenharmony_ci}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_cistatic const struct drm_crtc_helper_funcs lcdif_crtc_helper_funcs = {
64562306a36Sopenharmony_ci	.atomic_check = lcdif_crtc_atomic_check,
64662306a36Sopenharmony_ci	.atomic_flush = lcdif_crtc_atomic_flush,
64762306a36Sopenharmony_ci	.atomic_enable = lcdif_crtc_atomic_enable,
64862306a36Sopenharmony_ci	.atomic_disable = lcdif_crtc_atomic_disable,
64962306a36Sopenharmony_ci};
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_cistatic const struct drm_crtc_funcs lcdif_crtc_funcs = {
65262306a36Sopenharmony_ci	.reset = lcdif_crtc_reset,
65362306a36Sopenharmony_ci	.destroy = drm_crtc_cleanup,
65462306a36Sopenharmony_ci	.set_config = drm_atomic_helper_set_config,
65562306a36Sopenharmony_ci	.page_flip = drm_atomic_helper_page_flip,
65662306a36Sopenharmony_ci	.atomic_duplicate_state = lcdif_crtc_atomic_duplicate_state,
65762306a36Sopenharmony_ci	.atomic_destroy_state = lcdif_crtc_atomic_destroy_state,
65862306a36Sopenharmony_ci	.enable_vblank = lcdif_crtc_enable_vblank,
65962306a36Sopenharmony_ci	.disable_vblank = lcdif_crtc_disable_vblank,
66062306a36Sopenharmony_ci};
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci/* -----------------------------------------------------------------------------
66362306a36Sopenharmony_ci * Planes
66462306a36Sopenharmony_ci */
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_cistatic int lcdif_plane_atomic_check(struct drm_plane *plane,
66762306a36Sopenharmony_ci				    struct drm_atomic_state *state)
66862306a36Sopenharmony_ci{
66962306a36Sopenharmony_ci	struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state,
67062306a36Sopenharmony_ci									     plane);
67162306a36Sopenharmony_ci	struct lcdif_drm_private *lcdif = to_lcdif_drm_private(plane->dev);
67262306a36Sopenharmony_ci	struct drm_crtc_state *crtc_state;
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	crtc_state = drm_atomic_get_new_crtc_state(state,
67562306a36Sopenharmony_ci						   &lcdif->crtc);
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	return drm_atomic_helper_check_plane_state(plane_state, crtc_state,
67862306a36Sopenharmony_ci						   DRM_PLANE_NO_SCALING,
67962306a36Sopenharmony_ci						   DRM_PLANE_NO_SCALING,
68062306a36Sopenharmony_ci						   false, true);
68162306a36Sopenharmony_ci}
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_cistatic void lcdif_plane_primary_atomic_update(struct drm_plane *plane,
68462306a36Sopenharmony_ci					      struct drm_atomic_state *state)
68562306a36Sopenharmony_ci{
68662306a36Sopenharmony_ci	struct lcdif_drm_private *lcdif = to_lcdif_drm_private(plane->dev);
68762306a36Sopenharmony_ci	struct drm_plane_state *new_pstate = drm_atomic_get_new_plane_state(state,
68862306a36Sopenharmony_ci									    plane);
68962306a36Sopenharmony_ci	dma_addr_t paddr;
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	paddr = drm_fb_dma_get_gem_addr(new_pstate->fb, new_pstate, 0);
69262306a36Sopenharmony_ci	if (paddr) {
69362306a36Sopenharmony_ci		writel(lower_32_bits(paddr),
69462306a36Sopenharmony_ci		       lcdif->base + LCDC_V8_CTRLDESCL_LOW0_4);
69562306a36Sopenharmony_ci		writel(CTRLDESCL_HIGH0_4_ADDR_HIGH(upper_32_bits(paddr)),
69662306a36Sopenharmony_ci		       lcdif->base + LCDC_V8_CTRLDESCL_HIGH0_4);
69762306a36Sopenharmony_ci	}
69862306a36Sopenharmony_ci}
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_cistatic bool lcdif_format_mod_supported(struct drm_plane *plane,
70162306a36Sopenharmony_ci				       uint32_t format,
70262306a36Sopenharmony_ci				       uint64_t modifier)
70362306a36Sopenharmony_ci{
70462306a36Sopenharmony_ci	return modifier == DRM_FORMAT_MOD_LINEAR;
70562306a36Sopenharmony_ci}
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_cistatic const struct drm_plane_helper_funcs lcdif_plane_primary_helper_funcs = {
70862306a36Sopenharmony_ci	.atomic_check = lcdif_plane_atomic_check,
70962306a36Sopenharmony_ci	.atomic_update = lcdif_plane_primary_atomic_update,
71062306a36Sopenharmony_ci};
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_cistatic const struct drm_plane_funcs lcdif_plane_funcs = {
71362306a36Sopenharmony_ci	.format_mod_supported	= lcdif_format_mod_supported,
71462306a36Sopenharmony_ci	.update_plane		= drm_atomic_helper_update_plane,
71562306a36Sopenharmony_ci	.disable_plane		= drm_atomic_helper_disable_plane,
71662306a36Sopenharmony_ci	.destroy		= drm_plane_cleanup,
71762306a36Sopenharmony_ci	.reset			= drm_atomic_helper_plane_reset,
71862306a36Sopenharmony_ci	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
71962306a36Sopenharmony_ci	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
72062306a36Sopenharmony_ci};
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_cistatic const u32 lcdif_primary_plane_formats[] = {
72362306a36Sopenharmony_ci	/* RGB */
72462306a36Sopenharmony_ci	DRM_FORMAT_RGB565,
72562306a36Sopenharmony_ci	DRM_FORMAT_RGB888,
72662306a36Sopenharmony_ci	DRM_FORMAT_XBGR8888,
72762306a36Sopenharmony_ci	DRM_FORMAT_XRGB1555,
72862306a36Sopenharmony_ci	DRM_FORMAT_XRGB4444,
72962306a36Sopenharmony_ci	DRM_FORMAT_XRGB8888,
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	/* Packed YCbCr */
73262306a36Sopenharmony_ci	DRM_FORMAT_YUYV,
73362306a36Sopenharmony_ci	DRM_FORMAT_YVYU,
73462306a36Sopenharmony_ci	DRM_FORMAT_UYVY,
73562306a36Sopenharmony_ci	DRM_FORMAT_VYUY,
73662306a36Sopenharmony_ci};
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_cistatic const u64 lcdif_modifiers[] = {
73962306a36Sopenharmony_ci	DRM_FORMAT_MOD_LINEAR,
74062306a36Sopenharmony_ci	DRM_FORMAT_MOD_INVALID
74162306a36Sopenharmony_ci};
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci/* -----------------------------------------------------------------------------
74462306a36Sopenharmony_ci * Initialization
74562306a36Sopenharmony_ci */
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ciint lcdif_kms_init(struct lcdif_drm_private *lcdif)
74862306a36Sopenharmony_ci{
74962306a36Sopenharmony_ci	const u32 supported_encodings = BIT(DRM_COLOR_YCBCR_BT601) |
75062306a36Sopenharmony_ci					BIT(DRM_COLOR_YCBCR_BT709) |
75162306a36Sopenharmony_ci					BIT(DRM_COLOR_YCBCR_BT2020);
75262306a36Sopenharmony_ci	const u32 supported_ranges = BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) |
75362306a36Sopenharmony_ci				     BIT(DRM_COLOR_YCBCR_FULL_RANGE);
75462306a36Sopenharmony_ci	struct drm_crtc *crtc = &lcdif->crtc;
75562306a36Sopenharmony_ci	int ret;
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	drm_plane_helper_add(&lcdif->planes.primary,
75862306a36Sopenharmony_ci			     &lcdif_plane_primary_helper_funcs);
75962306a36Sopenharmony_ci	ret = drm_universal_plane_init(lcdif->drm, &lcdif->planes.primary, 1,
76062306a36Sopenharmony_ci				       &lcdif_plane_funcs,
76162306a36Sopenharmony_ci				       lcdif_primary_plane_formats,
76262306a36Sopenharmony_ci				       ARRAY_SIZE(lcdif_primary_plane_formats),
76362306a36Sopenharmony_ci				       lcdif_modifiers, DRM_PLANE_TYPE_PRIMARY,
76462306a36Sopenharmony_ci				       NULL);
76562306a36Sopenharmony_ci	if (ret)
76662306a36Sopenharmony_ci		return ret;
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	ret = drm_plane_create_color_properties(&lcdif->planes.primary,
76962306a36Sopenharmony_ci						supported_encodings,
77062306a36Sopenharmony_ci						supported_ranges,
77162306a36Sopenharmony_ci						DRM_COLOR_YCBCR_BT601,
77262306a36Sopenharmony_ci						DRM_COLOR_YCBCR_LIMITED_RANGE);
77362306a36Sopenharmony_ci	if (ret)
77462306a36Sopenharmony_ci		return ret;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	drm_crtc_helper_add(crtc, &lcdif_crtc_helper_funcs);
77762306a36Sopenharmony_ci	return drm_crtc_init_with_planes(lcdif->drm, crtc,
77862306a36Sopenharmony_ci					 &lcdif->planes.primary, NULL,
77962306a36Sopenharmony_ci					 &lcdif_crtc_funcs, NULL);
78062306a36Sopenharmony_ci}
781