xref: /kernel/linux/linux-6.6/drivers/gpu/drm/stm/ltdc.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) STMicroelectronics SA 2017
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Authors: Philippe Cornu <philippe.cornu@st.com>
662306a36Sopenharmony_ci *          Yannick Fertre <yannick.fertre@st.com>
762306a36Sopenharmony_ci *          Fabien Dessenne <fabien.dessenne@st.com>
862306a36Sopenharmony_ci *          Mickael Reulier <mickael.reulier@st.com>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/clk.h>
1262306a36Sopenharmony_ci#include <linux/component.h>
1362306a36Sopenharmony_ci#include <linux/delay.h>
1462306a36Sopenharmony_ci#include <linux/interrupt.h>
1562306a36Sopenharmony_ci#include <linux/media-bus-format.h>
1662306a36Sopenharmony_ci#include <linux/module.h>
1762306a36Sopenharmony_ci#include <linux/of_graph.h>
1862306a36Sopenharmony_ci#include <linux/pinctrl/consumer.h>
1962306a36Sopenharmony_ci#include <linux/platform_device.h>
2062306a36Sopenharmony_ci#include <linux/pm_runtime.h>
2162306a36Sopenharmony_ci#include <linux/regmap.h>
2262306a36Sopenharmony_ci#include <linux/reset.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include <drm/drm_atomic.h>
2562306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h>
2662306a36Sopenharmony_ci#include <drm/drm_blend.h>
2762306a36Sopenharmony_ci#include <drm/drm_bridge.h>
2862306a36Sopenharmony_ci#include <drm/drm_device.h>
2962306a36Sopenharmony_ci#include <drm/drm_edid.h>
3062306a36Sopenharmony_ci#include <drm/drm_fb_dma_helper.h>
3162306a36Sopenharmony_ci#include <drm/drm_fourcc.h>
3262306a36Sopenharmony_ci#include <drm/drm_framebuffer.h>
3362306a36Sopenharmony_ci#include <drm/drm_gem_atomic_helper.h>
3462306a36Sopenharmony_ci#include <drm/drm_gem_dma_helper.h>
3562306a36Sopenharmony_ci#include <drm/drm_of.h>
3662306a36Sopenharmony_ci#include <drm/drm_probe_helper.h>
3762306a36Sopenharmony_ci#include <drm/drm_simple_kms_helper.h>
3862306a36Sopenharmony_ci#include <drm/drm_vblank.h>
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#include <video/videomode.h>
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#include "ltdc.h"
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci#define NB_CRTC 1
4562306a36Sopenharmony_ci#define CRTC_MASK GENMASK(NB_CRTC - 1, 0)
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#define MAX_IRQ 4
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#define HWVER_10200 0x010200
5062306a36Sopenharmony_ci#define HWVER_10300 0x010300
5162306a36Sopenharmony_ci#define HWVER_20101 0x020101
5262306a36Sopenharmony_ci#define HWVER_40100 0x040100
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci/*
5562306a36Sopenharmony_ci * The address of some registers depends on the HW version: such registers have
5662306a36Sopenharmony_ci * an extra offset specified with layer_ofs.
5762306a36Sopenharmony_ci */
5862306a36Sopenharmony_ci#define LAY_OFS_0	0x80
5962306a36Sopenharmony_ci#define LAY_OFS_1	0x100
6062306a36Sopenharmony_ci#define LAY_OFS	(ldev->caps.layer_ofs)
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/* Global register offsets */
6362306a36Sopenharmony_ci#define LTDC_IDR	0x0000		/* IDentification */
6462306a36Sopenharmony_ci#define LTDC_LCR	0x0004		/* Layer Count */
6562306a36Sopenharmony_ci#define LTDC_SSCR	0x0008		/* Synchronization Size Configuration */
6662306a36Sopenharmony_ci#define LTDC_BPCR	0x000C		/* Back Porch Configuration */
6762306a36Sopenharmony_ci#define LTDC_AWCR	0x0010		/* Active Width Configuration */
6862306a36Sopenharmony_ci#define LTDC_TWCR	0x0014		/* Total Width Configuration */
6962306a36Sopenharmony_ci#define LTDC_GCR	0x0018		/* Global Control */
7062306a36Sopenharmony_ci#define LTDC_GC1R	0x001C		/* Global Configuration 1 */
7162306a36Sopenharmony_ci#define LTDC_GC2R	0x0020		/* Global Configuration 2 */
7262306a36Sopenharmony_ci#define LTDC_SRCR	0x0024		/* Shadow Reload Configuration */
7362306a36Sopenharmony_ci#define LTDC_GACR	0x0028		/* GAmma Correction */
7462306a36Sopenharmony_ci#define LTDC_BCCR	0x002C		/* Background Color Configuration */
7562306a36Sopenharmony_ci#define LTDC_IER	0x0034		/* Interrupt Enable */
7662306a36Sopenharmony_ci#define LTDC_ISR	0x0038		/* Interrupt Status */
7762306a36Sopenharmony_ci#define LTDC_ICR	0x003C		/* Interrupt Clear */
7862306a36Sopenharmony_ci#define LTDC_LIPCR	0x0040		/* Line Interrupt Position Conf. */
7962306a36Sopenharmony_ci#define LTDC_CPSR	0x0044		/* Current Position Status */
8062306a36Sopenharmony_ci#define LTDC_CDSR	0x0048		/* Current Display Status */
8162306a36Sopenharmony_ci#define LTDC_EDCR	0x0060		/* External Display Control */
8262306a36Sopenharmony_ci#define LTDC_CCRCR	0x007C		/* Computed CRC value */
8362306a36Sopenharmony_ci#define LTDC_FUT	0x0090		/* Fifo underrun Threshold */
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci/* Layer register offsets */
8662306a36Sopenharmony_ci#define LTDC_L1C0R	(ldev->caps.layer_regs[0])	/* L1 configuration 0 */
8762306a36Sopenharmony_ci#define LTDC_L1C1R	(ldev->caps.layer_regs[1])	/* L1 configuration 1 */
8862306a36Sopenharmony_ci#define LTDC_L1RCR	(ldev->caps.layer_regs[2])	/* L1 reload control */
8962306a36Sopenharmony_ci#define LTDC_L1CR	(ldev->caps.layer_regs[3])	/* L1 control register */
9062306a36Sopenharmony_ci#define LTDC_L1WHPCR	(ldev->caps.layer_regs[4])	/* L1 window horizontal position configuration */
9162306a36Sopenharmony_ci#define LTDC_L1WVPCR	(ldev->caps.layer_regs[5])	/* L1 window vertical position configuration */
9262306a36Sopenharmony_ci#define LTDC_L1CKCR	(ldev->caps.layer_regs[6])	/* L1 color keying configuration */
9362306a36Sopenharmony_ci#define LTDC_L1PFCR	(ldev->caps.layer_regs[7])	/* L1 pixel format configuration */
9462306a36Sopenharmony_ci#define LTDC_L1CACR	(ldev->caps.layer_regs[8])	/* L1 constant alpha configuration */
9562306a36Sopenharmony_ci#define LTDC_L1DCCR	(ldev->caps.layer_regs[9])	/* L1 default color configuration */
9662306a36Sopenharmony_ci#define LTDC_L1BFCR	(ldev->caps.layer_regs[10])	/* L1 blending factors configuration */
9762306a36Sopenharmony_ci#define LTDC_L1BLCR	(ldev->caps.layer_regs[11])	/* L1 burst length configuration */
9862306a36Sopenharmony_ci#define LTDC_L1PCR	(ldev->caps.layer_regs[12])	/* L1 planar configuration */
9962306a36Sopenharmony_ci#define LTDC_L1CFBAR	(ldev->caps.layer_regs[13])	/* L1 color frame buffer address */
10062306a36Sopenharmony_ci#define LTDC_L1CFBLR	(ldev->caps.layer_regs[14])	/* L1 color frame buffer length */
10162306a36Sopenharmony_ci#define LTDC_L1CFBLNR	(ldev->caps.layer_regs[15])	/* L1 color frame buffer line number */
10262306a36Sopenharmony_ci#define LTDC_L1AFBA0R	(ldev->caps.layer_regs[16])	/* L1 auxiliary frame buffer address 0 */
10362306a36Sopenharmony_ci#define LTDC_L1AFBA1R	(ldev->caps.layer_regs[17])	/* L1 auxiliary frame buffer address 1 */
10462306a36Sopenharmony_ci#define LTDC_L1AFBLR	(ldev->caps.layer_regs[18])	/* L1 auxiliary frame buffer length */
10562306a36Sopenharmony_ci#define LTDC_L1AFBLNR	(ldev->caps.layer_regs[19])	/* L1 auxiliary frame buffer line number */
10662306a36Sopenharmony_ci#define LTDC_L1CLUTWR	(ldev->caps.layer_regs[20])	/* L1 CLUT write */
10762306a36Sopenharmony_ci#define LTDC_L1CYR0R	(ldev->caps.layer_regs[21])	/* L1 Conversion YCbCr RGB 0 */
10862306a36Sopenharmony_ci#define LTDC_L1CYR1R	(ldev->caps.layer_regs[22])	/* L1 Conversion YCbCr RGB 1 */
10962306a36Sopenharmony_ci#define LTDC_L1FPF0R	(ldev->caps.layer_regs[23])	/* L1 Flexible Pixel Format 0 */
11062306a36Sopenharmony_ci#define LTDC_L1FPF1R	(ldev->caps.layer_regs[24])	/* L1 Flexible Pixel Format 1 */
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci/* Bit definitions */
11362306a36Sopenharmony_ci#define SSCR_VSH	GENMASK(10, 0)	/* Vertical Synchronization Height */
11462306a36Sopenharmony_ci#define SSCR_HSW	GENMASK(27, 16)	/* Horizontal Synchronization Width */
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci#define BPCR_AVBP	GENMASK(10, 0)	/* Accumulated Vertical Back Porch */
11762306a36Sopenharmony_ci#define BPCR_AHBP	GENMASK(27, 16)	/* Accumulated Horizontal Back Porch */
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci#define AWCR_AAH	GENMASK(10, 0)	/* Accumulated Active Height */
12062306a36Sopenharmony_ci#define AWCR_AAW	GENMASK(27, 16)	/* Accumulated Active Width */
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci#define TWCR_TOTALH	GENMASK(10, 0)	/* TOTAL Height */
12362306a36Sopenharmony_ci#define TWCR_TOTALW	GENMASK(27, 16)	/* TOTAL Width */
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci#define GCR_LTDCEN	BIT(0)		/* LTDC ENable */
12662306a36Sopenharmony_ci#define GCR_DEN		BIT(16)		/* Dither ENable */
12762306a36Sopenharmony_ci#define GCR_CRCEN	BIT(19)		/* CRC ENable */
12862306a36Sopenharmony_ci#define GCR_PCPOL	BIT(28)		/* Pixel Clock POLarity-Inverted */
12962306a36Sopenharmony_ci#define GCR_DEPOL	BIT(29)		/* Data Enable POLarity-High */
13062306a36Sopenharmony_ci#define GCR_VSPOL	BIT(30)		/* Vertical Synchro POLarity-High */
13162306a36Sopenharmony_ci#define GCR_HSPOL	BIT(31)		/* Horizontal Synchro POLarity-High */
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci#define GC1R_WBCH	GENMASK(3, 0)	/* Width of Blue CHannel output */
13462306a36Sopenharmony_ci#define GC1R_WGCH	GENMASK(7, 4)	/* Width of Green Channel output */
13562306a36Sopenharmony_ci#define GC1R_WRCH	GENMASK(11, 8)	/* Width of Red Channel output */
13662306a36Sopenharmony_ci#define GC1R_PBEN	BIT(12)		/* Precise Blending ENable */
13762306a36Sopenharmony_ci#define GC1R_DT		GENMASK(15, 14)	/* Dithering Technique */
13862306a36Sopenharmony_ci#define GC1R_GCT	GENMASK(19, 17)	/* Gamma Correction Technique */
13962306a36Sopenharmony_ci#define GC1R_SHREN	BIT(21)		/* SHadow Registers ENabled */
14062306a36Sopenharmony_ci#define GC1R_BCP	BIT(22)		/* Background Colour Programmable */
14162306a36Sopenharmony_ci#define GC1R_BBEN	BIT(23)		/* Background Blending ENabled */
14262306a36Sopenharmony_ci#define GC1R_LNIP	BIT(24)		/* Line Number IRQ Position */
14362306a36Sopenharmony_ci#define GC1R_TP		BIT(25)		/* Timing Programmable */
14462306a36Sopenharmony_ci#define GC1R_IPP	BIT(26)		/* IRQ Polarity Programmable */
14562306a36Sopenharmony_ci#define GC1R_SPP	BIT(27)		/* Sync Polarity Programmable */
14662306a36Sopenharmony_ci#define GC1R_DWP	BIT(28)		/* Dither Width Programmable */
14762306a36Sopenharmony_ci#define GC1R_STREN	BIT(29)		/* STatus Registers ENabled */
14862306a36Sopenharmony_ci#define GC1R_BMEN	BIT(31)		/* Blind Mode ENabled */
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci#define GC2R_EDCA	BIT(0)		/* External Display Control Ability  */
15162306a36Sopenharmony_ci#define GC2R_STSAEN	BIT(1)		/* Slave Timing Sync Ability ENabled */
15262306a36Sopenharmony_ci#define GC2R_DVAEN	BIT(2)		/* Dual-View Ability ENabled */
15362306a36Sopenharmony_ci#define GC2R_DPAEN	BIT(3)		/* Dual-Port Ability ENabled */
15462306a36Sopenharmony_ci#define GC2R_BW		GENMASK(6, 4)	/* Bus Width (log2 of nb of bytes) */
15562306a36Sopenharmony_ci#define GC2R_EDCEN	BIT(7)		/* External Display Control ENabled */
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci#define SRCR_IMR	BIT(0)		/* IMmediate Reload */
15862306a36Sopenharmony_ci#define SRCR_VBR	BIT(1)		/* Vertical Blanking Reload */
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci#define BCCR_BCBLACK	0x00		/* Background Color BLACK */
16162306a36Sopenharmony_ci#define BCCR_BCBLUE	GENMASK(7, 0)	/* Background Color BLUE */
16262306a36Sopenharmony_ci#define BCCR_BCGREEN	GENMASK(15, 8)	/* Background Color GREEN */
16362306a36Sopenharmony_ci#define BCCR_BCRED	GENMASK(23, 16)	/* Background Color RED */
16462306a36Sopenharmony_ci#define BCCR_BCWHITE	GENMASK(23, 0)	/* Background Color WHITE */
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci#define IER_LIE		BIT(0)		/* Line Interrupt Enable */
16762306a36Sopenharmony_ci#define IER_FUWIE	BIT(1)		/* Fifo Underrun Warning Interrupt Enable */
16862306a36Sopenharmony_ci#define IER_TERRIE	BIT(2)		/* Transfer ERRor Interrupt Enable */
16962306a36Sopenharmony_ci#define IER_RRIE	BIT(3)		/* Register Reload Interrupt Enable */
17062306a36Sopenharmony_ci#define IER_FUEIE	BIT(6)		/* Fifo Underrun Error Interrupt Enable */
17162306a36Sopenharmony_ci#define IER_CRCIE	BIT(7)		/* CRC Error Interrupt Enable */
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci#define CPSR_CYPOS	GENMASK(15, 0)	/* Current Y position */
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci#define ISR_LIF		BIT(0)		/* Line Interrupt Flag */
17662306a36Sopenharmony_ci#define ISR_FUWIF	BIT(1)		/* Fifo Underrun Warning Interrupt Flag */
17762306a36Sopenharmony_ci#define ISR_TERRIF	BIT(2)		/* Transfer ERRor Interrupt Flag */
17862306a36Sopenharmony_ci#define ISR_RRIF	BIT(3)		/* Register Reload Interrupt Flag */
17962306a36Sopenharmony_ci#define ISR_FUEIF	BIT(6)		/* Fifo Underrun Error Interrupt Flag */
18062306a36Sopenharmony_ci#define ISR_CRCIF	BIT(7)		/* CRC Error Interrupt Flag */
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci#define EDCR_OCYEN	BIT(25)		/* Output Conversion to YCbCr 422: ENable */
18362306a36Sopenharmony_ci#define EDCR_OCYSEL	BIT(26)		/* Output Conversion to YCbCr 422: SELection of the CCIR */
18462306a36Sopenharmony_ci#define EDCR_OCYCO	BIT(27)		/* Output Conversion to YCbCr 422: Chrominance Order */
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci#define LXCR_LEN	BIT(0)		/* Layer ENable */
18762306a36Sopenharmony_ci#define LXCR_COLKEN	BIT(1)		/* Color Keying Enable */
18862306a36Sopenharmony_ci#define LXCR_CLUTEN	BIT(4)		/* Color Look-Up Table ENable */
18962306a36Sopenharmony_ci#define LXCR_HMEN	BIT(8)		/* Horizontal Mirroring ENable */
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci#define LXWHPCR_WHSTPOS	GENMASK(11, 0)	/* Window Horizontal StarT POSition */
19262306a36Sopenharmony_ci#define LXWHPCR_WHSPPOS	GENMASK(27, 16)	/* Window Horizontal StoP POSition */
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci#define LXWVPCR_WVSTPOS	GENMASK(10, 0)	/* Window Vertical StarT POSition */
19562306a36Sopenharmony_ci#define LXWVPCR_WVSPPOS	GENMASK(26, 16)	/* Window Vertical StoP POSition */
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci#define LXPFCR_PF	GENMASK(2, 0)	/* Pixel Format */
19862306a36Sopenharmony_ci#define PF_FLEXIBLE	0x7		/* Flexible Pixel Format selected */
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci#define LXCACR_CONSTA	GENMASK(7, 0)	/* CONSTant Alpha */
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci#define LXBFCR_BF2	GENMASK(2, 0)	/* Blending Factor 2 */
20362306a36Sopenharmony_ci#define LXBFCR_BF1	GENMASK(10, 8)	/* Blending Factor 1 */
20462306a36Sopenharmony_ci#define LXBFCR_BOR	GENMASK(18, 16) /* Blending ORder */
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci#define LXCFBLR_CFBLL	GENMASK(12, 0)	/* Color Frame Buffer Line Length */
20762306a36Sopenharmony_ci#define LXCFBLR_CFBP	GENMASK(31, 16) /* Color Frame Buffer Pitch in bytes */
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci#define LXCFBLNR_CFBLN	GENMASK(10, 0)	/* Color Frame Buffer Line Number */
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci#define LXCR_C1R_YIA	BIT(0)		/* Ycbcr 422 Interleaved Ability */
21262306a36Sopenharmony_ci#define LXCR_C1R_YSPA	BIT(1)		/* Ycbcr 420 Semi-Planar Ability */
21362306a36Sopenharmony_ci#define LXCR_C1R_YFPA	BIT(2)		/* Ycbcr 420 Full-Planar Ability */
21462306a36Sopenharmony_ci#define LXCR_C1R_SCA	BIT(31)		/* SCaling Ability*/
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci#define LxPCR_YREN	BIT(9)		/* Y Rescale Enable for the color dynamic range */
21762306a36Sopenharmony_ci#define LxPCR_OF	BIT(8)		/* Odd pixel First */
21862306a36Sopenharmony_ci#define LxPCR_CBF	BIT(7)		/* CB component First */
21962306a36Sopenharmony_ci#define LxPCR_YF	BIT(6)		/* Y component First */
22062306a36Sopenharmony_ci#define LxPCR_YCM	GENMASK(5, 4)	/* Ycbcr Conversion Mode */
22162306a36Sopenharmony_ci#define YCM_I		0x0		/* Interleaved 422 */
22262306a36Sopenharmony_ci#define YCM_SP		0x1		/* Semi-Planar 420 */
22362306a36Sopenharmony_ci#define YCM_FP		0x2		/* Full-Planar 420 */
22462306a36Sopenharmony_ci#define LxPCR_YCEN	BIT(3)		/* YCbCr-to-RGB Conversion Enable */
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci#define LXRCR_IMR	BIT(0)		/* IMmediate Reload */
22762306a36Sopenharmony_ci#define LXRCR_VBR	BIT(1)		/* Vertical Blanking Reload */
22862306a36Sopenharmony_ci#define LXRCR_GRMSK	BIT(2)		/* Global (centralized) Reload MaSKed */
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci#define CLUT_SIZE	256
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci#define CONSTA_MAX	0xFF		/* CONSTant Alpha MAX= 1.0 */
23362306a36Sopenharmony_ci#define BF1_PAXCA	0x600		/* Pixel Alpha x Constant Alpha */
23462306a36Sopenharmony_ci#define BF1_CA		0x400		/* Constant Alpha */
23562306a36Sopenharmony_ci#define BF2_1PAXCA	0x007		/* 1 - (Pixel Alpha x Constant Alpha) */
23662306a36Sopenharmony_ci#define BF2_1CA		0x005		/* 1 - Constant Alpha */
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci#define NB_PF		8		/* Max nb of HW pixel format */
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci#define FUT_DFT		128		/* Default value of fifo underrun threshold */
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci/*
24362306a36Sopenharmony_ci * Skip the first value and the second in case CRC was enabled during
24462306a36Sopenharmony_ci * the thread irq. This is to be sure CRC value is relevant for the
24562306a36Sopenharmony_ci * frame.
24662306a36Sopenharmony_ci */
24762306a36Sopenharmony_ci#define CRC_SKIP_FRAMES 2
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_cienum ltdc_pix_fmt {
25062306a36Sopenharmony_ci	PF_NONE,
25162306a36Sopenharmony_ci	/* RGB formats */
25262306a36Sopenharmony_ci	PF_ARGB8888,		/* ARGB [32 bits] */
25362306a36Sopenharmony_ci	PF_RGBA8888,		/* RGBA [32 bits] */
25462306a36Sopenharmony_ci	PF_ABGR8888,		/* ABGR [32 bits] */
25562306a36Sopenharmony_ci	PF_BGRA8888,		/* BGRA [32 bits] */
25662306a36Sopenharmony_ci	PF_RGB888,		/* RGB [24 bits] */
25762306a36Sopenharmony_ci	PF_BGR888,		/* BGR [24 bits] */
25862306a36Sopenharmony_ci	PF_RGB565,		/* RGB [16 bits] */
25962306a36Sopenharmony_ci	PF_BGR565,		/* BGR [16 bits] */
26062306a36Sopenharmony_ci	PF_ARGB1555,		/* ARGB A:1 bit RGB:15 bits [16 bits] */
26162306a36Sopenharmony_ci	PF_ARGB4444,		/* ARGB A:4 bits R/G/B: 4 bits each [16 bits] */
26262306a36Sopenharmony_ci	/* Indexed formats */
26362306a36Sopenharmony_ci	PF_L8,			/* Indexed 8 bits [8 bits] */
26462306a36Sopenharmony_ci	PF_AL44,		/* Alpha:4 bits + indexed 4 bits [8 bits] */
26562306a36Sopenharmony_ci	PF_AL88			/* Alpha:8 bits + indexed 8 bits [16 bits] */
26662306a36Sopenharmony_ci};
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci/* The index gives the encoding of the pixel format for an HW version */
26962306a36Sopenharmony_cistatic const enum ltdc_pix_fmt ltdc_pix_fmt_a0[NB_PF] = {
27062306a36Sopenharmony_ci	PF_ARGB8888,		/* 0x00 */
27162306a36Sopenharmony_ci	PF_RGB888,		/* 0x01 */
27262306a36Sopenharmony_ci	PF_RGB565,		/* 0x02 */
27362306a36Sopenharmony_ci	PF_ARGB1555,		/* 0x03 */
27462306a36Sopenharmony_ci	PF_ARGB4444,		/* 0x04 */
27562306a36Sopenharmony_ci	PF_L8,			/* 0x05 */
27662306a36Sopenharmony_ci	PF_AL44,		/* 0x06 */
27762306a36Sopenharmony_ci	PF_AL88			/* 0x07 */
27862306a36Sopenharmony_ci};
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_cistatic const enum ltdc_pix_fmt ltdc_pix_fmt_a1[NB_PF] = {
28162306a36Sopenharmony_ci	PF_ARGB8888,		/* 0x00 */
28262306a36Sopenharmony_ci	PF_RGB888,		/* 0x01 */
28362306a36Sopenharmony_ci	PF_RGB565,		/* 0x02 */
28462306a36Sopenharmony_ci	PF_RGBA8888,		/* 0x03 */
28562306a36Sopenharmony_ci	PF_AL44,		/* 0x04 */
28662306a36Sopenharmony_ci	PF_L8,			/* 0x05 */
28762306a36Sopenharmony_ci	PF_ARGB1555,		/* 0x06 */
28862306a36Sopenharmony_ci	PF_ARGB4444		/* 0x07 */
28962306a36Sopenharmony_ci};
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_cistatic const enum ltdc_pix_fmt ltdc_pix_fmt_a2[NB_PF] = {
29262306a36Sopenharmony_ci	PF_ARGB8888,		/* 0x00 */
29362306a36Sopenharmony_ci	PF_ABGR8888,		/* 0x01 */
29462306a36Sopenharmony_ci	PF_RGBA8888,		/* 0x02 */
29562306a36Sopenharmony_ci	PF_BGRA8888,		/* 0x03 */
29662306a36Sopenharmony_ci	PF_RGB565,		/* 0x04 */
29762306a36Sopenharmony_ci	PF_BGR565,		/* 0x05 */
29862306a36Sopenharmony_ci	PF_RGB888,		/* 0x06 */
29962306a36Sopenharmony_ci	PF_NONE			/* 0x07 */
30062306a36Sopenharmony_ci};
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cistatic const u32 ltdc_drm_fmt_a0[] = {
30362306a36Sopenharmony_ci	DRM_FORMAT_ARGB8888,
30462306a36Sopenharmony_ci	DRM_FORMAT_XRGB8888,
30562306a36Sopenharmony_ci	DRM_FORMAT_RGB888,
30662306a36Sopenharmony_ci	DRM_FORMAT_RGB565,
30762306a36Sopenharmony_ci	DRM_FORMAT_ARGB1555,
30862306a36Sopenharmony_ci	DRM_FORMAT_XRGB1555,
30962306a36Sopenharmony_ci	DRM_FORMAT_ARGB4444,
31062306a36Sopenharmony_ci	DRM_FORMAT_XRGB4444,
31162306a36Sopenharmony_ci	DRM_FORMAT_C8
31262306a36Sopenharmony_ci};
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_cistatic const u32 ltdc_drm_fmt_a1[] = {
31562306a36Sopenharmony_ci	DRM_FORMAT_ARGB8888,
31662306a36Sopenharmony_ci	DRM_FORMAT_XRGB8888,
31762306a36Sopenharmony_ci	DRM_FORMAT_RGB888,
31862306a36Sopenharmony_ci	DRM_FORMAT_RGB565,
31962306a36Sopenharmony_ci	DRM_FORMAT_RGBA8888,
32062306a36Sopenharmony_ci	DRM_FORMAT_RGBX8888,
32162306a36Sopenharmony_ci	DRM_FORMAT_ARGB1555,
32262306a36Sopenharmony_ci	DRM_FORMAT_XRGB1555,
32362306a36Sopenharmony_ci	DRM_FORMAT_ARGB4444,
32462306a36Sopenharmony_ci	DRM_FORMAT_XRGB4444,
32562306a36Sopenharmony_ci	DRM_FORMAT_C8
32662306a36Sopenharmony_ci};
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_cistatic const u32 ltdc_drm_fmt_a2[] = {
32962306a36Sopenharmony_ci	DRM_FORMAT_ARGB8888,
33062306a36Sopenharmony_ci	DRM_FORMAT_XRGB8888,
33162306a36Sopenharmony_ci	DRM_FORMAT_ABGR8888,
33262306a36Sopenharmony_ci	DRM_FORMAT_XBGR8888,
33362306a36Sopenharmony_ci	DRM_FORMAT_RGBA8888,
33462306a36Sopenharmony_ci	DRM_FORMAT_RGBX8888,
33562306a36Sopenharmony_ci	DRM_FORMAT_BGRA8888,
33662306a36Sopenharmony_ci	DRM_FORMAT_BGRX8888,
33762306a36Sopenharmony_ci	DRM_FORMAT_RGB565,
33862306a36Sopenharmony_ci	DRM_FORMAT_BGR565,
33962306a36Sopenharmony_ci	DRM_FORMAT_RGB888,
34062306a36Sopenharmony_ci	DRM_FORMAT_BGR888,
34162306a36Sopenharmony_ci	DRM_FORMAT_ARGB1555,
34262306a36Sopenharmony_ci	DRM_FORMAT_XRGB1555,
34362306a36Sopenharmony_ci	DRM_FORMAT_ARGB4444,
34462306a36Sopenharmony_ci	DRM_FORMAT_XRGB4444,
34562306a36Sopenharmony_ci	DRM_FORMAT_C8
34662306a36Sopenharmony_ci};
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistatic const u32 ltdc_drm_fmt_ycbcr_cp[] = {
34962306a36Sopenharmony_ci	DRM_FORMAT_YUYV,
35062306a36Sopenharmony_ci	DRM_FORMAT_YVYU,
35162306a36Sopenharmony_ci	DRM_FORMAT_UYVY,
35262306a36Sopenharmony_ci	DRM_FORMAT_VYUY
35362306a36Sopenharmony_ci};
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_cistatic const u32 ltdc_drm_fmt_ycbcr_sp[] = {
35662306a36Sopenharmony_ci	DRM_FORMAT_NV12,
35762306a36Sopenharmony_ci	DRM_FORMAT_NV21
35862306a36Sopenharmony_ci};
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_cistatic const u32 ltdc_drm_fmt_ycbcr_fp[] = {
36162306a36Sopenharmony_ci	DRM_FORMAT_YUV420,
36262306a36Sopenharmony_ci	DRM_FORMAT_YVU420
36362306a36Sopenharmony_ci};
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci/* Layer register offsets */
36662306a36Sopenharmony_cistatic const u32 ltdc_layer_regs_a0[] = {
36762306a36Sopenharmony_ci	0x80,	/* L1 configuration 0 */
36862306a36Sopenharmony_ci	0x00,	/* not available */
36962306a36Sopenharmony_ci	0x00,	/* not available */
37062306a36Sopenharmony_ci	0x84,	/* L1 control register */
37162306a36Sopenharmony_ci	0x88,	/* L1 window horizontal position configuration */
37262306a36Sopenharmony_ci	0x8c,	/* L1 window vertical position configuration */
37362306a36Sopenharmony_ci	0x90,	/* L1 color keying configuration */
37462306a36Sopenharmony_ci	0x94,	/* L1 pixel format configuration */
37562306a36Sopenharmony_ci	0x98,	/* L1 constant alpha configuration */
37662306a36Sopenharmony_ci	0x9c,	/* L1 default color configuration */
37762306a36Sopenharmony_ci	0xa0,	/* L1 blending factors configuration */
37862306a36Sopenharmony_ci	0x00,	/* not available */
37962306a36Sopenharmony_ci	0x00,	/* not available */
38062306a36Sopenharmony_ci	0xac,	/* L1 color frame buffer address */
38162306a36Sopenharmony_ci	0xb0,	/* L1 color frame buffer length */
38262306a36Sopenharmony_ci	0xb4,	/* L1 color frame buffer line number */
38362306a36Sopenharmony_ci	0x00,	/* not available */
38462306a36Sopenharmony_ci	0x00,	/* not available */
38562306a36Sopenharmony_ci	0x00,	/* not available */
38662306a36Sopenharmony_ci	0x00,	/* not available */
38762306a36Sopenharmony_ci	0xc4,	/* L1 CLUT write */
38862306a36Sopenharmony_ci	0x00,	/* not available */
38962306a36Sopenharmony_ci	0x00,	/* not available */
39062306a36Sopenharmony_ci	0x00,	/* not available */
39162306a36Sopenharmony_ci	0x00	/* not available */
39262306a36Sopenharmony_ci};
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_cistatic const u32 ltdc_layer_regs_a1[] = {
39562306a36Sopenharmony_ci	0x80,	/* L1 configuration 0 */
39662306a36Sopenharmony_ci	0x84,	/* L1 configuration 1 */
39762306a36Sopenharmony_ci	0x00,	/* L1 reload control */
39862306a36Sopenharmony_ci	0x88,	/* L1 control register */
39962306a36Sopenharmony_ci	0x8c,	/* L1 window horizontal position configuration */
40062306a36Sopenharmony_ci	0x90,	/* L1 window vertical position configuration */
40162306a36Sopenharmony_ci	0x94,	/* L1 color keying configuration */
40262306a36Sopenharmony_ci	0x98,	/* L1 pixel format configuration */
40362306a36Sopenharmony_ci	0x9c,	/* L1 constant alpha configuration */
40462306a36Sopenharmony_ci	0xa0,	/* L1 default color configuration */
40562306a36Sopenharmony_ci	0xa4,	/* L1 blending factors configuration */
40662306a36Sopenharmony_ci	0xa8,	/* L1 burst length configuration */
40762306a36Sopenharmony_ci	0x00,	/* not available */
40862306a36Sopenharmony_ci	0xac,	/* L1 color frame buffer address */
40962306a36Sopenharmony_ci	0xb0,	/* L1 color frame buffer length */
41062306a36Sopenharmony_ci	0xb4,	/* L1 color frame buffer line number */
41162306a36Sopenharmony_ci	0xb8,	/* L1 auxiliary frame buffer address 0 */
41262306a36Sopenharmony_ci	0xbc,	/* L1 auxiliary frame buffer address 1 */
41362306a36Sopenharmony_ci	0xc0,	/* L1 auxiliary frame buffer length */
41462306a36Sopenharmony_ci	0xc4,	/* L1 auxiliary frame buffer line number */
41562306a36Sopenharmony_ci	0xc8,	/* L1 CLUT write */
41662306a36Sopenharmony_ci	0x00,	/* not available */
41762306a36Sopenharmony_ci	0x00,	/* not available */
41862306a36Sopenharmony_ci	0x00,	/* not available */
41962306a36Sopenharmony_ci	0x00	/* not available */
42062306a36Sopenharmony_ci};
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_cistatic const u32 ltdc_layer_regs_a2[] = {
42362306a36Sopenharmony_ci	0x100,	/* L1 configuration 0 */
42462306a36Sopenharmony_ci	0x104,	/* L1 configuration 1 */
42562306a36Sopenharmony_ci	0x108,	/* L1 reload control */
42662306a36Sopenharmony_ci	0x10c,	/* L1 control register */
42762306a36Sopenharmony_ci	0x110,	/* L1 window horizontal position configuration */
42862306a36Sopenharmony_ci	0x114,	/* L1 window vertical position configuration */
42962306a36Sopenharmony_ci	0x118,	/* L1 color keying configuration */
43062306a36Sopenharmony_ci	0x11c,	/* L1 pixel format configuration */
43162306a36Sopenharmony_ci	0x120,	/* L1 constant alpha configuration */
43262306a36Sopenharmony_ci	0x124,	/* L1 default color configuration */
43362306a36Sopenharmony_ci	0x128,	/* L1 blending factors configuration */
43462306a36Sopenharmony_ci	0x12c,	/* L1 burst length configuration */
43562306a36Sopenharmony_ci	0x130,	/* L1 planar configuration */
43662306a36Sopenharmony_ci	0x134,	/* L1 color frame buffer address */
43762306a36Sopenharmony_ci	0x138,	/* L1 color frame buffer length */
43862306a36Sopenharmony_ci	0x13c,	/* L1 color frame buffer line number */
43962306a36Sopenharmony_ci	0x140,	/* L1 auxiliary frame buffer address 0 */
44062306a36Sopenharmony_ci	0x144,	/* L1 auxiliary frame buffer address 1 */
44162306a36Sopenharmony_ci	0x148,	/* L1 auxiliary frame buffer length */
44262306a36Sopenharmony_ci	0x14c,	/* L1 auxiliary frame buffer line number */
44362306a36Sopenharmony_ci	0x150,	/* L1 CLUT write */
44462306a36Sopenharmony_ci	0x16c,	/* L1 Conversion YCbCr RGB 0 */
44562306a36Sopenharmony_ci	0x170,	/* L1 Conversion YCbCr RGB 1 */
44662306a36Sopenharmony_ci	0x174,	/* L1 Flexible Pixel Format 0 */
44762306a36Sopenharmony_ci	0x178	/* L1 Flexible Pixel Format 1 */
44862306a36Sopenharmony_ci};
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_cistatic const u64 ltdc_format_modifiers[] = {
45162306a36Sopenharmony_ci	DRM_FORMAT_MOD_LINEAR,
45262306a36Sopenharmony_ci	DRM_FORMAT_MOD_INVALID
45362306a36Sopenharmony_ci};
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_cistatic const struct regmap_config stm32_ltdc_regmap_cfg = {
45662306a36Sopenharmony_ci	.reg_bits = 32,
45762306a36Sopenharmony_ci	.val_bits = 32,
45862306a36Sopenharmony_ci	.reg_stride = sizeof(u32),
45962306a36Sopenharmony_ci	.max_register = 0x400,
46062306a36Sopenharmony_ci	.use_relaxed_mmio = true,
46162306a36Sopenharmony_ci	.cache_type = REGCACHE_NONE,
46262306a36Sopenharmony_ci};
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_cistatic const u32 ltdc_ycbcr2rgb_coeffs[DRM_COLOR_ENCODING_MAX][DRM_COLOR_RANGE_MAX][2] = {
46562306a36Sopenharmony_ci	[DRM_COLOR_YCBCR_BT601][DRM_COLOR_YCBCR_LIMITED_RANGE] = {
46662306a36Sopenharmony_ci		0x02040199,	/* (b_cb = 516 / r_cr = 409) */
46762306a36Sopenharmony_ci		0x006400D0	/* (g_cb = 100 / g_cr = 208) */
46862306a36Sopenharmony_ci	},
46962306a36Sopenharmony_ci	[DRM_COLOR_YCBCR_BT601][DRM_COLOR_YCBCR_FULL_RANGE] = {
47062306a36Sopenharmony_ci		0x01C60167,	/* (b_cb = 454 / r_cr = 359) */
47162306a36Sopenharmony_ci		0x005800B7	/* (g_cb = 88 / g_cr = 183) */
47262306a36Sopenharmony_ci	},
47362306a36Sopenharmony_ci	[DRM_COLOR_YCBCR_BT709][DRM_COLOR_YCBCR_LIMITED_RANGE] = {
47462306a36Sopenharmony_ci		0x021D01CB,	/* (b_cb = 541 / r_cr = 459) */
47562306a36Sopenharmony_ci		0x00370089	/* (g_cb = 55 / g_cr = 137) */
47662306a36Sopenharmony_ci	},
47762306a36Sopenharmony_ci	[DRM_COLOR_YCBCR_BT709][DRM_COLOR_YCBCR_FULL_RANGE] = {
47862306a36Sopenharmony_ci		0x01DB0193,	/* (b_cb = 475 / r_cr = 403) */
47962306a36Sopenharmony_ci		0x00300078	/* (g_cb = 48 / g_cr = 120) */
48062306a36Sopenharmony_ci	}
48162306a36Sopenharmony_ci	/* BT2020 not supported */
48262306a36Sopenharmony_ci};
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_cistatic inline struct ltdc_device *crtc_to_ltdc(struct drm_crtc *crtc)
48562306a36Sopenharmony_ci{
48662306a36Sopenharmony_ci	return (struct ltdc_device *)crtc->dev->dev_private;
48762306a36Sopenharmony_ci}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_cistatic inline struct ltdc_device *plane_to_ltdc(struct drm_plane *plane)
49062306a36Sopenharmony_ci{
49162306a36Sopenharmony_ci	return (struct ltdc_device *)plane->dev->dev_private;
49262306a36Sopenharmony_ci}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_cistatic inline struct ltdc_device *encoder_to_ltdc(struct drm_encoder *enc)
49562306a36Sopenharmony_ci{
49662306a36Sopenharmony_ci	return (struct ltdc_device *)enc->dev->dev_private;
49762306a36Sopenharmony_ci}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_cistatic inline enum ltdc_pix_fmt to_ltdc_pixelformat(u32 drm_fmt)
50062306a36Sopenharmony_ci{
50162306a36Sopenharmony_ci	enum ltdc_pix_fmt pf;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	switch (drm_fmt) {
50462306a36Sopenharmony_ci	case DRM_FORMAT_ARGB8888:
50562306a36Sopenharmony_ci	case DRM_FORMAT_XRGB8888:
50662306a36Sopenharmony_ci		pf = PF_ARGB8888;
50762306a36Sopenharmony_ci		break;
50862306a36Sopenharmony_ci	case DRM_FORMAT_ABGR8888:
50962306a36Sopenharmony_ci	case DRM_FORMAT_XBGR8888:
51062306a36Sopenharmony_ci		pf = PF_ABGR8888;
51162306a36Sopenharmony_ci		break;
51262306a36Sopenharmony_ci	case DRM_FORMAT_RGBA8888:
51362306a36Sopenharmony_ci	case DRM_FORMAT_RGBX8888:
51462306a36Sopenharmony_ci		pf = PF_RGBA8888;
51562306a36Sopenharmony_ci		break;
51662306a36Sopenharmony_ci	case DRM_FORMAT_BGRA8888:
51762306a36Sopenharmony_ci	case DRM_FORMAT_BGRX8888:
51862306a36Sopenharmony_ci		pf = PF_BGRA8888;
51962306a36Sopenharmony_ci		break;
52062306a36Sopenharmony_ci	case DRM_FORMAT_RGB888:
52162306a36Sopenharmony_ci		pf = PF_RGB888;
52262306a36Sopenharmony_ci		break;
52362306a36Sopenharmony_ci	case DRM_FORMAT_BGR888:
52462306a36Sopenharmony_ci		pf = PF_BGR888;
52562306a36Sopenharmony_ci		break;
52662306a36Sopenharmony_ci	case DRM_FORMAT_RGB565:
52762306a36Sopenharmony_ci		pf = PF_RGB565;
52862306a36Sopenharmony_ci		break;
52962306a36Sopenharmony_ci	case DRM_FORMAT_BGR565:
53062306a36Sopenharmony_ci		pf = PF_BGR565;
53162306a36Sopenharmony_ci		break;
53262306a36Sopenharmony_ci	case DRM_FORMAT_ARGB1555:
53362306a36Sopenharmony_ci	case DRM_FORMAT_XRGB1555:
53462306a36Sopenharmony_ci		pf = PF_ARGB1555;
53562306a36Sopenharmony_ci		break;
53662306a36Sopenharmony_ci	case DRM_FORMAT_ARGB4444:
53762306a36Sopenharmony_ci	case DRM_FORMAT_XRGB4444:
53862306a36Sopenharmony_ci		pf = PF_ARGB4444;
53962306a36Sopenharmony_ci		break;
54062306a36Sopenharmony_ci	case DRM_FORMAT_C8:
54162306a36Sopenharmony_ci		pf = PF_L8;
54262306a36Sopenharmony_ci		break;
54362306a36Sopenharmony_ci	default:
54462306a36Sopenharmony_ci		pf = PF_NONE;
54562306a36Sopenharmony_ci		break;
54662306a36Sopenharmony_ci		/* Note: There are no DRM_FORMAT for AL44 and AL88 */
54762306a36Sopenharmony_ci	}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	return pf;
55062306a36Sopenharmony_ci}
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_cistatic inline u32 ltdc_set_flexible_pixel_format(struct drm_plane *plane, enum ltdc_pix_fmt pix_fmt)
55362306a36Sopenharmony_ci{
55462306a36Sopenharmony_ci	struct ltdc_device *ldev = plane_to_ltdc(plane);
55562306a36Sopenharmony_ci	u32 lofs = plane->index * LAY_OFS, ret = PF_FLEXIBLE;
55662306a36Sopenharmony_ci	int psize, alen, apos, rlen, rpos, glen, gpos, blen, bpos;
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	switch (pix_fmt) {
55962306a36Sopenharmony_ci	case PF_BGR888:
56062306a36Sopenharmony_ci		psize = 3;
56162306a36Sopenharmony_ci		alen = 0; apos = 0; rlen = 8; rpos = 0;
56262306a36Sopenharmony_ci		glen = 8; gpos = 8; blen = 8; bpos = 16;
56362306a36Sopenharmony_ci	break;
56462306a36Sopenharmony_ci	case PF_ARGB1555:
56562306a36Sopenharmony_ci		psize = 2;
56662306a36Sopenharmony_ci		alen = 1; apos = 15; rlen = 5; rpos = 10;
56762306a36Sopenharmony_ci		glen = 5; gpos = 5;  blen = 5; bpos = 0;
56862306a36Sopenharmony_ci	break;
56962306a36Sopenharmony_ci	case PF_ARGB4444:
57062306a36Sopenharmony_ci		psize = 2;
57162306a36Sopenharmony_ci		alen = 4; apos = 12; rlen = 4; rpos = 8;
57262306a36Sopenharmony_ci		glen = 4; gpos = 4; blen = 4; bpos = 0;
57362306a36Sopenharmony_ci	break;
57462306a36Sopenharmony_ci	case PF_L8:
57562306a36Sopenharmony_ci		psize = 1;
57662306a36Sopenharmony_ci		alen = 0; apos = 0; rlen = 8; rpos = 0;
57762306a36Sopenharmony_ci		glen = 8; gpos = 0; blen = 8; bpos = 0;
57862306a36Sopenharmony_ci	break;
57962306a36Sopenharmony_ci	case PF_AL44:
58062306a36Sopenharmony_ci		psize = 1;
58162306a36Sopenharmony_ci		alen = 4; apos = 4; rlen = 4; rpos = 0;
58262306a36Sopenharmony_ci		glen = 4; gpos = 0; blen = 4; bpos = 0;
58362306a36Sopenharmony_ci	break;
58462306a36Sopenharmony_ci	case PF_AL88:
58562306a36Sopenharmony_ci		psize = 2;
58662306a36Sopenharmony_ci		alen = 8; apos = 8; rlen = 8; rpos = 0;
58762306a36Sopenharmony_ci		glen = 8; gpos = 0; blen = 8; bpos = 0;
58862306a36Sopenharmony_ci	break;
58962306a36Sopenharmony_ci	default:
59062306a36Sopenharmony_ci		ret = NB_PF; /* error case, trace msg is handled by the caller */
59162306a36Sopenharmony_ci	break;
59262306a36Sopenharmony_ci	}
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	if (ret == PF_FLEXIBLE) {
59562306a36Sopenharmony_ci		regmap_write(ldev->regmap, LTDC_L1FPF0R + lofs,
59662306a36Sopenharmony_ci			     (rlen << 14)  + (rpos << 9) + (alen << 5) + apos);
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci		regmap_write(ldev->regmap, LTDC_L1FPF1R + lofs,
59962306a36Sopenharmony_ci			     (psize << 18) + (blen << 14)  + (bpos << 9) + (glen << 5) + gpos);
60062306a36Sopenharmony_ci	}
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	return ret;
60362306a36Sopenharmony_ci}
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci/*
60662306a36Sopenharmony_ci * All non-alpha color formats derived from native alpha color formats are
60762306a36Sopenharmony_ci * either characterized by a FourCC format code
60862306a36Sopenharmony_ci */
60962306a36Sopenharmony_cistatic inline u32 is_xrgb(u32 drm)
61062306a36Sopenharmony_ci{
61162306a36Sopenharmony_ci	return ((drm & 0xFF) == 'X' || ((drm >> 8) & 0xFF) == 'X');
61262306a36Sopenharmony_ci}
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_cistatic inline void ltdc_set_ycbcr_config(struct drm_plane *plane, u32 drm_pix_fmt)
61562306a36Sopenharmony_ci{
61662306a36Sopenharmony_ci	struct ltdc_device *ldev = plane_to_ltdc(plane);
61762306a36Sopenharmony_ci	struct drm_plane_state *state = plane->state;
61862306a36Sopenharmony_ci	u32 lofs = plane->index * LAY_OFS;
61962306a36Sopenharmony_ci	u32 val;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	switch (drm_pix_fmt) {
62262306a36Sopenharmony_ci	case DRM_FORMAT_YUYV:
62362306a36Sopenharmony_ci		val = (YCM_I << 4) | LxPCR_YF | LxPCR_CBF;
62462306a36Sopenharmony_ci		break;
62562306a36Sopenharmony_ci	case DRM_FORMAT_YVYU:
62662306a36Sopenharmony_ci		val = (YCM_I << 4) | LxPCR_YF;
62762306a36Sopenharmony_ci		break;
62862306a36Sopenharmony_ci	case DRM_FORMAT_UYVY:
62962306a36Sopenharmony_ci		val = (YCM_I << 4) | LxPCR_CBF;
63062306a36Sopenharmony_ci		break;
63162306a36Sopenharmony_ci	case DRM_FORMAT_VYUY:
63262306a36Sopenharmony_ci		val = (YCM_I << 4);
63362306a36Sopenharmony_ci		break;
63462306a36Sopenharmony_ci	case DRM_FORMAT_NV12:
63562306a36Sopenharmony_ci		val = (YCM_SP << 4) | LxPCR_CBF;
63662306a36Sopenharmony_ci		break;
63762306a36Sopenharmony_ci	case DRM_FORMAT_NV21:
63862306a36Sopenharmony_ci		val = (YCM_SP << 4);
63962306a36Sopenharmony_ci		break;
64062306a36Sopenharmony_ci	case DRM_FORMAT_YUV420:
64162306a36Sopenharmony_ci	case DRM_FORMAT_YVU420:
64262306a36Sopenharmony_ci		val = (YCM_FP << 4);
64362306a36Sopenharmony_ci		break;
64462306a36Sopenharmony_ci	default:
64562306a36Sopenharmony_ci		/* RGB or not a YCbCr supported format */
64662306a36Sopenharmony_ci		DRM_ERROR("Unsupported pixel format: %u\n", drm_pix_fmt);
64762306a36Sopenharmony_ci		return;
64862306a36Sopenharmony_ci	}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	/* Enable limited range */
65162306a36Sopenharmony_ci	if (state->color_range == DRM_COLOR_YCBCR_LIMITED_RANGE)
65262306a36Sopenharmony_ci		val |= LxPCR_YREN;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	/* enable ycbcr conversion */
65562306a36Sopenharmony_ci	val |= LxPCR_YCEN;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	regmap_write(ldev->regmap, LTDC_L1PCR + lofs, val);
65862306a36Sopenharmony_ci}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_cistatic inline void ltdc_set_ycbcr_coeffs(struct drm_plane *plane)
66162306a36Sopenharmony_ci{
66262306a36Sopenharmony_ci	struct ltdc_device *ldev = plane_to_ltdc(plane);
66362306a36Sopenharmony_ci	struct drm_plane_state *state = plane->state;
66462306a36Sopenharmony_ci	enum drm_color_encoding enc = state->color_encoding;
66562306a36Sopenharmony_ci	enum drm_color_range ran = state->color_range;
66662306a36Sopenharmony_ci	u32 lofs = plane->index * LAY_OFS;
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	if (enc != DRM_COLOR_YCBCR_BT601 && enc != DRM_COLOR_YCBCR_BT709) {
66962306a36Sopenharmony_ci		DRM_ERROR("color encoding %d not supported, use bt601 by default\n", enc);
67062306a36Sopenharmony_ci		/* set by default color encoding to DRM_COLOR_YCBCR_BT601 */
67162306a36Sopenharmony_ci		enc = DRM_COLOR_YCBCR_BT601;
67262306a36Sopenharmony_ci	}
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	if (ran != DRM_COLOR_YCBCR_LIMITED_RANGE && ran != DRM_COLOR_YCBCR_FULL_RANGE) {
67562306a36Sopenharmony_ci		DRM_ERROR("color range %d not supported, use limited range by default\n", ran);
67662306a36Sopenharmony_ci		/* set by default color range to DRM_COLOR_YCBCR_LIMITED_RANGE */
67762306a36Sopenharmony_ci		ran = DRM_COLOR_YCBCR_LIMITED_RANGE;
67862306a36Sopenharmony_ci	}
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("Color encoding=%d, range=%d\n", enc, ran);
68162306a36Sopenharmony_ci	regmap_write(ldev->regmap, LTDC_L1CYR0R + lofs,
68262306a36Sopenharmony_ci		     ltdc_ycbcr2rgb_coeffs[enc][ran][0]);
68362306a36Sopenharmony_ci	regmap_write(ldev->regmap, LTDC_L1CYR1R + lofs,
68462306a36Sopenharmony_ci		     ltdc_ycbcr2rgb_coeffs[enc][ran][1]);
68562306a36Sopenharmony_ci}
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_cistatic inline void ltdc_irq_crc_handle(struct ltdc_device *ldev,
68862306a36Sopenharmony_ci				       struct drm_crtc *crtc)
68962306a36Sopenharmony_ci{
69062306a36Sopenharmony_ci	u32 crc;
69162306a36Sopenharmony_ci	int ret;
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	if (ldev->crc_skip_count < CRC_SKIP_FRAMES) {
69462306a36Sopenharmony_ci		ldev->crc_skip_count++;
69562306a36Sopenharmony_ci		return;
69662306a36Sopenharmony_ci	}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	/* Get the CRC of the frame */
69962306a36Sopenharmony_ci	ret = regmap_read(ldev->regmap, LTDC_CCRCR, &crc);
70062306a36Sopenharmony_ci	if (ret)
70162306a36Sopenharmony_ci		return;
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	/* Report to DRM the CRC (hw dependent feature) */
70462306a36Sopenharmony_ci	drm_crtc_add_crc_entry(crtc, true, drm_crtc_accurate_vblank_count(crtc), &crc);
70562306a36Sopenharmony_ci}
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_cistatic irqreturn_t ltdc_irq_thread(int irq, void *arg)
70862306a36Sopenharmony_ci{
70962306a36Sopenharmony_ci	struct drm_device *ddev = arg;
71062306a36Sopenharmony_ci	struct ltdc_device *ldev = ddev->dev_private;
71162306a36Sopenharmony_ci	struct drm_crtc *crtc = drm_crtc_from_index(ddev, 0);
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	/* Line IRQ : trigger the vblank event */
71462306a36Sopenharmony_ci	if (ldev->irq_status & ISR_LIF) {
71562306a36Sopenharmony_ci		drm_crtc_handle_vblank(crtc);
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci		/* Early return if CRC is not active */
71862306a36Sopenharmony_ci		if (ldev->crc_active)
71962306a36Sopenharmony_ci			ltdc_irq_crc_handle(ldev, crtc);
72062306a36Sopenharmony_ci	}
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	mutex_lock(&ldev->err_lock);
72362306a36Sopenharmony_ci	if (ldev->irq_status & ISR_TERRIF)
72462306a36Sopenharmony_ci		ldev->transfer_err++;
72562306a36Sopenharmony_ci	if (ldev->irq_status & ISR_FUEIF)
72662306a36Sopenharmony_ci		ldev->fifo_err++;
72762306a36Sopenharmony_ci	if (ldev->irq_status & ISR_FUWIF)
72862306a36Sopenharmony_ci		ldev->fifo_warn++;
72962306a36Sopenharmony_ci	mutex_unlock(&ldev->err_lock);
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	return IRQ_HANDLED;
73262306a36Sopenharmony_ci}
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_cistatic irqreturn_t ltdc_irq(int irq, void *arg)
73562306a36Sopenharmony_ci{
73662306a36Sopenharmony_ci	struct drm_device *ddev = arg;
73762306a36Sopenharmony_ci	struct ltdc_device *ldev = ddev->dev_private;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	/*
74062306a36Sopenharmony_ci	 *  Read & Clear the interrupt status
74162306a36Sopenharmony_ci	 *  In order to write / read registers in this critical section
74262306a36Sopenharmony_ci	 *  very quickly, the regmap functions are not used.
74362306a36Sopenharmony_ci	 */
74462306a36Sopenharmony_ci	ldev->irq_status = readl_relaxed(ldev->regs + LTDC_ISR);
74562306a36Sopenharmony_ci	writel_relaxed(ldev->irq_status, ldev->regs + LTDC_ICR);
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	return IRQ_WAKE_THREAD;
74862306a36Sopenharmony_ci}
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci/*
75162306a36Sopenharmony_ci * DRM_CRTC
75262306a36Sopenharmony_ci */
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_cistatic void ltdc_crtc_update_clut(struct drm_crtc *crtc)
75562306a36Sopenharmony_ci{
75662306a36Sopenharmony_ci	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
75762306a36Sopenharmony_ci	struct drm_color_lut *lut;
75862306a36Sopenharmony_ci	u32 val;
75962306a36Sopenharmony_ci	int i;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	if (!crtc->state->color_mgmt_changed || !crtc->state->gamma_lut)
76262306a36Sopenharmony_ci		return;
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	lut = (struct drm_color_lut *)crtc->state->gamma_lut->data;
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	for (i = 0; i < CLUT_SIZE; i++, lut++) {
76762306a36Sopenharmony_ci		val = ((lut->red << 8) & 0xff0000) | (lut->green & 0xff00) |
76862306a36Sopenharmony_ci			(lut->blue >> 8) | (i << 24);
76962306a36Sopenharmony_ci		regmap_write(ldev->regmap, LTDC_L1CLUTWR, val);
77062306a36Sopenharmony_ci	}
77162306a36Sopenharmony_ci}
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_cistatic void ltdc_crtc_atomic_enable(struct drm_crtc *crtc,
77462306a36Sopenharmony_ci				    struct drm_atomic_state *state)
77562306a36Sopenharmony_ci{
77662306a36Sopenharmony_ci	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
77762306a36Sopenharmony_ci	struct drm_device *ddev = crtc->dev;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("\n");
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	pm_runtime_get_sync(ddev->dev);
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	/* Sets the background color value */
78462306a36Sopenharmony_ci	regmap_write(ldev->regmap, LTDC_BCCR, BCCR_BCBLACK);
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	/* Enable IRQ */
78762306a36Sopenharmony_ci	regmap_set_bits(ldev->regmap, LTDC_IER, IER_FUWIE | IER_FUEIE | IER_RRIE | IER_TERRIE);
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	/* Commit shadow registers = update planes at next vblank */
79062306a36Sopenharmony_ci	if (!ldev->caps.plane_reg_shadow)
79162306a36Sopenharmony_ci		regmap_set_bits(ldev->regmap, LTDC_SRCR, SRCR_VBR);
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	drm_crtc_vblank_on(crtc);
79462306a36Sopenharmony_ci}
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_cistatic void ltdc_crtc_atomic_disable(struct drm_crtc *crtc,
79762306a36Sopenharmony_ci				     struct drm_atomic_state *state)
79862306a36Sopenharmony_ci{
79962306a36Sopenharmony_ci	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
80062306a36Sopenharmony_ci	struct drm_device *ddev = crtc->dev;
80162306a36Sopenharmony_ci	int layer_index = 0;
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("\n");
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	drm_crtc_vblank_off(crtc);
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	/* Disable all layers */
80862306a36Sopenharmony_ci	for (layer_index = 0; layer_index < ldev->caps.nb_layers; layer_index++)
80962306a36Sopenharmony_ci		regmap_write_bits(ldev->regmap, LTDC_L1CR + layer_index * LAY_OFS,
81062306a36Sopenharmony_ci				  LXCR_CLUTEN | LXCR_LEN, 0);
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	/* disable IRQ */
81362306a36Sopenharmony_ci	regmap_clear_bits(ldev->regmap, LTDC_IER, IER_FUWIE | IER_FUEIE | IER_RRIE | IER_TERRIE);
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	/* immediately commit disable of layers before switching off LTDC */
81662306a36Sopenharmony_ci	if (!ldev->caps.plane_reg_shadow)
81762306a36Sopenharmony_ci		regmap_set_bits(ldev->regmap, LTDC_SRCR, SRCR_IMR);
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	pm_runtime_put_sync(ddev->dev);
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	/*  clear interrupt error counters */
82262306a36Sopenharmony_ci	mutex_lock(&ldev->err_lock);
82362306a36Sopenharmony_ci	ldev->transfer_err = 0;
82462306a36Sopenharmony_ci	ldev->fifo_err = 0;
82562306a36Sopenharmony_ci	ldev->fifo_warn = 0;
82662306a36Sopenharmony_ci	mutex_unlock(&ldev->err_lock);
82762306a36Sopenharmony_ci}
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci#define CLK_TOLERANCE_HZ 50
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_cistatic enum drm_mode_status
83262306a36Sopenharmony_ciltdc_crtc_mode_valid(struct drm_crtc *crtc,
83362306a36Sopenharmony_ci		     const struct drm_display_mode *mode)
83462306a36Sopenharmony_ci{
83562306a36Sopenharmony_ci	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
83662306a36Sopenharmony_ci	int target = mode->clock * 1000;
83762306a36Sopenharmony_ci	int target_min = target - CLK_TOLERANCE_HZ;
83862306a36Sopenharmony_ci	int target_max = target + CLK_TOLERANCE_HZ;
83962306a36Sopenharmony_ci	int result;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	result = clk_round_rate(ldev->pixel_clk, target);
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("clk rate target %d, available %d\n", target, result);
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	/* Filter modes according to the max frequency supported by the pads */
84662306a36Sopenharmony_ci	if (result > ldev->caps.pad_max_freq_hz)
84762306a36Sopenharmony_ci		return MODE_CLOCK_HIGH;
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci	/*
85062306a36Sopenharmony_ci	 * Accept all "preferred" modes:
85162306a36Sopenharmony_ci	 * - this is important for panels because panel clock tolerances are
85262306a36Sopenharmony_ci	 *   bigger than hdmi ones and there is no reason to not accept them
85362306a36Sopenharmony_ci	 *   (the fps may vary a little but it is not a problem).
85462306a36Sopenharmony_ci	 * - the hdmi preferred mode will be accepted too, but userland will
85562306a36Sopenharmony_ci	 *   be able to use others hdmi "valid" modes if necessary.
85662306a36Sopenharmony_ci	 */
85762306a36Sopenharmony_ci	if (mode->type & DRM_MODE_TYPE_PREFERRED)
85862306a36Sopenharmony_ci		return MODE_OK;
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	/*
86162306a36Sopenharmony_ci	 * Filter modes according to the clock value, particularly useful for
86262306a36Sopenharmony_ci	 * hdmi modes that require precise pixel clocks.
86362306a36Sopenharmony_ci	 */
86462306a36Sopenharmony_ci	if (result < target_min || result > target_max)
86562306a36Sopenharmony_ci		return MODE_CLOCK_RANGE;
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	return MODE_OK;
86862306a36Sopenharmony_ci}
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_cistatic bool ltdc_crtc_mode_fixup(struct drm_crtc *crtc,
87162306a36Sopenharmony_ci				 const struct drm_display_mode *mode,
87262306a36Sopenharmony_ci				 struct drm_display_mode *adjusted_mode)
87362306a36Sopenharmony_ci{
87462306a36Sopenharmony_ci	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
87562306a36Sopenharmony_ci	int rate = mode->clock * 1000;
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	if (clk_set_rate(ldev->pixel_clk, rate) < 0) {
87862306a36Sopenharmony_ci		DRM_ERROR("Cannot set rate (%dHz) for pixel clk\n", rate);
87962306a36Sopenharmony_ci		return false;
88062306a36Sopenharmony_ci	}
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	adjusted_mode->clock = clk_get_rate(ldev->pixel_clk) / 1000;
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("requested clock %dkHz, adjusted clock %dkHz\n",
88562306a36Sopenharmony_ci			 mode->clock, adjusted_mode->clock);
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	return true;
88862306a36Sopenharmony_ci}
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_cistatic void ltdc_crtc_mode_set_nofb(struct drm_crtc *crtc)
89162306a36Sopenharmony_ci{
89262306a36Sopenharmony_ci	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
89362306a36Sopenharmony_ci	struct drm_device *ddev = crtc->dev;
89462306a36Sopenharmony_ci	struct drm_connector_list_iter iter;
89562306a36Sopenharmony_ci	struct drm_connector *connector = NULL;
89662306a36Sopenharmony_ci	struct drm_encoder *encoder = NULL, *en_iter;
89762306a36Sopenharmony_ci	struct drm_bridge *bridge = NULL, *br_iter;
89862306a36Sopenharmony_ci	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
89962306a36Sopenharmony_ci	u32 hsync, vsync, accum_hbp, accum_vbp, accum_act_w, accum_act_h;
90062306a36Sopenharmony_ci	u32 total_width, total_height;
90162306a36Sopenharmony_ci	u32 bus_formats = MEDIA_BUS_FMT_RGB888_1X24;
90262306a36Sopenharmony_ci	u32 bus_flags = 0;
90362306a36Sopenharmony_ci	u32 val;
90462306a36Sopenharmony_ci	int ret;
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	/* get encoder from crtc */
90762306a36Sopenharmony_ci	drm_for_each_encoder(en_iter, ddev)
90862306a36Sopenharmony_ci		if (en_iter->crtc == crtc) {
90962306a36Sopenharmony_ci			encoder = en_iter;
91062306a36Sopenharmony_ci			break;
91162306a36Sopenharmony_ci		}
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	if (encoder) {
91462306a36Sopenharmony_ci		/* get bridge from encoder */
91562306a36Sopenharmony_ci		list_for_each_entry(br_iter, &encoder->bridge_chain, chain_node)
91662306a36Sopenharmony_ci			if (br_iter->encoder == encoder) {
91762306a36Sopenharmony_ci				bridge = br_iter;
91862306a36Sopenharmony_ci				break;
91962306a36Sopenharmony_ci			}
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci		/* Get the connector from encoder */
92262306a36Sopenharmony_ci		drm_connector_list_iter_begin(ddev, &iter);
92362306a36Sopenharmony_ci		drm_for_each_connector_iter(connector, &iter)
92462306a36Sopenharmony_ci			if (connector->encoder == encoder)
92562306a36Sopenharmony_ci				break;
92662306a36Sopenharmony_ci		drm_connector_list_iter_end(&iter);
92762306a36Sopenharmony_ci	}
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	if (bridge && bridge->timings) {
93062306a36Sopenharmony_ci		bus_flags = bridge->timings->input_bus_flags;
93162306a36Sopenharmony_ci	} else if (connector) {
93262306a36Sopenharmony_ci		bus_flags = connector->display_info.bus_flags;
93362306a36Sopenharmony_ci		if (connector->display_info.num_bus_formats)
93462306a36Sopenharmony_ci			bus_formats = connector->display_info.bus_formats[0];
93562306a36Sopenharmony_ci	}
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	if (!pm_runtime_active(ddev->dev)) {
93862306a36Sopenharmony_ci		ret = pm_runtime_get_sync(ddev->dev);
93962306a36Sopenharmony_ci		if (ret) {
94062306a36Sopenharmony_ci			DRM_ERROR("Failed to set mode, cannot get sync\n");
94162306a36Sopenharmony_ci			return;
94262306a36Sopenharmony_ci		}
94362306a36Sopenharmony_ci	}
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("CRTC:%d mode:%s\n", crtc->base.id, mode->name);
94662306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("Video mode: %dx%d", mode->hdisplay, mode->vdisplay);
94762306a36Sopenharmony_ci	DRM_DEBUG_DRIVER(" hfp %d hbp %d hsl %d vfp %d vbp %d vsl %d\n",
94862306a36Sopenharmony_ci			 mode->hsync_start - mode->hdisplay,
94962306a36Sopenharmony_ci			 mode->htotal - mode->hsync_end,
95062306a36Sopenharmony_ci			 mode->hsync_end - mode->hsync_start,
95162306a36Sopenharmony_ci			 mode->vsync_start - mode->vdisplay,
95262306a36Sopenharmony_ci			 mode->vtotal - mode->vsync_end,
95362306a36Sopenharmony_ci			 mode->vsync_end - mode->vsync_start);
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	/* Convert video timings to ltdc timings */
95662306a36Sopenharmony_ci	hsync = mode->hsync_end - mode->hsync_start - 1;
95762306a36Sopenharmony_ci	vsync = mode->vsync_end - mode->vsync_start - 1;
95862306a36Sopenharmony_ci	accum_hbp = mode->htotal - mode->hsync_start - 1;
95962306a36Sopenharmony_ci	accum_vbp = mode->vtotal - mode->vsync_start - 1;
96062306a36Sopenharmony_ci	accum_act_w = accum_hbp + mode->hdisplay;
96162306a36Sopenharmony_ci	accum_act_h = accum_vbp + mode->vdisplay;
96262306a36Sopenharmony_ci	total_width = mode->htotal - 1;
96362306a36Sopenharmony_ci	total_height = mode->vtotal - 1;
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	/* Configures the HS, VS, DE and PC polarities. Default Active Low */
96662306a36Sopenharmony_ci	val = 0;
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
96962306a36Sopenharmony_ci		val |= GCR_HSPOL;
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
97262306a36Sopenharmony_ci		val |= GCR_VSPOL;
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	if (bus_flags & DRM_BUS_FLAG_DE_LOW)
97562306a36Sopenharmony_ci		val |= GCR_DEPOL;
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
97862306a36Sopenharmony_ci		val |= GCR_PCPOL;
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	regmap_update_bits(ldev->regmap, LTDC_GCR,
98162306a36Sopenharmony_ci			   GCR_HSPOL | GCR_VSPOL | GCR_DEPOL | GCR_PCPOL, val);
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	/* Set Synchronization size */
98462306a36Sopenharmony_ci	val = (hsync << 16) | vsync;
98562306a36Sopenharmony_ci	regmap_update_bits(ldev->regmap, LTDC_SSCR, SSCR_VSH | SSCR_HSW, val);
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	/* Set Accumulated Back porch */
98862306a36Sopenharmony_ci	val = (accum_hbp << 16) | accum_vbp;
98962306a36Sopenharmony_ci	regmap_update_bits(ldev->regmap, LTDC_BPCR, BPCR_AVBP | BPCR_AHBP, val);
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	/* Set Accumulated Active Width */
99262306a36Sopenharmony_ci	val = (accum_act_w << 16) | accum_act_h;
99362306a36Sopenharmony_ci	regmap_update_bits(ldev->regmap, LTDC_AWCR, AWCR_AAW | AWCR_AAH, val);
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	/* Set total width & height */
99662306a36Sopenharmony_ci	val = (total_width << 16) | total_height;
99762306a36Sopenharmony_ci	regmap_update_bits(ldev->regmap, LTDC_TWCR, TWCR_TOTALH | TWCR_TOTALW, val);
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	regmap_write(ldev->regmap, LTDC_LIPCR, (accum_act_h + 1));
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	/* Configure the output format (hw version dependent) */
100262306a36Sopenharmony_ci	if (ldev->caps.ycbcr_output) {
100362306a36Sopenharmony_ci		/* Input video dynamic_range & colorimetry */
100462306a36Sopenharmony_ci		int vic = drm_match_cea_mode(mode);
100562306a36Sopenharmony_ci		u32 val;
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci		if (vic == 6 || vic == 7 || vic == 21 || vic == 22 ||
100862306a36Sopenharmony_ci		    vic == 2 || vic == 3 || vic == 17 || vic == 18)
100962306a36Sopenharmony_ci			/* ITU-R BT.601 */
101062306a36Sopenharmony_ci			val = 0;
101162306a36Sopenharmony_ci		else
101262306a36Sopenharmony_ci			/* ITU-R BT.709 */
101362306a36Sopenharmony_ci			val = EDCR_OCYSEL;
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci		switch (bus_formats) {
101662306a36Sopenharmony_ci		case MEDIA_BUS_FMT_YUYV8_1X16:
101762306a36Sopenharmony_ci			/* enable ycbcr output converter */
101862306a36Sopenharmony_ci			regmap_write(ldev->regmap, LTDC_EDCR, EDCR_OCYEN | val);
101962306a36Sopenharmony_ci			break;
102062306a36Sopenharmony_ci		case MEDIA_BUS_FMT_YVYU8_1X16:
102162306a36Sopenharmony_ci			/* enable ycbcr output converter & invert chrominance order */
102262306a36Sopenharmony_ci			regmap_write(ldev->regmap, LTDC_EDCR, EDCR_OCYEN | EDCR_OCYCO | val);
102362306a36Sopenharmony_ci			break;
102462306a36Sopenharmony_ci		default:
102562306a36Sopenharmony_ci			/* disable ycbcr output converter */
102662306a36Sopenharmony_ci			regmap_write(ldev->regmap, LTDC_EDCR, 0);
102762306a36Sopenharmony_ci			break;
102862306a36Sopenharmony_ci		}
102962306a36Sopenharmony_ci	}
103062306a36Sopenharmony_ci}
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_cistatic void ltdc_crtc_atomic_flush(struct drm_crtc *crtc,
103362306a36Sopenharmony_ci				   struct drm_atomic_state *state)
103462306a36Sopenharmony_ci{
103562306a36Sopenharmony_ci	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
103662306a36Sopenharmony_ci	struct drm_device *ddev = crtc->dev;
103762306a36Sopenharmony_ci	struct drm_pending_vblank_event *event = crtc->state->event;
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	DRM_DEBUG_ATOMIC("\n");
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	ltdc_crtc_update_clut(crtc);
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	/* Commit shadow registers = update planes at next vblank */
104462306a36Sopenharmony_ci	if (!ldev->caps.plane_reg_shadow)
104562306a36Sopenharmony_ci		regmap_set_bits(ldev->regmap, LTDC_SRCR, SRCR_VBR);
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	if (event) {
104862306a36Sopenharmony_ci		crtc->state->event = NULL;
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci		spin_lock_irq(&ddev->event_lock);
105162306a36Sopenharmony_ci		if (drm_crtc_vblank_get(crtc) == 0)
105262306a36Sopenharmony_ci			drm_crtc_arm_vblank_event(crtc, event);
105362306a36Sopenharmony_ci		else
105462306a36Sopenharmony_ci			drm_crtc_send_vblank_event(crtc, event);
105562306a36Sopenharmony_ci		spin_unlock_irq(&ddev->event_lock);
105662306a36Sopenharmony_ci	}
105762306a36Sopenharmony_ci}
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_cistatic bool ltdc_crtc_get_scanout_position(struct drm_crtc *crtc,
106062306a36Sopenharmony_ci					   bool in_vblank_irq,
106162306a36Sopenharmony_ci					   int *vpos, int *hpos,
106262306a36Sopenharmony_ci					   ktime_t *stime, ktime_t *etime,
106362306a36Sopenharmony_ci					   const struct drm_display_mode *mode)
106462306a36Sopenharmony_ci{
106562306a36Sopenharmony_ci	struct drm_device *ddev = crtc->dev;
106662306a36Sopenharmony_ci	struct ltdc_device *ldev = ddev->dev_private;
106762306a36Sopenharmony_ci	int line, vactive_start, vactive_end, vtotal;
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	if (stime)
107062306a36Sopenharmony_ci		*stime = ktime_get();
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci	/* The active area starts after vsync + front porch and ends
107362306a36Sopenharmony_ci	 * at vsync + front porc + display size.
107462306a36Sopenharmony_ci	 * The total height also include back porch.
107562306a36Sopenharmony_ci	 * We have 3 possible cases to handle:
107662306a36Sopenharmony_ci	 * - line < vactive_start: vpos = line - vactive_start and will be
107762306a36Sopenharmony_ci	 * negative
107862306a36Sopenharmony_ci	 * - vactive_start < line < vactive_end: vpos = line - vactive_start
107962306a36Sopenharmony_ci	 * and will be positive
108062306a36Sopenharmony_ci	 * - line > vactive_end: vpos = line - vtotal - vactive_start
108162306a36Sopenharmony_ci	 * and will negative
108262306a36Sopenharmony_ci	 *
108362306a36Sopenharmony_ci	 * Computation for the two first cases are identical so we can
108462306a36Sopenharmony_ci	 * simplify the code and only test if line > vactive_end
108562306a36Sopenharmony_ci	 */
108662306a36Sopenharmony_ci	if (pm_runtime_active(ddev->dev)) {
108762306a36Sopenharmony_ci		regmap_read(ldev->regmap, LTDC_CPSR, &line);
108862306a36Sopenharmony_ci		line &= CPSR_CYPOS;
108962306a36Sopenharmony_ci		regmap_read(ldev->regmap, LTDC_BPCR, &vactive_start);
109062306a36Sopenharmony_ci		vactive_start &= BPCR_AVBP;
109162306a36Sopenharmony_ci		regmap_read(ldev->regmap, LTDC_AWCR, &vactive_end);
109262306a36Sopenharmony_ci		vactive_end &= AWCR_AAH;
109362306a36Sopenharmony_ci		regmap_read(ldev->regmap, LTDC_TWCR, &vtotal);
109462306a36Sopenharmony_ci		vtotal &= TWCR_TOTALH;
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci		if (line > vactive_end)
109762306a36Sopenharmony_ci			*vpos = line - vtotal - vactive_start;
109862306a36Sopenharmony_ci		else
109962306a36Sopenharmony_ci			*vpos = line - vactive_start;
110062306a36Sopenharmony_ci	} else {
110162306a36Sopenharmony_ci		*vpos = 0;
110262306a36Sopenharmony_ci	}
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	*hpos = 0;
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	if (etime)
110762306a36Sopenharmony_ci		*etime = ktime_get();
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	return true;
111062306a36Sopenharmony_ci}
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_cistatic const struct drm_crtc_helper_funcs ltdc_crtc_helper_funcs = {
111362306a36Sopenharmony_ci	.mode_valid = ltdc_crtc_mode_valid,
111462306a36Sopenharmony_ci	.mode_fixup = ltdc_crtc_mode_fixup,
111562306a36Sopenharmony_ci	.mode_set_nofb = ltdc_crtc_mode_set_nofb,
111662306a36Sopenharmony_ci	.atomic_flush = ltdc_crtc_atomic_flush,
111762306a36Sopenharmony_ci	.atomic_enable = ltdc_crtc_atomic_enable,
111862306a36Sopenharmony_ci	.atomic_disable = ltdc_crtc_atomic_disable,
111962306a36Sopenharmony_ci	.get_scanout_position = ltdc_crtc_get_scanout_position,
112062306a36Sopenharmony_ci};
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_cistatic int ltdc_crtc_enable_vblank(struct drm_crtc *crtc)
112362306a36Sopenharmony_ci{
112462306a36Sopenharmony_ci	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
112562306a36Sopenharmony_ci	struct drm_crtc_state *state = crtc->state;
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("\n");
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	if (state->enable)
113062306a36Sopenharmony_ci		regmap_set_bits(ldev->regmap, LTDC_IER, IER_LIE);
113162306a36Sopenharmony_ci	else
113262306a36Sopenharmony_ci		return -EPERM;
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	return 0;
113562306a36Sopenharmony_ci}
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_cistatic void ltdc_crtc_disable_vblank(struct drm_crtc *crtc)
113862306a36Sopenharmony_ci{
113962306a36Sopenharmony_ci	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("\n");
114262306a36Sopenharmony_ci	regmap_clear_bits(ldev->regmap, LTDC_IER, IER_LIE);
114362306a36Sopenharmony_ci}
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_cistatic int ltdc_crtc_set_crc_source(struct drm_crtc *crtc, const char *source)
114662306a36Sopenharmony_ci{
114762306a36Sopenharmony_ci	struct ltdc_device *ldev;
114862306a36Sopenharmony_ci	int ret;
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("\n");
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	if (!crtc)
115362306a36Sopenharmony_ci		return -ENODEV;
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	ldev = crtc_to_ltdc(crtc);
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	if (source && strcmp(source, "auto") == 0) {
115862306a36Sopenharmony_ci		ldev->crc_active = true;
115962306a36Sopenharmony_ci		ret = regmap_set_bits(ldev->regmap, LTDC_GCR, GCR_CRCEN);
116062306a36Sopenharmony_ci	} else if (!source) {
116162306a36Sopenharmony_ci		ldev->crc_active = false;
116262306a36Sopenharmony_ci		ret = regmap_clear_bits(ldev->regmap, LTDC_GCR, GCR_CRCEN);
116362306a36Sopenharmony_ci	} else {
116462306a36Sopenharmony_ci		ret = -EINVAL;
116562306a36Sopenharmony_ci	}
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci	ldev->crc_skip_count = 0;
116862306a36Sopenharmony_ci	return ret;
116962306a36Sopenharmony_ci}
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_cistatic int ltdc_crtc_verify_crc_source(struct drm_crtc *crtc,
117262306a36Sopenharmony_ci				       const char *source, size_t *values_cnt)
117362306a36Sopenharmony_ci{
117462306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("\n");
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci	if (!crtc)
117762306a36Sopenharmony_ci		return -ENODEV;
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	if (source && strcmp(source, "auto") != 0) {
118062306a36Sopenharmony_ci		DRM_DEBUG_DRIVER("Unknown CRC source %s for %s\n",
118162306a36Sopenharmony_ci				 source, crtc->name);
118262306a36Sopenharmony_ci		return -EINVAL;
118362306a36Sopenharmony_ci	}
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	*values_cnt = 1;
118662306a36Sopenharmony_ci	return 0;
118762306a36Sopenharmony_ci}
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_cistatic void ltdc_crtc_atomic_print_state(struct drm_printer *p,
119062306a36Sopenharmony_ci					 const struct drm_crtc_state *state)
119162306a36Sopenharmony_ci{
119262306a36Sopenharmony_ci	struct drm_crtc *crtc = state->crtc;
119362306a36Sopenharmony_ci	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci	drm_printf(p, "\ttransfer_error=%d\n", ldev->transfer_err);
119662306a36Sopenharmony_ci	drm_printf(p, "\tfifo_underrun_error=%d\n", ldev->fifo_err);
119762306a36Sopenharmony_ci	drm_printf(p, "\tfifo_underrun_warning=%d\n", ldev->fifo_warn);
119862306a36Sopenharmony_ci	drm_printf(p, "\tfifo_underrun_threshold=%d\n", ldev->fifo_threshold);
119962306a36Sopenharmony_ci}
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_cistatic const struct drm_crtc_funcs ltdc_crtc_funcs = {
120262306a36Sopenharmony_ci	.destroy = drm_crtc_cleanup,
120362306a36Sopenharmony_ci	.set_config = drm_atomic_helper_set_config,
120462306a36Sopenharmony_ci	.page_flip = drm_atomic_helper_page_flip,
120562306a36Sopenharmony_ci	.reset = drm_atomic_helper_crtc_reset,
120662306a36Sopenharmony_ci	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
120762306a36Sopenharmony_ci	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
120862306a36Sopenharmony_ci	.enable_vblank = ltdc_crtc_enable_vblank,
120962306a36Sopenharmony_ci	.disable_vblank = ltdc_crtc_disable_vblank,
121062306a36Sopenharmony_ci	.get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,
121162306a36Sopenharmony_ci	.atomic_print_state = ltdc_crtc_atomic_print_state,
121262306a36Sopenharmony_ci};
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_cistatic const struct drm_crtc_funcs ltdc_crtc_with_crc_support_funcs = {
121562306a36Sopenharmony_ci	.destroy = drm_crtc_cleanup,
121662306a36Sopenharmony_ci	.set_config = drm_atomic_helper_set_config,
121762306a36Sopenharmony_ci	.page_flip = drm_atomic_helper_page_flip,
121862306a36Sopenharmony_ci	.reset = drm_atomic_helper_crtc_reset,
121962306a36Sopenharmony_ci	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
122062306a36Sopenharmony_ci	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
122162306a36Sopenharmony_ci	.enable_vblank = ltdc_crtc_enable_vblank,
122262306a36Sopenharmony_ci	.disable_vblank = ltdc_crtc_disable_vblank,
122362306a36Sopenharmony_ci	.get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,
122462306a36Sopenharmony_ci	.set_crc_source = ltdc_crtc_set_crc_source,
122562306a36Sopenharmony_ci	.verify_crc_source = ltdc_crtc_verify_crc_source,
122662306a36Sopenharmony_ci	.atomic_print_state = ltdc_crtc_atomic_print_state,
122762306a36Sopenharmony_ci};
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci/*
123062306a36Sopenharmony_ci * DRM_PLANE
123162306a36Sopenharmony_ci */
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_cistatic int ltdc_plane_atomic_check(struct drm_plane *plane,
123462306a36Sopenharmony_ci				   struct drm_atomic_state *state)
123562306a36Sopenharmony_ci{
123662306a36Sopenharmony_ci	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
123762306a36Sopenharmony_ci										 plane);
123862306a36Sopenharmony_ci	struct drm_framebuffer *fb = new_plane_state->fb;
123962306a36Sopenharmony_ci	u32 src_w, src_h;
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("\n");
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	if (!fb)
124462306a36Sopenharmony_ci		return 0;
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci	/* convert src_ from 16:16 format */
124762306a36Sopenharmony_ci	src_w = new_plane_state->src_w >> 16;
124862306a36Sopenharmony_ci	src_h = new_plane_state->src_h >> 16;
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci	/* Reject scaling */
125162306a36Sopenharmony_ci	if (src_w != new_plane_state->crtc_w || src_h != new_plane_state->crtc_h) {
125262306a36Sopenharmony_ci		DRM_DEBUG_DRIVER("Scaling is not supported");
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci		return -EINVAL;
125562306a36Sopenharmony_ci	}
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	return 0;
125862306a36Sopenharmony_ci}
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_cistatic void ltdc_plane_atomic_update(struct drm_plane *plane,
126162306a36Sopenharmony_ci				     struct drm_atomic_state *state)
126262306a36Sopenharmony_ci{
126362306a36Sopenharmony_ci	struct ltdc_device *ldev = plane_to_ltdc(plane);
126462306a36Sopenharmony_ci	struct drm_plane_state *newstate = drm_atomic_get_new_plane_state(state,
126562306a36Sopenharmony_ci									  plane);
126662306a36Sopenharmony_ci	struct drm_framebuffer *fb = newstate->fb;
126762306a36Sopenharmony_ci	u32 lofs = plane->index * LAY_OFS;
126862306a36Sopenharmony_ci	u32 x0 = newstate->crtc_x;
126962306a36Sopenharmony_ci	u32 x1 = newstate->crtc_x + newstate->crtc_w - 1;
127062306a36Sopenharmony_ci	u32 y0 = newstate->crtc_y;
127162306a36Sopenharmony_ci	u32 y1 = newstate->crtc_y + newstate->crtc_h - 1;
127262306a36Sopenharmony_ci	u32 src_x, src_y, src_w, src_h;
127362306a36Sopenharmony_ci	u32 val, pitch_in_bytes, line_length, line_number, ahbp, avbp, bpcr;
127462306a36Sopenharmony_ci	u32 paddr, paddr1, paddr2;
127562306a36Sopenharmony_ci	enum ltdc_pix_fmt pf;
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci	if (!newstate->crtc || !fb) {
127862306a36Sopenharmony_ci		DRM_DEBUG_DRIVER("fb or crtc NULL");
127962306a36Sopenharmony_ci		return;
128062306a36Sopenharmony_ci	}
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci	/* convert src_ from 16:16 format */
128362306a36Sopenharmony_ci	src_x = newstate->src_x >> 16;
128462306a36Sopenharmony_ci	src_y = newstate->src_y >> 16;
128562306a36Sopenharmony_ci	src_w = newstate->src_w >> 16;
128662306a36Sopenharmony_ci	src_h = newstate->src_h >> 16;
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("plane:%d fb:%d (%dx%d)@(%d,%d) -> (%dx%d)@(%d,%d)\n",
128962306a36Sopenharmony_ci			 plane->base.id, fb->base.id,
129062306a36Sopenharmony_ci			 src_w, src_h, src_x, src_y,
129162306a36Sopenharmony_ci			 newstate->crtc_w, newstate->crtc_h,
129262306a36Sopenharmony_ci			 newstate->crtc_x, newstate->crtc_y);
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci	regmap_read(ldev->regmap, LTDC_BPCR, &bpcr);
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	ahbp = (bpcr & BPCR_AHBP) >> 16;
129762306a36Sopenharmony_ci	avbp = bpcr & BPCR_AVBP;
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci	/* Configures the horizontal start and stop position */
130062306a36Sopenharmony_ci	val = ((x1 + 1 + ahbp) << 16) + (x0 + 1 + ahbp);
130162306a36Sopenharmony_ci	regmap_write_bits(ldev->regmap, LTDC_L1WHPCR + lofs,
130262306a36Sopenharmony_ci			  LXWHPCR_WHSTPOS | LXWHPCR_WHSPPOS, val);
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci	/* Configures the vertical start and stop position */
130562306a36Sopenharmony_ci	val = ((y1 + 1 + avbp) << 16) + (y0 + 1 + avbp);
130662306a36Sopenharmony_ci	regmap_write_bits(ldev->regmap, LTDC_L1WVPCR + lofs,
130762306a36Sopenharmony_ci			  LXWVPCR_WVSTPOS | LXWVPCR_WVSPPOS, val);
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	/* Specifies the pixel format */
131062306a36Sopenharmony_ci	pf = to_ltdc_pixelformat(fb->format->format);
131162306a36Sopenharmony_ci	for (val = 0; val < NB_PF; val++)
131262306a36Sopenharmony_ci		if (ldev->caps.pix_fmt_hw[val] == pf)
131362306a36Sopenharmony_ci			break;
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	/* Use the flexible color format feature if necessary and available */
131662306a36Sopenharmony_ci	if (ldev->caps.pix_fmt_flex && val == NB_PF)
131762306a36Sopenharmony_ci		val = ltdc_set_flexible_pixel_format(plane, pf);
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci	if (val == NB_PF) {
132062306a36Sopenharmony_ci		DRM_ERROR("Pixel format %.4s not supported\n",
132162306a36Sopenharmony_ci			  (char *)&fb->format->format);
132262306a36Sopenharmony_ci		val = 0;	/* set by default ARGB 32 bits */
132362306a36Sopenharmony_ci	}
132462306a36Sopenharmony_ci	regmap_write_bits(ldev->regmap, LTDC_L1PFCR + lofs, LXPFCR_PF, val);
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	/* Specifies the constant alpha value */
132762306a36Sopenharmony_ci	val = newstate->alpha >> 8;
132862306a36Sopenharmony_ci	regmap_write_bits(ldev->regmap, LTDC_L1CACR + lofs, LXCACR_CONSTA, val);
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci	/* Specifies the blending factors */
133162306a36Sopenharmony_ci	val = BF1_PAXCA | BF2_1PAXCA;
133262306a36Sopenharmony_ci	if (!fb->format->has_alpha)
133362306a36Sopenharmony_ci		val = BF1_CA | BF2_1CA;
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci	/* Manage hw-specific capabilities */
133662306a36Sopenharmony_ci	if (ldev->caps.non_alpha_only_l1 &&
133762306a36Sopenharmony_ci	    plane->type != DRM_PLANE_TYPE_PRIMARY)
133862306a36Sopenharmony_ci		val = BF1_PAXCA | BF2_1PAXCA;
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci	if (ldev->caps.dynamic_zorder) {
134162306a36Sopenharmony_ci		val |= (newstate->normalized_zpos << 16);
134262306a36Sopenharmony_ci		regmap_write_bits(ldev->regmap, LTDC_L1BFCR + lofs,
134362306a36Sopenharmony_ci				  LXBFCR_BF2 | LXBFCR_BF1 | LXBFCR_BOR, val);
134462306a36Sopenharmony_ci	} else {
134562306a36Sopenharmony_ci		regmap_write_bits(ldev->regmap, LTDC_L1BFCR + lofs,
134662306a36Sopenharmony_ci				  LXBFCR_BF2 | LXBFCR_BF1, val);
134762306a36Sopenharmony_ci	}
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci	/* Sets the FB address */
135062306a36Sopenharmony_ci	paddr = (u32)drm_fb_dma_get_gem_addr(fb, newstate, 0);
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci	if (newstate->rotation & DRM_MODE_REFLECT_X)
135362306a36Sopenharmony_ci		paddr += (fb->format->cpp[0] * (x1 - x0 + 1)) - 1;
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	if (newstate->rotation & DRM_MODE_REFLECT_Y)
135662306a36Sopenharmony_ci		paddr += (fb->pitches[0] * (y1 - y0));
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("fb: phys 0x%08x", paddr);
135962306a36Sopenharmony_ci	regmap_write(ldev->regmap, LTDC_L1CFBAR + lofs, paddr);
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci	/* Configures the color frame buffer pitch in bytes & line length */
136262306a36Sopenharmony_ci	line_length = fb->format->cpp[0] *
136362306a36Sopenharmony_ci		      (x1 - x0 + 1) + (ldev->caps.bus_width >> 3) - 1;
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_ci	if (newstate->rotation & DRM_MODE_REFLECT_Y)
136662306a36Sopenharmony_ci		/* Compute negative value (signed on 16 bits) for the picth */
136762306a36Sopenharmony_ci		pitch_in_bytes = 0x10000 - fb->pitches[0];
136862306a36Sopenharmony_ci	else
136962306a36Sopenharmony_ci		pitch_in_bytes = fb->pitches[0];
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci	val = (pitch_in_bytes << 16) | line_length;
137262306a36Sopenharmony_ci	regmap_write_bits(ldev->regmap, LTDC_L1CFBLR + lofs, LXCFBLR_CFBLL | LXCFBLR_CFBP, val);
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	/* Configures the frame buffer line number */
137562306a36Sopenharmony_ci	line_number = y1 - y0 + 1;
137662306a36Sopenharmony_ci	regmap_write_bits(ldev->regmap, LTDC_L1CFBLNR + lofs, LXCFBLNR_CFBLN, line_number);
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ci	if (ldev->caps.ycbcr_input) {
137962306a36Sopenharmony_ci		if (fb->format->is_yuv) {
138062306a36Sopenharmony_ci			switch (fb->format->format) {
138162306a36Sopenharmony_ci			case DRM_FORMAT_NV12:
138262306a36Sopenharmony_ci			case DRM_FORMAT_NV21:
138362306a36Sopenharmony_ci			/* Configure the auxiliary frame buffer address 0 */
138462306a36Sopenharmony_ci			paddr1 = (u32)drm_fb_dma_get_gem_addr(fb, newstate, 1);
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci			if (newstate->rotation & DRM_MODE_REFLECT_X)
138762306a36Sopenharmony_ci				paddr1 += ((fb->format->cpp[1] * (x1 - x0 + 1)) >> 1) - 1;
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci			if (newstate->rotation & DRM_MODE_REFLECT_Y)
139062306a36Sopenharmony_ci				paddr1 += (fb->pitches[1] * (y1 - y0 - 1)) >> 1;
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci			regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr1);
139362306a36Sopenharmony_ci			break;
139462306a36Sopenharmony_ci			case DRM_FORMAT_YUV420:
139562306a36Sopenharmony_ci			/* Configure the auxiliary frame buffer address 0 & 1 */
139662306a36Sopenharmony_ci			paddr1 = (u32)drm_fb_dma_get_gem_addr(fb, newstate, 1);
139762306a36Sopenharmony_ci			paddr2 = (u32)drm_fb_dma_get_gem_addr(fb, newstate, 2);
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci			if (newstate->rotation & DRM_MODE_REFLECT_X) {
140062306a36Sopenharmony_ci				paddr1 += ((fb->format->cpp[1] * (x1 - x0 + 1)) >> 1) - 1;
140162306a36Sopenharmony_ci				paddr2 += ((fb->format->cpp[2] * (x1 - x0 + 1)) >> 1) - 1;
140262306a36Sopenharmony_ci			}
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci			if (newstate->rotation & DRM_MODE_REFLECT_Y) {
140562306a36Sopenharmony_ci				paddr1 += (fb->pitches[1] * (y1 - y0 - 1)) >> 1;
140662306a36Sopenharmony_ci				paddr2 += (fb->pitches[2] * (y1 - y0 - 1)) >> 1;
140762306a36Sopenharmony_ci			}
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci			regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr1);
141062306a36Sopenharmony_ci			regmap_write(ldev->regmap, LTDC_L1AFBA1R + lofs, paddr2);
141162306a36Sopenharmony_ci			break;
141262306a36Sopenharmony_ci			case DRM_FORMAT_YVU420:
141362306a36Sopenharmony_ci			/* Configure the auxiliary frame buffer address 0 & 1 */
141462306a36Sopenharmony_ci			paddr1 = (u32)drm_fb_dma_get_gem_addr(fb, newstate, 2);
141562306a36Sopenharmony_ci			paddr2 = (u32)drm_fb_dma_get_gem_addr(fb, newstate, 1);
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci			if (newstate->rotation & DRM_MODE_REFLECT_X) {
141862306a36Sopenharmony_ci				paddr1 += ((fb->format->cpp[1] * (x1 - x0 + 1)) >> 1) - 1;
141962306a36Sopenharmony_ci				paddr2 += ((fb->format->cpp[2] * (x1 - x0 + 1)) >> 1) - 1;
142062306a36Sopenharmony_ci			}
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_ci			if (newstate->rotation & DRM_MODE_REFLECT_Y) {
142362306a36Sopenharmony_ci				paddr1 += (fb->pitches[1] * (y1 - y0 - 1)) >> 1;
142462306a36Sopenharmony_ci				paddr2 += (fb->pitches[2] * (y1 - y0 - 1)) >> 1;
142562306a36Sopenharmony_ci			}
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci			regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr1);
142862306a36Sopenharmony_ci			regmap_write(ldev->regmap, LTDC_L1AFBA1R + lofs, paddr2);
142962306a36Sopenharmony_ci			break;
143062306a36Sopenharmony_ci			}
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci			/*
143362306a36Sopenharmony_ci			 * Set the length and the number of lines of the auxiliary
143462306a36Sopenharmony_ci			 * buffers if the framebuffer contains more than one plane.
143562306a36Sopenharmony_ci			 */
143662306a36Sopenharmony_ci			if (fb->format->num_planes > 1) {
143762306a36Sopenharmony_ci				if (newstate->rotation & DRM_MODE_REFLECT_Y)
143862306a36Sopenharmony_ci					/*
143962306a36Sopenharmony_ci					 * Compute negative value (signed on 16 bits)
144062306a36Sopenharmony_ci					 * for the picth
144162306a36Sopenharmony_ci					 */
144262306a36Sopenharmony_ci					pitch_in_bytes = 0x10000 - fb->pitches[1];
144362306a36Sopenharmony_ci				else
144462306a36Sopenharmony_ci					pitch_in_bytes = fb->pitches[1];
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_ci				line_length = ((fb->format->cpp[1] * (x1 - x0 + 1)) >> 1) +
144762306a36Sopenharmony_ci					      (ldev->caps.bus_width >> 3) - 1;
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_ci				/* Configure the auxiliary buffer length */
145062306a36Sopenharmony_ci				val = (pitch_in_bytes << 16) | line_length;
145162306a36Sopenharmony_ci				regmap_write(ldev->regmap, LTDC_L1AFBLR + lofs, val);
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci				/* Configure the auxiliary frame buffer line number */
145462306a36Sopenharmony_ci				val = line_number >> 1;
145562306a36Sopenharmony_ci				regmap_write(ldev->regmap, LTDC_L1AFBLNR + lofs, val);
145662306a36Sopenharmony_ci			}
145762306a36Sopenharmony_ci
145862306a36Sopenharmony_ci			/* Configure YCbC conversion coefficient */
145962306a36Sopenharmony_ci			ltdc_set_ycbcr_coeffs(plane);
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci			/* Configure YCbCr format and enable/disable conversion */
146262306a36Sopenharmony_ci			ltdc_set_ycbcr_config(plane, fb->format->format);
146362306a36Sopenharmony_ci		} else {
146462306a36Sopenharmony_ci			/* disable ycbcr conversion */
146562306a36Sopenharmony_ci			regmap_write(ldev->regmap, LTDC_L1PCR + lofs, 0);
146662306a36Sopenharmony_ci		}
146762306a36Sopenharmony_ci	}
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ci	/* Enable layer and CLUT if needed */
147062306a36Sopenharmony_ci	val = fb->format->format == DRM_FORMAT_C8 ? LXCR_CLUTEN : 0;
147162306a36Sopenharmony_ci	val |= LXCR_LEN;
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_ci	/* Enable horizontal mirroring if requested */
147462306a36Sopenharmony_ci	if (newstate->rotation & DRM_MODE_REFLECT_X)
147562306a36Sopenharmony_ci		val |= LXCR_HMEN;
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci	regmap_write_bits(ldev->regmap, LTDC_L1CR + lofs, LXCR_LEN | LXCR_CLUTEN | LXCR_HMEN, val);
147862306a36Sopenharmony_ci
147962306a36Sopenharmony_ci	/* Commit shadow registers = update plane at next vblank */
148062306a36Sopenharmony_ci	if (ldev->caps.plane_reg_shadow)
148162306a36Sopenharmony_ci		regmap_write_bits(ldev->regmap, LTDC_L1RCR + lofs,
148262306a36Sopenharmony_ci				  LXRCR_IMR | LXRCR_VBR | LXRCR_GRMSK, LXRCR_VBR);
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci	ldev->plane_fpsi[plane->index].counter++;
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_ci	mutex_lock(&ldev->err_lock);
148762306a36Sopenharmony_ci	if (ldev->transfer_err) {
148862306a36Sopenharmony_ci		DRM_WARN("ltdc transfer error: %d\n", ldev->transfer_err);
148962306a36Sopenharmony_ci		ldev->transfer_err = 0;
149062306a36Sopenharmony_ci	}
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci	if (ldev->caps.fifo_threshold) {
149362306a36Sopenharmony_ci		if (ldev->fifo_err) {
149462306a36Sopenharmony_ci			DRM_WARN("ltdc fifo underrun: please verify display mode\n");
149562306a36Sopenharmony_ci			ldev->fifo_err = 0;
149662306a36Sopenharmony_ci		}
149762306a36Sopenharmony_ci	} else {
149862306a36Sopenharmony_ci		if (ldev->fifo_warn >= ldev->fifo_threshold) {
149962306a36Sopenharmony_ci			DRM_WARN("ltdc fifo underrun: please verify display mode\n");
150062306a36Sopenharmony_ci			ldev->fifo_warn = 0;
150162306a36Sopenharmony_ci		}
150262306a36Sopenharmony_ci	}
150362306a36Sopenharmony_ci	mutex_unlock(&ldev->err_lock);
150462306a36Sopenharmony_ci}
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_cistatic void ltdc_plane_atomic_disable(struct drm_plane *plane,
150762306a36Sopenharmony_ci				      struct drm_atomic_state *state)
150862306a36Sopenharmony_ci{
150962306a36Sopenharmony_ci	struct drm_plane_state *oldstate = drm_atomic_get_old_plane_state(state,
151062306a36Sopenharmony_ci									  plane);
151162306a36Sopenharmony_ci	struct ltdc_device *ldev = plane_to_ltdc(plane);
151262306a36Sopenharmony_ci	u32 lofs = plane->index * LAY_OFS;
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ci	/* Disable layer */
151562306a36Sopenharmony_ci	regmap_write_bits(ldev->regmap, LTDC_L1CR + lofs, LXCR_LEN | LXCR_CLUTEN |  LXCR_HMEN, 0);
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_ci	/* Commit shadow registers = update plane at next vblank */
151862306a36Sopenharmony_ci	if (ldev->caps.plane_reg_shadow)
151962306a36Sopenharmony_ci		regmap_write_bits(ldev->regmap, LTDC_L1RCR + lofs,
152062306a36Sopenharmony_ci				  LXRCR_IMR | LXRCR_VBR | LXRCR_GRMSK, LXRCR_VBR);
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("CRTC:%d plane:%d\n",
152362306a36Sopenharmony_ci			 oldstate->crtc->base.id, plane->base.id);
152462306a36Sopenharmony_ci}
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_cistatic void ltdc_plane_atomic_print_state(struct drm_printer *p,
152762306a36Sopenharmony_ci					  const struct drm_plane_state *state)
152862306a36Sopenharmony_ci{
152962306a36Sopenharmony_ci	struct drm_plane *plane = state->plane;
153062306a36Sopenharmony_ci	struct ltdc_device *ldev = plane_to_ltdc(plane);
153162306a36Sopenharmony_ci	struct fps_info *fpsi = &ldev->plane_fpsi[plane->index];
153262306a36Sopenharmony_ci	int ms_since_last;
153362306a36Sopenharmony_ci	ktime_t now;
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_ci	now = ktime_get();
153662306a36Sopenharmony_ci	ms_since_last = ktime_to_ms(ktime_sub(now, fpsi->last_timestamp));
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci	drm_printf(p, "\tuser_updates=%dfps\n",
153962306a36Sopenharmony_ci		   DIV_ROUND_CLOSEST(fpsi->counter * 1000, ms_since_last));
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci	fpsi->last_timestamp = now;
154262306a36Sopenharmony_ci	fpsi->counter = 0;
154362306a36Sopenharmony_ci}
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_cistatic const struct drm_plane_funcs ltdc_plane_funcs = {
154662306a36Sopenharmony_ci	.update_plane = drm_atomic_helper_update_plane,
154762306a36Sopenharmony_ci	.disable_plane = drm_atomic_helper_disable_plane,
154862306a36Sopenharmony_ci	.destroy = drm_plane_cleanup,
154962306a36Sopenharmony_ci	.reset = drm_atomic_helper_plane_reset,
155062306a36Sopenharmony_ci	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
155162306a36Sopenharmony_ci	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
155262306a36Sopenharmony_ci	.atomic_print_state = ltdc_plane_atomic_print_state,
155362306a36Sopenharmony_ci};
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_cistatic const struct drm_plane_helper_funcs ltdc_plane_helper_funcs = {
155662306a36Sopenharmony_ci	.atomic_check = ltdc_plane_atomic_check,
155762306a36Sopenharmony_ci	.atomic_update = ltdc_plane_atomic_update,
155862306a36Sopenharmony_ci	.atomic_disable = ltdc_plane_atomic_disable,
155962306a36Sopenharmony_ci};
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_cistatic struct drm_plane *ltdc_plane_create(struct drm_device *ddev,
156262306a36Sopenharmony_ci					   enum drm_plane_type type,
156362306a36Sopenharmony_ci					   int index)
156462306a36Sopenharmony_ci{
156562306a36Sopenharmony_ci	unsigned long possible_crtcs = CRTC_MASK;
156662306a36Sopenharmony_ci	struct ltdc_device *ldev = ddev->dev_private;
156762306a36Sopenharmony_ci	struct device *dev = ddev->dev;
156862306a36Sopenharmony_ci	struct drm_plane *plane;
156962306a36Sopenharmony_ci	unsigned int i, nb_fmt = 0;
157062306a36Sopenharmony_ci	u32 *formats;
157162306a36Sopenharmony_ci	u32 drm_fmt;
157262306a36Sopenharmony_ci	const u64 *modifiers = ltdc_format_modifiers;
157362306a36Sopenharmony_ci	u32 lofs = index * LAY_OFS;
157462306a36Sopenharmony_ci	u32 val;
157562306a36Sopenharmony_ci	int ret;
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_ci	/* Allocate the biggest size according to supported color formats */
157862306a36Sopenharmony_ci	formats = devm_kzalloc(dev, (ldev->caps.pix_fmt_nb +
157962306a36Sopenharmony_ci			       ARRAY_SIZE(ltdc_drm_fmt_ycbcr_cp) +
158062306a36Sopenharmony_ci			       ARRAY_SIZE(ltdc_drm_fmt_ycbcr_sp) +
158162306a36Sopenharmony_ci			       ARRAY_SIZE(ltdc_drm_fmt_ycbcr_fp)) *
158262306a36Sopenharmony_ci			       sizeof(*formats), GFP_KERNEL);
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_ci	for (i = 0; i < ldev->caps.pix_fmt_nb; i++) {
158562306a36Sopenharmony_ci		drm_fmt = ldev->caps.pix_fmt_drm[i];
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_ci		/* Manage hw-specific capabilities */
158862306a36Sopenharmony_ci		if (ldev->caps.non_alpha_only_l1)
158962306a36Sopenharmony_ci			/* XR24 & RX24 like formats supported only on primary layer */
159062306a36Sopenharmony_ci			if (type != DRM_PLANE_TYPE_PRIMARY && is_xrgb(drm_fmt))
159162306a36Sopenharmony_ci				continue;
159262306a36Sopenharmony_ci
159362306a36Sopenharmony_ci		formats[nb_fmt++] = drm_fmt;
159462306a36Sopenharmony_ci	}
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci	/* Add YCbCr supported pixel formats */
159762306a36Sopenharmony_ci	if (ldev->caps.ycbcr_input) {
159862306a36Sopenharmony_ci		regmap_read(ldev->regmap, LTDC_L1C1R + lofs, &val);
159962306a36Sopenharmony_ci		if (val & LXCR_C1R_YIA) {
160062306a36Sopenharmony_ci			memcpy(&formats[nb_fmt], ltdc_drm_fmt_ycbcr_cp,
160162306a36Sopenharmony_ci			       ARRAY_SIZE(ltdc_drm_fmt_ycbcr_cp) * sizeof(*formats));
160262306a36Sopenharmony_ci			nb_fmt += ARRAY_SIZE(ltdc_drm_fmt_ycbcr_cp);
160362306a36Sopenharmony_ci		}
160462306a36Sopenharmony_ci		if (val & LXCR_C1R_YSPA) {
160562306a36Sopenharmony_ci			memcpy(&formats[nb_fmt], ltdc_drm_fmt_ycbcr_sp,
160662306a36Sopenharmony_ci			       ARRAY_SIZE(ltdc_drm_fmt_ycbcr_sp) * sizeof(*formats));
160762306a36Sopenharmony_ci			nb_fmt += ARRAY_SIZE(ltdc_drm_fmt_ycbcr_sp);
160862306a36Sopenharmony_ci		}
160962306a36Sopenharmony_ci		if (val & LXCR_C1R_YFPA) {
161062306a36Sopenharmony_ci			memcpy(&formats[nb_fmt], ltdc_drm_fmt_ycbcr_fp,
161162306a36Sopenharmony_ci			       ARRAY_SIZE(ltdc_drm_fmt_ycbcr_fp) * sizeof(*formats));
161262306a36Sopenharmony_ci			nb_fmt += ARRAY_SIZE(ltdc_drm_fmt_ycbcr_fp);
161362306a36Sopenharmony_ci		}
161462306a36Sopenharmony_ci	}
161562306a36Sopenharmony_ci
161662306a36Sopenharmony_ci	plane = devm_kzalloc(dev, sizeof(*plane), GFP_KERNEL);
161762306a36Sopenharmony_ci	if (!plane)
161862306a36Sopenharmony_ci		return NULL;
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_ci	ret = drm_universal_plane_init(ddev, plane, possible_crtcs,
162162306a36Sopenharmony_ci				       &ltdc_plane_funcs, formats, nb_fmt,
162262306a36Sopenharmony_ci				       modifiers, type, NULL);
162362306a36Sopenharmony_ci	if (ret < 0)
162462306a36Sopenharmony_ci		return NULL;
162562306a36Sopenharmony_ci
162662306a36Sopenharmony_ci	if (ldev->caps.ycbcr_input) {
162762306a36Sopenharmony_ci		if (val & (LXCR_C1R_YIA | LXCR_C1R_YSPA | LXCR_C1R_YFPA))
162862306a36Sopenharmony_ci			drm_plane_create_color_properties(plane,
162962306a36Sopenharmony_ci							  BIT(DRM_COLOR_YCBCR_BT601) |
163062306a36Sopenharmony_ci							  BIT(DRM_COLOR_YCBCR_BT709),
163162306a36Sopenharmony_ci							  BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) |
163262306a36Sopenharmony_ci							  BIT(DRM_COLOR_YCBCR_FULL_RANGE),
163362306a36Sopenharmony_ci							  DRM_COLOR_YCBCR_BT601,
163462306a36Sopenharmony_ci							  DRM_COLOR_YCBCR_LIMITED_RANGE);
163562306a36Sopenharmony_ci	}
163662306a36Sopenharmony_ci
163762306a36Sopenharmony_ci	drm_plane_helper_add(plane, &ltdc_plane_helper_funcs);
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci	drm_plane_create_alpha_property(plane);
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("plane:%d created\n", plane->base.id);
164262306a36Sopenharmony_ci
164362306a36Sopenharmony_ci	return plane;
164462306a36Sopenharmony_ci}
164562306a36Sopenharmony_ci
164662306a36Sopenharmony_cistatic void ltdc_plane_destroy_all(struct drm_device *ddev)
164762306a36Sopenharmony_ci{
164862306a36Sopenharmony_ci	struct drm_plane *plane, *plane_temp;
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci	list_for_each_entry_safe(plane, plane_temp,
165162306a36Sopenharmony_ci				 &ddev->mode_config.plane_list, head)
165262306a36Sopenharmony_ci		drm_plane_cleanup(plane);
165362306a36Sopenharmony_ci}
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_cistatic int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc)
165662306a36Sopenharmony_ci{
165762306a36Sopenharmony_ci	struct ltdc_device *ldev = ddev->dev_private;
165862306a36Sopenharmony_ci	struct drm_plane *primary, *overlay;
165962306a36Sopenharmony_ci	int supported_rotations = DRM_MODE_ROTATE_0 | DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y;
166062306a36Sopenharmony_ci	unsigned int i;
166162306a36Sopenharmony_ci	int ret;
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_ci	primary = ltdc_plane_create(ddev, DRM_PLANE_TYPE_PRIMARY, 0);
166462306a36Sopenharmony_ci	if (!primary) {
166562306a36Sopenharmony_ci		DRM_ERROR("Can not create primary plane\n");
166662306a36Sopenharmony_ci		return -EINVAL;
166762306a36Sopenharmony_ci	}
166862306a36Sopenharmony_ci
166962306a36Sopenharmony_ci	if (ldev->caps.dynamic_zorder)
167062306a36Sopenharmony_ci		drm_plane_create_zpos_property(primary, 0, 0, ldev->caps.nb_layers - 1);
167162306a36Sopenharmony_ci	else
167262306a36Sopenharmony_ci		drm_plane_create_zpos_immutable_property(primary, 0);
167362306a36Sopenharmony_ci
167462306a36Sopenharmony_ci	if (ldev->caps.plane_rotation)
167562306a36Sopenharmony_ci		drm_plane_create_rotation_property(primary, DRM_MODE_ROTATE_0,
167662306a36Sopenharmony_ci						   supported_rotations);
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_ci	/* Init CRTC according to its hardware features */
167962306a36Sopenharmony_ci	if (ldev->caps.crc)
168062306a36Sopenharmony_ci		ret = drm_crtc_init_with_planes(ddev, crtc, primary, NULL,
168162306a36Sopenharmony_ci						&ltdc_crtc_with_crc_support_funcs, NULL);
168262306a36Sopenharmony_ci	else
168362306a36Sopenharmony_ci		ret = drm_crtc_init_with_planes(ddev, crtc, primary, NULL,
168462306a36Sopenharmony_ci						&ltdc_crtc_funcs, NULL);
168562306a36Sopenharmony_ci	if (ret) {
168662306a36Sopenharmony_ci		DRM_ERROR("Can not initialize CRTC\n");
168762306a36Sopenharmony_ci		goto cleanup;
168862306a36Sopenharmony_ci	}
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_ci	drm_crtc_helper_add(crtc, &ltdc_crtc_helper_funcs);
169162306a36Sopenharmony_ci
169262306a36Sopenharmony_ci	drm_mode_crtc_set_gamma_size(crtc, CLUT_SIZE);
169362306a36Sopenharmony_ci	drm_crtc_enable_color_mgmt(crtc, 0, false, CLUT_SIZE);
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("CRTC:%d created\n", crtc->base.id);
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci	/* Add planes. Note : the first layer is used by primary plane */
169862306a36Sopenharmony_ci	for (i = 1; i < ldev->caps.nb_layers; i++) {
169962306a36Sopenharmony_ci		overlay = ltdc_plane_create(ddev, DRM_PLANE_TYPE_OVERLAY, i);
170062306a36Sopenharmony_ci		if (!overlay) {
170162306a36Sopenharmony_ci			ret = -ENOMEM;
170262306a36Sopenharmony_ci			DRM_ERROR("Can not create overlay plane %d\n", i);
170362306a36Sopenharmony_ci			goto cleanup;
170462306a36Sopenharmony_ci		}
170562306a36Sopenharmony_ci		if (ldev->caps.dynamic_zorder)
170662306a36Sopenharmony_ci			drm_plane_create_zpos_property(overlay, i, 0, ldev->caps.nb_layers - 1);
170762306a36Sopenharmony_ci		else
170862306a36Sopenharmony_ci			drm_plane_create_zpos_immutable_property(overlay, i);
170962306a36Sopenharmony_ci
171062306a36Sopenharmony_ci		if (ldev->caps.plane_rotation)
171162306a36Sopenharmony_ci			drm_plane_create_rotation_property(overlay, DRM_MODE_ROTATE_0,
171262306a36Sopenharmony_ci							   supported_rotations);
171362306a36Sopenharmony_ci	}
171462306a36Sopenharmony_ci
171562306a36Sopenharmony_ci	return 0;
171662306a36Sopenharmony_ci
171762306a36Sopenharmony_cicleanup:
171862306a36Sopenharmony_ci	ltdc_plane_destroy_all(ddev);
171962306a36Sopenharmony_ci	return ret;
172062306a36Sopenharmony_ci}
172162306a36Sopenharmony_ci
172262306a36Sopenharmony_cistatic void ltdc_encoder_disable(struct drm_encoder *encoder)
172362306a36Sopenharmony_ci{
172462306a36Sopenharmony_ci	struct drm_device *ddev = encoder->dev;
172562306a36Sopenharmony_ci	struct ltdc_device *ldev = ddev->dev_private;
172662306a36Sopenharmony_ci
172762306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("\n");
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_ci	/* Disable LTDC */
173062306a36Sopenharmony_ci	regmap_clear_bits(ldev->regmap, LTDC_GCR, GCR_LTDCEN);
173162306a36Sopenharmony_ci
173262306a36Sopenharmony_ci	/* Set to sleep state the pinctrl whatever type of encoder */
173362306a36Sopenharmony_ci	pinctrl_pm_select_sleep_state(ddev->dev);
173462306a36Sopenharmony_ci}
173562306a36Sopenharmony_ci
173662306a36Sopenharmony_cistatic void ltdc_encoder_enable(struct drm_encoder *encoder)
173762306a36Sopenharmony_ci{
173862306a36Sopenharmony_ci	struct drm_device *ddev = encoder->dev;
173962306a36Sopenharmony_ci	struct ltdc_device *ldev = ddev->dev_private;
174062306a36Sopenharmony_ci
174162306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("\n");
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_ci	/* set fifo underrun threshold register */
174462306a36Sopenharmony_ci	if (ldev->caps.fifo_threshold)
174562306a36Sopenharmony_ci		regmap_write(ldev->regmap, LTDC_FUT, ldev->fifo_threshold);
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_ci	/* Enable LTDC */
174862306a36Sopenharmony_ci	regmap_set_bits(ldev->regmap, LTDC_GCR, GCR_LTDCEN);
174962306a36Sopenharmony_ci}
175062306a36Sopenharmony_ci
175162306a36Sopenharmony_cistatic void ltdc_encoder_mode_set(struct drm_encoder *encoder,
175262306a36Sopenharmony_ci				  struct drm_display_mode *mode,
175362306a36Sopenharmony_ci				  struct drm_display_mode *adjusted_mode)
175462306a36Sopenharmony_ci{
175562306a36Sopenharmony_ci	struct drm_device *ddev = encoder->dev;
175662306a36Sopenharmony_ci
175762306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("\n");
175862306a36Sopenharmony_ci
175962306a36Sopenharmony_ci	/*
176062306a36Sopenharmony_ci	 * Set to default state the pinctrl only with DPI type.
176162306a36Sopenharmony_ci	 * Others types like DSI, don't need pinctrl due to
176262306a36Sopenharmony_ci	 * internal bridge (the signals do not come out of the chipset).
176362306a36Sopenharmony_ci	 */
176462306a36Sopenharmony_ci	if (encoder->encoder_type == DRM_MODE_ENCODER_DPI)
176562306a36Sopenharmony_ci		pinctrl_pm_select_default_state(ddev->dev);
176662306a36Sopenharmony_ci}
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_cistatic const struct drm_encoder_helper_funcs ltdc_encoder_helper_funcs = {
176962306a36Sopenharmony_ci	.disable = ltdc_encoder_disable,
177062306a36Sopenharmony_ci	.enable = ltdc_encoder_enable,
177162306a36Sopenharmony_ci	.mode_set = ltdc_encoder_mode_set,
177262306a36Sopenharmony_ci};
177362306a36Sopenharmony_ci
177462306a36Sopenharmony_cistatic int ltdc_encoder_init(struct drm_device *ddev, struct drm_bridge *bridge)
177562306a36Sopenharmony_ci{
177662306a36Sopenharmony_ci	struct drm_encoder *encoder;
177762306a36Sopenharmony_ci	int ret;
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_ci	encoder = devm_kzalloc(ddev->dev, sizeof(*encoder), GFP_KERNEL);
178062306a36Sopenharmony_ci	if (!encoder)
178162306a36Sopenharmony_ci		return -ENOMEM;
178262306a36Sopenharmony_ci
178362306a36Sopenharmony_ci	encoder->possible_crtcs = CRTC_MASK;
178462306a36Sopenharmony_ci	encoder->possible_clones = 0;	/* No cloning support */
178562306a36Sopenharmony_ci
178662306a36Sopenharmony_ci	drm_simple_encoder_init(ddev, encoder, DRM_MODE_ENCODER_DPI);
178762306a36Sopenharmony_ci
178862306a36Sopenharmony_ci	drm_encoder_helper_add(encoder, &ltdc_encoder_helper_funcs);
178962306a36Sopenharmony_ci
179062306a36Sopenharmony_ci	ret = drm_bridge_attach(encoder, bridge, NULL, 0);
179162306a36Sopenharmony_ci	if (ret) {
179262306a36Sopenharmony_ci		if (ret != -EPROBE_DEFER)
179362306a36Sopenharmony_ci			drm_encoder_cleanup(encoder);
179462306a36Sopenharmony_ci		return ret;
179562306a36Sopenharmony_ci	}
179662306a36Sopenharmony_ci
179762306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("Bridge encoder:%d created\n", encoder->base.id);
179862306a36Sopenharmony_ci
179962306a36Sopenharmony_ci	return 0;
180062306a36Sopenharmony_ci}
180162306a36Sopenharmony_ci
180262306a36Sopenharmony_cistatic int ltdc_get_caps(struct drm_device *ddev)
180362306a36Sopenharmony_ci{
180462306a36Sopenharmony_ci	struct ltdc_device *ldev = ddev->dev_private;
180562306a36Sopenharmony_ci	u32 bus_width_log2, lcr, gc2r;
180662306a36Sopenharmony_ci
180762306a36Sopenharmony_ci	/*
180862306a36Sopenharmony_ci	 * at least 1 layer must be managed & the number of layers
180962306a36Sopenharmony_ci	 * must not exceed LTDC_MAX_LAYER
181062306a36Sopenharmony_ci	 */
181162306a36Sopenharmony_ci	regmap_read(ldev->regmap, LTDC_LCR, &lcr);
181262306a36Sopenharmony_ci
181362306a36Sopenharmony_ci	ldev->caps.nb_layers = clamp((int)lcr, 1, LTDC_MAX_LAYER);
181462306a36Sopenharmony_ci
181562306a36Sopenharmony_ci	/* set data bus width */
181662306a36Sopenharmony_ci	regmap_read(ldev->regmap, LTDC_GC2R, &gc2r);
181762306a36Sopenharmony_ci	bus_width_log2 = (gc2r & GC2R_BW) >> 4;
181862306a36Sopenharmony_ci	ldev->caps.bus_width = 8 << bus_width_log2;
181962306a36Sopenharmony_ci	regmap_read(ldev->regmap, LTDC_IDR, &ldev->caps.hw_version);
182062306a36Sopenharmony_ci
182162306a36Sopenharmony_ci	switch (ldev->caps.hw_version) {
182262306a36Sopenharmony_ci	case HWVER_10200:
182362306a36Sopenharmony_ci	case HWVER_10300:
182462306a36Sopenharmony_ci		ldev->caps.layer_ofs = LAY_OFS_0;
182562306a36Sopenharmony_ci		ldev->caps.layer_regs = ltdc_layer_regs_a0;
182662306a36Sopenharmony_ci		ldev->caps.pix_fmt_hw = ltdc_pix_fmt_a0;
182762306a36Sopenharmony_ci		ldev->caps.pix_fmt_drm = ltdc_drm_fmt_a0;
182862306a36Sopenharmony_ci		ldev->caps.pix_fmt_nb = ARRAY_SIZE(ltdc_drm_fmt_a0);
182962306a36Sopenharmony_ci		ldev->caps.pix_fmt_flex = false;
183062306a36Sopenharmony_ci		/*
183162306a36Sopenharmony_ci		 * Hw older versions support non-alpha color formats derived
183262306a36Sopenharmony_ci		 * from native alpha color formats only on the primary layer.
183362306a36Sopenharmony_ci		 * For instance, RG16 native format without alpha works fine
183462306a36Sopenharmony_ci		 * on 2nd layer but XR24 (derived color format from AR24)
183562306a36Sopenharmony_ci		 * does not work on 2nd layer.
183662306a36Sopenharmony_ci		 */
183762306a36Sopenharmony_ci		ldev->caps.non_alpha_only_l1 = true;
183862306a36Sopenharmony_ci		ldev->caps.pad_max_freq_hz = 90000000;
183962306a36Sopenharmony_ci		if (ldev->caps.hw_version == HWVER_10200)
184062306a36Sopenharmony_ci			ldev->caps.pad_max_freq_hz = 65000000;
184162306a36Sopenharmony_ci		ldev->caps.nb_irq = 2;
184262306a36Sopenharmony_ci		ldev->caps.ycbcr_input = false;
184362306a36Sopenharmony_ci		ldev->caps.ycbcr_output = false;
184462306a36Sopenharmony_ci		ldev->caps.plane_reg_shadow = false;
184562306a36Sopenharmony_ci		ldev->caps.crc = false;
184662306a36Sopenharmony_ci		ldev->caps.dynamic_zorder = false;
184762306a36Sopenharmony_ci		ldev->caps.plane_rotation = false;
184862306a36Sopenharmony_ci		ldev->caps.fifo_threshold = false;
184962306a36Sopenharmony_ci		break;
185062306a36Sopenharmony_ci	case HWVER_20101:
185162306a36Sopenharmony_ci		ldev->caps.layer_ofs = LAY_OFS_0;
185262306a36Sopenharmony_ci		ldev->caps.layer_regs = ltdc_layer_regs_a1;
185362306a36Sopenharmony_ci		ldev->caps.pix_fmt_hw = ltdc_pix_fmt_a1;
185462306a36Sopenharmony_ci		ldev->caps.pix_fmt_drm = ltdc_drm_fmt_a1;
185562306a36Sopenharmony_ci		ldev->caps.pix_fmt_nb = ARRAY_SIZE(ltdc_drm_fmt_a1);
185662306a36Sopenharmony_ci		ldev->caps.pix_fmt_flex = false;
185762306a36Sopenharmony_ci		ldev->caps.non_alpha_only_l1 = false;
185862306a36Sopenharmony_ci		ldev->caps.pad_max_freq_hz = 150000000;
185962306a36Sopenharmony_ci		ldev->caps.nb_irq = 4;
186062306a36Sopenharmony_ci		ldev->caps.ycbcr_input = false;
186162306a36Sopenharmony_ci		ldev->caps.ycbcr_output = false;
186262306a36Sopenharmony_ci		ldev->caps.plane_reg_shadow = false;
186362306a36Sopenharmony_ci		ldev->caps.crc = false;
186462306a36Sopenharmony_ci		ldev->caps.dynamic_zorder = false;
186562306a36Sopenharmony_ci		ldev->caps.plane_rotation = false;
186662306a36Sopenharmony_ci		ldev->caps.fifo_threshold = false;
186762306a36Sopenharmony_ci		break;
186862306a36Sopenharmony_ci	case HWVER_40100:
186962306a36Sopenharmony_ci		ldev->caps.layer_ofs = LAY_OFS_1;
187062306a36Sopenharmony_ci		ldev->caps.layer_regs = ltdc_layer_regs_a2;
187162306a36Sopenharmony_ci		ldev->caps.pix_fmt_hw = ltdc_pix_fmt_a2;
187262306a36Sopenharmony_ci		ldev->caps.pix_fmt_drm = ltdc_drm_fmt_a2;
187362306a36Sopenharmony_ci		ldev->caps.pix_fmt_nb = ARRAY_SIZE(ltdc_drm_fmt_a2);
187462306a36Sopenharmony_ci		ldev->caps.pix_fmt_flex = true;
187562306a36Sopenharmony_ci		ldev->caps.non_alpha_only_l1 = false;
187662306a36Sopenharmony_ci		ldev->caps.pad_max_freq_hz = 90000000;
187762306a36Sopenharmony_ci		ldev->caps.nb_irq = 2;
187862306a36Sopenharmony_ci		ldev->caps.ycbcr_input = true;
187962306a36Sopenharmony_ci		ldev->caps.ycbcr_output = true;
188062306a36Sopenharmony_ci		ldev->caps.plane_reg_shadow = true;
188162306a36Sopenharmony_ci		ldev->caps.crc = true;
188262306a36Sopenharmony_ci		ldev->caps.dynamic_zorder = true;
188362306a36Sopenharmony_ci		ldev->caps.plane_rotation = true;
188462306a36Sopenharmony_ci		ldev->caps.fifo_threshold = true;
188562306a36Sopenharmony_ci		break;
188662306a36Sopenharmony_ci	default:
188762306a36Sopenharmony_ci		return -ENODEV;
188862306a36Sopenharmony_ci	}
188962306a36Sopenharmony_ci
189062306a36Sopenharmony_ci	return 0;
189162306a36Sopenharmony_ci}
189262306a36Sopenharmony_ci
189362306a36Sopenharmony_civoid ltdc_suspend(struct drm_device *ddev)
189462306a36Sopenharmony_ci{
189562306a36Sopenharmony_ci	struct ltdc_device *ldev = ddev->dev_private;
189662306a36Sopenharmony_ci
189762306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("\n");
189862306a36Sopenharmony_ci	clk_disable_unprepare(ldev->pixel_clk);
189962306a36Sopenharmony_ci}
190062306a36Sopenharmony_ci
190162306a36Sopenharmony_ciint ltdc_resume(struct drm_device *ddev)
190262306a36Sopenharmony_ci{
190362306a36Sopenharmony_ci	struct ltdc_device *ldev = ddev->dev_private;
190462306a36Sopenharmony_ci	int ret;
190562306a36Sopenharmony_ci
190662306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("\n");
190762306a36Sopenharmony_ci
190862306a36Sopenharmony_ci	ret = clk_prepare_enable(ldev->pixel_clk);
190962306a36Sopenharmony_ci	if (ret) {
191062306a36Sopenharmony_ci		DRM_ERROR("failed to enable pixel clock (%d)\n", ret);
191162306a36Sopenharmony_ci		return ret;
191262306a36Sopenharmony_ci	}
191362306a36Sopenharmony_ci
191462306a36Sopenharmony_ci	return 0;
191562306a36Sopenharmony_ci}
191662306a36Sopenharmony_ci
191762306a36Sopenharmony_ciint ltdc_load(struct drm_device *ddev)
191862306a36Sopenharmony_ci{
191962306a36Sopenharmony_ci	struct platform_device *pdev = to_platform_device(ddev->dev);
192062306a36Sopenharmony_ci	struct ltdc_device *ldev = ddev->dev_private;
192162306a36Sopenharmony_ci	struct device *dev = ddev->dev;
192262306a36Sopenharmony_ci	struct device_node *np = dev->of_node;
192362306a36Sopenharmony_ci	struct drm_bridge *bridge;
192462306a36Sopenharmony_ci	struct drm_panel *panel;
192562306a36Sopenharmony_ci	struct drm_crtc *crtc;
192662306a36Sopenharmony_ci	struct reset_control *rstc;
192762306a36Sopenharmony_ci	struct resource *res;
192862306a36Sopenharmony_ci	int irq, i, nb_endpoints;
192962306a36Sopenharmony_ci	int ret = -ENODEV;
193062306a36Sopenharmony_ci
193162306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("\n");
193262306a36Sopenharmony_ci
193362306a36Sopenharmony_ci	/* Get number of endpoints */
193462306a36Sopenharmony_ci	nb_endpoints = of_graph_get_endpoint_count(np);
193562306a36Sopenharmony_ci	if (!nb_endpoints)
193662306a36Sopenharmony_ci		return -ENODEV;
193762306a36Sopenharmony_ci
193862306a36Sopenharmony_ci	ldev->pixel_clk = devm_clk_get(dev, "lcd");
193962306a36Sopenharmony_ci	if (IS_ERR(ldev->pixel_clk)) {
194062306a36Sopenharmony_ci		if (PTR_ERR(ldev->pixel_clk) != -EPROBE_DEFER)
194162306a36Sopenharmony_ci			DRM_ERROR("Unable to get lcd clock\n");
194262306a36Sopenharmony_ci		return PTR_ERR(ldev->pixel_clk);
194362306a36Sopenharmony_ci	}
194462306a36Sopenharmony_ci
194562306a36Sopenharmony_ci	if (clk_prepare_enable(ldev->pixel_clk)) {
194662306a36Sopenharmony_ci		DRM_ERROR("Unable to prepare pixel clock\n");
194762306a36Sopenharmony_ci		return -ENODEV;
194862306a36Sopenharmony_ci	}
194962306a36Sopenharmony_ci
195062306a36Sopenharmony_ci	/* Get endpoints if any */
195162306a36Sopenharmony_ci	for (i = 0; i < nb_endpoints; i++) {
195262306a36Sopenharmony_ci		ret = drm_of_find_panel_or_bridge(np, 0, i, &panel, &bridge);
195362306a36Sopenharmony_ci
195462306a36Sopenharmony_ci		/*
195562306a36Sopenharmony_ci		 * If at least one endpoint is -ENODEV, continue probing,
195662306a36Sopenharmony_ci		 * else if at least one endpoint returned an error
195762306a36Sopenharmony_ci		 * (ie -EPROBE_DEFER) then stop probing.
195862306a36Sopenharmony_ci		 */
195962306a36Sopenharmony_ci		if (ret == -ENODEV)
196062306a36Sopenharmony_ci			continue;
196162306a36Sopenharmony_ci		else if (ret)
196262306a36Sopenharmony_ci			goto err;
196362306a36Sopenharmony_ci
196462306a36Sopenharmony_ci		if (panel) {
196562306a36Sopenharmony_ci			bridge = drm_panel_bridge_add_typed(panel,
196662306a36Sopenharmony_ci							    DRM_MODE_CONNECTOR_DPI);
196762306a36Sopenharmony_ci			if (IS_ERR(bridge)) {
196862306a36Sopenharmony_ci				DRM_ERROR("panel-bridge endpoint %d\n", i);
196962306a36Sopenharmony_ci				ret = PTR_ERR(bridge);
197062306a36Sopenharmony_ci				goto err;
197162306a36Sopenharmony_ci			}
197262306a36Sopenharmony_ci		}
197362306a36Sopenharmony_ci
197462306a36Sopenharmony_ci		if (bridge) {
197562306a36Sopenharmony_ci			ret = ltdc_encoder_init(ddev, bridge);
197662306a36Sopenharmony_ci			if (ret) {
197762306a36Sopenharmony_ci				if (ret != -EPROBE_DEFER)
197862306a36Sopenharmony_ci					DRM_ERROR("init encoder endpoint %d\n", i);
197962306a36Sopenharmony_ci				goto err;
198062306a36Sopenharmony_ci			}
198162306a36Sopenharmony_ci		}
198262306a36Sopenharmony_ci	}
198362306a36Sopenharmony_ci
198462306a36Sopenharmony_ci	rstc = devm_reset_control_get_exclusive(dev, NULL);
198562306a36Sopenharmony_ci
198662306a36Sopenharmony_ci	mutex_init(&ldev->err_lock);
198762306a36Sopenharmony_ci
198862306a36Sopenharmony_ci	if (!IS_ERR(rstc)) {
198962306a36Sopenharmony_ci		reset_control_assert(rstc);
199062306a36Sopenharmony_ci		usleep_range(10, 20);
199162306a36Sopenharmony_ci		reset_control_deassert(rstc);
199262306a36Sopenharmony_ci	}
199362306a36Sopenharmony_ci
199462306a36Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
199562306a36Sopenharmony_ci	ldev->regs = devm_ioremap_resource(dev, res);
199662306a36Sopenharmony_ci	if (IS_ERR(ldev->regs)) {
199762306a36Sopenharmony_ci		DRM_ERROR("Unable to get ltdc registers\n");
199862306a36Sopenharmony_ci		ret = PTR_ERR(ldev->regs);
199962306a36Sopenharmony_ci		goto err;
200062306a36Sopenharmony_ci	}
200162306a36Sopenharmony_ci
200262306a36Sopenharmony_ci	ldev->regmap = devm_regmap_init_mmio(&pdev->dev, ldev->regs, &stm32_ltdc_regmap_cfg);
200362306a36Sopenharmony_ci	if (IS_ERR(ldev->regmap)) {
200462306a36Sopenharmony_ci		DRM_ERROR("Unable to regmap ltdc registers\n");
200562306a36Sopenharmony_ci		ret = PTR_ERR(ldev->regmap);
200662306a36Sopenharmony_ci		goto err;
200762306a36Sopenharmony_ci	}
200862306a36Sopenharmony_ci
200962306a36Sopenharmony_ci	ret = ltdc_get_caps(ddev);
201062306a36Sopenharmony_ci	if (ret) {
201162306a36Sopenharmony_ci		DRM_ERROR("hardware identifier (0x%08x) not supported!\n",
201262306a36Sopenharmony_ci			  ldev->caps.hw_version);
201362306a36Sopenharmony_ci		goto err;
201462306a36Sopenharmony_ci	}
201562306a36Sopenharmony_ci
201662306a36Sopenharmony_ci	/* Disable interrupts */
201762306a36Sopenharmony_ci	if (ldev->caps.fifo_threshold)
201862306a36Sopenharmony_ci		regmap_clear_bits(ldev->regmap, LTDC_IER, IER_LIE | IER_RRIE | IER_FUWIE |
201962306a36Sopenharmony_ci				  IER_TERRIE);
202062306a36Sopenharmony_ci	else
202162306a36Sopenharmony_ci		regmap_clear_bits(ldev->regmap, LTDC_IER, IER_LIE | IER_RRIE | IER_FUWIE |
202262306a36Sopenharmony_ci				  IER_TERRIE | IER_FUEIE);
202362306a36Sopenharmony_ci
202462306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("ltdc hw version 0x%08x\n", ldev->caps.hw_version);
202562306a36Sopenharmony_ci
202662306a36Sopenharmony_ci	/* initialize default value for fifo underrun threshold & clear interrupt error counters */
202762306a36Sopenharmony_ci	ldev->transfer_err = 0;
202862306a36Sopenharmony_ci	ldev->fifo_err = 0;
202962306a36Sopenharmony_ci	ldev->fifo_warn = 0;
203062306a36Sopenharmony_ci	ldev->fifo_threshold = FUT_DFT;
203162306a36Sopenharmony_ci
203262306a36Sopenharmony_ci	for (i = 0; i < ldev->caps.nb_irq; i++) {
203362306a36Sopenharmony_ci		irq = platform_get_irq(pdev, i);
203462306a36Sopenharmony_ci		if (irq < 0) {
203562306a36Sopenharmony_ci			ret = irq;
203662306a36Sopenharmony_ci			goto err;
203762306a36Sopenharmony_ci		}
203862306a36Sopenharmony_ci
203962306a36Sopenharmony_ci		ret = devm_request_threaded_irq(dev, irq, ltdc_irq,
204062306a36Sopenharmony_ci						ltdc_irq_thread, IRQF_ONESHOT,
204162306a36Sopenharmony_ci						dev_name(dev), ddev);
204262306a36Sopenharmony_ci		if (ret) {
204362306a36Sopenharmony_ci			DRM_ERROR("Failed to register LTDC interrupt\n");
204462306a36Sopenharmony_ci			goto err;
204562306a36Sopenharmony_ci		}
204662306a36Sopenharmony_ci	}
204762306a36Sopenharmony_ci
204862306a36Sopenharmony_ci	crtc = devm_kzalloc(dev, sizeof(*crtc), GFP_KERNEL);
204962306a36Sopenharmony_ci	if (!crtc) {
205062306a36Sopenharmony_ci		DRM_ERROR("Failed to allocate crtc\n");
205162306a36Sopenharmony_ci		ret = -ENOMEM;
205262306a36Sopenharmony_ci		goto err;
205362306a36Sopenharmony_ci	}
205462306a36Sopenharmony_ci
205562306a36Sopenharmony_ci	ret = ltdc_crtc_init(ddev, crtc);
205662306a36Sopenharmony_ci	if (ret) {
205762306a36Sopenharmony_ci		DRM_ERROR("Failed to init crtc\n");
205862306a36Sopenharmony_ci		goto err;
205962306a36Sopenharmony_ci	}
206062306a36Sopenharmony_ci
206162306a36Sopenharmony_ci	ret = drm_vblank_init(ddev, NB_CRTC);
206262306a36Sopenharmony_ci	if (ret) {
206362306a36Sopenharmony_ci		DRM_ERROR("Failed calling drm_vblank_init()\n");
206462306a36Sopenharmony_ci		goto err;
206562306a36Sopenharmony_ci	}
206662306a36Sopenharmony_ci
206762306a36Sopenharmony_ci	clk_disable_unprepare(ldev->pixel_clk);
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_ci	pinctrl_pm_select_sleep_state(ddev->dev);
207062306a36Sopenharmony_ci
207162306a36Sopenharmony_ci	pm_runtime_enable(ddev->dev);
207262306a36Sopenharmony_ci
207362306a36Sopenharmony_ci	return 0;
207462306a36Sopenharmony_cierr:
207562306a36Sopenharmony_ci	for (i = 0; i < nb_endpoints; i++)
207662306a36Sopenharmony_ci		drm_of_panel_bridge_remove(ddev->dev->of_node, 0, i);
207762306a36Sopenharmony_ci
207862306a36Sopenharmony_ci	clk_disable_unprepare(ldev->pixel_clk);
207962306a36Sopenharmony_ci
208062306a36Sopenharmony_ci	return ret;
208162306a36Sopenharmony_ci}
208262306a36Sopenharmony_ci
208362306a36Sopenharmony_civoid ltdc_unload(struct drm_device *ddev)
208462306a36Sopenharmony_ci{
208562306a36Sopenharmony_ci	struct device *dev = ddev->dev;
208662306a36Sopenharmony_ci	int nb_endpoints, i;
208762306a36Sopenharmony_ci
208862306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("\n");
208962306a36Sopenharmony_ci
209062306a36Sopenharmony_ci	nb_endpoints = of_graph_get_endpoint_count(dev->of_node);
209162306a36Sopenharmony_ci
209262306a36Sopenharmony_ci	for (i = 0; i < nb_endpoints; i++)
209362306a36Sopenharmony_ci		drm_of_panel_bridge_remove(ddev->dev->of_node, 0, i);
209462306a36Sopenharmony_ci
209562306a36Sopenharmony_ci	pm_runtime_disable(ddev->dev);
209662306a36Sopenharmony_ci}
209762306a36Sopenharmony_ci
209862306a36Sopenharmony_ciMODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>");
209962306a36Sopenharmony_ciMODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
210062306a36Sopenharmony_ciMODULE_AUTHOR("Fabien Dessenne <fabien.dessenne@st.com>");
210162306a36Sopenharmony_ciMODULE_AUTHOR("Mickael Reulier <mickael.reulier@st.com>");
210262306a36Sopenharmony_ciMODULE_DESCRIPTION("STMicroelectronics ST DRM LTDC driver");
210362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
2104