xref: /openbmc/linux/drivers/gpu/drm/stm/ltdc.c (revision 722d4f06)
1ec17f034SBenjamin Gaignard // SPDX-License-Identifier: GPL-2.0
2b759012cSYannick Fertre /*
3b759012cSYannick Fertre  * Copyright (C) STMicroelectronics SA 2017
4b759012cSYannick Fertre  *
5b759012cSYannick Fertre  * Authors: Philippe Cornu <philippe.cornu@st.com>
6b759012cSYannick Fertre  *          Yannick Fertre <yannick.fertre@st.com>
7b759012cSYannick Fertre  *          Fabien Dessenne <fabien.dessenne@st.com>
8b759012cSYannick Fertre  *          Mickael Reulier <mickael.reulier@st.com>
9b759012cSYannick Fertre  */
10b759012cSYannick Fertre 
11b759012cSYannick Fertre #include <linux/clk.h>
12b759012cSYannick Fertre #include <linux/component.h>
132a6b4990SSam Ravnborg #include <linux/delay.h>
142a6b4990SSam Ravnborg #include <linux/interrupt.h>
1572bd9ea3SVille Syrjälä #include <linux/media-bus-format.h>
162a6b4990SSam Ravnborg #include <linux/module.h>
17b759012cSYannick Fertre #include <linux/of_graph.h>
1892a57b3fSYannick Fertré #include <linux/pinctrl/consumer.h>
192a6b4990SSam Ravnborg #include <linux/platform_device.h>
2035ab6cfbSYannick Fertré #include <linux/pm_runtime.h>
21734c2645SYannick Fertre #include <linux/regmap.h>
22b759012cSYannick Fertre #include <linux/reset.h>
23b759012cSYannick Fertre 
24b759012cSYannick Fertre #include <drm/drm_atomic.h>
25b759012cSYannick Fertre #include <drm/drm_atomic_helper.h>
2690bb087fSVille Syrjälä #include <drm/drm_blend.h>
272a6b4990SSam Ravnborg #include <drm/drm_bridge.h>
282a6b4990SSam Ravnborg #include <drm/drm_device.h>
29255490f9SVille Syrjälä #include <drm/drm_edid.h>
306bcfe8eaSDanilo Krummrich #include <drm/drm_fb_dma_helper.h>
312a6b4990SSam Ravnborg #include <drm/drm_fourcc.h>
32720cf96dSVille Syrjälä #include <drm/drm_framebuffer.h>
33820c1707SThomas Zimmermann #include <drm/drm_gem_atomic_helper.h>
344a83c26aSDanilo Krummrich #include <drm/drm_gem_dma_helper.h>
35b759012cSYannick Fertre #include <drm/drm_of.h>
36fcd70cd3SDaniel Vetter #include <drm/drm_probe_helper.h>
37a9cdf680SJagan Teki #include <drm/drm_simple_kms_helper.h>
382a6b4990SSam Ravnborg #include <drm/drm_vblank.h>
39b759012cSYannick Fertre 
40b759012cSYannick Fertre #include <video/videomode.h>
41b759012cSYannick Fertre 
42b759012cSYannick Fertre #include "ltdc.h"
43b759012cSYannick Fertre 
44b759012cSYannick Fertre #define NB_CRTC 1
45b759012cSYannick Fertre #define CRTC_MASK GENMASK(NB_CRTC - 1, 0)
46b759012cSYannick Fertre 
47b759012cSYannick Fertre #define MAX_IRQ 4
48b759012cSYannick Fertre 
49b759012cSYannick Fertre #define HWVER_10200 0x010200
50b759012cSYannick Fertre #define HWVER_10300 0x010300
51b759012cSYannick Fertre #define HWVER_20101 0x020101
521726cee3SYannick Fertre #define HWVER_40100 0x040100
53b759012cSYannick Fertre 
54b759012cSYannick Fertre /*
55b759012cSYannick Fertre  * The address of some registers depends on the HW version: such registers have
561726cee3SYannick Fertre  * an extra offset specified with layer_ofs.
57b759012cSYannick Fertre  */
581726cee3SYannick Fertre #define LAY_OFS_0	0x80
591726cee3SYannick Fertre #define LAY_OFS_1	0x100
601726cee3SYannick Fertre #define LAY_OFS	(ldev->caps.layer_ofs)
61b759012cSYannick Fertre 
62b759012cSYannick Fertre /* Global register offsets */
63b759012cSYannick Fertre #define LTDC_IDR	0x0000		/* IDentification */
64b759012cSYannick Fertre #define LTDC_LCR	0x0004		/* Layer Count */
65b759012cSYannick Fertre #define LTDC_SSCR	0x0008		/* Synchronization Size Configuration */
66b759012cSYannick Fertre #define LTDC_BPCR	0x000C		/* Back Porch Configuration */
67b759012cSYannick Fertre #define LTDC_AWCR	0x0010		/* Active Width Configuration */
68b759012cSYannick Fertre #define LTDC_TWCR	0x0014		/* Total Width Configuration */
69b759012cSYannick Fertre #define LTDC_GCR	0x0018		/* Global Control */
70b759012cSYannick Fertre #define LTDC_GC1R	0x001C		/* Global Configuration 1 */
71b759012cSYannick Fertre #define LTDC_GC2R	0x0020		/* Global Configuration 2 */
72b759012cSYannick Fertre #define LTDC_SRCR	0x0024		/* Shadow Reload Configuration */
73b759012cSYannick Fertre #define LTDC_GACR	0x0028		/* GAmma Correction */
74b759012cSYannick Fertre #define LTDC_BCCR	0x002C		/* Background Color Configuration */
75b759012cSYannick Fertre #define LTDC_IER	0x0034		/* Interrupt Enable */
76b759012cSYannick Fertre #define LTDC_ISR	0x0038		/* Interrupt Status */
77b759012cSYannick Fertre #define LTDC_ICR	0x003C		/* Interrupt Clear */
780e21e3b0SPhilippe CORNU #define LTDC_LIPCR	0x0040		/* Line Interrupt Position Conf. */
79b759012cSYannick Fertre #define LTDC_CPSR	0x0044		/* Current Position Status */
80b759012cSYannick Fertre #define LTDC_CDSR	0x0048		/* Current Display Status */
81fb998edfSYannick Fertre #define LTDC_EDCR	0x0060		/* External Display Control */
8279b44684SRaphael Gallais-Pou #define LTDC_CCRCR	0x007C		/* Computed CRC value */
831726cee3SYannick Fertre #define LTDC_FUT	0x0090		/* Fifo underrun Threshold */
84b759012cSYannick Fertre 
85b759012cSYannick Fertre /* Layer register offsets */
861726cee3SYannick Fertre #define LTDC_L1C0R	(ldev->caps.layer_regs[0])	/* L1 configuration 0 */
871726cee3SYannick Fertre #define LTDC_L1C1R	(ldev->caps.layer_regs[1])	/* L1 configuration 1 */
881726cee3SYannick Fertre #define LTDC_L1RCR	(ldev->caps.layer_regs[2])	/* L1 reload control */
891726cee3SYannick Fertre #define LTDC_L1CR	(ldev->caps.layer_regs[3])	/* L1 control register */
901726cee3SYannick Fertre #define LTDC_L1WHPCR	(ldev->caps.layer_regs[4])	/* L1 window horizontal position configuration */
911726cee3SYannick Fertre #define LTDC_L1WVPCR	(ldev->caps.layer_regs[5])	/* L1 window vertical position configuration */
921726cee3SYannick Fertre #define LTDC_L1CKCR	(ldev->caps.layer_regs[6])	/* L1 color keying configuration */
931726cee3SYannick Fertre #define LTDC_L1PFCR	(ldev->caps.layer_regs[7])	/* L1 pixel format configuration */
941726cee3SYannick Fertre #define LTDC_L1CACR	(ldev->caps.layer_regs[8])	/* L1 constant alpha configuration */
951726cee3SYannick Fertre #define LTDC_L1DCCR	(ldev->caps.layer_regs[9])	/* L1 default color configuration */
961726cee3SYannick Fertre #define LTDC_L1BFCR	(ldev->caps.layer_regs[10])	/* L1 blending factors configuration */
971726cee3SYannick Fertre #define LTDC_L1BLCR	(ldev->caps.layer_regs[11])	/* L1 burst length configuration */
981726cee3SYannick Fertre #define LTDC_L1PCR	(ldev->caps.layer_regs[12])	/* L1 planar configuration */
991726cee3SYannick Fertre #define LTDC_L1CFBAR	(ldev->caps.layer_regs[13])	/* L1 color frame buffer address */
1001726cee3SYannick Fertre #define LTDC_L1CFBLR	(ldev->caps.layer_regs[14])	/* L1 color frame buffer length */
1011726cee3SYannick Fertre #define LTDC_L1CFBLNR	(ldev->caps.layer_regs[15])	/* L1 color frame buffer line number */
1021726cee3SYannick Fertre #define LTDC_L1AFBA0R	(ldev->caps.layer_regs[16])	/* L1 auxiliary frame buffer address 0 */
1031726cee3SYannick Fertre #define LTDC_L1AFBA1R	(ldev->caps.layer_regs[17])	/* L1 auxiliary frame buffer address 1 */
1041726cee3SYannick Fertre #define LTDC_L1AFBLR	(ldev->caps.layer_regs[18])	/* L1 auxiliary frame buffer length */
1051726cee3SYannick Fertre #define LTDC_L1AFBLNR	(ldev->caps.layer_regs[19])	/* L1 auxiliary frame buffer line number */
1061726cee3SYannick Fertre #define LTDC_L1CLUTWR	(ldev->caps.layer_regs[20])	/* L1 CLUT write */
1071726cee3SYannick Fertre #define LTDC_L1CYR0R	(ldev->caps.layer_regs[21])	/* L1 Conversion YCbCr RGB 0 */
1081726cee3SYannick Fertre #define LTDC_L1CYR1R	(ldev->caps.layer_regs[22])	/* L1 Conversion YCbCr RGB 1 */
1091726cee3SYannick Fertre #define LTDC_L1FPF0R	(ldev->caps.layer_regs[23])	/* L1 Flexible Pixel Format 0 */
1101726cee3SYannick Fertre #define LTDC_L1FPF1R	(ldev->caps.layer_regs[24])	/* L1 Flexible Pixel Format 1 */
111b759012cSYannick Fertre 
112b759012cSYannick Fertre /* Bit definitions */
113b759012cSYannick Fertre #define SSCR_VSH	GENMASK(10, 0)	/* Vertical Synchronization Height */
114b759012cSYannick Fertre #define SSCR_HSW	GENMASK(27, 16)	/* Horizontal Synchronization Width */
115b759012cSYannick Fertre 
116b759012cSYannick Fertre #define BPCR_AVBP	GENMASK(10, 0)	/* Accumulated Vertical Back Porch */
117b759012cSYannick Fertre #define BPCR_AHBP	GENMASK(27, 16)	/* Accumulated Horizontal Back Porch */
118b759012cSYannick Fertre 
119b759012cSYannick Fertre #define AWCR_AAH	GENMASK(10, 0)	/* Accumulated Active Height */
120b759012cSYannick Fertre #define AWCR_AAW	GENMASK(27, 16)	/* Accumulated Active Width */
121b759012cSYannick Fertre 
122b759012cSYannick Fertre #define TWCR_TOTALH	GENMASK(10, 0)	/* TOTAL Height */
123b759012cSYannick Fertre #define TWCR_TOTALW	GENMASK(27, 16)	/* TOTAL Width */
124b759012cSYannick Fertre 
125b759012cSYannick Fertre #define GCR_LTDCEN	BIT(0)		/* LTDC ENable */
126b759012cSYannick Fertre #define GCR_DEN		BIT(16)		/* Dither ENable */
12779b44684SRaphael Gallais-Pou #define GCR_CRCEN	BIT(19)		/* CRC ENable */
128444d0db5SPhilippe CORNU #define GCR_PCPOL	BIT(28)		/* Pixel Clock POLarity-Inverted */
129444d0db5SPhilippe CORNU #define GCR_DEPOL	BIT(29)		/* Data Enable POLarity-High */
130444d0db5SPhilippe CORNU #define GCR_VSPOL	BIT(30)		/* Vertical Synchro POLarity-High */
131444d0db5SPhilippe CORNU #define GCR_HSPOL	BIT(31)		/* Horizontal Synchro POLarity-High */
132b759012cSYannick Fertre 
133b759012cSYannick Fertre #define GC1R_WBCH	GENMASK(3, 0)	/* Width of Blue CHannel output */
134b759012cSYannick Fertre #define GC1R_WGCH	GENMASK(7, 4)	/* Width of Green Channel output */
135b759012cSYannick Fertre #define GC1R_WRCH	GENMASK(11, 8)	/* Width of Red Channel output */
136b759012cSYannick Fertre #define GC1R_PBEN	BIT(12)		/* Precise Blending ENable */
137b759012cSYannick Fertre #define GC1R_DT		GENMASK(15, 14)	/* Dithering Technique */
138b759012cSYannick Fertre #define GC1R_GCT	GENMASK(19, 17)	/* Gamma Correction Technique */
139b759012cSYannick Fertre #define GC1R_SHREN	BIT(21)		/* SHadow Registers ENabled */
140b759012cSYannick Fertre #define GC1R_BCP	BIT(22)		/* Background Colour Programmable */
141b759012cSYannick Fertre #define GC1R_BBEN	BIT(23)		/* Background Blending ENabled */
142b759012cSYannick Fertre #define GC1R_LNIP	BIT(24)		/* Line Number IRQ Position */
143b759012cSYannick Fertre #define GC1R_TP		BIT(25)		/* Timing Programmable */
144b759012cSYannick Fertre #define GC1R_IPP	BIT(26)		/* IRQ Polarity Programmable */
145b759012cSYannick Fertre #define GC1R_SPP	BIT(27)		/* Sync Polarity Programmable */
146b759012cSYannick Fertre #define GC1R_DWP	BIT(28)		/* Dither Width Programmable */
147b759012cSYannick Fertre #define GC1R_STREN	BIT(29)		/* STatus Registers ENabled */
148b759012cSYannick Fertre #define GC1R_BMEN	BIT(31)		/* Blind Mode ENabled */
149b759012cSYannick Fertre 
150b759012cSYannick Fertre #define GC2R_EDCA	BIT(0)		/* External Display Control Ability  */
151b759012cSYannick Fertre #define GC2R_STSAEN	BIT(1)		/* Slave Timing Sync Ability ENabled */
152b759012cSYannick Fertre #define GC2R_DVAEN	BIT(2)		/* Dual-View Ability ENabled */
153b759012cSYannick Fertre #define GC2R_DPAEN	BIT(3)		/* Dual-Port Ability ENabled */
154b759012cSYannick Fertre #define GC2R_BW		GENMASK(6, 4)	/* Bus Width (log2 of nb of bytes) */
155b759012cSYannick Fertre #define GC2R_EDCEN	BIT(7)		/* External Display Control ENabled */
156b759012cSYannick Fertre 
157b759012cSYannick Fertre #define SRCR_IMR	BIT(0)		/* IMmediate Reload */
158b759012cSYannick Fertre #define SRCR_VBR	BIT(1)		/* Vertical Blanking Reload */
159b759012cSYannick Fertre 
160b759012cSYannick Fertre #define BCCR_BCBLACK	0x00		/* Background Color BLACK */
161b759012cSYannick Fertre #define BCCR_BCBLUE	GENMASK(7, 0)	/* Background Color BLUE */
162b759012cSYannick Fertre #define BCCR_BCGREEN	GENMASK(15, 8)	/* Background Color GREEN */
163b759012cSYannick Fertre #define BCCR_BCRED	GENMASK(23, 16)	/* Background Color RED */
164b759012cSYannick Fertre #define BCCR_BCWHITE	GENMASK(23, 0)	/* Background Color WHITE */
165b759012cSYannick Fertre 
166b759012cSYannick Fertre #define IER_LIE		BIT(0)		/* Line Interrupt Enable */
1677d008eecSYannick Fertre #define IER_FUWIE	BIT(1)		/* Fifo Underrun Warning Interrupt Enable */
168b759012cSYannick Fertre #define IER_TERRIE	BIT(2)		/* Transfer ERRor Interrupt Enable */
1697d008eecSYannick Fertre #define IER_RRIE	BIT(3)		/* Register Reload Interrupt Enable */
1707d008eecSYannick Fertre #define IER_FUEIE	BIT(6)		/* Fifo Underrun Error Interrupt Enable */
1717d008eecSYannick Fertre #define IER_CRCIE	BIT(7)		/* CRC Error Interrupt Enable */
172b759012cSYannick Fertre 
17353273b52SBenjamin Gaignard #define CPSR_CYPOS	GENMASK(15, 0)	/* Current Y position */
17453273b52SBenjamin Gaignard 
175b759012cSYannick Fertre #define ISR_LIF		BIT(0)		/* Line Interrupt Flag */
1767d008eecSYannick Fertre #define ISR_FUWIF	BIT(1)		/* Fifo Underrun Warning Interrupt Flag */
177b759012cSYannick Fertre #define ISR_TERRIF	BIT(2)		/* Transfer ERRor Interrupt Flag */
178b759012cSYannick Fertre #define ISR_RRIF	BIT(3)		/* Register Reload Interrupt Flag */
1797d008eecSYannick Fertre #define ISR_FUEIF	BIT(6)		/* Fifo Underrun Error Interrupt Flag */
1807d008eecSYannick Fertre #define ISR_CRCIF	BIT(7)		/* CRC Error Interrupt Flag */
181b759012cSYannick Fertre 
182fb998edfSYannick Fertre #define EDCR_OCYEN	BIT(25)		/* Output Conversion to YCbCr 422: ENable */
183fb998edfSYannick Fertre #define EDCR_OCYSEL	BIT(26)		/* Output Conversion to YCbCr 422: SELection of the CCIR */
184fb998edfSYannick Fertre #define EDCR_OCYCO	BIT(27)		/* Output Conversion to YCbCr 422: Chrominance Order */
185fb998edfSYannick Fertre 
186b759012cSYannick Fertre #define LXCR_LEN	BIT(0)		/* Layer ENable */
187b759012cSYannick Fertre #define LXCR_COLKEN	BIT(1)		/* Color Keying Enable */
188b759012cSYannick Fertre #define LXCR_CLUTEN	BIT(4)		/* Color Look-Up Table ENable */
189c6193dc5SYannick Fertre #define LXCR_HMEN	BIT(8)		/* Horizontal Mirroring ENable */
190b759012cSYannick Fertre 
191b759012cSYannick Fertre #define LXWHPCR_WHSTPOS	GENMASK(11, 0)	/* Window Horizontal StarT POSition */
192b759012cSYannick Fertre #define LXWHPCR_WHSPPOS	GENMASK(27, 16)	/* Window Horizontal StoP POSition */
193b759012cSYannick Fertre 
194b759012cSYannick Fertre #define LXWVPCR_WVSTPOS	GENMASK(10, 0)	/* Window Vertical StarT POSition */
195b759012cSYannick Fertre #define LXWVPCR_WVSPPOS	GENMASK(26, 16)	/* Window Vertical StoP POSition */
196b759012cSYannick Fertre 
197b759012cSYannick Fertre #define LXPFCR_PF	GENMASK(2, 0)	/* Pixel Format */
1988f2b5f6dSYannick Fertre #define PF_FLEXIBLE	0x7		/* Flexible Pixel Format selected */
199b759012cSYannick Fertre 
200b759012cSYannick Fertre #define LXCACR_CONSTA	GENMASK(7, 0)	/* CONSTant Alpha */
201b759012cSYannick Fertre 
202b759012cSYannick Fertre #define LXBFCR_BF2	GENMASK(2, 0)	/* Blending Factor 2 */
203b759012cSYannick Fertre #define LXBFCR_BF1	GENMASK(10, 8)	/* Blending Factor 1 */
20462467fccSYannick Fertre #define LXBFCR_BOR	GENMASK(18, 16) /* Blending ORder */
205b759012cSYannick Fertre 
206b759012cSYannick Fertre #define LXCFBLR_CFBLL	GENMASK(12, 0)	/* Color Frame Buffer Line Length */
207c6193dc5SYannick Fertre #define LXCFBLR_CFBP	GENMASK(31, 16) /* Color Frame Buffer Pitch in bytes */
208b759012cSYannick Fertre 
209b759012cSYannick Fertre #define LXCFBLNR_CFBLN	GENMASK(10, 0)	/* Color Frame Buffer Line Number */
210b759012cSYannick Fertre 
211484e72d3SYannick Fertre #define LXCR_C1R_YIA	BIT(0)		/* Ycbcr 422 Interleaved Ability */
212484e72d3SYannick Fertre #define LXCR_C1R_YSPA	BIT(1)		/* Ycbcr 420 Semi-Planar Ability */
213484e72d3SYannick Fertre #define LXCR_C1R_YFPA	BIT(2)		/* Ycbcr 420 Full-Planar Ability */
214484e72d3SYannick Fertre #define LXCR_C1R_SCA	BIT(31)		/* SCaling Ability*/
215484e72d3SYannick Fertre 
216484e72d3SYannick Fertre #define LxPCR_YREN	BIT(9)		/* Y Rescale Enable for the color dynamic range */
217484e72d3SYannick Fertre #define LxPCR_OF	BIT(8)		/* Odd pixel First */
218484e72d3SYannick Fertre #define LxPCR_CBF	BIT(7)		/* CB component First */
219484e72d3SYannick Fertre #define LxPCR_YF	BIT(6)		/* Y component First */
220484e72d3SYannick Fertre #define LxPCR_YCM	GENMASK(5, 4)	/* Ycbcr Conversion Mode */
221484e72d3SYannick Fertre #define YCM_I		0x0		/* Interleaved 422 */
222484e72d3SYannick Fertre #define YCM_SP		0x1		/* Semi-Planar 420 */
223484e72d3SYannick Fertre #define YCM_FP		0x2		/* Full-Planar 420 */
224484e72d3SYannick Fertre #define LxPCR_YCEN	BIT(3)		/* YCbCr-to-RGB Conversion Enable */
225484e72d3SYannick Fertre 
226a55d08e0SYannick Fertre #define LXRCR_IMR	BIT(0)		/* IMmediate Reload */
227a55d08e0SYannick Fertre #define LXRCR_VBR	BIT(1)		/* Vertical Blanking Reload */
228a55d08e0SYannick Fertre #define LXRCR_GRMSK	BIT(2)		/* Global (centralized) Reload MaSKed */
229a55d08e0SYannick Fertre 
230b706a25eSPhilippe CORNU #define CLUT_SIZE	256
231b706a25eSPhilippe CORNU 
232b759012cSYannick Fertre #define CONSTA_MAX	0xFF		/* CONSTant Alpha MAX= 1.0 */
233b759012cSYannick Fertre #define BF1_PAXCA	0x600		/* Pixel Alpha x Constant Alpha */
234b759012cSYannick Fertre #define BF1_CA		0x400		/* Constant Alpha */
235b759012cSYannick Fertre #define BF2_1PAXCA	0x007		/* 1 - (Pixel Alpha x Constant Alpha) */
236b759012cSYannick Fertre #define BF2_1CA		0x005		/* 1 - Constant Alpha */
237b759012cSYannick Fertre 
238b759012cSYannick Fertre #define NB_PF		8		/* Max nb of HW pixel format */
239b759012cSYannick Fertre 
2407d008eecSYannick Fertre #define FUT_DFT		128		/* Default value of fifo underrun threshold */
2417d008eecSYannick Fertre 
24279b44684SRaphael Gallais-Pou /*
24379b44684SRaphael Gallais-Pou  * Skip the first value and the second in case CRC was enabled during
24479b44684SRaphael Gallais-Pou  * the thread irq. This is to be sure CRC value is relevant for the
24579b44684SRaphael Gallais-Pou  * frame.
24679b44684SRaphael Gallais-Pou  */
24779b44684SRaphael Gallais-Pou #define CRC_SKIP_FRAMES 2
24879b44684SRaphael Gallais-Pou 
249b759012cSYannick Fertre enum ltdc_pix_fmt {
250b759012cSYannick Fertre 	PF_NONE,
251b759012cSYannick Fertre 	/* RGB formats */
252b759012cSYannick Fertre 	PF_ARGB8888,		/* ARGB [32 bits] */
253b759012cSYannick Fertre 	PF_RGBA8888,		/* RGBA [32 bits] */
2548f2b5f6dSYannick Fertre 	PF_ABGR8888,		/* ABGR [32 bits] */
2558f2b5f6dSYannick Fertre 	PF_BGRA8888,		/* BGRA [32 bits] */
256b759012cSYannick Fertre 	PF_RGB888,		/* RGB [24 bits] */
2578f2b5f6dSYannick Fertre 	PF_BGR888,		/* BGR [24 bits] */
258b759012cSYannick Fertre 	PF_RGB565,		/* RGB [16 bits] */
2598f2b5f6dSYannick Fertre 	PF_BGR565,		/* BGR [16 bits] */
260b759012cSYannick Fertre 	PF_ARGB1555,		/* ARGB A:1 bit RGB:15 bits [16 bits] */
261b759012cSYannick Fertre 	PF_ARGB4444,		/* ARGB A:4 bits R/G/B: 4 bits each [16 bits] */
262b759012cSYannick Fertre 	/* Indexed formats */
263b759012cSYannick Fertre 	PF_L8,			/* Indexed 8 bits [8 bits] */
264b759012cSYannick Fertre 	PF_AL44,		/* Alpha:4 bits + indexed 4 bits [8 bits] */
2658f2b5f6dSYannick Fertre 	PF_AL88			/* Alpha:8 bits + indexed 8 bits [16 bits] */
266b759012cSYannick Fertre };
267b759012cSYannick Fertre 
268b759012cSYannick Fertre /* The index gives the encoding of the pixel format for an HW version */
269b759012cSYannick Fertre static const enum ltdc_pix_fmt ltdc_pix_fmt_a0[NB_PF] = {
270b759012cSYannick Fertre 	PF_ARGB8888,		/* 0x00 */
271b759012cSYannick Fertre 	PF_RGB888,		/* 0x01 */
272b759012cSYannick Fertre 	PF_RGB565,		/* 0x02 */
273b759012cSYannick Fertre 	PF_ARGB1555,		/* 0x03 */
274b759012cSYannick Fertre 	PF_ARGB4444,		/* 0x04 */
275b759012cSYannick Fertre 	PF_L8,			/* 0x05 */
276b759012cSYannick Fertre 	PF_AL44,		/* 0x06 */
277b759012cSYannick Fertre 	PF_AL88			/* 0x07 */
278b759012cSYannick Fertre };
279b759012cSYannick Fertre 
280b759012cSYannick Fertre static const enum ltdc_pix_fmt ltdc_pix_fmt_a1[NB_PF] = {
281b759012cSYannick Fertre 	PF_ARGB8888,		/* 0x00 */
282b759012cSYannick Fertre 	PF_RGB888,		/* 0x01 */
283b759012cSYannick Fertre 	PF_RGB565,		/* 0x02 */
284b759012cSYannick Fertre 	PF_RGBA8888,		/* 0x03 */
285b759012cSYannick Fertre 	PF_AL44,		/* 0x04 */
286b759012cSYannick Fertre 	PF_L8,			/* 0x05 */
287b759012cSYannick Fertre 	PF_ARGB1555,		/* 0x06 */
288b759012cSYannick Fertre 	PF_ARGB4444		/* 0x07 */
289b759012cSYannick Fertre };
290b759012cSYannick Fertre 
2911726cee3SYannick Fertre static const enum ltdc_pix_fmt ltdc_pix_fmt_a2[NB_PF] = {
2921726cee3SYannick Fertre 	PF_ARGB8888,		/* 0x00 */
2931726cee3SYannick Fertre 	PF_ABGR8888,		/* 0x01 */
2941726cee3SYannick Fertre 	PF_RGBA8888,		/* 0x02 */
2951726cee3SYannick Fertre 	PF_BGRA8888,		/* 0x03 */
2961726cee3SYannick Fertre 	PF_RGB565,		/* 0x04 */
2971726cee3SYannick Fertre 	PF_BGR565,		/* 0x05 */
2981726cee3SYannick Fertre 	PF_RGB888,		/* 0x06 */
2998f2b5f6dSYannick Fertre 	PF_NONE			/* 0x07 */
3008f2b5f6dSYannick Fertre };
3018f2b5f6dSYannick Fertre 
3028f2b5f6dSYannick Fertre static const u32 ltdc_drm_fmt_a0[] = {
3038f2b5f6dSYannick Fertre 	DRM_FORMAT_ARGB8888,
3048f2b5f6dSYannick Fertre 	DRM_FORMAT_XRGB8888,
3058f2b5f6dSYannick Fertre 	DRM_FORMAT_RGB888,
3068f2b5f6dSYannick Fertre 	DRM_FORMAT_RGB565,
3078f2b5f6dSYannick Fertre 	DRM_FORMAT_ARGB1555,
3088f2b5f6dSYannick Fertre 	DRM_FORMAT_XRGB1555,
3098f2b5f6dSYannick Fertre 	DRM_FORMAT_ARGB4444,
3108f2b5f6dSYannick Fertre 	DRM_FORMAT_XRGB4444,
3118f2b5f6dSYannick Fertre 	DRM_FORMAT_C8
3128f2b5f6dSYannick Fertre };
3138f2b5f6dSYannick Fertre 
3148f2b5f6dSYannick Fertre static const u32 ltdc_drm_fmt_a1[] = {
3158f2b5f6dSYannick Fertre 	DRM_FORMAT_ARGB8888,
3168f2b5f6dSYannick Fertre 	DRM_FORMAT_XRGB8888,
3178f2b5f6dSYannick Fertre 	DRM_FORMAT_RGB888,
3188f2b5f6dSYannick Fertre 	DRM_FORMAT_RGB565,
3198f2b5f6dSYannick Fertre 	DRM_FORMAT_RGBA8888,
3208f2b5f6dSYannick Fertre 	DRM_FORMAT_RGBX8888,
3218f2b5f6dSYannick Fertre 	DRM_FORMAT_ARGB1555,
3228f2b5f6dSYannick Fertre 	DRM_FORMAT_XRGB1555,
3238f2b5f6dSYannick Fertre 	DRM_FORMAT_ARGB4444,
3248f2b5f6dSYannick Fertre 	DRM_FORMAT_XRGB4444,
3258f2b5f6dSYannick Fertre 	DRM_FORMAT_C8
3268f2b5f6dSYannick Fertre };
3278f2b5f6dSYannick Fertre 
3288f2b5f6dSYannick Fertre static const u32 ltdc_drm_fmt_a2[] = {
3298f2b5f6dSYannick Fertre 	DRM_FORMAT_ARGB8888,
3308f2b5f6dSYannick Fertre 	DRM_FORMAT_XRGB8888,
3318f2b5f6dSYannick Fertre 	DRM_FORMAT_ABGR8888,
3328f2b5f6dSYannick Fertre 	DRM_FORMAT_XBGR8888,
3338f2b5f6dSYannick Fertre 	DRM_FORMAT_RGBA8888,
3348f2b5f6dSYannick Fertre 	DRM_FORMAT_RGBX8888,
3358f2b5f6dSYannick Fertre 	DRM_FORMAT_BGRA8888,
3368f2b5f6dSYannick Fertre 	DRM_FORMAT_BGRX8888,
3378f2b5f6dSYannick Fertre 	DRM_FORMAT_RGB565,
3388f2b5f6dSYannick Fertre 	DRM_FORMAT_BGR565,
3398f2b5f6dSYannick Fertre 	DRM_FORMAT_RGB888,
3408f2b5f6dSYannick Fertre 	DRM_FORMAT_BGR888,
3418f2b5f6dSYannick Fertre 	DRM_FORMAT_ARGB1555,
3428f2b5f6dSYannick Fertre 	DRM_FORMAT_XRGB1555,
3438f2b5f6dSYannick Fertre 	DRM_FORMAT_ARGB4444,
3448f2b5f6dSYannick Fertre 	DRM_FORMAT_XRGB4444,
3458f2b5f6dSYannick Fertre 	DRM_FORMAT_C8
3461726cee3SYannick Fertre };
3471726cee3SYannick Fertre 
348484e72d3SYannick Fertre static const u32 ltdc_drm_fmt_ycbcr_cp[] = {
349484e72d3SYannick Fertre 	DRM_FORMAT_YUYV,
350484e72d3SYannick Fertre 	DRM_FORMAT_YVYU,
351484e72d3SYannick Fertre 	DRM_FORMAT_UYVY,
352484e72d3SYannick Fertre 	DRM_FORMAT_VYUY
353484e72d3SYannick Fertre };
354484e72d3SYannick Fertre 
355484e72d3SYannick Fertre static const u32 ltdc_drm_fmt_ycbcr_sp[] = {
356484e72d3SYannick Fertre 	DRM_FORMAT_NV12,
357484e72d3SYannick Fertre 	DRM_FORMAT_NV21
358484e72d3SYannick Fertre };
359484e72d3SYannick Fertre 
360484e72d3SYannick Fertre static const u32 ltdc_drm_fmt_ycbcr_fp[] = {
361484e72d3SYannick Fertre 	DRM_FORMAT_YUV420,
362484e72d3SYannick Fertre 	DRM_FORMAT_YVU420
363484e72d3SYannick Fertre };
364484e72d3SYannick Fertre 
3651726cee3SYannick Fertre /* Layer register offsets */
3661726cee3SYannick Fertre static const u32 ltdc_layer_regs_a0[] = {
3671726cee3SYannick Fertre 	0x80,	/* L1 configuration 0 */
3681726cee3SYannick Fertre 	0x00,	/* not available */
3691726cee3SYannick Fertre 	0x00,	/* not available */
3701726cee3SYannick Fertre 	0x84,	/* L1 control register */
3711726cee3SYannick Fertre 	0x88,	/* L1 window horizontal position configuration */
3721726cee3SYannick Fertre 	0x8c,	/* L1 window vertical position configuration */
3731726cee3SYannick Fertre 	0x90,	/* L1 color keying configuration */
3741726cee3SYannick Fertre 	0x94,	/* L1 pixel format configuration */
3751726cee3SYannick Fertre 	0x98,	/* L1 constant alpha configuration */
3761726cee3SYannick Fertre 	0x9c,	/* L1 default color configuration */
3771726cee3SYannick Fertre 	0xa0,	/* L1 blending factors configuration */
3781726cee3SYannick Fertre 	0x00,	/* not available */
3791726cee3SYannick Fertre 	0x00,	/* not available */
3801726cee3SYannick Fertre 	0xac,	/* L1 color frame buffer address */
3811726cee3SYannick Fertre 	0xb0,	/* L1 color frame buffer length */
3821726cee3SYannick Fertre 	0xb4,	/* L1 color frame buffer line number */
3831726cee3SYannick Fertre 	0x00,	/* not available */
3841726cee3SYannick Fertre 	0x00,	/* not available */
3851726cee3SYannick Fertre 	0x00,	/* not available */
3861726cee3SYannick Fertre 	0x00,	/* not available */
3871726cee3SYannick Fertre 	0xc4,	/* L1 CLUT write */
3881726cee3SYannick Fertre 	0x00,	/* not available */
3891726cee3SYannick Fertre 	0x00,	/* not available */
3901726cee3SYannick Fertre 	0x00,	/* not available */
3911726cee3SYannick Fertre 	0x00	/* not available */
3921726cee3SYannick Fertre };
3931726cee3SYannick Fertre 
3941726cee3SYannick Fertre static const u32 ltdc_layer_regs_a1[] = {
3951726cee3SYannick Fertre 	0x80,	/* L1 configuration 0 */
3961726cee3SYannick Fertre 	0x84,	/* L1 configuration 1 */
3971726cee3SYannick Fertre 	0x00,	/* L1 reload control */
3981726cee3SYannick Fertre 	0x88,	/* L1 control register */
3991726cee3SYannick Fertre 	0x8c,	/* L1 window horizontal position configuration */
4001726cee3SYannick Fertre 	0x90,	/* L1 window vertical position configuration */
4011726cee3SYannick Fertre 	0x94,	/* L1 color keying configuration */
4021726cee3SYannick Fertre 	0x98,	/* L1 pixel format configuration */
4031726cee3SYannick Fertre 	0x9c,	/* L1 constant alpha configuration */
4041726cee3SYannick Fertre 	0xa0,	/* L1 default color configuration */
4051726cee3SYannick Fertre 	0xa4,	/* L1 blending factors configuration */
4061726cee3SYannick Fertre 	0xa8,	/* L1 burst length configuration */
4071726cee3SYannick Fertre 	0x00,	/* not available */
4081726cee3SYannick Fertre 	0xac,	/* L1 color frame buffer address */
4091726cee3SYannick Fertre 	0xb0,	/* L1 color frame buffer length */
4101726cee3SYannick Fertre 	0xb4,	/* L1 color frame buffer line number */
4111726cee3SYannick Fertre 	0xb8,	/* L1 auxiliary frame buffer address 0 */
4121726cee3SYannick Fertre 	0xbc,	/* L1 auxiliary frame buffer address 1 */
4131726cee3SYannick Fertre 	0xc0,	/* L1 auxiliary frame buffer length */
4141726cee3SYannick Fertre 	0xc4,	/* L1 auxiliary frame buffer line number */
4151726cee3SYannick Fertre 	0xc8,	/* L1 CLUT write */
4161726cee3SYannick Fertre 	0x00,	/* not available */
4171726cee3SYannick Fertre 	0x00,	/* not available */
4181726cee3SYannick Fertre 	0x00,	/* not available */
4191726cee3SYannick Fertre 	0x00	/* not available */
4201726cee3SYannick Fertre };
4211726cee3SYannick Fertre 
4221726cee3SYannick Fertre static const u32 ltdc_layer_regs_a2[] = {
4231726cee3SYannick Fertre 	0x100,	/* L1 configuration 0 */
4241726cee3SYannick Fertre 	0x104,	/* L1 configuration 1 */
4251726cee3SYannick Fertre 	0x108,	/* L1 reload control */
4261726cee3SYannick Fertre 	0x10c,	/* L1 control register */
4271726cee3SYannick Fertre 	0x110,	/* L1 window horizontal position configuration */
4281726cee3SYannick Fertre 	0x114,	/* L1 window vertical position configuration */
4291726cee3SYannick Fertre 	0x118,	/* L1 color keying configuration */
4301726cee3SYannick Fertre 	0x11c,	/* L1 pixel format configuration */
4311726cee3SYannick Fertre 	0x120,	/* L1 constant alpha configuration */
4321726cee3SYannick Fertre 	0x124,	/* L1 default color configuration */
4331726cee3SYannick Fertre 	0x128,	/* L1 blending factors configuration */
4341726cee3SYannick Fertre 	0x12c,	/* L1 burst length configuration */
4351726cee3SYannick Fertre 	0x130,	/* L1 planar configuration */
4361726cee3SYannick Fertre 	0x134,	/* L1 color frame buffer address */
4371726cee3SYannick Fertre 	0x138,	/* L1 color frame buffer length */
4381726cee3SYannick Fertre 	0x13c,	/* L1 color frame buffer line number */
4391726cee3SYannick Fertre 	0x140,	/* L1 auxiliary frame buffer address 0 */
4401726cee3SYannick Fertre 	0x144,	/* L1 auxiliary frame buffer address 1 */
4411726cee3SYannick Fertre 	0x148,	/* L1 auxiliary frame buffer length */
4421726cee3SYannick Fertre 	0x14c,	/* L1 auxiliary frame buffer line number */
4431726cee3SYannick Fertre 	0x150,	/* L1 CLUT write */
4441726cee3SYannick Fertre 	0x16c,	/* L1 Conversion YCbCr RGB 0 */
4451726cee3SYannick Fertre 	0x170,	/* L1 Conversion YCbCr RGB 1 */
4461726cee3SYannick Fertre 	0x174,	/* L1 Flexible Pixel Format 0 */
4471726cee3SYannick Fertre 	0x178	/* L1 Flexible Pixel Format 1 */
4481726cee3SYannick Fertre };
4491726cee3SYannick Fertre 
450e7c03dbaSYannick Fertré static const u64 ltdc_format_modifiers[] = {
451e7c03dbaSYannick Fertré 	DRM_FORMAT_MOD_LINEAR,
452e7c03dbaSYannick Fertré 	DRM_FORMAT_MOD_INVALID
453e7c03dbaSYannick Fertré };
454e7c03dbaSYannick Fertré 
455734c2645SYannick Fertre static const struct regmap_config stm32_ltdc_regmap_cfg = {
456734c2645SYannick Fertre 	.reg_bits = 32,
457734c2645SYannick Fertre 	.val_bits = 32,
458734c2645SYannick Fertre 	.reg_stride = sizeof(u32),
459734c2645SYannick Fertre 	.max_register = 0x400,
460734c2645SYannick Fertre 	.use_relaxed_mmio = true,
461734c2645SYannick Fertre 	.cache_type = REGCACHE_NONE,
462734c2645SYannick Fertre };
463b759012cSYannick Fertre 
464484e72d3SYannick Fertre static const u32 ltdc_ycbcr2rgb_coeffs[DRM_COLOR_ENCODING_MAX][DRM_COLOR_RANGE_MAX][2] = {
465484e72d3SYannick Fertre 	[DRM_COLOR_YCBCR_BT601][DRM_COLOR_YCBCR_LIMITED_RANGE] = {
466484e72d3SYannick Fertre 		0x02040199,	/* (b_cb = 516 / r_cr = 409) */
467484e72d3SYannick Fertre 		0x006400D0	/* (g_cb = 100 / g_cr = 208) */
468484e72d3SYannick Fertre 	},
469484e72d3SYannick Fertre 	[DRM_COLOR_YCBCR_BT601][DRM_COLOR_YCBCR_FULL_RANGE] = {
470484e72d3SYannick Fertre 		0x01C60167,	/* (b_cb = 454 / r_cr = 359) */
471484e72d3SYannick Fertre 		0x005800B7	/* (g_cb = 88 / g_cr = 183) */
472484e72d3SYannick Fertre 	},
473484e72d3SYannick Fertre 	[DRM_COLOR_YCBCR_BT709][DRM_COLOR_YCBCR_LIMITED_RANGE] = {
474484e72d3SYannick Fertre 		0x021D01CB,	/* (b_cb = 541 / r_cr = 459) */
475484e72d3SYannick Fertre 		0x00370089	/* (g_cb = 55 / g_cr = 137) */
476484e72d3SYannick Fertre 	},
477484e72d3SYannick Fertre 	[DRM_COLOR_YCBCR_BT709][DRM_COLOR_YCBCR_FULL_RANGE] = {
478484e72d3SYannick Fertre 		0x01DB0193,	/* (b_cb = 475 / r_cr = 403) */
479484e72d3SYannick Fertre 		0x00300078	/* (g_cb = 48 / g_cr = 120) */
480484e72d3SYannick Fertre 	}
481484e72d3SYannick Fertre 	/* BT2020 not supported */
482484e72d3SYannick Fertre };
483484e72d3SYannick Fertre 
crtc_to_ltdc(struct drm_crtc * crtc)484b759012cSYannick Fertre static inline struct ltdc_device *crtc_to_ltdc(struct drm_crtc *crtc)
485b759012cSYannick Fertre {
486b759012cSYannick Fertre 	return (struct ltdc_device *)crtc->dev->dev_private;
487b759012cSYannick Fertre }
488b759012cSYannick Fertre 
plane_to_ltdc(struct drm_plane * plane)489b759012cSYannick Fertre static inline struct ltdc_device *plane_to_ltdc(struct drm_plane *plane)
490b759012cSYannick Fertre {
491b759012cSYannick Fertre 	return (struct ltdc_device *)plane->dev->dev_private;
492b759012cSYannick Fertre }
493b759012cSYannick Fertre 
encoder_to_ltdc(struct drm_encoder * enc)494b759012cSYannick Fertre static inline struct ltdc_device *encoder_to_ltdc(struct drm_encoder *enc)
495b759012cSYannick Fertre {
496b759012cSYannick Fertre 	return (struct ltdc_device *)enc->dev->dev_private;
497b759012cSYannick Fertre }
498b759012cSYannick Fertre 
to_ltdc_pixelformat(u32 drm_fmt)499b759012cSYannick Fertre static inline enum ltdc_pix_fmt to_ltdc_pixelformat(u32 drm_fmt)
500b759012cSYannick Fertre {
501b759012cSYannick Fertre 	enum ltdc_pix_fmt pf;
502b759012cSYannick Fertre 
503b759012cSYannick Fertre 	switch (drm_fmt) {
504b759012cSYannick Fertre 	case DRM_FORMAT_ARGB8888:
505b759012cSYannick Fertre 	case DRM_FORMAT_XRGB8888:
506b759012cSYannick Fertre 		pf = PF_ARGB8888;
507b759012cSYannick Fertre 		break;
5088f2b5f6dSYannick Fertre 	case DRM_FORMAT_ABGR8888:
5098f2b5f6dSYannick Fertre 	case DRM_FORMAT_XBGR8888:
5108f2b5f6dSYannick Fertre 		pf = PF_ABGR8888;
5118f2b5f6dSYannick Fertre 		break;
512b759012cSYannick Fertre 	case DRM_FORMAT_RGBA8888:
513b759012cSYannick Fertre 	case DRM_FORMAT_RGBX8888:
514b759012cSYannick Fertre 		pf = PF_RGBA8888;
515b759012cSYannick Fertre 		break;
5168f2b5f6dSYannick Fertre 	case DRM_FORMAT_BGRA8888:
5178f2b5f6dSYannick Fertre 	case DRM_FORMAT_BGRX8888:
5188f2b5f6dSYannick Fertre 		pf = PF_BGRA8888;
5198f2b5f6dSYannick Fertre 		break;
520b759012cSYannick Fertre 	case DRM_FORMAT_RGB888:
521b759012cSYannick Fertre 		pf = PF_RGB888;
522b759012cSYannick Fertre 		break;
5238f2b5f6dSYannick Fertre 	case DRM_FORMAT_BGR888:
5248f2b5f6dSYannick Fertre 		pf = PF_BGR888;
5258f2b5f6dSYannick Fertre 		break;
526b759012cSYannick Fertre 	case DRM_FORMAT_RGB565:
527b759012cSYannick Fertre 		pf = PF_RGB565;
528b759012cSYannick Fertre 		break;
5298f2b5f6dSYannick Fertre 	case DRM_FORMAT_BGR565:
5308f2b5f6dSYannick Fertre 		pf = PF_BGR565;
5318f2b5f6dSYannick Fertre 		break;
532b759012cSYannick Fertre 	case DRM_FORMAT_ARGB1555:
533b759012cSYannick Fertre 	case DRM_FORMAT_XRGB1555:
534b759012cSYannick Fertre 		pf = PF_ARGB1555;
535b759012cSYannick Fertre 		break;
536b759012cSYannick Fertre 	case DRM_FORMAT_ARGB4444:
537b759012cSYannick Fertre 	case DRM_FORMAT_XRGB4444:
538b759012cSYannick Fertre 		pf = PF_ARGB4444;
539b759012cSYannick Fertre 		break;
540b759012cSYannick Fertre 	case DRM_FORMAT_C8:
541b759012cSYannick Fertre 		pf = PF_L8;
542b759012cSYannick Fertre 		break;
543b759012cSYannick Fertre 	default:
544b759012cSYannick Fertre 		pf = PF_NONE;
545b759012cSYannick Fertre 		break;
546b759012cSYannick Fertre 		/* Note: There are no DRM_FORMAT for AL44 and AL88 */
547b759012cSYannick Fertre 	}
548b759012cSYannick Fertre 
549b759012cSYannick Fertre 	return pf;
550b759012cSYannick Fertre }
551b759012cSYannick Fertre 
ltdc_set_flexible_pixel_format(struct drm_plane * plane,enum ltdc_pix_fmt pix_fmt)5528f2b5f6dSYannick Fertre static inline u32 ltdc_set_flexible_pixel_format(struct drm_plane *plane, enum ltdc_pix_fmt pix_fmt)
553b759012cSYannick Fertre {
5548f2b5f6dSYannick Fertre 	struct ltdc_device *ldev = plane_to_ltdc(plane);
5558f2b5f6dSYannick Fertre 	u32 lofs = plane->index * LAY_OFS, ret = PF_FLEXIBLE;
5568f2b5f6dSYannick Fertre 	int psize, alen, apos, rlen, rpos, glen, gpos, blen, bpos;
5578f2b5f6dSYannick Fertre 
5588f2b5f6dSYannick Fertre 	switch (pix_fmt) {
5598f2b5f6dSYannick Fertre 	case PF_BGR888:
5608f2b5f6dSYannick Fertre 		psize = 3;
5618f2b5f6dSYannick Fertre 		alen = 0; apos = 0; rlen = 8; rpos = 0;
5628f2b5f6dSYannick Fertre 		glen = 8; gpos = 8; blen = 8; bpos = 16;
5638f2b5f6dSYannick Fertre 	break;
564b759012cSYannick Fertre 	case PF_ARGB1555:
5658f2b5f6dSYannick Fertre 		psize = 2;
5668f2b5f6dSYannick Fertre 		alen = 1; apos = 15; rlen = 5; rpos = 10;
5678f2b5f6dSYannick Fertre 		glen = 5; gpos = 5;  blen = 5; bpos = 0;
5688f2b5f6dSYannick Fertre 	break;
569b759012cSYannick Fertre 	case PF_ARGB4444:
5708f2b5f6dSYannick Fertre 		psize = 2;
5718f2b5f6dSYannick Fertre 		alen = 4; apos = 12; rlen = 4; rpos = 8;
5728f2b5f6dSYannick Fertre 		glen = 4; gpos = 4; blen = 4; bpos = 0;
5738f2b5f6dSYannick Fertre 	break;
574b759012cSYannick Fertre 	case PF_L8:
5758f2b5f6dSYannick Fertre 		psize = 1;
5768f2b5f6dSYannick Fertre 		alen = 0; apos = 0; rlen = 8; rpos = 0;
5778f2b5f6dSYannick Fertre 		glen = 8; gpos = 0; blen = 8; bpos = 0;
5788f2b5f6dSYannick Fertre 	break;
5798f2b5f6dSYannick Fertre 	case PF_AL44:
5808f2b5f6dSYannick Fertre 		psize = 1;
5818f2b5f6dSYannick Fertre 		alen = 4; apos = 4; rlen = 4; rpos = 0;
5828f2b5f6dSYannick Fertre 		glen = 4; gpos = 0; blen = 4; bpos = 0;
5838f2b5f6dSYannick Fertre 	break;
5848f2b5f6dSYannick Fertre 	case PF_AL88:
5858f2b5f6dSYannick Fertre 		psize = 2;
5868f2b5f6dSYannick Fertre 		alen = 8; apos = 8; rlen = 8; rpos = 0;
5878f2b5f6dSYannick Fertre 		glen = 8; gpos = 0; blen = 8; bpos = 0;
5888f2b5f6dSYannick Fertre 	break;
589b759012cSYannick Fertre 	default:
5908f2b5f6dSYannick Fertre 		ret = NB_PF; /* error case, trace msg is handled by the caller */
5918f2b5f6dSYannick Fertre 	break;
592b759012cSYannick Fertre 	}
593b759012cSYannick Fertre 
5948f2b5f6dSYannick Fertre 	if (ret == PF_FLEXIBLE) {
5958f2b5f6dSYannick Fertre 		regmap_write(ldev->regmap, LTDC_L1FPF0R + lofs,
5968f2b5f6dSYannick Fertre 			     (rlen << 14)  + (rpos << 9) + (alen << 5) + apos);
5978f2b5f6dSYannick Fertre 
5988f2b5f6dSYannick Fertre 		regmap_write(ldev->regmap, LTDC_L1FPF1R + lofs,
5998f2b5f6dSYannick Fertre 			     (psize << 18) + (blen << 14)  + (bpos << 9) + (glen << 5) + gpos);
600aefa8301SPhilippe CORNU 	}
6018f2b5f6dSYannick Fertre 
6028f2b5f6dSYannick Fertre 	return ret;
6038f2b5f6dSYannick Fertre }
6048f2b5f6dSYannick Fertre 
6058f2b5f6dSYannick Fertre /*
6068f2b5f6dSYannick Fertre  * All non-alpha color formats derived from native alpha color formats are
6078f2b5f6dSYannick Fertre  * either characterized by a FourCC format code
6088f2b5f6dSYannick Fertre  */
is_xrgb(u32 drm)6098f2b5f6dSYannick Fertre static inline u32 is_xrgb(u32 drm)
6108f2b5f6dSYannick Fertre {
6118f2b5f6dSYannick Fertre 	return ((drm & 0xFF) == 'X' || ((drm >> 8) & 0xFF) == 'X');
612aefa8301SPhilippe CORNU }
613aefa8301SPhilippe CORNU 
ltdc_set_ycbcr_config(struct drm_plane * plane,u32 drm_pix_fmt)614484e72d3SYannick Fertre static inline void ltdc_set_ycbcr_config(struct drm_plane *plane, u32 drm_pix_fmt)
615484e72d3SYannick Fertre {
616484e72d3SYannick Fertre 	struct ltdc_device *ldev = plane_to_ltdc(plane);
617484e72d3SYannick Fertre 	struct drm_plane_state *state = plane->state;
618484e72d3SYannick Fertre 	u32 lofs = plane->index * LAY_OFS;
619484e72d3SYannick Fertre 	u32 val;
620484e72d3SYannick Fertre 
621484e72d3SYannick Fertre 	switch (drm_pix_fmt) {
622484e72d3SYannick Fertre 	case DRM_FORMAT_YUYV:
623484e72d3SYannick Fertre 		val = (YCM_I << 4) | LxPCR_YF | LxPCR_CBF;
624484e72d3SYannick Fertre 		break;
625484e72d3SYannick Fertre 	case DRM_FORMAT_YVYU:
626484e72d3SYannick Fertre 		val = (YCM_I << 4) | LxPCR_YF;
627484e72d3SYannick Fertre 		break;
628484e72d3SYannick Fertre 	case DRM_FORMAT_UYVY:
629484e72d3SYannick Fertre 		val = (YCM_I << 4) | LxPCR_CBF;
630484e72d3SYannick Fertre 		break;
631484e72d3SYannick Fertre 	case DRM_FORMAT_VYUY:
632484e72d3SYannick Fertre 		val = (YCM_I << 4);
633484e72d3SYannick Fertre 		break;
634484e72d3SYannick Fertre 	case DRM_FORMAT_NV12:
635484e72d3SYannick Fertre 		val = (YCM_SP << 4) | LxPCR_CBF;
636484e72d3SYannick Fertre 		break;
637484e72d3SYannick Fertre 	case DRM_FORMAT_NV21:
638484e72d3SYannick Fertre 		val = (YCM_SP << 4);
639484e72d3SYannick Fertre 		break;
640484e72d3SYannick Fertre 	case DRM_FORMAT_YUV420:
641484e72d3SYannick Fertre 	case DRM_FORMAT_YVU420:
642484e72d3SYannick Fertre 		val = (YCM_FP << 4);
643484e72d3SYannick Fertre 		break;
644484e72d3SYannick Fertre 	default:
645484e72d3SYannick Fertre 		/* RGB or not a YCbCr supported format */
6463b2f68f1SNathan Chancellor 		DRM_ERROR("Unsupported pixel format: %u\n", drm_pix_fmt);
6473b2f68f1SNathan Chancellor 		return;
648484e72d3SYannick Fertre 	}
649484e72d3SYannick Fertre 
650484e72d3SYannick Fertre 	/* Enable limited range */
651484e72d3SYannick Fertre 	if (state->color_range == DRM_COLOR_YCBCR_LIMITED_RANGE)
652484e72d3SYannick Fertre 		val |= LxPCR_YREN;
653484e72d3SYannick Fertre 
654484e72d3SYannick Fertre 	/* enable ycbcr conversion */
655484e72d3SYannick Fertre 	val |= LxPCR_YCEN;
656484e72d3SYannick Fertre 
657484e72d3SYannick Fertre 	regmap_write(ldev->regmap, LTDC_L1PCR + lofs, val);
658484e72d3SYannick Fertre }
659484e72d3SYannick Fertre 
ltdc_set_ycbcr_coeffs(struct drm_plane * plane)660484e72d3SYannick Fertre static inline void ltdc_set_ycbcr_coeffs(struct drm_plane *plane)
661484e72d3SYannick Fertre {
662484e72d3SYannick Fertre 	struct ltdc_device *ldev = plane_to_ltdc(plane);
663484e72d3SYannick Fertre 	struct drm_plane_state *state = plane->state;
664484e72d3SYannick Fertre 	enum drm_color_encoding enc = state->color_encoding;
665484e72d3SYannick Fertre 	enum drm_color_range ran = state->color_range;
666484e72d3SYannick Fertre 	u32 lofs = plane->index * LAY_OFS;
667484e72d3SYannick Fertre 
668484e72d3SYannick Fertre 	if (enc != DRM_COLOR_YCBCR_BT601 && enc != DRM_COLOR_YCBCR_BT709) {
669484e72d3SYannick Fertre 		DRM_ERROR("color encoding %d not supported, use bt601 by default\n", enc);
670484e72d3SYannick Fertre 		/* set by default color encoding to DRM_COLOR_YCBCR_BT601 */
671484e72d3SYannick Fertre 		enc = DRM_COLOR_YCBCR_BT601;
672484e72d3SYannick Fertre 	}
673484e72d3SYannick Fertre 
674484e72d3SYannick Fertre 	if (ran != DRM_COLOR_YCBCR_LIMITED_RANGE && ran != DRM_COLOR_YCBCR_FULL_RANGE) {
675484e72d3SYannick Fertre 		DRM_ERROR("color range %d not supported, use limited range by default\n", ran);
676484e72d3SYannick Fertre 		/* set by default color range to DRM_COLOR_YCBCR_LIMITED_RANGE */
677484e72d3SYannick Fertre 		ran = DRM_COLOR_YCBCR_LIMITED_RANGE;
678484e72d3SYannick Fertre 	}
679484e72d3SYannick Fertre 
680484e72d3SYannick Fertre 	DRM_DEBUG_DRIVER("Color encoding=%d, range=%d\n", enc, ran);
681484e72d3SYannick Fertre 	regmap_write(ldev->regmap, LTDC_L1CYR0R + lofs,
682484e72d3SYannick Fertre 		     ltdc_ycbcr2rgb_coeffs[enc][ran][0]);
683484e72d3SYannick Fertre 	regmap_write(ldev->regmap, LTDC_L1CYR1R + lofs,
684484e72d3SYannick Fertre 		     ltdc_ycbcr2rgb_coeffs[enc][ran][1]);
685484e72d3SYannick Fertre }
686484e72d3SYannick Fertre 
ltdc_irq_crc_handle(struct ltdc_device * ldev,struct drm_crtc * crtc)68779b44684SRaphael Gallais-Pou static inline void ltdc_irq_crc_handle(struct ltdc_device *ldev,
68879b44684SRaphael Gallais-Pou 				       struct drm_crtc *crtc)
68979b44684SRaphael Gallais-Pou {
69079b44684SRaphael Gallais-Pou 	u32 crc;
69179b44684SRaphael Gallais-Pou 	int ret;
69279b44684SRaphael Gallais-Pou 
69379b44684SRaphael Gallais-Pou 	if (ldev->crc_skip_count < CRC_SKIP_FRAMES) {
69479b44684SRaphael Gallais-Pou 		ldev->crc_skip_count++;
69579b44684SRaphael Gallais-Pou 		return;
69679b44684SRaphael Gallais-Pou 	}
69779b44684SRaphael Gallais-Pou 
69879b44684SRaphael Gallais-Pou 	/* Get the CRC of the frame */
69979b44684SRaphael Gallais-Pou 	ret = regmap_read(ldev->regmap, LTDC_CCRCR, &crc);
70079b44684SRaphael Gallais-Pou 	if (ret)
70179b44684SRaphael Gallais-Pou 		return;
70279b44684SRaphael Gallais-Pou 
70379b44684SRaphael Gallais-Pou 	/* Report to DRM the CRC (hw dependent feature) */
70479b44684SRaphael Gallais-Pou 	drm_crtc_add_crc_entry(crtc, true, drm_crtc_accurate_vblank_count(crtc), &crc);
70579b44684SRaphael Gallais-Pou }
70679b44684SRaphael Gallais-Pou 
ltdc_irq_thread(int irq,void * arg)707b759012cSYannick Fertre static irqreturn_t ltdc_irq_thread(int irq, void *arg)
708b759012cSYannick Fertre {
709b759012cSYannick Fertre 	struct drm_device *ddev = arg;
710b759012cSYannick Fertre 	struct ltdc_device *ldev = ddev->dev_private;
711b759012cSYannick Fertre 	struct drm_crtc *crtc = drm_crtc_from_index(ddev, 0);
712b759012cSYannick Fertre 
713b759012cSYannick Fertre 	/* Line IRQ : trigger the vblank event */
71479b44684SRaphael Gallais-Pou 	if (ldev->irq_status & ISR_LIF) {
715b759012cSYannick Fertre 		drm_crtc_handle_vblank(crtc);
716b759012cSYannick Fertre 
71779b44684SRaphael Gallais-Pou 		/* Early return if CRC is not active */
71879b44684SRaphael Gallais-Pou 		if (ldev->crc_active)
71979b44684SRaphael Gallais-Pou 			ltdc_irq_crc_handle(ldev, crtc);
72079b44684SRaphael Gallais-Pou 	}
72179b44684SRaphael Gallais-Pou 
722b759012cSYannick Fertre 	mutex_lock(&ldev->err_lock);
723b759012cSYannick Fertre 	if (ldev->irq_status & ISR_TERRIF)
7247d008eecSYannick Fertre 		ldev->transfer_err++;
7257d008eecSYannick Fertre 	if (ldev->irq_status & ISR_FUEIF)
7267d008eecSYannick Fertre 		ldev->fifo_err++;
7277d008eecSYannick Fertre 	if (ldev->irq_status & ISR_FUWIF)
7287d008eecSYannick Fertre 		ldev->fifo_warn++;
729b759012cSYannick Fertre 	mutex_unlock(&ldev->err_lock);
730b759012cSYannick Fertre 
731b759012cSYannick Fertre 	return IRQ_HANDLED;
732b759012cSYannick Fertre }
733b759012cSYannick Fertre 
ltdc_irq(int irq,void * arg)734b759012cSYannick Fertre static irqreturn_t ltdc_irq(int irq, void *arg)
735b759012cSYannick Fertre {
736b759012cSYannick Fertre 	struct drm_device *ddev = arg;
737b759012cSYannick Fertre 	struct ltdc_device *ldev = ddev->dev_private;
738b759012cSYannick Fertre 
739734c2645SYannick Fertre 	/*
740734c2645SYannick Fertre 	 *  Read & Clear the interrupt status
741734c2645SYannick Fertre 	 *  In order to write / read registers in this critical section
742734c2645SYannick Fertre 	 *  very quickly, the regmap functions are not used.
743734c2645SYannick Fertre 	 */
744734c2645SYannick Fertre 	ldev->irq_status = readl_relaxed(ldev->regs + LTDC_ISR);
745734c2645SYannick Fertre 	writel_relaxed(ldev->irq_status, ldev->regs + LTDC_ICR);
746b759012cSYannick Fertre 
747b759012cSYannick Fertre 	return IRQ_WAKE_THREAD;
748b759012cSYannick Fertre }
749b759012cSYannick Fertre 
750b759012cSYannick Fertre /*
751b759012cSYannick Fertre  * DRM_CRTC
752b759012cSYannick Fertre  */
753b759012cSYannick Fertre 
ltdc_crtc_update_clut(struct drm_crtc * crtc)754b706a25eSPhilippe CORNU static void ltdc_crtc_update_clut(struct drm_crtc *crtc)
755b706a25eSPhilippe CORNU {
756b706a25eSPhilippe CORNU 	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
757b706a25eSPhilippe CORNU 	struct drm_color_lut *lut;
758b706a25eSPhilippe CORNU 	u32 val;
759b706a25eSPhilippe CORNU 	int i;
760b706a25eSPhilippe CORNU 
761b706a25eSPhilippe CORNU 	if (!crtc->state->color_mgmt_changed || !crtc->state->gamma_lut)
762b706a25eSPhilippe CORNU 		return;
763b706a25eSPhilippe CORNU 
764b706a25eSPhilippe CORNU 	lut = (struct drm_color_lut *)crtc->state->gamma_lut->data;
765b706a25eSPhilippe CORNU 
766b706a25eSPhilippe CORNU 	for (i = 0; i < CLUT_SIZE; i++, lut++) {
767b706a25eSPhilippe CORNU 		val = ((lut->red << 8) & 0xff0000) | (lut->green & 0xff00) |
768b706a25eSPhilippe CORNU 			(lut->blue >> 8) | (i << 24);
769734c2645SYannick Fertre 		regmap_write(ldev->regmap, LTDC_L1CLUTWR, val);
770b706a25eSPhilippe CORNU 	}
771b706a25eSPhilippe CORNU }
772b706a25eSPhilippe CORNU 
ltdc_crtc_atomic_enable(struct drm_crtc * crtc,struct drm_atomic_state * state)7730b20a0f8SLaurent Pinchart static void ltdc_crtc_atomic_enable(struct drm_crtc *crtc,
774351f950dSMaxime Ripard 				    struct drm_atomic_state *state)
775b759012cSYannick Fertre {
776b759012cSYannick Fertre 	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
777ebd267b2SMarek Vasut 	struct drm_device *ddev = crtc->dev;
778b759012cSYannick Fertre 
779b759012cSYannick Fertre 	DRM_DEBUG_DRIVER("\n");
780b759012cSYannick Fertre 
781ebd267b2SMarek Vasut 	pm_runtime_get_sync(ddev->dev);
782ebd267b2SMarek Vasut 
783b759012cSYannick Fertre 	/* Sets the background color value */
784734c2645SYannick Fertre 	regmap_write(ldev->regmap, LTDC_BCCR, BCCR_BCBLACK);
785b759012cSYannick Fertre 
786b759012cSYannick Fertre 	/* Enable IRQ */
7877d008eecSYannick Fertre 	regmap_set_bits(ldev->regmap, LTDC_IER, IER_FUWIE | IER_FUEIE | IER_RRIE | IER_TERRIE);
788b759012cSYannick Fertre 
7898ceb8568SYannick Fertré 	/* Commit shadow registers = update planes at next vblank */
790a55d08e0SYannick Fertre 	if (!ldev->caps.plane_reg_shadow)
791734c2645SYannick Fertre 		regmap_set_bits(ldev->regmap, LTDC_SRCR, SRCR_VBR);
792b759012cSYannick Fertre 
793b759012cSYannick Fertre 	drm_crtc_vblank_on(crtc);
794b759012cSYannick Fertre }
795b759012cSYannick Fertre 
ltdc_crtc_atomic_disable(struct drm_crtc * crtc,struct drm_atomic_state * state)79664581714SLaurent Pinchart static void ltdc_crtc_atomic_disable(struct drm_crtc *crtc,
797351f950dSMaxime Ripard 				     struct drm_atomic_state *state)
798b759012cSYannick Fertre {
799b759012cSYannick Fertre 	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
80035ab6cfbSYannick Fertré 	struct drm_device *ddev = crtc->dev;
801c4f218d4SYannick Fertre 	int layer_index = 0;
802b759012cSYannick Fertre 
803b759012cSYannick Fertre 	DRM_DEBUG_DRIVER("\n");
804b759012cSYannick Fertre 
805b759012cSYannick Fertre 	drm_crtc_vblank_off(crtc);
806b759012cSYannick Fertre 
807c4f218d4SYannick Fertre 	/* Disable all layers */
808c4f218d4SYannick Fertre 	for (layer_index = 0; layer_index < ldev->caps.nb_layers; layer_index++)
809c4f218d4SYannick Fertre 		regmap_write_bits(ldev->regmap, LTDC_L1CR + layer_index * LAY_OFS,
810c4f218d4SYannick Fertre 				  LXCR_CLUTEN | LXCR_LEN, 0);
811c4f218d4SYannick Fertre 
812b759012cSYannick Fertre 	/* disable IRQ */
8137d008eecSYannick Fertre 	regmap_clear_bits(ldev->regmap, LTDC_IER, IER_FUWIE | IER_FUEIE | IER_RRIE | IER_TERRIE);
814b759012cSYannick Fertre 
815b759012cSYannick Fertre 	/* immediately commit disable of layers before switching off LTDC */
816a55d08e0SYannick Fertre 	if (!ldev->caps.plane_reg_shadow)
817734c2645SYannick Fertre 		regmap_set_bits(ldev->regmap, LTDC_SRCR, SRCR_IMR);
81835ab6cfbSYannick Fertré 
81935ab6cfbSYannick Fertré 	pm_runtime_put_sync(ddev->dev);
8207d008eecSYannick Fertre 
8217d008eecSYannick Fertre 	/*  clear interrupt error counters */
8227d008eecSYannick Fertre 	mutex_lock(&ldev->err_lock);
8237d008eecSYannick Fertre 	ldev->transfer_err = 0;
8247d008eecSYannick Fertre 	ldev->fifo_err = 0;
8257d008eecSYannick Fertre 	ldev->fifo_warn = 0;
8267d008eecSYannick Fertre 	mutex_unlock(&ldev->err_lock);
827b759012cSYannick Fertre }
828b759012cSYannick Fertre 
8290cefff96SPhilippe CORNU #define CLK_TOLERANCE_HZ 50
8300cefff96SPhilippe CORNU 
8310cefff96SPhilippe CORNU static enum drm_mode_status
ltdc_crtc_mode_valid(struct drm_crtc * crtc,const struct drm_display_mode * mode)8320cefff96SPhilippe CORNU ltdc_crtc_mode_valid(struct drm_crtc *crtc,
8330cefff96SPhilippe CORNU 		     const struct drm_display_mode *mode)
8340cefff96SPhilippe CORNU {
8350cefff96SPhilippe CORNU 	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
8360cefff96SPhilippe CORNU 	int target = mode->clock * 1000;
8370cefff96SPhilippe CORNU 	int target_min = target - CLK_TOLERANCE_HZ;
8380cefff96SPhilippe CORNU 	int target_max = target + CLK_TOLERANCE_HZ;
8390cefff96SPhilippe CORNU 	int result;
8400cefff96SPhilippe CORNU 
8417868e507SYannick Fertre 	result = clk_round_rate(ldev->pixel_clk, target);
8427868e507SYannick Fertre 
8437868e507SYannick Fertre 	DRM_DEBUG_DRIVER("clk rate target %d, available %d\n", target, result);
8447868e507SYannick Fertre 
8457868e507SYannick Fertre 	/* Filter modes according to the max frequency supported by the pads */
8467868e507SYannick Fertre 	if (result > ldev->caps.pad_max_freq_hz)
8477868e507SYannick Fertre 		return MODE_CLOCK_HIGH;
8487868e507SYannick Fertre 
8490cefff96SPhilippe CORNU 	/*
8500cefff96SPhilippe CORNU 	 * Accept all "preferred" modes:
8510cefff96SPhilippe CORNU 	 * - this is important for panels because panel clock tolerances are
8520cefff96SPhilippe CORNU 	 *   bigger than hdmi ones and there is no reason to not accept them
8530cefff96SPhilippe CORNU 	 *   (the fps may vary a little but it is not a problem).
8540cefff96SPhilippe CORNU 	 * - the hdmi preferred mode will be accepted too, but userland will
8550cefff96SPhilippe CORNU 	 *   be able to use others hdmi "valid" modes if necessary.
8560cefff96SPhilippe CORNU 	 */
8570cefff96SPhilippe CORNU 	if (mode->type & DRM_MODE_TYPE_PREFERRED)
8580cefff96SPhilippe CORNU 		return MODE_OK;
8590cefff96SPhilippe CORNU 
8600cefff96SPhilippe CORNU 	/*
8610cefff96SPhilippe CORNU 	 * Filter modes according to the clock value, particularly useful for
8620cefff96SPhilippe CORNU 	 * hdmi modes that require precise pixel clocks.
8630cefff96SPhilippe CORNU 	 */
8640cefff96SPhilippe CORNU 	if (result < target_min || result > target_max)
8650cefff96SPhilippe CORNU 		return MODE_CLOCK_RANGE;
8660cefff96SPhilippe CORNU 
8670cefff96SPhilippe CORNU 	return MODE_OK;
8680cefff96SPhilippe CORNU }
8690cefff96SPhilippe CORNU 
ltdc_crtc_mode_fixup(struct drm_crtc * crtc,const struct drm_display_mode * mode,struct drm_display_mode * adjusted_mode)8701a32a938SPhilippe CORNU static bool ltdc_crtc_mode_fixup(struct drm_crtc *crtc,
8711a32a938SPhilippe CORNU 				 const struct drm_display_mode *mode,
8721a32a938SPhilippe CORNU 				 struct drm_display_mode *adjusted_mode)
8731a32a938SPhilippe CORNU {
8741a32a938SPhilippe CORNU 	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
8751a32a938SPhilippe CORNU 	int rate = mode->clock * 1000;
87635ab6cfbSYannick Fertré 
8771a32a938SPhilippe CORNU 	if (clk_set_rate(ldev->pixel_clk, rate) < 0) {
8781a32a938SPhilippe CORNU 		DRM_ERROR("Cannot set rate (%dHz) for pixel clk\n", rate);
8791a32a938SPhilippe CORNU 		return false;
8801a32a938SPhilippe CORNU 	}
8811a32a938SPhilippe CORNU 
8821a32a938SPhilippe CORNU 	adjusted_mode->clock = clk_get_rate(ldev->pixel_clk) / 1000;
8831a32a938SPhilippe CORNU 
884fd6905fcSYannick Fertré 	DRM_DEBUG_DRIVER("requested clock %dkHz, adjusted clock %dkHz\n",
885fd6905fcSYannick Fertré 			 mode->clock, adjusted_mode->clock);
886fd6905fcSYannick Fertré 
8871a32a938SPhilippe CORNU 	return true;
8881a32a938SPhilippe CORNU }
8891a32a938SPhilippe CORNU 
ltdc_crtc_mode_set_nofb(struct drm_crtc * crtc)890b759012cSYannick Fertre static void ltdc_crtc_mode_set_nofb(struct drm_crtc *crtc)
891b759012cSYannick Fertre {
892b759012cSYannick Fertre 	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
89335ab6cfbSYannick Fertré 	struct drm_device *ddev = crtc->dev;
89499e36044SMarek Vasut 	struct drm_connector_list_iter iter;
89599e36044SMarek Vasut 	struct drm_connector *connector = NULL;
8962e6c86beSXiaomeng Tong 	struct drm_encoder *encoder = NULL, *en_iter;
8972e6c86beSXiaomeng Tong 	struct drm_bridge *bridge = NULL, *br_iter;
898b759012cSYannick Fertre 	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
899b759012cSYannick Fertre 	u32 hsync, vsync, accum_hbp, accum_vbp, accum_act_w, accum_act_h;
900b759012cSYannick Fertre 	u32 total_width, total_height;
901fb998edfSYannick Fertre 	u32 bus_formats = MEDIA_BUS_FMT_RGB888_1X24;
90299e36044SMarek Vasut 	u32 bus_flags = 0;
903b759012cSYannick Fertre 	u32 val;
90435ab6cfbSYannick Fertré 	int ret;
90535ab6cfbSYannick Fertré 
90699e36044SMarek Vasut 	/* get encoder from crtc */
9072e6c86beSXiaomeng Tong 	drm_for_each_encoder(en_iter, ddev)
9082e6c86beSXiaomeng Tong 		if (en_iter->crtc == crtc) {
9092e6c86beSXiaomeng Tong 			encoder = en_iter;
91099e36044SMarek Vasut 			break;
9112e6c86beSXiaomeng Tong 		}
91299e36044SMarek Vasut 
91399e36044SMarek Vasut 	if (encoder) {
91499e36044SMarek Vasut 		/* get bridge from encoder */
9152e6c86beSXiaomeng Tong 		list_for_each_entry(br_iter, &encoder->bridge_chain, chain_node)
9162e6c86beSXiaomeng Tong 			if (br_iter->encoder == encoder) {
9172e6c86beSXiaomeng Tong 				bridge = br_iter;
91899e36044SMarek Vasut 				break;
9192e6c86beSXiaomeng Tong 			}
92099e36044SMarek Vasut 
92199e36044SMarek Vasut 		/* Get the connector from encoder */
92299e36044SMarek Vasut 		drm_connector_list_iter_begin(ddev, &iter);
92399e36044SMarek Vasut 		drm_for_each_connector_iter(connector, &iter)
92499e36044SMarek Vasut 			if (connector->encoder == encoder)
92599e36044SMarek Vasut 				break;
92699e36044SMarek Vasut 		drm_connector_list_iter_end(&iter);
92799e36044SMarek Vasut 	}
92899e36044SMarek Vasut 
92944b4e728SYannick Fertre 	if (bridge && bridge->timings) {
93099e36044SMarek Vasut 		bus_flags = bridge->timings->input_bus_flags;
93144b4e728SYannick Fertre 	} else if (connector) {
93299e36044SMarek Vasut 		bus_flags = connector->display_info.bus_flags;
933fb998edfSYannick Fertre 		if (connector->display_info.num_bus_formats)
934fb998edfSYannick Fertre 			bus_formats = connector->display_info.bus_formats[0];
935fb998edfSYannick Fertre 	}
93699e36044SMarek Vasut 
93735ab6cfbSYannick Fertré 	if (!pm_runtime_active(ddev->dev)) {
93835ab6cfbSYannick Fertré 		ret = pm_runtime_get_sync(ddev->dev);
93935ab6cfbSYannick Fertré 		if (ret) {
94035ab6cfbSYannick Fertré 			DRM_ERROR("Failed to set mode, cannot get sync\n");
94135ab6cfbSYannick Fertré 			return;
94235ab6cfbSYannick Fertré 		}
94335ab6cfbSYannick Fertré 	}
944b759012cSYannick Fertre 
945b759012cSYannick Fertre 	DRM_DEBUG_DRIVER("CRTC:%d mode:%s\n", crtc->base.id, mode->name);
946e99168f9SMarek Vasut 	DRM_DEBUG_DRIVER("Video mode: %dx%d", mode->hdisplay, mode->vdisplay);
947b759012cSYannick Fertre 	DRM_DEBUG_DRIVER(" hfp %d hbp %d hsl %d vfp %d vbp %d vsl %d\n",
948e99168f9SMarek Vasut 			 mode->hsync_start - mode->hdisplay,
949e99168f9SMarek Vasut 			 mode->htotal - mode->hsync_end,
950e99168f9SMarek Vasut 			 mode->hsync_end - mode->hsync_start,
951e99168f9SMarek Vasut 			 mode->vsync_start - mode->vdisplay,
952e99168f9SMarek Vasut 			 mode->vtotal - mode->vsync_end,
953e99168f9SMarek Vasut 			 mode->vsync_end - mode->vsync_start);
954b759012cSYannick Fertre 
955b759012cSYannick Fertre 	/* Convert video timings to ltdc timings */
956e99168f9SMarek Vasut 	hsync = mode->hsync_end - mode->hsync_start - 1;
957e99168f9SMarek Vasut 	vsync = mode->vsync_end - mode->vsync_start - 1;
958e99168f9SMarek Vasut 	accum_hbp = mode->htotal - mode->hsync_start - 1;
959e99168f9SMarek Vasut 	accum_vbp = mode->vtotal - mode->vsync_start - 1;
960e99168f9SMarek Vasut 	accum_act_w = accum_hbp + mode->hdisplay;
961e99168f9SMarek Vasut 	accum_act_h = accum_vbp + mode->vdisplay;
962e99168f9SMarek Vasut 	total_width = mode->htotal - 1;
963e99168f9SMarek Vasut 	total_height = mode->vtotal - 1;
964b759012cSYannick Fertre 
965444d0db5SPhilippe CORNU 	/* Configures the HS, VS, DE and PC polarities. Default Active Low */
966444d0db5SPhilippe CORNU 	val = 0;
967b759012cSYannick Fertre 
968e99168f9SMarek Vasut 	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
969444d0db5SPhilippe CORNU 		val |= GCR_HSPOL;
970b759012cSYannick Fertre 
971e99168f9SMarek Vasut 	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
972444d0db5SPhilippe CORNU 		val |= GCR_VSPOL;
973b759012cSYannick Fertre 
97499e36044SMarek Vasut 	if (bus_flags & DRM_BUS_FLAG_DE_LOW)
975444d0db5SPhilippe CORNU 		val |= GCR_DEPOL;
976b759012cSYannick Fertre 
97799e36044SMarek Vasut 	if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
978444d0db5SPhilippe CORNU 		val |= GCR_PCPOL;
979b759012cSYannick Fertre 
980734c2645SYannick Fertre 	regmap_update_bits(ldev->regmap, LTDC_GCR,
981b759012cSYannick Fertre 			   GCR_HSPOL | GCR_VSPOL | GCR_DEPOL | GCR_PCPOL, val);
982b759012cSYannick Fertre 
983b759012cSYannick Fertre 	/* Set Synchronization size */
984b759012cSYannick Fertre 	val = (hsync << 16) | vsync;
985734c2645SYannick Fertre 	regmap_update_bits(ldev->regmap, LTDC_SSCR, SSCR_VSH | SSCR_HSW, val);
986b759012cSYannick Fertre 
987b759012cSYannick Fertre 	/* Set Accumulated Back porch */
988b759012cSYannick Fertre 	val = (accum_hbp << 16) | accum_vbp;
989734c2645SYannick Fertre 	regmap_update_bits(ldev->regmap, LTDC_BPCR, BPCR_AVBP | BPCR_AHBP, val);
990b759012cSYannick Fertre 
991b759012cSYannick Fertre 	/* Set Accumulated Active Width */
992b759012cSYannick Fertre 	val = (accum_act_w << 16) | accum_act_h;
993734c2645SYannick Fertre 	regmap_update_bits(ldev->regmap, LTDC_AWCR, AWCR_AAW | AWCR_AAH, val);
994b759012cSYannick Fertre 
995b759012cSYannick Fertre 	/* Set total width & height */
996b759012cSYannick Fertre 	val = (total_width << 16) | total_height;
997734c2645SYannick Fertre 	regmap_update_bits(ldev->regmap, LTDC_TWCR, TWCR_TOTALH | TWCR_TOTALW, val);
998b759012cSYannick Fertre 
999734c2645SYannick Fertre 	regmap_write(ldev->regmap, LTDC_LIPCR, (accum_act_h + 1));
1000fb998edfSYannick Fertre 
1001fb998edfSYannick Fertre 	/* Configure the output format (hw version dependent) */
1002fb998edfSYannick Fertre 	if (ldev->caps.ycbcr_output) {
1003fb998edfSYannick Fertre 		/* Input video dynamic_range & colorimetry */
1004fb998edfSYannick Fertre 		int vic = drm_match_cea_mode(mode);
1005fb998edfSYannick Fertre 		u32 val;
1006fb998edfSYannick Fertre 
1007fb998edfSYannick Fertre 		if (vic == 6 || vic == 7 || vic == 21 || vic == 22 ||
1008fb998edfSYannick Fertre 		    vic == 2 || vic == 3 || vic == 17 || vic == 18)
1009fb998edfSYannick Fertre 			/* ITU-R BT.601 */
1010fb998edfSYannick Fertre 			val = 0;
1011fb998edfSYannick Fertre 		else
1012fb998edfSYannick Fertre 			/* ITU-R BT.709 */
1013fb998edfSYannick Fertre 			val = EDCR_OCYSEL;
1014fb998edfSYannick Fertre 
1015fb998edfSYannick Fertre 		switch (bus_formats) {
1016fb998edfSYannick Fertre 		case MEDIA_BUS_FMT_YUYV8_1X16:
1017fb998edfSYannick Fertre 			/* enable ycbcr output converter */
1018fb998edfSYannick Fertre 			regmap_write(ldev->regmap, LTDC_EDCR, EDCR_OCYEN | val);
1019fb998edfSYannick Fertre 			break;
1020fb998edfSYannick Fertre 		case MEDIA_BUS_FMT_YVYU8_1X16:
1021fb998edfSYannick Fertre 			/* enable ycbcr output converter & invert chrominance order */
1022fb998edfSYannick Fertre 			regmap_write(ldev->regmap, LTDC_EDCR, EDCR_OCYEN | EDCR_OCYCO | val);
1023fb998edfSYannick Fertre 			break;
1024fb998edfSYannick Fertre 		default:
1025fb998edfSYannick Fertre 			/* disable ycbcr output converter */
1026fb998edfSYannick Fertre 			regmap_write(ldev->regmap, LTDC_EDCR, 0);
1027fb998edfSYannick Fertre 			break;
1028fb998edfSYannick Fertre 		}
1029fb998edfSYannick Fertre 	}
1030b759012cSYannick Fertre }
1031b759012cSYannick Fertre 
ltdc_crtc_atomic_flush(struct drm_crtc * crtc,struct drm_atomic_state * state)1032b759012cSYannick Fertre static void ltdc_crtc_atomic_flush(struct drm_crtc *crtc,
1033f6ebe9f9SMaxime Ripard 				   struct drm_atomic_state *state)
1034b759012cSYannick Fertre {
1035b759012cSYannick Fertre 	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
103635ab6cfbSYannick Fertré 	struct drm_device *ddev = crtc->dev;
1037b759012cSYannick Fertre 	struct drm_pending_vblank_event *event = crtc->state->event;
1038b759012cSYannick Fertre 
1039b759012cSYannick Fertre 	DRM_DEBUG_ATOMIC("\n");
1040b759012cSYannick Fertre 
1041b706a25eSPhilippe CORNU 	ltdc_crtc_update_clut(crtc);
1042b706a25eSPhilippe CORNU 
1043b759012cSYannick Fertre 	/* Commit shadow registers = update planes at next vblank */
1044a55d08e0SYannick Fertre 	if (!ldev->caps.plane_reg_shadow)
1045734c2645SYannick Fertre 		regmap_set_bits(ldev->regmap, LTDC_SRCR, SRCR_VBR);
1046b759012cSYannick Fertre 
1047b759012cSYannick Fertre 	if (event) {
1048b759012cSYannick Fertre 		crtc->state->event = NULL;
1049b759012cSYannick Fertre 
105035ab6cfbSYannick Fertré 		spin_lock_irq(&ddev->event_lock);
1051b759012cSYannick Fertre 		if (drm_crtc_vblank_get(crtc) == 0)
1052b759012cSYannick Fertre 			drm_crtc_arm_vblank_event(crtc, event);
1053b759012cSYannick Fertre 		else
1054b759012cSYannick Fertre 			drm_crtc_send_vblank_event(crtc, event);
105535ab6cfbSYannick Fertré 		spin_unlock_irq(&ddev->event_lock);
1056b759012cSYannick Fertre 	}
1057b759012cSYannick Fertre }
1058b759012cSYannick Fertre 
ltdc_crtc_get_scanout_position(struct drm_crtc * crtc,bool in_vblank_irq,int * vpos,int * hpos,ktime_t * stime,ktime_t * etime,const struct drm_display_mode * mode)1059b70fbfc7SThomas Zimmermann static bool ltdc_crtc_get_scanout_position(struct drm_crtc *crtc,
1060b70fbfc7SThomas Zimmermann 					   bool in_vblank_irq,
1061b70fbfc7SThomas Zimmermann 					   int *vpos, int *hpos,
106253273b52SBenjamin Gaignard 					   ktime_t *stime, ktime_t *etime,
106353273b52SBenjamin Gaignard 					   const struct drm_display_mode *mode)
106453273b52SBenjamin Gaignard {
1065b70fbfc7SThomas Zimmermann 	struct drm_device *ddev = crtc->dev;
106653273b52SBenjamin Gaignard 	struct ltdc_device *ldev = ddev->dev_private;
106753273b52SBenjamin Gaignard 	int line, vactive_start, vactive_end, vtotal;
106853273b52SBenjamin Gaignard 
106953273b52SBenjamin Gaignard 	if (stime)
107053273b52SBenjamin Gaignard 		*stime = ktime_get();
107153273b52SBenjamin Gaignard 
107253273b52SBenjamin Gaignard 	/* The active area starts after vsync + front porch and ends
107353273b52SBenjamin Gaignard 	 * at vsync + front porc + display size.
107453273b52SBenjamin Gaignard 	 * The total height also include back porch.
107553273b52SBenjamin Gaignard 	 * We have 3 possible cases to handle:
107653273b52SBenjamin Gaignard 	 * - line < vactive_start: vpos = line - vactive_start and will be
107753273b52SBenjamin Gaignard 	 * negative
107853273b52SBenjamin Gaignard 	 * - vactive_start < line < vactive_end: vpos = line - vactive_start
107953273b52SBenjamin Gaignard 	 * and will be positive
108053273b52SBenjamin Gaignard 	 * - line > vactive_end: vpos = line - vtotal - vactive_start
108153273b52SBenjamin Gaignard 	 * and will negative
108253273b52SBenjamin Gaignard 	 *
108353273b52SBenjamin Gaignard 	 * Computation for the two first cases are identical so we can
108453273b52SBenjamin Gaignard 	 * simplify the code and only test if line > vactive_end
108553273b52SBenjamin Gaignard 	 */
108635ab6cfbSYannick Fertré 	if (pm_runtime_active(ddev->dev)) {
1087734c2645SYannick Fertre 		regmap_read(ldev->regmap, LTDC_CPSR, &line);
1088734c2645SYannick Fertre 		line &= CPSR_CYPOS;
1089734c2645SYannick Fertre 		regmap_read(ldev->regmap, LTDC_BPCR, &vactive_start);
1090734c2645SYannick Fertre 		vactive_start &= BPCR_AVBP;
1091734c2645SYannick Fertre 		regmap_read(ldev->regmap, LTDC_AWCR, &vactive_end);
1092734c2645SYannick Fertre 		vactive_end &= AWCR_AAH;
1093734c2645SYannick Fertre 		regmap_read(ldev->regmap, LTDC_TWCR, &vtotal);
1094734c2645SYannick Fertre 		vtotal &= TWCR_TOTALH;
109553273b52SBenjamin Gaignard 
109653273b52SBenjamin Gaignard 		if (line > vactive_end)
109753273b52SBenjamin Gaignard 			*vpos = line - vtotal - vactive_start;
109853273b52SBenjamin Gaignard 		else
109953273b52SBenjamin Gaignard 			*vpos = line - vactive_start;
110035ab6cfbSYannick Fertré 	} else {
110135ab6cfbSYannick Fertré 		*vpos = 0;
110235ab6cfbSYannick Fertré 	}
110353273b52SBenjamin Gaignard 
110453273b52SBenjamin Gaignard 	*hpos = 0;
110553273b52SBenjamin Gaignard 
110653273b52SBenjamin Gaignard 	if (etime)
110753273b52SBenjamin Gaignard 		*etime = ktime_get();
110853273b52SBenjamin Gaignard 
110953273b52SBenjamin Gaignard 	return true;
111053273b52SBenjamin Gaignard }
111153273b52SBenjamin Gaignard 
1112b70fbfc7SThomas Zimmermann static const struct drm_crtc_helper_funcs ltdc_crtc_helper_funcs = {
1113b70fbfc7SThomas Zimmermann 	.mode_valid = ltdc_crtc_mode_valid,
1114b70fbfc7SThomas Zimmermann 	.mode_fixup = ltdc_crtc_mode_fixup,
1115b70fbfc7SThomas Zimmermann 	.mode_set_nofb = ltdc_crtc_mode_set_nofb,
1116b70fbfc7SThomas Zimmermann 	.atomic_flush = ltdc_crtc_atomic_flush,
1117b70fbfc7SThomas Zimmermann 	.atomic_enable = ltdc_crtc_atomic_enable,
1118b70fbfc7SThomas Zimmermann 	.atomic_disable = ltdc_crtc_atomic_disable,
1119b70fbfc7SThomas Zimmermann 	.get_scanout_position = ltdc_crtc_get_scanout_position,
1120b70fbfc7SThomas Zimmermann };
1121b70fbfc7SThomas Zimmermann 
ltdc_crtc_enable_vblank(struct drm_crtc * crtc)1122b70fbfc7SThomas Zimmermann static int ltdc_crtc_enable_vblank(struct drm_crtc *crtc)
1123b70fbfc7SThomas Zimmermann {
1124b70fbfc7SThomas Zimmermann 	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
1125b70fbfc7SThomas Zimmermann 	struct drm_crtc_state *state = crtc->state;
1126b70fbfc7SThomas Zimmermann 
1127b70fbfc7SThomas Zimmermann 	DRM_DEBUG_DRIVER("\n");
1128b70fbfc7SThomas Zimmermann 
1129b70fbfc7SThomas Zimmermann 	if (state->enable)
1130734c2645SYannick Fertre 		regmap_set_bits(ldev->regmap, LTDC_IER, IER_LIE);
1131b70fbfc7SThomas Zimmermann 	else
1132b70fbfc7SThomas Zimmermann 		return -EPERM;
1133b70fbfc7SThomas Zimmermann 
1134b70fbfc7SThomas Zimmermann 	return 0;
1135b70fbfc7SThomas Zimmermann }
1136b70fbfc7SThomas Zimmermann 
ltdc_crtc_disable_vblank(struct drm_crtc * crtc)1137b70fbfc7SThomas Zimmermann static void ltdc_crtc_disable_vblank(struct drm_crtc *crtc)
1138b70fbfc7SThomas Zimmermann {
1139b70fbfc7SThomas Zimmermann 	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
1140b70fbfc7SThomas Zimmermann 
1141b70fbfc7SThomas Zimmermann 	DRM_DEBUG_DRIVER("\n");
1142734c2645SYannick Fertre 	regmap_clear_bits(ldev->regmap, LTDC_IER, IER_LIE);
1143b70fbfc7SThomas Zimmermann }
1144b70fbfc7SThomas Zimmermann 
ltdc_crtc_set_crc_source(struct drm_crtc * crtc,const char * source)114579b44684SRaphael Gallais-Pou static int ltdc_crtc_set_crc_source(struct drm_crtc *crtc, const char *source)
114679b44684SRaphael Gallais-Pou {
1147*898a9e3fSRaphael Gallais-Pou 	struct ltdc_device *ldev;
114879b44684SRaphael Gallais-Pou 	int ret;
114979b44684SRaphael Gallais-Pou 
115079b44684SRaphael Gallais-Pou 	DRM_DEBUG_DRIVER("\n");
115179b44684SRaphael Gallais-Pou 
115279b44684SRaphael Gallais-Pou 	if (!crtc)
115379b44684SRaphael Gallais-Pou 		return -ENODEV;
115479b44684SRaphael Gallais-Pou 
1155*898a9e3fSRaphael Gallais-Pou 	ldev = crtc_to_ltdc(crtc);
1156*898a9e3fSRaphael Gallais-Pou 
115779b44684SRaphael Gallais-Pou 	if (source && strcmp(source, "auto") == 0) {
115879b44684SRaphael Gallais-Pou 		ldev->crc_active = true;
115979b44684SRaphael Gallais-Pou 		ret = regmap_set_bits(ldev->regmap, LTDC_GCR, GCR_CRCEN);
116079b44684SRaphael Gallais-Pou 	} else if (!source) {
116179b44684SRaphael Gallais-Pou 		ldev->crc_active = false;
116279b44684SRaphael Gallais-Pou 		ret = regmap_clear_bits(ldev->regmap, LTDC_GCR, GCR_CRCEN);
116379b44684SRaphael Gallais-Pou 	} else {
116479b44684SRaphael Gallais-Pou 		ret = -EINVAL;
116579b44684SRaphael Gallais-Pou 	}
116679b44684SRaphael Gallais-Pou 
116779b44684SRaphael Gallais-Pou 	ldev->crc_skip_count = 0;
116879b44684SRaphael Gallais-Pou 	return ret;
116979b44684SRaphael Gallais-Pou }
117079b44684SRaphael Gallais-Pou 
ltdc_crtc_verify_crc_source(struct drm_crtc * crtc,const char * source,size_t * values_cnt)117179b44684SRaphael Gallais-Pou static int ltdc_crtc_verify_crc_source(struct drm_crtc *crtc,
117279b44684SRaphael Gallais-Pou 				       const char *source, size_t *values_cnt)
117379b44684SRaphael Gallais-Pou {
117479b44684SRaphael Gallais-Pou 	DRM_DEBUG_DRIVER("\n");
117579b44684SRaphael Gallais-Pou 
117679b44684SRaphael Gallais-Pou 	if (!crtc)
117779b44684SRaphael Gallais-Pou 		return -ENODEV;
117879b44684SRaphael Gallais-Pou 
117979b44684SRaphael Gallais-Pou 	if (source && strcmp(source, "auto") != 0) {
118079b44684SRaphael Gallais-Pou 		DRM_DEBUG_DRIVER("Unknown CRC source %s for %s\n",
118179b44684SRaphael Gallais-Pou 				 source, crtc->name);
118279b44684SRaphael Gallais-Pou 		return -EINVAL;
118379b44684SRaphael Gallais-Pou 	}
118479b44684SRaphael Gallais-Pou 
118579b44684SRaphael Gallais-Pou 	*values_cnt = 1;
118679b44684SRaphael Gallais-Pou 	return 0;
118779b44684SRaphael Gallais-Pou }
118879b44684SRaphael Gallais-Pou 
ltdc_crtc_atomic_print_state(struct drm_printer * p,const struct drm_crtc_state * state)11897d008eecSYannick Fertre static void ltdc_crtc_atomic_print_state(struct drm_printer *p,
11907d008eecSYannick Fertre 					 const struct drm_crtc_state *state)
11917d008eecSYannick Fertre {
11927d008eecSYannick Fertre 	struct drm_crtc *crtc = state->crtc;
11937d008eecSYannick Fertre 	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
11947d008eecSYannick Fertre 
11957d008eecSYannick Fertre 	drm_printf(p, "\ttransfer_error=%d\n", ldev->transfer_err);
11967d008eecSYannick Fertre 	drm_printf(p, "\tfifo_underrun_error=%d\n", ldev->fifo_err);
11977d008eecSYannick Fertre 	drm_printf(p, "\tfifo_underrun_warning=%d\n", ldev->fifo_warn);
11987d008eecSYannick Fertre 	drm_printf(p, "\tfifo_underrun_threshold=%d\n", ldev->fifo_threshold);
11997d008eecSYannick Fertre }
12007d008eecSYannick Fertre 
1201c994796fSPhilippe CORNU static const struct drm_crtc_funcs ltdc_crtc_funcs = {
1202b759012cSYannick Fertre 	.destroy = drm_crtc_cleanup,
1203b759012cSYannick Fertre 	.set_config = drm_atomic_helper_set_config,
1204b759012cSYannick Fertre 	.page_flip = drm_atomic_helper_page_flip,
1205b759012cSYannick Fertre 	.reset = drm_atomic_helper_crtc_reset,
1206b759012cSYannick Fertre 	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
1207b759012cSYannick Fertre 	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
1208c8da8194SPhilippe CORNU 	.enable_vblank = ltdc_crtc_enable_vblank,
1209c8da8194SPhilippe CORNU 	.disable_vblank = ltdc_crtc_disable_vblank,
12109661510eSThomas Zimmermann 	.get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,
12117d008eecSYannick Fertre 	.atomic_print_state = ltdc_crtc_atomic_print_state,
1212b759012cSYannick Fertre };
1213b759012cSYannick Fertre 
121479b44684SRaphael Gallais-Pou static const struct drm_crtc_funcs ltdc_crtc_with_crc_support_funcs = {
121579b44684SRaphael Gallais-Pou 	.destroy = drm_crtc_cleanup,
121679b44684SRaphael Gallais-Pou 	.set_config = drm_atomic_helper_set_config,
121779b44684SRaphael Gallais-Pou 	.page_flip = drm_atomic_helper_page_flip,
121879b44684SRaphael Gallais-Pou 	.reset = drm_atomic_helper_crtc_reset,
121979b44684SRaphael Gallais-Pou 	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
122079b44684SRaphael Gallais-Pou 	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
122179b44684SRaphael Gallais-Pou 	.enable_vblank = ltdc_crtc_enable_vblank,
122279b44684SRaphael Gallais-Pou 	.disable_vblank = ltdc_crtc_disable_vblank,
122379b44684SRaphael Gallais-Pou 	.get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,
122479b44684SRaphael Gallais-Pou 	.set_crc_source = ltdc_crtc_set_crc_source,
122579b44684SRaphael Gallais-Pou 	.verify_crc_source = ltdc_crtc_verify_crc_source,
12267d008eecSYannick Fertre 	.atomic_print_state = ltdc_crtc_atomic_print_state,
122779b44684SRaphael Gallais-Pou };
122879b44684SRaphael Gallais-Pou 
1229b759012cSYannick Fertre /*
1230b759012cSYannick Fertre  * DRM_PLANE
1231b759012cSYannick Fertre  */
1232b759012cSYannick Fertre 
ltdc_plane_atomic_check(struct drm_plane * plane,struct drm_atomic_state * state)1233b759012cSYannick Fertre static int ltdc_plane_atomic_check(struct drm_plane *plane,
12347c11b99aSMaxime Ripard 				   struct drm_atomic_state *state)
1235b759012cSYannick Fertre {
12367c11b99aSMaxime Ripard 	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
12377c11b99aSMaxime Ripard 										 plane);
1238ba5c1649SMaxime Ripard 	struct drm_framebuffer *fb = new_plane_state->fb;
1239a236a669SYueHaibing 	u32 src_w, src_h;
1240b759012cSYannick Fertre 
1241b759012cSYannick Fertre 	DRM_DEBUG_DRIVER("\n");
1242b759012cSYannick Fertre 
1243b759012cSYannick Fertre 	if (!fb)
1244b759012cSYannick Fertre 		return 0;
1245b759012cSYannick Fertre 
1246b759012cSYannick Fertre 	/* convert src_ from 16:16 format */
1247ba5c1649SMaxime Ripard 	src_w = new_plane_state->src_w >> 16;
1248ba5c1649SMaxime Ripard 	src_h = new_plane_state->src_h >> 16;
1249b759012cSYannick Fertre 
1250b759012cSYannick Fertre 	/* Reject scaling */
1251ba5c1649SMaxime Ripard 	if (src_w != new_plane_state->crtc_w || src_h != new_plane_state->crtc_h) {
1252fb37cfa0SYannick Fertre 		DRM_DEBUG_DRIVER("Scaling is not supported");
1253fb37cfa0SYannick Fertre 
1254b759012cSYannick Fertre 		return -EINVAL;
1255b759012cSYannick Fertre 	}
1256b759012cSYannick Fertre 
1257b759012cSYannick Fertre 	return 0;
1258b759012cSYannick Fertre }
1259b759012cSYannick Fertre 
ltdc_plane_atomic_update(struct drm_plane * plane,struct drm_atomic_state * state)1260b759012cSYannick Fertre static void ltdc_plane_atomic_update(struct drm_plane *plane,
1261977697e2SMaxime Ripard 				     struct drm_atomic_state *state)
1262b759012cSYannick Fertre {
1263b759012cSYannick Fertre 	struct ltdc_device *ldev = plane_to_ltdc(plane);
126437418bf1SMaxime Ripard 	struct drm_plane_state *newstate = drm_atomic_get_new_plane_state(state,
126537418bf1SMaxime Ripard 									  plane);
126641016fe1SMaxime Ripard 	struct drm_framebuffer *fb = newstate->fb;
1267b759012cSYannick Fertre 	u32 lofs = plane->index * LAY_OFS;
126841016fe1SMaxime Ripard 	u32 x0 = newstate->crtc_x;
126941016fe1SMaxime Ripard 	u32 x1 = newstate->crtc_x + newstate->crtc_w - 1;
127041016fe1SMaxime Ripard 	u32 y0 = newstate->crtc_y;
127141016fe1SMaxime Ripard 	u32 y1 = newstate->crtc_y + newstate->crtc_h - 1;
1272b759012cSYannick Fertre 	u32 src_x, src_y, src_w, src_h;
1273c6193dc5SYannick Fertre 	u32 val, pitch_in_bytes, line_length, line_number, ahbp, avbp, bpcr;
1274c6193dc5SYannick Fertre 	u32 paddr, paddr1, paddr2;
1275b759012cSYannick Fertre 	enum ltdc_pix_fmt pf;
1276b759012cSYannick Fertre 
127741016fe1SMaxime Ripard 	if (!newstate->crtc || !fb) {
1278b759012cSYannick Fertre 		DRM_DEBUG_DRIVER("fb or crtc NULL");
1279b759012cSYannick Fertre 		return;
1280b759012cSYannick Fertre 	}
1281b759012cSYannick Fertre 
1282b759012cSYannick Fertre 	/* convert src_ from 16:16 format */
128341016fe1SMaxime Ripard 	src_x = newstate->src_x >> 16;
128441016fe1SMaxime Ripard 	src_y = newstate->src_y >> 16;
128541016fe1SMaxime Ripard 	src_w = newstate->src_w >> 16;
128641016fe1SMaxime Ripard 	src_h = newstate->src_h >> 16;
1287b759012cSYannick Fertre 
12880e21e3b0SPhilippe CORNU 	DRM_DEBUG_DRIVER("plane:%d fb:%d (%dx%d)@(%d,%d) -> (%dx%d)@(%d,%d)\n",
1289b759012cSYannick Fertre 			 plane->base.id, fb->base.id,
1290b759012cSYannick Fertre 			 src_w, src_h, src_x, src_y,
129141016fe1SMaxime Ripard 			 newstate->crtc_w, newstate->crtc_h,
129241016fe1SMaxime Ripard 			 newstate->crtc_x, newstate->crtc_y);
1293b759012cSYannick Fertre 
1294734c2645SYannick Fertre 	regmap_read(ldev->regmap, LTDC_BPCR, &bpcr);
1295734c2645SYannick Fertre 
1296b759012cSYannick Fertre 	ahbp = (bpcr & BPCR_AHBP) >> 16;
1297b759012cSYannick Fertre 	avbp = bpcr & BPCR_AVBP;
1298b759012cSYannick Fertre 
1299b759012cSYannick Fertre 	/* Configures the horizontal start and stop position */
1300b759012cSYannick Fertre 	val = ((x1 + 1 + ahbp) << 16) + (x0 + 1 + ahbp);
1301734c2645SYannick Fertre 	regmap_write_bits(ldev->regmap, LTDC_L1WHPCR + lofs,
1302b759012cSYannick Fertre 			  LXWHPCR_WHSTPOS | LXWHPCR_WHSPPOS, val);
1303b759012cSYannick Fertre 
1304b759012cSYannick Fertre 	/* Configures the vertical start and stop position */
1305b759012cSYannick Fertre 	val = ((y1 + 1 + avbp) << 16) + (y0 + 1 + avbp);
1306734c2645SYannick Fertre 	regmap_write_bits(ldev->regmap, LTDC_L1WVPCR + lofs,
1307b759012cSYannick Fertre 			  LXWVPCR_WVSTPOS | LXWVPCR_WVSPPOS, val);
1308b759012cSYannick Fertre 
1309b759012cSYannick Fertre 	/* Specifies the pixel format */
1310b759012cSYannick Fertre 	pf = to_ltdc_pixelformat(fb->format->format);
1311b759012cSYannick Fertre 	for (val = 0; val < NB_PF; val++)
1312b759012cSYannick Fertre 		if (ldev->caps.pix_fmt_hw[val] == pf)
1313b759012cSYannick Fertre 			break;
1314b759012cSYannick Fertre 
13158f2b5f6dSYannick Fertre 	/* Use the flexible color format feature if necessary and available */
13168f2b5f6dSYannick Fertre 	if (ldev->caps.pix_fmt_flex && val == NB_PF)
13178f2b5f6dSYannick Fertre 		val = ltdc_set_flexible_pixel_format(plane, pf);
13188f2b5f6dSYannick Fertre 
1319b759012cSYannick Fertre 	if (val == NB_PF) {
1320b759012cSYannick Fertre 		DRM_ERROR("Pixel format %.4s not supported\n",
1321b759012cSYannick Fertre 			  (char *)&fb->format->format);
1322b759012cSYannick Fertre 		val = 0;	/* set by default ARGB 32 bits */
1323b759012cSYannick Fertre 	}
1324734c2645SYannick Fertre 	regmap_write_bits(ldev->regmap, LTDC_L1PFCR + lofs, LXPFCR_PF, val);
1325b759012cSYannick Fertre 
1326b759012cSYannick Fertre 	/* Specifies the constant alpha value */
1327c20351adSRaphael Gallais-Pou 	val = newstate->alpha >> 8;
1328734c2645SYannick Fertre 	regmap_write_bits(ldev->regmap, LTDC_L1CACR + lofs, LXCACR_CONSTA, val);
1329b759012cSYannick Fertre 
1330b759012cSYannick Fertre 	/* Specifies the blending factors */
1331b759012cSYannick Fertre 	val = BF1_PAXCA | BF2_1PAXCA;
1332aefa8301SPhilippe CORNU 	if (!fb->format->has_alpha)
1333aefa8301SPhilippe CORNU 		val = BF1_CA | BF2_1CA;
1334aefa8301SPhilippe CORNU 
13359569002aSPhilippe CORNU 	/* Manage hw-specific capabilities */
13369569002aSPhilippe CORNU 	if (ldev->caps.non_alpha_only_l1 &&
13379569002aSPhilippe CORNU 	    plane->type != DRM_PLANE_TYPE_PRIMARY)
13389569002aSPhilippe CORNU 		val = BF1_PAXCA | BF2_1PAXCA;
13399569002aSPhilippe CORNU 
134062467fccSYannick Fertre 	if (ldev->caps.dynamic_zorder) {
134162467fccSYannick Fertre 		val |= (newstate->normalized_zpos << 16);
134262467fccSYannick Fertre 		regmap_write_bits(ldev->regmap, LTDC_L1BFCR + lofs,
134362467fccSYannick Fertre 				  LXBFCR_BF2 | LXBFCR_BF1 | LXBFCR_BOR, val);
134462467fccSYannick Fertre 	} else {
134562467fccSYannick Fertre 		regmap_write_bits(ldev->regmap, LTDC_L1BFCR + lofs,
134662467fccSYannick Fertre 				  LXBFCR_BF2 | LXBFCR_BF1, val);
134762467fccSYannick Fertre 	}
1348b759012cSYannick Fertre 
1349b759012cSYannick Fertre 	/* Sets the FB address */
13506bcfe8eaSDanilo Krummrich 	paddr = (u32)drm_fb_dma_get_gem_addr(fb, newstate, 0);
1351b759012cSYannick Fertre 
1352c6193dc5SYannick Fertre 	if (newstate->rotation & DRM_MODE_REFLECT_X)
1353c6193dc5SYannick Fertre 		paddr += (fb->format->cpp[0] * (x1 - x0 + 1)) - 1;
1354c6193dc5SYannick Fertre 
1355c6193dc5SYannick Fertre 	if (newstate->rotation & DRM_MODE_REFLECT_Y)
1356c6193dc5SYannick Fertre 		paddr += (fb->pitches[0] * (y1 - y0));
1357c6193dc5SYannick Fertre 
1358b759012cSYannick Fertre 	DRM_DEBUG_DRIVER("fb: phys 0x%08x", paddr);
1359734c2645SYannick Fertre 	regmap_write(ldev->regmap, LTDC_L1CFBAR + lofs, paddr);
1360b759012cSYannick Fertre 
1361c6193dc5SYannick Fertre 	/* Configures the color frame buffer pitch in bytes & line length */
1362c6193dc5SYannick Fertre 	line_length = fb->format->cpp[0] *
1363c6193dc5SYannick Fertre 		      (x1 - x0 + 1) + (ldev->caps.bus_width >> 3) - 1;
1364c6193dc5SYannick Fertre 
1365c6193dc5SYannick Fertre 	if (newstate->rotation & DRM_MODE_REFLECT_Y)
1366c6193dc5SYannick Fertre 		/* Compute negative value (signed on 16 bits) for the picth */
1367c6193dc5SYannick Fertre 		pitch_in_bytes = 0x10000 - fb->pitches[0];
1368c6193dc5SYannick Fertre 	else
1369c6193dc5SYannick Fertre 		pitch_in_bytes = fb->pitches[0];
1370c6193dc5SYannick Fertre 
1371c6193dc5SYannick Fertre 	val = (pitch_in_bytes << 16) | line_length;
1372c6193dc5SYannick Fertre 	regmap_write_bits(ldev->regmap, LTDC_L1CFBLR + lofs, LXCFBLR_CFBLL | LXCFBLR_CFBP, val);
1373c6193dc5SYannick Fertre 
1374c6193dc5SYannick Fertre 	/* Configures the frame buffer line number */
1375c6193dc5SYannick Fertre 	line_number = y1 - y0 + 1;
1376c6193dc5SYannick Fertre 	regmap_write_bits(ldev->regmap, LTDC_L1CFBLNR + lofs, LXCFBLNR_CFBLN, line_number);
1377c6193dc5SYannick Fertre 
1378484e72d3SYannick Fertre 	if (ldev->caps.ycbcr_input) {
1379484e72d3SYannick Fertre 		if (fb->format->is_yuv) {
1380484e72d3SYannick Fertre 			switch (fb->format->format) {
1381484e72d3SYannick Fertre 			case DRM_FORMAT_NV12:
1382484e72d3SYannick Fertre 			case DRM_FORMAT_NV21:
1383c6193dc5SYannick Fertre 			/* Configure the auxiliary frame buffer address 0 */
13846bcfe8eaSDanilo Krummrich 			paddr1 = (u32)drm_fb_dma_get_gem_addr(fb, newstate, 1);
1385484e72d3SYannick Fertre 
1386c6193dc5SYannick Fertre 			if (newstate->rotation & DRM_MODE_REFLECT_X)
1387c6193dc5SYannick Fertre 				paddr1 += ((fb->format->cpp[1] * (x1 - x0 + 1)) >> 1) - 1;
1388484e72d3SYannick Fertre 
1389c6193dc5SYannick Fertre 			if (newstate->rotation & DRM_MODE_REFLECT_Y)
1390c6193dc5SYannick Fertre 				paddr1 += (fb->pitches[1] * (y1 - y0 - 1)) >> 1;
1391c6193dc5SYannick Fertre 
1392c6193dc5SYannick Fertre 			regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr1);
1393484e72d3SYannick Fertre 			break;
1394484e72d3SYannick Fertre 			case DRM_FORMAT_YUV420:
1395c6193dc5SYannick Fertre 			/* Configure the auxiliary frame buffer address 0 & 1 */
13966bcfe8eaSDanilo Krummrich 			paddr1 = (u32)drm_fb_dma_get_gem_addr(fb, newstate, 1);
13976bcfe8eaSDanilo Krummrich 			paddr2 = (u32)drm_fb_dma_get_gem_addr(fb, newstate, 2);
1398484e72d3SYannick Fertre 
1399c6193dc5SYannick Fertre 			if (newstate->rotation & DRM_MODE_REFLECT_X) {
1400c6193dc5SYannick Fertre 				paddr1 += ((fb->format->cpp[1] * (x1 - x0 + 1)) >> 1) - 1;
1401c6193dc5SYannick Fertre 				paddr2 += ((fb->format->cpp[2] * (x1 - x0 + 1)) >> 1) - 1;
1402c6193dc5SYannick Fertre 			}
1403484e72d3SYannick Fertre 
1404c6193dc5SYannick Fertre 			if (newstate->rotation & DRM_MODE_REFLECT_Y) {
1405c6193dc5SYannick Fertre 				paddr1 += (fb->pitches[1] * (y1 - y0 - 1)) >> 1;
1406c6193dc5SYannick Fertre 				paddr2 += (fb->pitches[2] * (y1 - y0 - 1)) >> 1;
1407c6193dc5SYannick Fertre 			}
1408484e72d3SYannick Fertre 
1409c6193dc5SYannick Fertre 			regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr1);
1410c6193dc5SYannick Fertre 			regmap_write(ldev->regmap, LTDC_L1AFBA1R + lofs, paddr2);
1411484e72d3SYannick Fertre 			break;
1412484e72d3SYannick Fertre 			case DRM_FORMAT_YVU420:
1413c6193dc5SYannick Fertre 			/* Configure the auxiliary frame buffer address 0 & 1 */
14146bcfe8eaSDanilo Krummrich 			paddr1 = (u32)drm_fb_dma_get_gem_addr(fb, newstate, 2);
14156bcfe8eaSDanilo Krummrich 			paddr2 = (u32)drm_fb_dma_get_gem_addr(fb, newstate, 1);
1416484e72d3SYannick Fertre 
1417c6193dc5SYannick Fertre 			if (newstate->rotation & DRM_MODE_REFLECT_X) {
1418c6193dc5SYannick Fertre 				paddr1 += ((fb->format->cpp[1] * (x1 - x0 + 1)) >> 1) - 1;
1419c6193dc5SYannick Fertre 				paddr2 += ((fb->format->cpp[2] * (x1 - x0 + 1)) >> 1) - 1;
1420c6193dc5SYannick Fertre 			}
1421484e72d3SYannick Fertre 
1422c6193dc5SYannick Fertre 			if (newstate->rotation & DRM_MODE_REFLECT_Y) {
1423c6193dc5SYannick Fertre 				paddr1 += (fb->pitches[1] * (y1 - y0 - 1)) >> 1;
1424c6193dc5SYannick Fertre 				paddr2 += (fb->pitches[2] * (y1 - y0 - 1)) >> 1;
1425c6193dc5SYannick Fertre 			}
1426c6193dc5SYannick Fertre 
1427c6193dc5SYannick Fertre 			regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr1);
1428c6193dc5SYannick Fertre 			regmap_write(ldev->regmap, LTDC_L1AFBA1R + lofs, paddr2);
1429c6193dc5SYannick Fertre 			break;
1430c6193dc5SYannick Fertre 			}
1431c6193dc5SYannick Fertre 
1432c6193dc5SYannick Fertre 			/*
1433c6193dc5SYannick Fertre 			 * Set the length and the number of lines of the auxiliary
1434c6193dc5SYannick Fertre 			 * buffers if the framebuffer contains more than one plane.
1435c6193dc5SYannick Fertre 			 */
1436c6193dc5SYannick Fertre 			if (fb->format->num_planes > 1) {
1437c6193dc5SYannick Fertre 				if (newstate->rotation & DRM_MODE_REFLECT_Y)
1438c6193dc5SYannick Fertre 					/*
1439c6193dc5SYannick Fertre 					 * Compute negative value (signed on 16 bits)
1440c6193dc5SYannick Fertre 					 * for the picth
1441c6193dc5SYannick Fertre 					 */
1442c6193dc5SYannick Fertre 					pitch_in_bytes = 0x10000 - fb->pitches[1];
1443c6193dc5SYannick Fertre 				else
1444c6193dc5SYannick Fertre 					pitch_in_bytes = fb->pitches[1];
1445c6193dc5SYannick Fertre 
1446c6193dc5SYannick Fertre 				line_length = ((fb->format->cpp[1] * (x1 - x0 + 1)) >> 1) +
1447484e72d3SYannick Fertre 					      (ldev->caps.bus_width >> 3) - 1;
1448484e72d3SYannick Fertre 
1449c6193dc5SYannick Fertre 				/* Configure the auxiliary buffer length */
1450c6193dc5SYannick Fertre 				val = (pitch_in_bytes << 16) | line_length;
1451484e72d3SYannick Fertre 				regmap_write(ldev->regmap, LTDC_L1AFBLR + lofs, val);
1452484e72d3SYannick Fertre 
1453c6193dc5SYannick Fertre 				/* Configure the auxiliary frame buffer line number */
1454c6193dc5SYannick Fertre 				val = line_number >> 1;
1455484e72d3SYannick Fertre 				regmap_write(ldev->regmap, LTDC_L1AFBLNR + lofs, val);
1456484e72d3SYannick Fertre 			}
1457484e72d3SYannick Fertre 
1458484e72d3SYannick Fertre 			/* Configure YCbC conversion coefficient */
1459484e72d3SYannick Fertre 			ltdc_set_ycbcr_coeffs(plane);
1460484e72d3SYannick Fertre 
1461484e72d3SYannick Fertre 			/* Configure YCbCr format and enable/disable conversion */
1462484e72d3SYannick Fertre 			ltdc_set_ycbcr_config(plane, fb->format->format);
1463484e72d3SYannick Fertre 		} else {
1464484e72d3SYannick Fertre 			/* disable ycbcr conversion */
1465484e72d3SYannick Fertre 			regmap_write(ldev->regmap, LTDC_L1PCR + lofs, 0);
1466484e72d3SYannick Fertre 		}
1467484e72d3SYannick Fertre 	}
1468484e72d3SYannick Fertre 
1469b759012cSYannick Fertre 	/* Enable layer and CLUT if needed */
1470b759012cSYannick Fertre 	val = fb->format->format == DRM_FORMAT_C8 ? LXCR_CLUTEN : 0;
1471b759012cSYannick Fertre 	val |= LXCR_LEN;
1472c6193dc5SYannick Fertre 
1473c6193dc5SYannick Fertre 	/* Enable horizontal mirroring if requested */
1474c6193dc5SYannick Fertre 	if (newstate->rotation & DRM_MODE_REFLECT_X)
1475c6193dc5SYannick Fertre 		val |= LXCR_HMEN;
1476c6193dc5SYannick Fertre 
1477c6193dc5SYannick Fertre 	regmap_write_bits(ldev->regmap, LTDC_L1CR + lofs, LXCR_LEN | LXCR_CLUTEN | LXCR_HMEN, val);
1478b759012cSYannick Fertre 
1479a55d08e0SYannick Fertre 	/* Commit shadow registers = update plane at next vblank */
1480a55d08e0SYannick Fertre 	if (ldev->caps.plane_reg_shadow)
1481a55d08e0SYannick Fertre 		regmap_write_bits(ldev->regmap, LTDC_L1RCR + lofs,
1482a55d08e0SYannick Fertre 				  LXRCR_IMR | LXRCR_VBR | LXRCR_GRMSK, LXRCR_VBR);
1483a55d08e0SYannick Fertre 
148425bb1a9dSPhilippe CORNU 	ldev->plane_fpsi[plane->index].counter++;
148525bb1a9dSPhilippe CORNU 
1486b759012cSYannick Fertre 	mutex_lock(&ldev->err_lock);
14877d008eecSYannick Fertre 	if (ldev->transfer_err) {
14887d008eecSYannick Fertre 		DRM_WARN("ltdc transfer error: %d\n", ldev->transfer_err);
14897d008eecSYannick Fertre 		ldev->transfer_err = 0;
1490b759012cSYannick Fertre 	}
14917d008eecSYannick Fertre 
14927d008eecSYannick Fertre 	if (ldev->caps.fifo_threshold) {
14937d008eecSYannick Fertre 		if (ldev->fifo_err) {
14947d008eecSYannick Fertre 			DRM_WARN("ltdc fifo underrun: please verify display mode\n");
14957d008eecSYannick Fertre 			ldev->fifo_err = 0;
14967d008eecSYannick Fertre 		}
14977d008eecSYannick Fertre 	} else {
14987d008eecSYannick Fertre 		if (ldev->fifo_warn >= ldev->fifo_threshold) {
14997d008eecSYannick Fertre 			DRM_WARN("ltdc fifo underrun: please verify display mode\n");
15007d008eecSYannick Fertre 			ldev->fifo_warn = 0;
15017d008eecSYannick Fertre 		}
1502b759012cSYannick Fertre 	}
1503b759012cSYannick Fertre 	mutex_unlock(&ldev->err_lock);
1504b759012cSYannick Fertre }
1505b759012cSYannick Fertre 
ltdc_plane_atomic_disable(struct drm_plane * plane,struct drm_atomic_state * state)1506b759012cSYannick Fertre static void ltdc_plane_atomic_disable(struct drm_plane *plane,
1507977697e2SMaxime Ripard 				      struct drm_atomic_state *state)
1508b759012cSYannick Fertre {
1509977697e2SMaxime Ripard 	struct drm_plane_state *oldstate = drm_atomic_get_old_plane_state(state,
1510977697e2SMaxime Ripard 									  plane);
1511b759012cSYannick Fertre 	struct ltdc_device *ldev = plane_to_ltdc(plane);
1512b759012cSYannick Fertre 	u32 lofs = plane->index * LAY_OFS;
1513b759012cSYannick Fertre 
1514c6193dc5SYannick Fertre 	/* Disable layer */
1515c6193dc5SYannick Fertre 	regmap_write_bits(ldev->regmap, LTDC_L1CR + lofs, LXCR_LEN | LXCR_CLUTEN |  LXCR_HMEN, 0);
1516b759012cSYannick Fertre 
1517a55d08e0SYannick Fertre 	/* Commit shadow registers = update plane at next vblank */
1518a55d08e0SYannick Fertre 	if (ldev->caps.plane_reg_shadow)
1519a55d08e0SYannick Fertre 		regmap_write_bits(ldev->regmap, LTDC_L1RCR + lofs,
1520a55d08e0SYannick Fertre 				  LXRCR_IMR | LXRCR_VBR | LXRCR_GRMSK, LXRCR_VBR);
1521a55d08e0SYannick Fertre 
1522b759012cSYannick Fertre 	DRM_DEBUG_DRIVER("CRTC:%d plane:%d\n",
1523b759012cSYannick Fertre 			 oldstate->crtc->base.id, plane->base.id);
1524b759012cSYannick Fertre }
1525b759012cSYannick Fertre 
ltdc_plane_atomic_print_state(struct drm_printer * p,const struct drm_plane_state * state)152625bb1a9dSPhilippe CORNU static void ltdc_plane_atomic_print_state(struct drm_printer *p,
152725bb1a9dSPhilippe CORNU 					  const struct drm_plane_state *state)
152825bb1a9dSPhilippe CORNU {
152925bb1a9dSPhilippe CORNU 	struct drm_plane *plane = state->plane;
153025bb1a9dSPhilippe CORNU 	struct ltdc_device *ldev = plane_to_ltdc(plane);
153125bb1a9dSPhilippe CORNU 	struct fps_info *fpsi = &ldev->plane_fpsi[plane->index];
153225bb1a9dSPhilippe CORNU 	int ms_since_last;
153325bb1a9dSPhilippe CORNU 	ktime_t now;
153425bb1a9dSPhilippe CORNU 
153525bb1a9dSPhilippe CORNU 	now = ktime_get();
153625bb1a9dSPhilippe CORNU 	ms_since_last = ktime_to_ms(ktime_sub(now, fpsi->last_timestamp));
153725bb1a9dSPhilippe CORNU 
153825bb1a9dSPhilippe CORNU 	drm_printf(p, "\tuser_updates=%dfps\n",
153925bb1a9dSPhilippe CORNU 		   DIV_ROUND_CLOSEST(fpsi->counter * 1000, ms_since_last));
154025bb1a9dSPhilippe CORNU 
154125bb1a9dSPhilippe CORNU 	fpsi->last_timestamp = now;
154225bb1a9dSPhilippe CORNU 	fpsi->counter = 0;
154325bb1a9dSPhilippe CORNU }
154425bb1a9dSPhilippe CORNU 
1545c994796fSPhilippe CORNU static const struct drm_plane_funcs ltdc_plane_funcs = {
1546b759012cSYannick Fertre 	.update_plane = drm_atomic_helper_update_plane,
1547b759012cSYannick Fertre 	.disable_plane = drm_atomic_helper_disable_plane,
1548b759012cSYannick Fertre 	.destroy = drm_plane_cleanup,
1549b759012cSYannick Fertre 	.reset = drm_atomic_helper_plane_reset,
1550b759012cSYannick Fertre 	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
1551b759012cSYannick Fertre 	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
155225bb1a9dSPhilippe CORNU 	.atomic_print_state = ltdc_plane_atomic_print_state,
1553b759012cSYannick Fertre };
1554b759012cSYannick Fertre 
1555b759012cSYannick Fertre static const struct drm_plane_helper_funcs ltdc_plane_helper_funcs = {
1556b759012cSYannick Fertre 	.atomic_check = ltdc_plane_atomic_check,
1557b759012cSYannick Fertre 	.atomic_update = ltdc_plane_atomic_update,
1558b759012cSYannick Fertre 	.atomic_disable = ltdc_plane_atomic_disable,
1559b759012cSYannick Fertre };
1560b759012cSYannick Fertre 
ltdc_plane_create(struct drm_device * ddev,enum drm_plane_type type,int index)1561b759012cSYannick Fertre static struct drm_plane *ltdc_plane_create(struct drm_device *ddev,
1562484e72d3SYannick Fertre 					   enum drm_plane_type type,
1563484e72d3SYannick Fertre 					   int index)
1564b759012cSYannick Fertre {
1565b759012cSYannick Fertre 	unsigned long possible_crtcs = CRTC_MASK;
1566b759012cSYannick Fertre 	struct ltdc_device *ldev = ddev->dev_private;
1567b759012cSYannick Fertre 	struct device *dev = ddev->dev;
1568b759012cSYannick Fertre 	struct drm_plane *plane;
1569b759012cSYannick Fertre 	unsigned int i, nb_fmt = 0;
15708f2b5f6dSYannick Fertre 	u32 *formats;
15718f2b5f6dSYannick Fertre 	u32 drm_fmt;
1572e7c03dbaSYannick Fertré 	const u64 *modifiers = ltdc_format_modifiers;
1573484e72d3SYannick Fertre 	u32 lofs = index * LAY_OFS;
1574484e72d3SYannick Fertre 	u32 val;
1575b759012cSYannick Fertre 	int ret;
1576b759012cSYannick Fertre 
1577484e72d3SYannick Fertre 	/* Allocate the biggest size according to supported color formats */
1578484e72d3SYannick Fertre 	formats = devm_kzalloc(dev, (ldev->caps.pix_fmt_nb +
1579484e72d3SYannick Fertre 			       ARRAY_SIZE(ltdc_drm_fmt_ycbcr_cp) +
1580484e72d3SYannick Fertre 			       ARRAY_SIZE(ltdc_drm_fmt_ycbcr_sp) +
1581484e72d3SYannick Fertre 			       ARRAY_SIZE(ltdc_drm_fmt_ycbcr_fp)) *
1582484e72d3SYannick Fertre 			       sizeof(*formats), GFP_KERNEL);
1583aefa8301SPhilippe CORNU 
15848f2b5f6dSYannick Fertre 	for (i = 0; i < ldev->caps.pix_fmt_nb; i++) {
15858f2b5f6dSYannick Fertre 		drm_fmt = ldev->caps.pix_fmt_drm[i];
15869569002aSPhilippe CORNU 
15879569002aSPhilippe CORNU 		/* Manage hw-specific capabilities */
15888f2b5f6dSYannick Fertre 		if (ldev->caps.non_alpha_only_l1)
15898f2b5f6dSYannick Fertre 			/* XR24 & RX24 like formats supported only on primary layer */
15908f2b5f6dSYannick Fertre 			if (type != DRM_PLANE_TYPE_PRIMARY && is_xrgb(drm_fmt))
15919569002aSPhilippe CORNU 				continue;
15929569002aSPhilippe CORNU 
15938f2b5f6dSYannick Fertre 		formats[nb_fmt++] = drm_fmt;
1594b759012cSYannick Fertre 	}
1595b759012cSYannick Fertre 
1596484e72d3SYannick Fertre 	/* Add YCbCr supported pixel formats */
1597484e72d3SYannick Fertre 	if (ldev->caps.ycbcr_input) {
1598484e72d3SYannick Fertre 		regmap_read(ldev->regmap, LTDC_L1C1R + lofs, &val);
1599484e72d3SYannick Fertre 		if (val & LXCR_C1R_YIA) {
1600484e72d3SYannick Fertre 			memcpy(&formats[nb_fmt], ltdc_drm_fmt_ycbcr_cp,
1601484e72d3SYannick Fertre 			       ARRAY_SIZE(ltdc_drm_fmt_ycbcr_cp) * sizeof(*formats));
1602484e72d3SYannick Fertre 			nb_fmt += ARRAY_SIZE(ltdc_drm_fmt_ycbcr_cp);
1603484e72d3SYannick Fertre 		}
1604484e72d3SYannick Fertre 		if (val & LXCR_C1R_YSPA) {
1605484e72d3SYannick Fertre 			memcpy(&formats[nb_fmt], ltdc_drm_fmt_ycbcr_sp,
1606484e72d3SYannick Fertre 			       ARRAY_SIZE(ltdc_drm_fmt_ycbcr_sp) * sizeof(*formats));
1607484e72d3SYannick Fertre 			nb_fmt += ARRAY_SIZE(ltdc_drm_fmt_ycbcr_sp);
1608484e72d3SYannick Fertre 		}
1609484e72d3SYannick Fertre 		if (val & LXCR_C1R_YFPA) {
1610484e72d3SYannick Fertre 			memcpy(&formats[nb_fmt], ltdc_drm_fmt_ycbcr_fp,
1611484e72d3SYannick Fertre 			       ARRAY_SIZE(ltdc_drm_fmt_ycbcr_fp) * sizeof(*formats));
1612484e72d3SYannick Fertre 			nb_fmt += ARRAY_SIZE(ltdc_drm_fmt_ycbcr_fp);
1613484e72d3SYannick Fertre 		}
1614484e72d3SYannick Fertre 	}
1615484e72d3SYannick Fertre 
1616b759012cSYannick Fertre 	plane = devm_kzalloc(dev, sizeof(*plane), GFP_KERNEL);
1617b759012cSYannick Fertre 	if (!plane)
1618cccb57d8SPhilippe CORNU 		return NULL;
1619b759012cSYannick Fertre 
1620b759012cSYannick Fertre 	ret = drm_universal_plane_init(ddev, plane, possible_crtcs,
1621b759012cSYannick Fertre 				       &ltdc_plane_funcs, formats, nb_fmt,
1622e7c03dbaSYannick Fertré 				       modifiers, type, NULL);
1623b759012cSYannick Fertre 	if (ret < 0)
1624cccb57d8SPhilippe CORNU 		return NULL;
1625b759012cSYannick Fertre 
1626484e72d3SYannick Fertre 	if (ldev->caps.ycbcr_input) {
1627484e72d3SYannick Fertre 		if (val & (LXCR_C1R_YIA | LXCR_C1R_YSPA | LXCR_C1R_YFPA))
1628484e72d3SYannick Fertre 			drm_plane_create_color_properties(plane,
1629484e72d3SYannick Fertre 							  BIT(DRM_COLOR_YCBCR_BT601) |
1630484e72d3SYannick Fertre 							  BIT(DRM_COLOR_YCBCR_BT709),
1631484e72d3SYannick Fertre 							  BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) |
1632484e72d3SYannick Fertre 							  BIT(DRM_COLOR_YCBCR_FULL_RANGE),
1633484e72d3SYannick Fertre 							  DRM_COLOR_YCBCR_BT601,
1634484e72d3SYannick Fertre 							  DRM_COLOR_YCBCR_LIMITED_RANGE);
1635484e72d3SYannick Fertre 	}
1636484e72d3SYannick Fertre 
1637b759012cSYannick Fertre 	drm_plane_helper_add(plane, &ltdc_plane_helper_funcs);
1638b759012cSYannick Fertre 
1639c20351adSRaphael Gallais-Pou 	drm_plane_create_alpha_property(plane);
1640c20351adSRaphael Gallais-Pou 
1641b759012cSYannick Fertre 	DRM_DEBUG_DRIVER("plane:%d created\n", plane->base.id);
1642b759012cSYannick Fertre 
1643b759012cSYannick Fertre 	return plane;
1644b759012cSYannick Fertre }
1645b759012cSYannick Fertre 
ltdc_plane_destroy_all(struct drm_device * ddev)1646b759012cSYannick Fertre static void ltdc_plane_destroy_all(struct drm_device *ddev)
1647b759012cSYannick Fertre {
1648b759012cSYannick Fertre 	struct drm_plane *plane, *plane_temp;
1649b759012cSYannick Fertre 
1650b759012cSYannick Fertre 	list_for_each_entry_safe(plane, plane_temp,
1651b759012cSYannick Fertre 				 &ddev->mode_config.plane_list, head)
1652b759012cSYannick Fertre 		drm_plane_cleanup(plane);
1653b759012cSYannick Fertre }
1654b759012cSYannick Fertre 
ltdc_crtc_init(struct drm_device * ddev,struct drm_crtc * crtc)1655b759012cSYannick Fertre static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc)
1656b759012cSYannick Fertre {
1657b759012cSYannick Fertre 	struct ltdc_device *ldev = ddev->dev_private;
1658b759012cSYannick Fertre 	struct drm_plane *primary, *overlay;
1659c6193dc5SYannick Fertre 	int supported_rotations = DRM_MODE_ROTATE_0 | DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y;
1660b759012cSYannick Fertre 	unsigned int i;
1661dc5e0cd2SPhilippe CORNU 	int ret;
1662b759012cSYannick Fertre 
1663484e72d3SYannick Fertre 	primary = ltdc_plane_create(ddev, DRM_PLANE_TYPE_PRIMARY, 0);
1664b759012cSYannick Fertre 	if (!primary) {
1665b759012cSYannick Fertre 		DRM_ERROR("Can not create primary plane\n");
1666b759012cSYannick Fertre 		return -EINVAL;
1667b759012cSYannick Fertre 	}
1668b759012cSYannick Fertre 
166962467fccSYannick Fertre 	if (ldev->caps.dynamic_zorder)
167062467fccSYannick Fertre 		drm_plane_create_zpos_property(primary, 0, 0, ldev->caps.nb_layers - 1);
167162467fccSYannick Fertre 	else
1672ee2cda7bSRaphael Gallais-Pou 		drm_plane_create_zpos_immutable_property(primary, 0);
1673ee2cda7bSRaphael Gallais-Pou 
1674c6193dc5SYannick Fertre 	if (ldev->caps.plane_rotation)
1675c6193dc5SYannick Fertre 		drm_plane_create_rotation_property(primary, DRM_MODE_ROTATE_0,
1676c6193dc5SYannick Fertre 						   supported_rotations);
1677c6193dc5SYannick Fertre 
167879b44684SRaphael Gallais-Pou 	/* Init CRTC according to its hardware features */
167979b44684SRaphael Gallais-Pou 	if (ldev->caps.crc)
168079b44684SRaphael Gallais-Pou 		ret = drm_crtc_init_with_planes(ddev, crtc, primary, NULL,
168179b44684SRaphael Gallais-Pou 						&ltdc_crtc_with_crc_support_funcs, NULL);
168279b44684SRaphael Gallais-Pou 	else
1683dc5e0cd2SPhilippe CORNU 		ret = drm_crtc_init_with_planes(ddev, crtc, primary, NULL,
1684b759012cSYannick Fertre 						&ltdc_crtc_funcs, NULL);
1685dc5e0cd2SPhilippe CORNU 	if (ret) {
1686b759012cSYannick Fertre 		DRM_ERROR("Can not initialize CRTC\n");
1687b759012cSYannick Fertre 		goto cleanup;
1688b759012cSYannick Fertre 	}
1689b759012cSYannick Fertre 
1690b759012cSYannick Fertre 	drm_crtc_helper_add(crtc, &ltdc_crtc_helper_funcs);
1691b759012cSYannick Fertre 
1692b706a25eSPhilippe CORNU 	drm_mode_crtc_set_gamma_size(crtc, CLUT_SIZE);
1693b706a25eSPhilippe CORNU 	drm_crtc_enable_color_mgmt(crtc, 0, false, CLUT_SIZE);
1694b706a25eSPhilippe CORNU 
1695b759012cSYannick Fertre 	DRM_DEBUG_DRIVER("CRTC:%d created\n", crtc->base.id);
1696b759012cSYannick Fertre 
1697b759012cSYannick Fertre 	/* Add planes. Note : the first layer is used by primary plane */
1698b759012cSYannick Fertre 	for (i = 1; i < ldev->caps.nb_layers; i++) {
1699484e72d3SYannick Fertre 		overlay = ltdc_plane_create(ddev, DRM_PLANE_TYPE_OVERLAY, i);
1700b759012cSYannick Fertre 		if (!overlay) {
1701dc5e0cd2SPhilippe CORNU 			ret = -ENOMEM;
1702b759012cSYannick Fertre 			DRM_ERROR("Can not create overlay plane %d\n", i);
1703b759012cSYannick Fertre 			goto cleanup;
1704b759012cSYannick Fertre 		}
170562467fccSYannick Fertre 		if (ldev->caps.dynamic_zorder)
170662467fccSYannick Fertre 			drm_plane_create_zpos_property(overlay, i, 0, ldev->caps.nb_layers - 1);
170762467fccSYannick Fertre 		else
1708ee2cda7bSRaphael Gallais-Pou 			drm_plane_create_zpos_immutable_property(overlay, i);
1709c6193dc5SYannick Fertre 
1710c6193dc5SYannick Fertre 		if (ldev->caps.plane_rotation)
1711c6193dc5SYannick Fertre 			drm_plane_create_rotation_property(overlay, DRM_MODE_ROTATE_0,
1712c6193dc5SYannick Fertre 							   supported_rotations);
1713b759012cSYannick Fertre 	}
1714b759012cSYannick Fertre 
1715b759012cSYannick Fertre 	return 0;
1716b759012cSYannick Fertre 
1717b759012cSYannick Fertre cleanup:
1718b759012cSYannick Fertre 	ltdc_plane_destroy_all(ddev);
1719dc5e0cd2SPhilippe CORNU 	return ret;
1720b759012cSYannick Fertre }
1721b759012cSYannick Fertre 
ltdc_encoder_disable(struct drm_encoder * encoder)172292a57b3fSYannick Fertré static void ltdc_encoder_disable(struct drm_encoder *encoder)
172392a57b3fSYannick Fertré {
172492a57b3fSYannick Fertré 	struct drm_device *ddev = encoder->dev;
1725f412af18SYannick Fertré 	struct ltdc_device *ldev = ddev->dev_private;
172692a57b3fSYannick Fertré 
172792a57b3fSYannick Fertré 	DRM_DEBUG_DRIVER("\n");
172892a57b3fSYannick Fertré 
1729f412af18SYannick Fertré 	/* Disable LTDC */
1730734c2645SYannick Fertre 	regmap_clear_bits(ldev->regmap, LTDC_GCR, GCR_LTDCEN);
1731f412af18SYannick Fertré 
173292a57b3fSYannick Fertré 	/* Set to sleep state the pinctrl whatever type of encoder */
173392a57b3fSYannick Fertré 	pinctrl_pm_select_sleep_state(ddev->dev);
173492a57b3fSYannick Fertré }
173592a57b3fSYannick Fertré 
ltdc_encoder_enable(struct drm_encoder * encoder)173692a57b3fSYannick Fertré static void ltdc_encoder_enable(struct drm_encoder *encoder)
173792a57b3fSYannick Fertré {
173892a57b3fSYannick Fertré 	struct drm_device *ddev = encoder->dev;
1739f412af18SYannick Fertré 	struct ltdc_device *ldev = ddev->dev_private;
1740f412af18SYannick Fertré 
1741f412af18SYannick Fertré 	DRM_DEBUG_DRIVER("\n");
1742f412af18SYannick Fertré 
17437d008eecSYannick Fertre 	/* set fifo underrun threshold register */
17447d008eecSYannick Fertre 	if (ldev->caps.fifo_threshold)
17457d008eecSYannick Fertre 		regmap_write(ldev->regmap, LTDC_FUT, ldev->fifo_threshold);
17467d008eecSYannick Fertre 
1747f412af18SYannick Fertré 	/* Enable LTDC */
1748734c2645SYannick Fertre 	regmap_set_bits(ldev->regmap, LTDC_GCR, GCR_LTDCEN);
1749f412af18SYannick Fertré }
1750f412af18SYannick Fertré 
ltdc_encoder_mode_set(struct drm_encoder * encoder,struct drm_display_mode * mode,struct drm_display_mode * adjusted_mode)1751f412af18SYannick Fertré static void ltdc_encoder_mode_set(struct drm_encoder *encoder,
1752f412af18SYannick Fertré 				  struct drm_display_mode *mode,
1753f412af18SYannick Fertré 				  struct drm_display_mode *adjusted_mode)
1754f412af18SYannick Fertré {
1755f412af18SYannick Fertré 	struct drm_device *ddev = encoder->dev;
175692a57b3fSYannick Fertré 
175792a57b3fSYannick Fertré 	DRM_DEBUG_DRIVER("\n");
175892a57b3fSYannick Fertré 
175992a57b3fSYannick Fertré 	/*
176092a57b3fSYannick Fertré 	 * Set to default state the pinctrl only with DPI type.
176192a57b3fSYannick Fertré 	 * Others types like DSI, don't need pinctrl due to
176292a57b3fSYannick Fertré 	 * internal bridge (the signals do not come out of the chipset).
176392a57b3fSYannick Fertré 	 */
176492a57b3fSYannick Fertré 	if (encoder->encoder_type == DRM_MODE_ENCODER_DPI)
176592a57b3fSYannick Fertré 		pinctrl_pm_select_default_state(ddev->dev);
176692a57b3fSYannick Fertré }
176792a57b3fSYannick Fertré 
176892a57b3fSYannick Fertré static const struct drm_encoder_helper_funcs ltdc_encoder_helper_funcs = {
176992a57b3fSYannick Fertré 	.disable = ltdc_encoder_disable,
177092a57b3fSYannick Fertré 	.enable = ltdc_encoder_enable,
1771f412af18SYannick Fertré 	.mode_set = ltdc_encoder_mode_set,
177292a57b3fSYannick Fertré };
177392a57b3fSYannick Fertré 
ltdc_encoder_init(struct drm_device * ddev,struct drm_bridge * bridge)177408de7afaSbenjamin.gaignard@linaro.org static int ltdc_encoder_init(struct drm_device *ddev, struct drm_bridge *bridge)
1775b759012cSYannick Fertre {
1776b759012cSYannick Fertre 	struct drm_encoder *encoder;
1777bdf31bcfSPhilippe CORNU 	int ret;
1778b759012cSYannick Fertre 
1779b759012cSYannick Fertre 	encoder = devm_kzalloc(ddev->dev, sizeof(*encoder), GFP_KERNEL);
1780b759012cSYannick Fertre 	if (!encoder)
1781bdf31bcfSPhilippe CORNU 		return -ENOMEM;
1782b759012cSYannick Fertre 
1783b759012cSYannick Fertre 	encoder->possible_crtcs = CRTC_MASK;
1784b759012cSYannick Fertre 	encoder->possible_clones = 0;	/* No cloning support */
1785b759012cSYannick Fertre 
1786a9cdf680SJagan Teki 	drm_simple_encoder_init(ddev, encoder, DRM_MODE_ENCODER_DPI);
1787b759012cSYannick Fertre 
178892a57b3fSYannick Fertré 	drm_encoder_helper_add(encoder, &ltdc_encoder_helper_funcs);
178992a57b3fSYannick Fertré 
1790a25b988fSLaurent Pinchart 	ret = drm_bridge_attach(encoder, bridge, NULL, 0);
1791bdf31bcfSPhilippe CORNU 	if (ret) {
1792648ce7fdSJagan Teki 		if (ret != -EPROBE_DEFER)
1793bdf31bcfSPhilippe CORNU 			drm_encoder_cleanup(encoder);
1794648ce7fdSJagan Teki 		return ret;
1795b759012cSYannick Fertre 	}
1796b759012cSYannick Fertre 
1797bdf31bcfSPhilippe CORNU 	DRM_DEBUG_DRIVER("Bridge encoder:%d created\n", encoder->base.id);
1798b759012cSYannick Fertre 
1799bdf31bcfSPhilippe CORNU 	return 0;
1800b759012cSYannick Fertre }
1801b759012cSYannick Fertre 
ltdc_get_caps(struct drm_device * ddev)1802b759012cSYannick Fertre static int ltdc_get_caps(struct drm_device *ddev)
1803b759012cSYannick Fertre {
1804b759012cSYannick Fertre 	struct ltdc_device *ldev = ddev->dev_private;
1805b759012cSYannick Fertre 	u32 bus_width_log2, lcr, gc2r;
1806b759012cSYannick Fertre 
180777756ad6SYannick Fertré 	/*
180877756ad6SYannick Fertré 	 * at least 1 layer must be managed & the number of layers
180977756ad6SYannick Fertré 	 * must not exceed LTDC_MAX_LAYER
181077756ad6SYannick Fertré 	 */
1811734c2645SYannick Fertre 	regmap_read(ldev->regmap, LTDC_LCR, &lcr);
1812b759012cSYannick Fertre 
181377756ad6SYannick Fertré 	ldev->caps.nb_layers = clamp((int)lcr, 1, LTDC_MAX_LAYER);
1814b759012cSYannick Fertre 
1815b759012cSYannick Fertre 	/* set data bus width */
1816734c2645SYannick Fertre 	regmap_read(ldev->regmap, LTDC_GC2R, &gc2r);
1817b759012cSYannick Fertre 	bus_width_log2 = (gc2r & GC2R_BW) >> 4;
1818b759012cSYannick Fertre 	ldev->caps.bus_width = 8 << bus_width_log2;
1819734c2645SYannick Fertre 	regmap_read(ldev->regmap, LTDC_IDR, &ldev->caps.hw_version);
1820b759012cSYannick Fertre 
1821b759012cSYannick Fertre 	switch (ldev->caps.hw_version) {
1822b759012cSYannick Fertre 	case HWVER_10200:
1823b759012cSYannick Fertre 	case HWVER_10300:
18241726cee3SYannick Fertre 		ldev->caps.layer_ofs = LAY_OFS_0;
18251726cee3SYannick Fertre 		ldev->caps.layer_regs = ltdc_layer_regs_a0;
1826b759012cSYannick Fertre 		ldev->caps.pix_fmt_hw = ltdc_pix_fmt_a0;
18278f2b5f6dSYannick Fertre 		ldev->caps.pix_fmt_drm = ltdc_drm_fmt_a0;
18288f2b5f6dSYannick Fertre 		ldev->caps.pix_fmt_nb = ARRAY_SIZE(ltdc_drm_fmt_a0);
18298f2b5f6dSYannick Fertre 		ldev->caps.pix_fmt_flex = false;
18309569002aSPhilippe CORNU 		/*
18319569002aSPhilippe CORNU 		 * Hw older versions support non-alpha color formats derived
18329569002aSPhilippe CORNU 		 * from native alpha color formats only on the primary layer.
18339569002aSPhilippe CORNU 		 * For instance, RG16 native format without alpha works fine
18349569002aSPhilippe CORNU 		 * on 2nd layer but XR24 (derived color format from AR24)
18359569002aSPhilippe CORNU 		 * does not work on 2nd layer.
18369569002aSPhilippe CORNU 		 */
18379569002aSPhilippe CORNU 		ldev->caps.non_alpha_only_l1 = true;
18387868e507SYannick Fertre 		ldev->caps.pad_max_freq_hz = 90000000;
18397868e507SYannick Fertre 		if (ldev->caps.hw_version == HWVER_10200)
18407868e507SYannick Fertre 			ldev->caps.pad_max_freq_hz = 65000000;
1841544aa6ceSYannick Fertre 		ldev->caps.nb_irq = 2;
1842484e72d3SYannick Fertre 		ldev->caps.ycbcr_input = false;
1843fb998edfSYannick Fertre 		ldev->caps.ycbcr_output = false;
1844a55d08e0SYannick Fertre 		ldev->caps.plane_reg_shadow = false;
184579b44684SRaphael Gallais-Pou 		ldev->caps.crc = false;
184662467fccSYannick Fertre 		ldev->caps.dynamic_zorder = false;
1847c6193dc5SYannick Fertre 		ldev->caps.plane_rotation = false;
18487d008eecSYannick Fertre 		ldev->caps.fifo_threshold = false;
1849b759012cSYannick Fertre 		break;
1850b759012cSYannick Fertre 	case HWVER_20101:
18511726cee3SYannick Fertre 		ldev->caps.layer_ofs = LAY_OFS_0;
18521726cee3SYannick Fertre 		ldev->caps.layer_regs = ltdc_layer_regs_a1;
1853b759012cSYannick Fertre 		ldev->caps.pix_fmt_hw = ltdc_pix_fmt_a1;
18548f2b5f6dSYannick Fertre 		ldev->caps.pix_fmt_drm = ltdc_drm_fmt_a1;
18558f2b5f6dSYannick Fertre 		ldev->caps.pix_fmt_nb = ARRAY_SIZE(ltdc_drm_fmt_a1);
18568f2b5f6dSYannick Fertre 		ldev->caps.pix_fmt_flex = false;
18579569002aSPhilippe CORNU 		ldev->caps.non_alpha_only_l1 = false;
18587868e507SYannick Fertre 		ldev->caps.pad_max_freq_hz = 150000000;
1859544aa6ceSYannick Fertre 		ldev->caps.nb_irq = 4;
1860484e72d3SYannick Fertre 		ldev->caps.ycbcr_input = false;
1861fb998edfSYannick Fertre 		ldev->caps.ycbcr_output = false;
1862a55d08e0SYannick Fertre 		ldev->caps.plane_reg_shadow = false;
186379b44684SRaphael Gallais-Pou 		ldev->caps.crc = false;
186462467fccSYannick Fertre 		ldev->caps.dynamic_zorder = false;
1865c6193dc5SYannick Fertre 		ldev->caps.plane_rotation = false;
18667d008eecSYannick Fertre 		ldev->caps.fifo_threshold = false;
1867b759012cSYannick Fertre 		break;
18681726cee3SYannick Fertre 	case HWVER_40100:
18691726cee3SYannick Fertre 		ldev->caps.layer_ofs = LAY_OFS_1;
18701726cee3SYannick Fertre 		ldev->caps.layer_regs = ltdc_layer_regs_a2;
18711726cee3SYannick Fertre 		ldev->caps.pix_fmt_hw = ltdc_pix_fmt_a2;
18728f2b5f6dSYannick Fertre 		ldev->caps.pix_fmt_drm = ltdc_drm_fmt_a2;
18738f2b5f6dSYannick Fertre 		ldev->caps.pix_fmt_nb = ARRAY_SIZE(ltdc_drm_fmt_a2);
18748f2b5f6dSYannick Fertre 		ldev->caps.pix_fmt_flex = true;
18751726cee3SYannick Fertre 		ldev->caps.non_alpha_only_l1 = false;
18761726cee3SYannick Fertre 		ldev->caps.pad_max_freq_hz = 90000000;
18771726cee3SYannick Fertre 		ldev->caps.nb_irq = 2;
1878484e72d3SYannick Fertre 		ldev->caps.ycbcr_input = true;
1879fb998edfSYannick Fertre 		ldev->caps.ycbcr_output = true;
1880a55d08e0SYannick Fertre 		ldev->caps.plane_reg_shadow = true;
188179b44684SRaphael Gallais-Pou 		ldev->caps.crc = true;
188262467fccSYannick Fertre 		ldev->caps.dynamic_zorder = true;
1883c6193dc5SYannick Fertre 		ldev->caps.plane_rotation = true;
18847d008eecSYannick Fertre 		ldev->caps.fifo_threshold = true;
18851726cee3SYannick Fertre 		break;
1886b759012cSYannick Fertre 	default:
1887b759012cSYannick Fertre 		return -ENODEV;
1888b759012cSYannick Fertre 	}
1889b759012cSYannick Fertre 
1890b759012cSYannick Fertre 	return 0;
1891b759012cSYannick Fertre }
1892b759012cSYannick Fertre 
ltdc_suspend(struct drm_device * ddev)1893df61c776SYannick Fertré void ltdc_suspend(struct drm_device *ddev)
1894df61c776SYannick Fertré {
1895df61c776SYannick Fertré 	struct ltdc_device *ldev = ddev->dev_private;
1896df61c776SYannick Fertré 
1897df61c776SYannick Fertré 	DRM_DEBUG_DRIVER("\n");
1898df61c776SYannick Fertré 	clk_disable_unprepare(ldev->pixel_clk);
1899df61c776SYannick Fertré }
1900df61c776SYannick Fertré 
ltdc_resume(struct drm_device * ddev)1901df61c776SYannick Fertré int ltdc_resume(struct drm_device *ddev)
1902df61c776SYannick Fertré {
1903df61c776SYannick Fertré 	struct ltdc_device *ldev = ddev->dev_private;
1904df61c776SYannick Fertré 	int ret;
1905df61c776SYannick Fertré 
1906df61c776SYannick Fertré 	DRM_DEBUG_DRIVER("\n");
1907df61c776SYannick Fertré 
1908df61c776SYannick Fertré 	ret = clk_prepare_enable(ldev->pixel_clk);
1909df61c776SYannick Fertré 	if (ret) {
1910df61c776SYannick Fertré 		DRM_ERROR("failed to enable pixel clock (%d)\n", ret);
1911df61c776SYannick Fertré 		return ret;
1912df61c776SYannick Fertré 	}
1913df61c776SYannick Fertré 
1914df61c776SYannick Fertré 	return 0;
1915df61c776SYannick Fertré }
1916df61c776SYannick Fertré 
ltdc_load(struct drm_device * ddev)1917b759012cSYannick Fertre int ltdc_load(struct drm_device *ddev)
1918b759012cSYannick Fertre {
1919b759012cSYannick Fertre 	struct platform_device *pdev = to_platform_device(ddev->dev);
1920b759012cSYannick Fertre 	struct ltdc_device *ldev = ddev->dev_private;
1921b759012cSYannick Fertre 	struct device *dev = ddev->dev;
1922b759012cSYannick Fertre 	struct device_node *np = dev->of_node;
1923b430ff7eSYannick Fertre 	struct drm_bridge *bridge;
1924b430ff7eSYannick Fertre 	struct drm_panel *panel;
1925b759012cSYannick Fertre 	struct drm_crtc *crtc;
1926b759012cSYannick Fertre 	struct reset_control *rstc;
1927589b6482SPhilippe CORNU 	struct resource *res;
1928b430ff7eSYannick Fertre 	int irq, i, nb_endpoints;
1929b430ff7eSYannick Fertre 	int ret = -ENODEV;
1930b759012cSYannick Fertre 
1931b759012cSYannick Fertre 	DRM_DEBUG_DRIVER("\n");
1932b759012cSYannick Fertre 
1933b430ff7eSYannick Fertre 	/* Get number of endpoints */
1934b430ff7eSYannick Fertre 	nb_endpoints = of_graph_get_endpoint_count(np);
1935b430ff7eSYannick Fertre 	if (!nb_endpoints)
1936b430ff7eSYannick Fertre 		return -ENODEV;
1937b759012cSYannick Fertre 
1938b759012cSYannick Fertre 	ldev->pixel_clk = devm_clk_get(dev, "lcd");
1939b759012cSYannick Fertre 	if (IS_ERR(ldev->pixel_clk)) {
19401f358bc6SFabien Dessenne 		if (PTR_ERR(ldev->pixel_clk) != -EPROBE_DEFER)
1941b759012cSYannick Fertre 			DRM_ERROR("Unable to get lcd clock\n");
19421f358bc6SFabien Dessenne 		return PTR_ERR(ldev->pixel_clk);
1943b759012cSYannick Fertre 	}
1944b759012cSYannick Fertre 
1945b759012cSYannick Fertre 	if (clk_prepare_enable(ldev->pixel_clk)) {
1946b759012cSYannick Fertre 		DRM_ERROR("Unable to prepare pixel clock\n");
1947b759012cSYannick Fertre 		return -ENODEV;
1948b759012cSYannick Fertre 	}
1949b759012cSYannick Fertre 
1950b430ff7eSYannick Fertre 	/* Get endpoints if any */
1951b430ff7eSYannick Fertre 	for (i = 0; i < nb_endpoints; i++) {
1952b430ff7eSYannick Fertre 		ret = drm_of_find_panel_or_bridge(np, 0, i, &panel, &bridge);
1953b430ff7eSYannick Fertre 
1954b430ff7eSYannick Fertre 		/*
1955b430ff7eSYannick Fertre 		 * If at least one endpoint is -ENODEV, continue probing,
1956b430ff7eSYannick Fertre 		 * else if at least one endpoint returned an error
1957b430ff7eSYannick Fertre 		 * (ie -EPROBE_DEFER) then stop probing.
1958b430ff7eSYannick Fertre 		 */
1959b430ff7eSYannick Fertre 		if (ret == -ENODEV)
1960b430ff7eSYannick Fertre 			continue;
1961b430ff7eSYannick Fertre 		else if (ret)
1962b430ff7eSYannick Fertre 			goto err;
1963b430ff7eSYannick Fertre 
1964b430ff7eSYannick Fertre 		if (panel) {
1965b430ff7eSYannick Fertre 			bridge = drm_panel_bridge_add_typed(panel,
1966b430ff7eSYannick Fertre 							    DRM_MODE_CONNECTOR_DPI);
1967b430ff7eSYannick Fertre 			if (IS_ERR(bridge)) {
1968b430ff7eSYannick Fertre 				DRM_ERROR("panel-bridge endpoint %d\n", i);
1969b430ff7eSYannick Fertre 				ret = PTR_ERR(bridge);
1970b430ff7eSYannick Fertre 				goto err;
1971b430ff7eSYannick Fertre 			}
1972b430ff7eSYannick Fertre 		}
1973b430ff7eSYannick Fertre 
1974b430ff7eSYannick Fertre 		if (bridge) {
1975b430ff7eSYannick Fertre 			ret = ltdc_encoder_init(ddev, bridge);
1976b430ff7eSYannick Fertre 			if (ret) {
1977648ce7fdSJagan Teki 				if (ret != -EPROBE_DEFER)
1978b430ff7eSYannick Fertre 					DRM_ERROR("init encoder endpoint %d\n", i);
1979b430ff7eSYannick Fertre 				goto err;
1980b430ff7eSYannick Fertre 			}
1981b430ff7eSYannick Fertre 		}
1982b430ff7eSYannick Fertre 	}
1983b430ff7eSYannick Fertre 
1984b430ff7eSYannick Fertre 	rstc = devm_reset_control_get_exclusive(dev, NULL);
1985b430ff7eSYannick Fertre 
1986b430ff7eSYannick Fertre 	mutex_init(&ldev->err_lock);
1987b430ff7eSYannick Fertre 
1988f42f540bSYannick Fertré 	if (!IS_ERR(rstc)) {
1989f42f540bSYannick Fertré 		reset_control_assert(rstc);
1990f42f540bSYannick Fertré 		usleep_range(10, 20);
1991f42f540bSYannick Fertré 		reset_control_deassert(rstc);
1992f42f540bSYannick Fertré 	}
1993f42f540bSYannick Fertré 
1994589b6482SPhilippe CORNU 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1995589b6482SPhilippe CORNU 	ldev->regs = devm_ioremap_resource(dev, res);
1996b759012cSYannick Fertre 	if (IS_ERR(ldev->regs)) {
1997b759012cSYannick Fertre 		DRM_ERROR("Unable to get ltdc registers\n");
1998cea3a330SPhilippe CORNU 		ret = PTR_ERR(ldev->regs);
1999cea3a330SPhilippe CORNU 		goto err;
2000b759012cSYannick Fertre 	}
2001b759012cSYannick Fertre 
2002734c2645SYannick Fertre 	ldev->regmap = devm_regmap_init_mmio(&pdev->dev, ldev->regs, &stm32_ltdc_regmap_cfg);
2003734c2645SYannick Fertre 	if (IS_ERR(ldev->regmap)) {
2004734c2645SYannick Fertre 		DRM_ERROR("Unable to regmap ltdc registers\n");
2005734c2645SYannick Fertre 		ret = PTR_ERR(ldev->regmap);
2006734c2645SYannick Fertre 		goto err;
2007734c2645SYannick Fertre 	}
2008734c2645SYannick Fertre 
2009544aa6ceSYannick Fertre 	ret = ltdc_get_caps(ddev);
2010544aa6ceSYannick Fertre 	if (ret) {
2011544aa6ceSYannick Fertre 		DRM_ERROR("hardware identifier (0x%08x) not supported!\n",
2012544aa6ceSYannick Fertre 			  ldev->caps.hw_version);
20139e759fc7SFabien Dessenne 		goto err;
2014544aa6ceSYannick Fertre 	}
20159e759fc7SFabien Dessenne 
20167d008eecSYannick Fertre 	/* Disable interrupts */
20177d008eecSYannick Fertre 	if (ldev->caps.fifo_threshold)
20187d008eecSYannick Fertre 		regmap_clear_bits(ldev->regmap, LTDC_IER, IER_LIE | IER_RRIE | IER_FUWIE |
20197d008eecSYannick Fertre 				  IER_TERRIE);
20207d008eecSYannick Fertre 	else
20217d008eecSYannick Fertre 		regmap_clear_bits(ldev->regmap, LTDC_IER, IER_LIE | IER_RRIE | IER_FUWIE |
20227d008eecSYannick Fertre 				  IER_TERRIE | IER_FUEIE);
20237d008eecSYannick Fertre 
2024544aa6ceSYannick Fertre 	DRM_DEBUG_DRIVER("ltdc hw version 0x%08x\n", ldev->caps.hw_version);
2025544aa6ceSYannick Fertre 
20267d008eecSYannick Fertre 	/* initialize default value for fifo underrun threshold & clear interrupt error counters */
20277d008eecSYannick Fertre 	ldev->transfer_err = 0;
20287d008eecSYannick Fertre 	ldev->fifo_err = 0;
20297d008eecSYannick Fertre 	ldev->fifo_warn = 0;
20307d008eecSYannick Fertre 	ldev->fifo_threshold = FUT_DFT;
20317d008eecSYannick Fertre 
2032544aa6ceSYannick Fertre 	for (i = 0; i < ldev->caps.nb_irq; i++) {
2033544aa6ceSYannick Fertre 		irq = platform_get_irq(pdev, i);
2034544aa6ceSYannick Fertre 		if (irq < 0) {
2035544aa6ceSYannick Fertre 			ret = irq;
2036544aa6ceSYannick Fertre 			goto err;
2037544aa6ceSYannick Fertre 		}
2038b759012cSYannick Fertre 
2039b759012cSYannick Fertre 		ret = devm_request_threaded_irq(dev, irq, ltdc_irq,
2040b759012cSYannick Fertre 						ltdc_irq_thread, IRQF_ONESHOT,
2041b759012cSYannick Fertre 						dev_name(dev), ddev);
2042b759012cSYannick Fertre 		if (ret) {
2043b759012cSYannick Fertre 			DRM_ERROR("Failed to register LTDC interrupt\n");
2044cea3a330SPhilippe CORNU 			goto err;
2045b759012cSYannick Fertre 		}
2046c188d7ebSPhilippe CORNU 	}
2047b759012cSYannick Fertre 
2048b759012cSYannick Fertre 	crtc = devm_kzalloc(dev, sizeof(*crtc), GFP_KERNEL);
2049b759012cSYannick Fertre 	if (!crtc) {
2050b759012cSYannick Fertre 		DRM_ERROR("Failed to allocate crtc\n");
2051b759012cSYannick Fertre 		ret = -ENOMEM;
2052b759012cSYannick Fertre 		goto err;
2053b759012cSYannick Fertre 	}
2054b759012cSYannick Fertre 
2055b759012cSYannick Fertre 	ret = ltdc_crtc_init(ddev, crtc);
2056b759012cSYannick Fertre 	if (ret) {
2057b759012cSYannick Fertre 		DRM_ERROR("Failed to init crtc\n");
2058b759012cSYannick Fertre 		goto err;
2059b759012cSYannick Fertre 	}
2060b759012cSYannick Fertre 
2061b759012cSYannick Fertre 	ret = drm_vblank_init(ddev, NB_CRTC);
2062b759012cSYannick Fertre 	if (ret) {
2063b759012cSYannick Fertre 		DRM_ERROR("Failed calling drm_vblank_init()\n");
2064b759012cSYannick Fertre 		goto err;
2065b759012cSYannick Fertre 	}
2066b759012cSYannick Fertre 
206735ab6cfbSYannick Fertré 	clk_disable_unprepare(ldev->pixel_clk);
2068bdf31bcfSPhilippe CORNU 
206992a57b3fSYannick Fertré 	pinctrl_pm_select_sleep_state(ddev->dev);
207092a57b3fSYannick Fertré 
207135ab6cfbSYannick Fertré 	pm_runtime_enable(ddev->dev);
207235ab6cfbSYannick Fertré 
207335ab6cfbSYannick Fertré 	return 0;
2074b759012cSYannick Fertre err:
2075b430ff7eSYannick Fertre 	for (i = 0; i < nb_endpoints; i++)
2076b430ff7eSYannick Fertre 		drm_of_panel_bridge_remove(ddev->dev->of_node, 0, i);
2077b759012cSYannick Fertre 
2078b759012cSYannick Fertre 	clk_disable_unprepare(ldev->pixel_clk);
2079b759012cSYannick Fertre 
2080b759012cSYannick Fertre 	return ret;
2081b759012cSYannick Fertre }
2082b759012cSYannick Fertre 
ltdc_unload(struct drm_device * ddev)2083b759012cSYannick Fertre void ltdc_unload(struct drm_device *ddev)
2084b759012cSYannick Fertre {
2085b430ff7eSYannick Fertre 	struct device *dev = ddev->dev;
2086b430ff7eSYannick Fertre 	int nb_endpoints, i;
2087b759012cSYannick Fertre 
2088b759012cSYannick Fertre 	DRM_DEBUG_DRIVER("\n");
2089b759012cSYannick Fertre 
2090b430ff7eSYannick Fertre 	nb_endpoints = of_graph_get_endpoint_count(dev->of_node);
2091b430ff7eSYannick Fertre 
2092b430ff7eSYannick Fertre 	for (i = 0; i < nb_endpoints; i++)
2093c188d7ebSPhilippe CORNU 		drm_of_panel_bridge_remove(ddev->dev->of_node, 0, i);
2094b759012cSYannick Fertre 
209535ab6cfbSYannick Fertré 	pm_runtime_disable(ddev->dev);
2096b759012cSYannick Fertre }
2097b759012cSYannick Fertre 
2098b759012cSYannick Fertre MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>");
2099b759012cSYannick Fertre MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
2100b759012cSYannick Fertre MODULE_AUTHOR("Fabien Dessenne <fabien.dessenne@st.com>");
2101b759012cSYannick Fertre MODULE_AUTHOR("Mickael Reulier <mickael.reulier@st.com>");
2102b759012cSYannick Fertre MODULE_DESCRIPTION("STMicroelectronics ST DRM LTDC driver");
2103b759012cSYannick Fertre MODULE_LICENSE("GPL v2");
2104