xref: /openbmc/linux/drivers/gpu/drm/stm/ltdc.c (revision fac59652993f075d57860769c99045b3ca18780d)
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>
390a1741d1SKatya Orlova #include <drm/drm_managed.h>
40b759012cSYannick Fertre 
41b759012cSYannick Fertre #include <video/videomode.h>
42b759012cSYannick Fertre 
43b759012cSYannick Fertre #include "ltdc.h"
44b759012cSYannick Fertre 
45b759012cSYannick Fertre #define NB_CRTC 1
46b759012cSYannick Fertre #define CRTC_MASK GENMASK(NB_CRTC - 1, 0)
47b759012cSYannick Fertre 
48b759012cSYannick Fertre #define MAX_IRQ 4
49b759012cSYannick Fertre 
50b759012cSYannick Fertre #define HWVER_10200 0x010200
51b759012cSYannick Fertre #define HWVER_10300 0x010300
52b759012cSYannick Fertre #define HWVER_20101 0x020101
531726cee3SYannick Fertre #define HWVER_40100 0x040100
54b759012cSYannick Fertre 
55b759012cSYannick Fertre /*
56b759012cSYannick Fertre  * The address of some registers depends on the HW version: such registers have
571726cee3SYannick Fertre  * an extra offset specified with layer_ofs.
58b759012cSYannick Fertre  */
591726cee3SYannick Fertre #define LAY_OFS_0	0x80
601726cee3SYannick Fertre #define LAY_OFS_1	0x100
611726cee3SYannick Fertre #define LAY_OFS	(ldev->caps.layer_ofs)
62b759012cSYannick Fertre 
63b759012cSYannick Fertre /* Global register offsets */
64b759012cSYannick Fertre #define LTDC_IDR	0x0000		/* IDentification */
65b759012cSYannick Fertre #define LTDC_LCR	0x0004		/* Layer Count */
66b759012cSYannick Fertre #define LTDC_SSCR	0x0008		/* Synchronization Size Configuration */
67b759012cSYannick Fertre #define LTDC_BPCR	0x000C		/* Back Porch Configuration */
68b759012cSYannick Fertre #define LTDC_AWCR	0x0010		/* Active Width Configuration */
69b759012cSYannick Fertre #define LTDC_TWCR	0x0014		/* Total Width Configuration */
70b759012cSYannick Fertre #define LTDC_GCR	0x0018		/* Global Control */
71b759012cSYannick Fertre #define LTDC_GC1R	0x001C		/* Global Configuration 1 */
72b759012cSYannick Fertre #define LTDC_GC2R	0x0020		/* Global Configuration 2 */
73b759012cSYannick Fertre #define LTDC_SRCR	0x0024		/* Shadow Reload Configuration */
74b759012cSYannick Fertre #define LTDC_GACR	0x0028		/* GAmma Correction */
75b759012cSYannick Fertre #define LTDC_BCCR	0x002C		/* Background Color Configuration */
76b759012cSYannick Fertre #define LTDC_IER	0x0034		/* Interrupt Enable */
77b759012cSYannick Fertre #define LTDC_ISR	0x0038		/* Interrupt Status */
78b759012cSYannick Fertre #define LTDC_ICR	0x003C		/* Interrupt Clear */
790e21e3b0SPhilippe CORNU #define LTDC_LIPCR	0x0040		/* Line Interrupt Position Conf. */
80b759012cSYannick Fertre #define LTDC_CPSR	0x0044		/* Current Position Status */
81b759012cSYannick Fertre #define LTDC_CDSR	0x0048		/* Current Display Status */
82fb998edfSYannick Fertre #define LTDC_EDCR	0x0060		/* External Display Control */
8379b44684SRaphael Gallais-Pou #define LTDC_CCRCR	0x007C		/* Computed CRC value */
841726cee3SYannick Fertre #define LTDC_FUT	0x0090		/* Fifo underrun Threshold */
85b759012cSYannick Fertre 
86b759012cSYannick Fertre /* Layer register offsets */
871726cee3SYannick Fertre #define LTDC_L1C0R	(ldev->caps.layer_regs[0])	/* L1 configuration 0 */
881726cee3SYannick Fertre #define LTDC_L1C1R	(ldev->caps.layer_regs[1])	/* L1 configuration 1 */
891726cee3SYannick Fertre #define LTDC_L1RCR	(ldev->caps.layer_regs[2])	/* L1 reload control */
901726cee3SYannick Fertre #define LTDC_L1CR	(ldev->caps.layer_regs[3])	/* L1 control register */
911726cee3SYannick Fertre #define LTDC_L1WHPCR	(ldev->caps.layer_regs[4])	/* L1 window horizontal position configuration */
921726cee3SYannick Fertre #define LTDC_L1WVPCR	(ldev->caps.layer_regs[5])	/* L1 window vertical position configuration */
931726cee3SYannick Fertre #define LTDC_L1CKCR	(ldev->caps.layer_regs[6])	/* L1 color keying configuration */
941726cee3SYannick Fertre #define LTDC_L1PFCR	(ldev->caps.layer_regs[7])	/* L1 pixel format configuration */
951726cee3SYannick Fertre #define LTDC_L1CACR	(ldev->caps.layer_regs[8])	/* L1 constant alpha configuration */
961726cee3SYannick Fertre #define LTDC_L1DCCR	(ldev->caps.layer_regs[9])	/* L1 default color configuration */
971726cee3SYannick Fertre #define LTDC_L1BFCR	(ldev->caps.layer_regs[10])	/* L1 blending factors configuration */
981726cee3SYannick Fertre #define LTDC_L1BLCR	(ldev->caps.layer_regs[11])	/* L1 burst length configuration */
991726cee3SYannick Fertre #define LTDC_L1PCR	(ldev->caps.layer_regs[12])	/* L1 planar configuration */
1001726cee3SYannick Fertre #define LTDC_L1CFBAR	(ldev->caps.layer_regs[13])	/* L1 color frame buffer address */
1011726cee3SYannick Fertre #define LTDC_L1CFBLR	(ldev->caps.layer_regs[14])	/* L1 color frame buffer length */
1021726cee3SYannick Fertre #define LTDC_L1CFBLNR	(ldev->caps.layer_regs[15])	/* L1 color frame buffer line number */
1031726cee3SYannick Fertre #define LTDC_L1AFBA0R	(ldev->caps.layer_regs[16])	/* L1 auxiliary frame buffer address 0 */
1041726cee3SYannick Fertre #define LTDC_L1AFBA1R	(ldev->caps.layer_regs[17])	/* L1 auxiliary frame buffer address 1 */
1051726cee3SYannick Fertre #define LTDC_L1AFBLR	(ldev->caps.layer_regs[18])	/* L1 auxiliary frame buffer length */
1061726cee3SYannick Fertre #define LTDC_L1AFBLNR	(ldev->caps.layer_regs[19])	/* L1 auxiliary frame buffer line number */
1071726cee3SYannick Fertre #define LTDC_L1CLUTWR	(ldev->caps.layer_regs[20])	/* L1 CLUT write */
1081726cee3SYannick Fertre #define LTDC_L1CYR0R	(ldev->caps.layer_regs[21])	/* L1 Conversion YCbCr RGB 0 */
1091726cee3SYannick Fertre #define LTDC_L1CYR1R	(ldev->caps.layer_regs[22])	/* L1 Conversion YCbCr RGB 1 */
1101726cee3SYannick Fertre #define LTDC_L1FPF0R	(ldev->caps.layer_regs[23])	/* L1 Flexible Pixel Format 0 */
1111726cee3SYannick Fertre #define LTDC_L1FPF1R	(ldev->caps.layer_regs[24])	/* L1 Flexible Pixel Format 1 */
112b759012cSYannick Fertre 
113b759012cSYannick Fertre /* Bit definitions */
114b759012cSYannick Fertre #define SSCR_VSH	GENMASK(10, 0)	/* Vertical Synchronization Height */
115b759012cSYannick Fertre #define SSCR_HSW	GENMASK(27, 16)	/* Horizontal Synchronization Width */
116b759012cSYannick Fertre 
117b759012cSYannick Fertre #define BPCR_AVBP	GENMASK(10, 0)	/* Accumulated Vertical Back Porch */
118b759012cSYannick Fertre #define BPCR_AHBP	GENMASK(27, 16)	/* Accumulated Horizontal Back Porch */
119b759012cSYannick Fertre 
120b759012cSYannick Fertre #define AWCR_AAH	GENMASK(10, 0)	/* Accumulated Active Height */
121b759012cSYannick Fertre #define AWCR_AAW	GENMASK(27, 16)	/* Accumulated Active Width */
122b759012cSYannick Fertre 
123b759012cSYannick Fertre #define TWCR_TOTALH	GENMASK(10, 0)	/* TOTAL Height */
124b759012cSYannick Fertre #define TWCR_TOTALW	GENMASK(27, 16)	/* TOTAL Width */
125b759012cSYannick Fertre 
126b759012cSYannick Fertre #define GCR_LTDCEN	BIT(0)		/* LTDC ENable */
127b759012cSYannick Fertre #define GCR_DEN		BIT(16)		/* Dither ENable */
12879b44684SRaphael Gallais-Pou #define GCR_CRCEN	BIT(19)		/* CRC ENable */
129444d0db5SPhilippe CORNU #define GCR_PCPOL	BIT(28)		/* Pixel Clock POLarity-Inverted */
130444d0db5SPhilippe CORNU #define GCR_DEPOL	BIT(29)		/* Data Enable POLarity-High */
131444d0db5SPhilippe CORNU #define GCR_VSPOL	BIT(30)		/* Vertical Synchro POLarity-High */
132444d0db5SPhilippe CORNU #define GCR_HSPOL	BIT(31)		/* Horizontal Synchro POLarity-High */
133b759012cSYannick Fertre 
134b759012cSYannick Fertre #define GC1R_WBCH	GENMASK(3, 0)	/* Width of Blue CHannel output */
135b759012cSYannick Fertre #define GC1R_WGCH	GENMASK(7, 4)	/* Width of Green Channel output */
136b759012cSYannick Fertre #define GC1R_WRCH	GENMASK(11, 8)	/* Width of Red Channel output */
137b759012cSYannick Fertre #define GC1R_PBEN	BIT(12)		/* Precise Blending ENable */
138b759012cSYannick Fertre #define GC1R_DT		GENMASK(15, 14)	/* Dithering Technique */
139b759012cSYannick Fertre #define GC1R_GCT	GENMASK(19, 17)	/* Gamma Correction Technique */
140b759012cSYannick Fertre #define GC1R_SHREN	BIT(21)		/* SHadow Registers ENabled */
141b759012cSYannick Fertre #define GC1R_BCP	BIT(22)		/* Background Colour Programmable */
142b759012cSYannick Fertre #define GC1R_BBEN	BIT(23)		/* Background Blending ENabled */
143b759012cSYannick Fertre #define GC1R_LNIP	BIT(24)		/* Line Number IRQ Position */
144b759012cSYannick Fertre #define GC1R_TP		BIT(25)		/* Timing Programmable */
145b759012cSYannick Fertre #define GC1R_IPP	BIT(26)		/* IRQ Polarity Programmable */
146b759012cSYannick Fertre #define GC1R_SPP	BIT(27)		/* Sync Polarity Programmable */
147b759012cSYannick Fertre #define GC1R_DWP	BIT(28)		/* Dither Width Programmable */
148b759012cSYannick Fertre #define GC1R_STREN	BIT(29)		/* STatus Registers ENabled */
149b759012cSYannick Fertre #define GC1R_BMEN	BIT(31)		/* Blind Mode ENabled */
150b759012cSYannick Fertre 
151b759012cSYannick Fertre #define GC2R_EDCA	BIT(0)		/* External Display Control Ability  */
152b759012cSYannick Fertre #define GC2R_STSAEN	BIT(1)		/* Slave Timing Sync Ability ENabled */
153b759012cSYannick Fertre #define GC2R_DVAEN	BIT(2)		/* Dual-View Ability ENabled */
154b759012cSYannick Fertre #define GC2R_DPAEN	BIT(3)		/* Dual-Port Ability ENabled */
155b759012cSYannick Fertre #define GC2R_BW		GENMASK(6, 4)	/* Bus Width (log2 of nb of bytes) */
156b759012cSYannick Fertre #define GC2R_EDCEN	BIT(7)		/* External Display Control ENabled */
157b759012cSYannick Fertre 
158b759012cSYannick Fertre #define SRCR_IMR	BIT(0)		/* IMmediate Reload */
159b759012cSYannick Fertre #define SRCR_VBR	BIT(1)		/* Vertical Blanking Reload */
160b759012cSYannick Fertre 
161b759012cSYannick Fertre #define BCCR_BCBLACK	0x00		/* Background Color BLACK */
162b759012cSYannick Fertre #define BCCR_BCBLUE	GENMASK(7, 0)	/* Background Color BLUE */
163b759012cSYannick Fertre #define BCCR_BCGREEN	GENMASK(15, 8)	/* Background Color GREEN */
164b759012cSYannick Fertre #define BCCR_BCRED	GENMASK(23, 16)	/* Background Color RED */
165b759012cSYannick Fertre #define BCCR_BCWHITE	GENMASK(23, 0)	/* Background Color WHITE */
166b759012cSYannick Fertre 
167b759012cSYannick Fertre #define IER_LIE		BIT(0)		/* Line Interrupt Enable */
1687d008eecSYannick Fertre #define IER_FUWIE	BIT(1)		/* Fifo Underrun Warning Interrupt Enable */
169b759012cSYannick Fertre #define IER_TERRIE	BIT(2)		/* Transfer ERRor Interrupt Enable */
1707d008eecSYannick Fertre #define IER_RRIE	BIT(3)		/* Register Reload Interrupt Enable */
1717d008eecSYannick Fertre #define IER_FUEIE	BIT(6)		/* Fifo Underrun Error Interrupt Enable */
1727d008eecSYannick Fertre #define IER_CRCIE	BIT(7)		/* CRC Error Interrupt Enable */
173b759012cSYannick Fertre 
17453273b52SBenjamin Gaignard #define CPSR_CYPOS	GENMASK(15, 0)	/* Current Y position */
17553273b52SBenjamin Gaignard 
176b759012cSYannick Fertre #define ISR_LIF		BIT(0)		/* Line Interrupt Flag */
1777d008eecSYannick Fertre #define ISR_FUWIF	BIT(1)		/* Fifo Underrun Warning Interrupt Flag */
178b759012cSYannick Fertre #define ISR_TERRIF	BIT(2)		/* Transfer ERRor Interrupt Flag */
179b759012cSYannick Fertre #define ISR_RRIF	BIT(3)		/* Register Reload Interrupt Flag */
1807d008eecSYannick Fertre #define ISR_FUEIF	BIT(6)		/* Fifo Underrun Error Interrupt Flag */
1817d008eecSYannick Fertre #define ISR_CRCIF	BIT(7)		/* CRC Error Interrupt Flag */
182b759012cSYannick Fertre 
183fb998edfSYannick Fertre #define EDCR_OCYEN	BIT(25)		/* Output Conversion to YCbCr 422: ENable */
184fb998edfSYannick Fertre #define EDCR_OCYSEL	BIT(26)		/* Output Conversion to YCbCr 422: SELection of the CCIR */
185fb998edfSYannick Fertre #define EDCR_OCYCO	BIT(27)		/* Output Conversion to YCbCr 422: Chrominance Order */
186fb998edfSYannick Fertre 
187b759012cSYannick Fertre #define LXCR_LEN	BIT(0)		/* Layer ENable */
188b759012cSYannick Fertre #define LXCR_COLKEN	BIT(1)		/* Color Keying Enable */
189b759012cSYannick Fertre #define LXCR_CLUTEN	BIT(4)		/* Color Look-Up Table ENable */
190c6193dc5SYannick Fertre #define LXCR_HMEN	BIT(8)		/* Horizontal Mirroring ENable */
191b759012cSYannick Fertre 
192b759012cSYannick Fertre #define LXWHPCR_WHSTPOS	GENMASK(11, 0)	/* Window Horizontal StarT POSition */
193b759012cSYannick Fertre #define LXWHPCR_WHSPPOS	GENMASK(27, 16)	/* Window Horizontal StoP POSition */
194b759012cSYannick Fertre 
195b759012cSYannick Fertre #define LXWVPCR_WVSTPOS	GENMASK(10, 0)	/* Window Vertical StarT POSition */
196b759012cSYannick Fertre #define LXWVPCR_WVSPPOS	GENMASK(26, 16)	/* Window Vertical StoP POSition */
197b759012cSYannick Fertre 
198b759012cSYannick Fertre #define LXPFCR_PF	GENMASK(2, 0)	/* Pixel Format */
1998f2b5f6dSYannick Fertre #define PF_FLEXIBLE	0x7		/* Flexible Pixel Format selected */
200b759012cSYannick Fertre 
201b759012cSYannick Fertre #define LXCACR_CONSTA	GENMASK(7, 0)	/* CONSTant Alpha */
202b759012cSYannick Fertre 
203b759012cSYannick Fertre #define LXBFCR_BF2	GENMASK(2, 0)	/* Blending Factor 2 */
204b759012cSYannick Fertre #define LXBFCR_BF1	GENMASK(10, 8)	/* Blending Factor 1 */
20562467fccSYannick Fertre #define LXBFCR_BOR	GENMASK(18, 16) /* Blending ORder */
206b759012cSYannick Fertre 
207b759012cSYannick Fertre #define LXCFBLR_CFBLL	GENMASK(12, 0)	/* Color Frame Buffer Line Length */
208c6193dc5SYannick Fertre #define LXCFBLR_CFBP	GENMASK(31, 16) /* Color Frame Buffer Pitch in bytes */
209b759012cSYannick Fertre 
210b759012cSYannick Fertre #define LXCFBLNR_CFBLN	GENMASK(10, 0)	/* Color Frame Buffer Line Number */
211b759012cSYannick Fertre 
212484e72d3SYannick Fertre #define LXCR_C1R_YIA	BIT(0)		/* Ycbcr 422 Interleaved Ability */
213484e72d3SYannick Fertre #define LXCR_C1R_YSPA	BIT(1)		/* Ycbcr 420 Semi-Planar Ability */
214484e72d3SYannick Fertre #define LXCR_C1R_YFPA	BIT(2)		/* Ycbcr 420 Full-Planar Ability */
215484e72d3SYannick Fertre #define LXCR_C1R_SCA	BIT(31)		/* SCaling Ability*/
216484e72d3SYannick Fertre 
217484e72d3SYannick Fertre #define LxPCR_YREN	BIT(9)		/* Y Rescale Enable for the color dynamic range */
218484e72d3SYannick Fertre #define LxPCR_OF	BIT(8)		/* Odd pixel First */
219484e72d3SYannick Fertre #define LxPCR_CBF	BIT(7)		/* CB component First */
220484e72d3SYannick Fertre #define LxPCR_YF	BIT(6)		/* Y component First */
221484e72d3SYannick Fertre #define LxPCR_YCM	GENMASK(5, 4)	/* Ycbcr Conversion Mode */
222484e72d3SYannick Fertre #define YCM_I		0x0		/* Interleaved 422 */
223484e72d3SYannick Fertre #define YCM_SP		0x1		/* Semi-Planar 420 */
224484e72d3SYannick Fertre #define YCM_FP		0x2		/* Full-Planar 420 */
225484e72d3SYannick Fertre #define LxPCR_YCEN	BIT(3)		/* YCbCr-to-RGB Conversion Enable */
226484e72d3SYannick Fertre 
227a55d08e0SYannick Fertre #define LXRCR_IMR	BIT(0)		/* IMmediate Reload */
228a55d08e0SYannick Fertre #define LXRCR_VBR	BIT(1)		/* Vertical Blanking Reload */
229a55d08e0SYannick Fertre #define LXRCR_GRMSK	BIT(2)		/* Global (centralized) Reload MaSKed */
230a55d08e0SYannick Fertre 
231b706a25eSPhilippe CORNU #define CLUT_SIZE	256
232b706a25eSPhilippe CORNU 
233b759012cSYannick Fertre #define CONSTA_MAX	0xFF		/* CONSTant Alpha MAX= 1.0 */
234b759012cSYannick Fertre #define BF1_PAXCA	0x600		/* Pixel Alpha x Constant Alpha */
235b759012cSYannick Fertre #define BF1_CA		0x400		/* Constant Alpha */
236b759012cSYannick Fertre #define BF2_1PAXCA	0x007		/* 1 - (Pixel Alpha x Constant Alpha) */
237b759012cSYannick Fertre #define BF2_1CA		0x005		/* 1 - Constant Alpha */
238b759012cSYannick Fertre 
239b759012cSYannick Fertre #define NB_PF		8		/* Max nb of HW pixel format */
240b759012cSYannick Fertre 
2417d008eecSYannick Fertre #define FUT_DFT		128		/* Default value of fifo underrun threshold */
2427d008eecSYannick Fertre 
24379b44684SRaphael Gallais-Pou /*
24479b44684SRaphael Gallais-Pou  * Skip the first value and the second in case CRC was enabled during
24579b44684SRaphael Gallais-Pou  * the thread irq. This is to be sure CRC value is relevant for the
24679b44684SRaphael Gallais-Pou  * frame.
24779b44684SRaphael Gallais-Pou  */
24879b44684SRaphael Gallais-Pou #define CRC_SKIP_FRAMES 2
24979b44684SRaphael Gallais-Pou 
250b759012cSYannick Fertre enum ltdc_pix_fmt {
251b759012cSYannick Fertre 	PF_NONE,
252b759012cSYannick Fertre 	/* RGB formats */
253b759012cSYannick Fertre 	PF_ARGB8888,		/* ARGB [32 bits] */
254b759012cSYannick Fertre 	PF_RGBA8888,		/* RGBA [32 bits] */
2558f2b5f6dSYannick Fertre 	PF_ABGR8888,		/* ABGR [32 bits] */
2568f2b5f6dSYannick Fertre 	PF_BGRA8888,		/* BGRA [32 bits] */
257b759012cSYannick Fertre 	PF_RGB888,		/* RGB [24 bits] */
2588f2b5f6dSYannick Fertre 	PF_BGR888,		/* BGR [24 bits] */
259b759012cSYannick Fertre 	PF_RGB565,		/* RGB [16 bits] */
2608f2b5f6dSYannick Fertre 	PF_BGR565,		/* BGR [16 bits] */
261b759012cSYannick Fertre 	PF_ARGB1555,		/* ARGB A:1 bit RGB:15 bits [16 bits] */
262b759012cSYannick Fertre 	PF_ARGB4444,		/* ARGB A:4 bits R/G/B: 4 bits each [16 bits] */
263b759012cSYannick Fertre 	/* Indexed formats */
264b759012cSYannick Fertre 	PF_L8,			/* Indexed 8 bits [8 bits] */
265b759012cSYannick Fertre 	PF_AL44,		/* Alpha:4 bits + indexed 4 bits [8 bits] */
2668f2b5f6dSYannick Fertre 	PF_AL88			/* Alpha:8 bits + indexed 8 bits [16 bits] */
267b759012cSYannick Fertre };
268b759012cSYannick Fertre 
269b759012cSYannick Fertre /* The index gives the encoding of the pixel format for an HW version */
270b759012cSYannick Fertre static const enum ltdc_pix_fmt ltdc_pix_fmt_a0[NB_PF] = {
271b759012cSYannick Fertre 	PF_ARGB8888,		/* 0x00 */
272b759012cSYannick Fertre 	PF_RGB888,		/* 0x01 */
273b759012cSYannick Fertre 	PF_RGB565,		/* 0x02 */
274b759012cSYannick Fertre 	PF_ARGB1555,		/* 0x03 */
275b759012cSYannick Fertre 	PF_ARGB4444,		/* 0x04 */
276b759012cSYannick Fertre 	PF_L8,			/* 0x05 */
277b759012cSYannick Fertre 	PF_AL44,		/* 0x06 */
278b759012cSYannick Fertre 	PF_AL88			/* 0x07 */
279b759012cSYannick Fertre };
280b759012cSYannick Fertre 
281b759012cSYannick Fertre static const enum ltdc_pix_fmt ltdc_pix_fmt_a1[NB_PF] = {
282b759012cSYannick Fertre 	PF_ARGB8888,		/* 0x00 */
283b759012cSYannick Fertre 	PF_RGB888,		/* 0x01 */
284b759012cSYannick Fertre 	PF_RGB565,		/* 0x02 */
285b759012cSYannick Fertre 	PF_RGBA8888,		/* 0x03 */
286b759012cSYannick Fertre 	PF_AL44,		/* 0x04 */
287b759012cSYannick Fertre 	PF_L8,			/* 0x05 */
288b759012cSYannick Fertre 	PF_ARGB1555,		/* 0x06 */
289b759012cSYannick Fertre 	PF_ARGB4444		/* 0x07 */
290b759012cSYannick Fertre };
291b759012cSYannick Fertre 
2921726cee3SYannick Fertre static const enum ltdc_pix_fmt ltdc_pix_fmt_a2[NB_PF] = {
2931726cee3SYannick Fertre 	PF_ARGB8888,		/* 0x00 */
2941726cee3SYannick Fertre 	PF_ABGR8888,		/* 0x01 */
2951726cee3SYannick Fertre 	PF_RGBA8888,		/* 0x02 */
2961726cee3SYannick Fertre 	PF_BGRA8888,		/* 0x03 */
2971726cee3SYannick Fertre 	PF_RGB565,		/* 0x04 */
2981726cee3SYannick Fertre 	PF_BGR565,		/* 0x05 */
2991726cee3SYannick Fertre 	PF_RGB888,		/* 0x06 */
3008f2b5f6dSYannick Fertre 	PF_NONE			/* 0x07 */
3018f2b5f6dSYannick Fertre };
3028f2b5f6dSYannick Fertre 
3038f2b5f6dSYannick Fertre static const u32 ltdc_drm_fmt_a0[] = {
3048f2b5f6dSYannick Fertre 	DRM_FORMAT_ARGB8888,
3058f2b5f6dSYannick Fertre 	DRM_FORMAT_XRGB8888,
3068f2b5f6dSYannick Fertre 	DRM_FORMAT_RGB888,
3078f2b5f6dSYannick Fertre 	DRM_FORMAT_RGB565,
3088f2b5f6dSYannick Fertre 	DRM_FORMAT_ARGB1555,
3098f2b5f6dSYannick Fertre 	DRM_FORMAT_XRGB1555,
3108f2b5f6dSYannick Fertre 	DRM_FORMAT_ARGB4444,
3118f2b5f6dSYannick Fertre 	DRM_FORMAT_XRGB4444,
3128f2b5f6dSYannick Fertre 	DRM_FORMAT_C8
3138f2b5f6dSYannick Fertre };
3148f2b5f6dSYannick Fertre 
3158f2b5f6dSYannick Fertre static const u32 ltdc_drm_fmt_a1[] = {
3168f2b5f6dSYannick Fertre 	DRM_FORMAT_ARGB8888,
3178f2b5f6dSYannick Fertre 	DRM_FORMAT_XRGB8888,
3188f2b5f6dSYannick Fertre 	DRM_FORMAT_RGB888,
3198f2b5f6dSYannick Fertre 	DRM_FORMAT_RGB565,
3208f2b5f6dSYannick Fertre 	DRM_FORMAT_RGBA8888,
3218f2b5f6dSYannick Fertre 	DRM_FORMAT_RGBX8888,
3228f2b5f6dSYannick Fertre 	DRM_FORMAT_ARGB1555,
3238f2b5f6dSYannick Fertre 	DRM_FORMAT_XRGB1555,
3248f2b5f6dSYannick Fertre 	DRM_FORMAT_ARGB4444,
3258f2b5f6dSYannick Fertre 	DRM_FORMAT_XRGB4444,
3268f2b5f6dSYannick Fertre 	DRM_FORMAT_C8
3278f2b5f6dSYannick Fertre };
3288f2b5f6dSYannick Fertre 
3298f2b5f6dSYannick Fertre static const u32 ltdc_drm_fmt_a2[] = {
3308f2b5f6dSYannick Fertre 	DRM_FORMAT_ARGB8888,
3318f2b5f6dSYannick Fertre 	DRM_FORMAT_XRGB8888,
3328f2b5f6dSYannick Fertre 	DRM_FORMAT_ABGR8888,
3338f2b5f6dSYannick Fertre 	DRM_FORMAT_XBGR8888,
3348f2b5f6dSYannick Fertre 	DRM_FORMAT_RGBA8888,
3358f2b5f6dSYannick Fertre 	DRM_FORMAT_RGBX8888,
3368f2b5f6dSYannick Fertre 	DRM_FORMAT_BGRA8888,
3378f2b5f6dSYannick Fertre 	DRM_FORMAT_BGRX8888,
3388f2b5f6dSYannick Fertre 	DRM_FORMAT_RGB565,
3398f2b5f6dSYannick Fertre 	DRM_FORMAT_BGR565,
3408f2b5f6dSYannick Fertre 	DRM_FORMAT_RGB888,
3418f2b5f6dSYannick Fertre 	DRM_FORMAT_BGR888,
3428f2b5f6dSYannick Fertre 	DRM_FORMAT_ARGB1555,
3438f2b5f6dSYannick Fertre 	DRM_FORMAT_XRGB1555,
3448f2b5f6dSYannick Fertre 	DRM_FORMAT_ARGB4444,
3458f2b5f6dSYannick Fertre 	DRM_FORMAT_XRGB4444,
3468f2b5f6dSYannick Fertre 	DRM_FORMAT_C8
3471726cee3SYannick Fertre };
3481726cee3SYannick Fertre 
349484e72d3SYannick Fertre static const u32 ltdc_drm_fmt_ycbcr_cp[] = {
350484e72d3SYannick Fertre 	DRM_FORMAT_YUYV,
351484e72d3SYannick Fertre 	DRM_FORMAT_YVYU,
352484e72d3SYannick Fertre 	DRM_FORMAT_UYVY,
353484e72d3SYannick Fertre 	DRM_FORMAT_VYUY
354484e72d3SYannick Fertre };
355484e72d3SYannick Fertre 
356484e72d3SYannick Fertre static const u32 ltdc_drm_fmt_ycbcr_sp[] = {
357484e72d3SYannick Fertre 	DRM_FORMAT_NV12,
358484e72d3SYannick Fertre 	DRM_FORMAT_NV21
359484e72d3SYannick Fertre };
360484e72d3SYannick Fertre 
361484e72d3SYannick Fertre static const u32 ltdc_drm_fmt_ycbcr_fp[] = {
362484e72d3SYannick Fertre 	DRM_FORMAT_YUV420,
363484e72d3SYannick Fertre 	DRM_FORMAT_YVU420
364484e72d3SYannick Fertre };
365484e72d3SYannick Fertre 
3661726cee3SYannick Fertre /* Layer register offsets */
3671726cee3SYannick Fertre static const u32 ltdc_layer_regs_a0[] = {
3681726cee3SYannick Fertre 	0x80,	/* L1 configuration 0 */
3691726cee3SYannick Fertre 	0x00,	/* not available */
3701726cee3SYannick Fertre 	0x00,	/* not available */
3711726cee3SYannick Fertre 	0x84,	/* L1 control register */
3721726cee3SYannick Fertre 	0x88,	/* L1 window horizontal position configuration */
3731726cee3SYannick Fertre 	0x8c,	/* L1 window vertical position configuration */
3741726cee3SYannick Fertre 	0x90,	/* L1 color keying configuration */
3751726cee3SYannick Fertre 	0x94,	/* L1 pixel format configuration */
3761726cee3SYannick Fertre 	0x98,	/* L1 constant alpha configuration */
3771726cee3SYannick Fertre 	0x9c,	/* L1 default color configuration */
3781726cee3SYannick Fertre 	0xa0,	/* L1 blending factors configuration */
3791726cee3SYannick Fertre 	0x00,	/* not available */
3801726cee3SYannick Fertre 	0x00,	/* not available */
3811726cee3SYannick Fertre 	0xac,	/* L1 color frame buffer address */
3821726cee3SYannick Fertre 	0xb0,	/* L1 color frame buffer length */
3831726cee3SYannick Fertre 	0xb4,	/* L1 color frame buffer line number */
3841726cee3SYannick Fertre 	0x00,	/* not available */
3851726cee3SYannick Fertre 	0x00,	/* not available */
3861726cee3SYannick Fertre 	0x00,	/* not available */
3871726cee3SYannick Fertre 	0x00,	/* not available */
3881726cee3SYannick Fertre 	0xc4,	/* L1 CLUT write */
3891726cee3SYannick Fertre 	0x00,	/* not available */
3901726cee3SYannick Fertre 	0x00,	/* not available */
3911726cee3SYannick Fertre 	0x00,	/* not available */
3921726cee3SYannick Fertre 	0x00	/* not available */
3931726cee3SYannick Fertre };
3941726cee3SYannick Fertre 
3951726cee3SYannick Fertre static const u32 ltdc_layer_regs_a1[] = {
3961726cee3SYannick Fertre 	0x80,	/* L1 configuration 0 */
3971726cee3SYannick Fertre 	0x84,	/* L1 configuration 1 */
3981726cee3SYannick Fertre 	0x00,	/* L1 reload control */
3991726cee3SYannick Fertre 	0x88,	/* L1 control register */
4001726cee3SYannick Fertre 	0x8c,	/* L1 window horizontal position configuration */
4011726cee3SYannick Fertre 	0x90,	/* L1 window vertical position configuration */
4021726cee3SYannick Fertre 	0x94,	/* L1 color keying configuration */
4031726cee3SYannick Fertre 	0x98,	/* L1 pixel format configuration */
4041726cee3SYannick Fertre 	0x9c,	/* L1 constant alpha configuration */
4051726cee3SYannick Fertre 	0xa0,	/* L1 default color configuration */
4061726cee3SYannick Fertre 	0xa4,	/* L1 blending factors configuration */
4071726cee3SYannick Fertre 	0xa8,	/* L1 burst length configuration */
4081726cee3SYannick Fertre 	0x00,	/* not available */
4091726cee3SYannick Fertre 	0xac,	/* L1 color frame buffer address */
4101726cee3SYannick Fertre 	0xb0,	/* L1 color frame buffer length */
4111726cee3SYannick Fertre 	0xb4,	/* L1 color frame buffer line number */
4121726cee3SYannick Fertre 	0xb8,	/* L1 auxiliary frame buffer address 0 */
4131726cee3SYannick Fertre 	0xbc,	/* L1 auxiliary frame buffer address 1 */
4141726cee3SYannick Fertre 	0xc0,	/* L1 auxiliary frame buffer length */
4151726cee3SYannick Fertre 	0xc4,	/* L1 auxiliary frame buffer line number */
4161726cee3SYannick Fertre 	0xc8,	/* L1 CLUT write */
4171726cee3SYannick Fertre 	0x00,	/* not available */
4181726cee3SYannick Fertre 	0x00,	/* not available */
4191726cee3SYannick Fertre 	0x00,	/* not available */
4201726cee3SYannick Fertre 	0x00	/* not available */
4211726cee3SYannick Fertre };
4221726cee3SYannick Fertre 
4231726cee3SYannick Fertre static const u32 ltdc_layer_regs_a2[] = {
4241726cee3SYannick Fertre 	0x100,	/* L1 configuration 0 */
4251726cee3SYannick Fertre 	0x104,	/* L1 configuration 1 */
4261726cee3SYannick Fertre 	0x108,	/* L1 reload control */
4271726cee3SYannick Fertre 	0x10c,	/* L1 control register */
4281726cee3SYannick Fertre 	0x110,	/* L1 window horizontal position configuration */
4291726cee3SYannick Fertre 	0x114,	/* L1 window vertical position configuration */
4301726cee3SYannick Fertre 	0x118,	/* L1 color keying configuration */
4311726cee3SYannick Fertre 	0x11c,	/* L1 pixel format configuration */
4321726cee3SYannick Fertre 	0x120,	/* L1 constant alpha configuration */
4331726cee3SYannick Fertre 	0x124,	/* L1 default color configuration */
4341726cee3SYannick Fertre 	0x128,	/* L1 blending factors configuration */
4351726cee3SYannick Fertre 	0x12c,	/* L1 burst length configuration */
4361726cee3SYannick Fertre 	0x130,	/* L1 planar configuration */
4371726cee3SYannick Fertre 	0x134,	/* L1 color frame buffer address */
4381726cee3SYannick Fertre 	0x138,	/* L1 color frame buffer length */
4391726cee3SYannick Fertre 	0x13c,	/* L1 color frame buffer line number */
4401726cee3SYannick Fertre 	0x140,	/* L1 auxiliary frame buffer address 0 */
4411726cee3SYannick Fertre 	0x144,	/* L1 auxiliary frame buffer address 1 */
4421726cee3SYannick Fertre 	0x148,	/* L1 auxiliary frame buffer length */
4431726cee3SYannick Fertre 	0x14c,	/* L1 auxiliary frame buffer line number */
4441726cee3SYannick Fertre 	0x150,	/* L1 CLUT write */
4451726cee3SYannick Fertre 	0x16c,	/* L1 Conversion YCbCr RGB 0 */
4461726cee3SYannick Fertre 	0x170,	/* L1 Conversion YCbCr RGB 1 */
4471726cee3SYannick Fertre 	0x174,	/* L1 Flexible Pixel Format 0 */
4481726cee3SYannick Fertre 	0x178	/* L1 Flexible Pixel Format 1 */
4491726cee3SYannick Fertre };
4501726cee3SYannick Fertre 
451e7c03dbaSYannick Fertré static const u64 ltdc_format_modifiers[] = {
452e7c03dbaSYannick Fertré 	DRM_FORMAT_MOD_LINEAR,
453e7c03dbaSYannick Fertré 	DRM_FORMAT_MOD_INVALID
454e7c03dbaSYannick Fertré };
455e7c03dbaSYannick Fertré 
456734c2645SYannick Fertre static const struct regmap_config stm32_ltdc_regmap_cfg = {
457734c2645SYannick Fertre 	.reg_bits = 32,
458734c2645SYannick Fertre 	.val_bits = 32,
459734c2645SYannick Fertre 	.reg_stride = sizeof(u32),
460734c2645SYannick Fertre 	.max_register = 0x400,
461734c2645SYannick Fertre 	.use_relaxed_mmio = true,
462734c2645SYannick Fertre 	.cache_type = REGCACHE_NONE,
463734c2645SYannick Fertre };
464b759012cSYannick Fertre 
465484e72d3SYannick Fertre static const u32 ltdc_ycbcr2rgb_coeffs[DRM_COLOR_ENCODING_MAX][DRM_COLOR_RANGE_MAX][2] = {
466484e72d3SYannick Fertre 	[DRM_COLOR_YCBCR_BT601][DRM_COLOR_YCBCR_LIMITED_RANGE] = {
467484e72d3SYannick Fertre 		0x02040199,	/* (b_cb = 516 / r_cr = 409) */
468484e72d3SYannick Fertre 		0x006400D0	/* (g_cb = 100 / g_cr = 208) */
469484e72d3SYannick Fertre 	},
470484e72d3SYannick Fertre 	[DRM_COLOR_YCBCR_BT601][DRM_COLOR_YCBCR_FULL_RANGE] = {
471484e72d3SYannick Fertre 		0x01C60167,	/* (b_cb = 454 / r_cr = 359) */
472484e72d3SYannick Fertre 		0x005800B7	/* (g_cb = 88 / g_cr = 183) */
473484e72d3SYannick Fertre 	},
474484e72d3SYannick Fertre 	[DRM_COLOR_YCBCR_BT709][DRM_COLOR_YCBCR_LIMITED_RANGE] = {
475484e72d3SYannick Fertre 		0x021D01CB,	/* (b_cb = 541 / r_cr = 459) */
476484e72d3SYannick Fertre 		0x00370089	/* (g_cb = 55 / g_cr = 137) */
477484e72d3SYannick Fertre 	},
478484e72d3SYannick Fertre 	[DRM_COLOR_YCBCR_BT709][DRM_COLOR_YCBCR_FULL_RANGE] = {
479484e72d3SYannick Fertre 		0x01DB0193,	/* (b_cb = 475 / r_cr = 403) */
480484e72d3SYannick Fertre 		0x00300078	/* (g_cb = 48 / g_cr = 120) */
481484e72d3SYannick Fertre 	}
482484e72d3SYannick Fertre 	/* BT2020 not supported */
483484e72d3SYannick Fertre };
484484e72d3SYannick Fertre 
crtc_to_ltdc(struct drm_crtc * crtc)485b759012cSYannick Fertre static inline struct ltdc_device *crtc_to_ltdc(struct drm_crtc *crtc)
486b759012cSYannick Fertre {
487b759012cSYannick Fertre 	return (struct ltdc_device *)crtc->dev->dev_private;
488b759012cSYannick Fertre }
489b759012cSYannick Fertre 
plane_to_ltdc(struct drm_plane * plane)490b759012cSYannick Fertre static inline struct ltdc_device *plane_to_ltdc(struct drm_plane *plane)
491b759012cSYannick Fertre {
492b759012cSYannick Fertre 	return (struct ltdc_device *)plane->dev->dev_private;
493b759012cSYannick Fertre }
494b759012cSYannick Fertre 
encoder_to_ltdc(struct drm_encoder * enc)495b759012cSYannick Fertre static inline struct ltdc_device *encoder_to_ltdc(struct drm_encoder *enc)
496b759012cSYannick Fertre {
497b759012cSYannick Fertre 	return (struct ltdc_device *)enc->dev->dev_private;
498b759012cSYannick Fertre }
499b759012cSYannick Fertre 
to_ltdc_pixelformat(u32 drm_fmt)500b759012cSYannick Fertre static inline enum ltdc_pix_fmt to_ltdc_pixelformat(u32 drm_fmt)
501b759012cSYannick Fertre {
502b759012cSYannick Fertre 	enum ltdc_pix_fmt pf;
503b759012cSYannick Fertre 
504b759012cSYannick Fertre 	switch (drm_fmt) {
505b759012cSYannick Fertre 	case DRM_FORMAT_ARGB8888:
506b759012cSYannick Fertre 	case DRM_FORMAT_XRGB8888:
507b759012cSYannick Fertre 		pf = PF_ARGB8888;
508b759012cSYannick Fertre 		break;
5098f2b5f6dSYannick Fertre 	case DRM_FORMAT_ABGR8888:
5108f2b5f6dSYannick Fertre 	case DRM_FORMAT_XBGR8888:
5118f2b5f6dSYannick Fertre 		pf = PF_ABGR8888;
5128f2b5f6dSYannick Fertre 		break;
513b759012cSYannick Fertre 	case DRM_FORMAT_RGBA8888:
514b759012cSYannick Fertre 	case DRM_FORMAT_RGBX8888:
515b759012cSYannick Fertre 		pf = PF_RGBA8888;
516b759012cSYannick Fertre 		break;
5178f2b5f6dSYannick Fertre 	case DRM_FORMAT_BGRA8888:
5188f2b5f6dSYannick Fertre 	case DRM_FORMAT_BGRX8888:
5198f2b5f6dSYannick Fertre 		pf = PF_BGRA8888;
5208f2b5f6dSYannick Fertre 		break;
521b759012cSYannick Fertre 	case DRM_FORMAT_RGB888:
522b759012cSYannick Fertre 		pf = PF_RGB888;
523b759012cSYannick Fertre 		break;
5248f2b5f6dSYannick Fertre 	case DRM_FORMAT_BGR888:
5258f2b5f6dSYannick Fertre 		pf = PF_BGR888;
5268f2b5f6dSYannick Fertre 		break;
527b759012cSYannick Fertre 	case DRM_FORMAT_RGB565:
528b759012cSYannick Fertre 		pf = PF_RGB565;
529b759012cSYannick Fertre 		break;
5308f2b5f6dSYannick Fertre 	case DRM_FORMAT_BGR565:
5318f2b5f6dSYannick Fertre 		pf = PF_BGR565;
5328f2b5f6dSYannick Fertre 		break;
533b759012cSYannick Fertre 	case DRM_FORMAT_ARGB1555:
534b759012cSYannick Fertre 	case DRM_FORMAT_XRGB1555:
535b759012cSYannick Fertre 		pf = PF_ARGB1555;
536b759012cSYannick Fertre 		break;
537b759012cSYannick Fertre 	case DRM_FORMAT_ARGB4444:
538b759012cSYannick Fertre 	case DRM_FORMAT_XRGB4444:
539b759012cSYannick Fertre 		pf = PF_ARGB4444;
540b759012cSYannick Fertre 		break;
541b759012cSYannick Fertre 	case DRM_FORMAT_C8:
542b759012cSYannick Fertre 		pf = PF_L8;
543b759012cSYannick Fertre 		break;
544b759012cSYannick Fertre 	default:
545b759012cSYannick Fertre 		pf = PF_NONE;
546b759012cSYannick Fertre 		break;
547b759012cSYannick Fertre 		/* Note: There are no DRM_FORMAT for AL44 and AL88 */
548b759012cSYannick Fertre 	}
549b759012cSYannick Fertre 
550b759012cSYannick Fertre 	return pf;
551b759012cSYannick Fertre }
552b759012cSYannick Fertre 
ltdc_set_flexible_pixel_format(struct drm_plane * plane,enum ltdc_pix_fmt pix_fmt)5538f2b5f6dSYannick Fertre static inline u32 ltdc_set_flexible_pixel_format(struct drm_plane *plane, enum ltdc_pix_fmt pix_fmt)
554b759012cSYannick Fertre {
5558f2b5f6dSYannick Fertre 	struct ltdc_device *ldev = plane_to_ltdc(plane);
5568f2b5f6dSYannick Fertre 	u32 lofs = plane->index * LAY_OFS, ret = PF_FLEXIBLE;
5578f2b5f6dSYannick Fertre 	int psize, alen, apos, rlen, rpos, glen, gpos, blen, bpos;
5588f2b5f6dSYannick Fertre 
5598f2b5f6dSYannick Fertre 	switch (pix_fmt) {
5608f2b5f6dSYannick Fertre 	case PF_BGR888:
5618f2b5f6dSYannick Fertre 		psize = 3;
5628f2b5f6dSYannick Fertre 		alen = 0; apos = 0; rlen = 8; rpos = 0;
5638f2b5f6dSYannick Fertre 		glen = 8; gpos = 8; blen = 8; bpos = 16;
5648f2b5f6dSYannick Fertre 	break;
565b759012cSYannick Fertre 	case PF_ARGB1555:
5668f2b5f6dSYannick Fertre 		psize = 2;
5678f2b5f6dSYannick Fertre 		alen = 1; apos = 15; rlen = 5; rpos = 10;
5688f2b5f6dSYannick Fertre 		glen = 5; gpos = 5;  blen = 5; bpos = 0;
5698f2b5f6dSYannick Fertre 	break;
570b759012cSYannick Fertre 	case PF_ARGB4444:
5718f2b5f6dSYannick Fertre 		psize = 2;
5728f2b5f6dSYannick Fertre 		alen = 4; apos = 12; rlen = 4; rpos = 8;
5738f2b5f6dSYannick Fertre 		glen = 4; gpos = 4; blen = 4; bpos = 0;
5748f2b5f6dSYannick Fertre 	break;
575b759012cSYannick Fertre 	case PF_L8:
5768f2b5f6dSYannick Fertre 		psize = 1;
5778f2b5f6dSYannick Fertre 		alen = 0; apos = 0; rlen = 8; rpos = 0;
5788f2b5f6dSYannick Fertre 		glen = 8; gpos = 0; blen = 8; bpos = 0;
5798f2b5f6dSYannick Fertre 	break;
5808f2b5f6dSYannick Fertre 	case PF_AL44:
5818f2b5f6dSYannick Fertre 		psize = 1;
5828f2b5f6dSYannick Fertre 		alen = 4; apos = 4; rlen = 4; rpos = 0;
5838f2b5f6dSYannick Fertre 		glen = 4; gpos = 0; blen = 4; bpos = 0;
5848f2b5f6dSYannick Fertre 	break;
5858f2b5f6dSYannick Fertre 	case PF_AL88:
5868f2b5f6dSYannick Fertre 		psize = 2;
5878f2b5f6dSYannick Fertre 		alen = 8; apos = 8; rlen = 8; rpos = 0;
5888f2b5f6dSYannick Fertre 		glen = 8; gpos = 0; blen = 8; bpos = 0;
5898f2b5f6dSYannick Fertre 	break;
590b759012cSYannick Fertre 	default:
5918f2b5f6dSYannick Fertre 		ret = NB_PF; /* error case, trace msg is handled by the caller */
5928f2b5f6dSYannick Fertre 	break;
593b759012cSYannick Fertre 	}
594b759012cSYannick Fertre 
5958f2b5f6dSYannick Fertre 	if (ret == PF_FLEXIBLE) {
5968f2b5f6dSYannick Fertre 		regmap_write(ldev->regmap, LTDC_L1FPF0R + lofs,
5978f2b5f6dSYannick Fertre 			     (rlen << 14)  + (rpos << 9) + (alen << 5) + apos);
5988f2b5f6dSYannick Fertre 
5998f2b5f6dSYannick Fertre 		regmap_write(ldev->regmap, LTDC_L1FPF1R + lofs,
6008f2b5f6dSYannick Fertre 			     (psize << 18) + (blen << 14)  + (bpos << 9) + (glen << 5) + gpos);
601aefa8301SPhilippe CORNU 	}
6028f2b5f6dSYannick Fertre 
6038f2b5f6dSYannick Fertre 	return ret;
6048f2b5f6dSYannick Fertre }
6058f2b5f6dSYannick Fertre 
6068f2b5f6dSYannick Fertre /*
6078f2b5f6dSYannick Fertre  * All non-alpha color formats derived from native alpha color formats are
6088f2b5f6dSYannick Fertre  * either characterized by a FourCC format code
6098f2b5f6dSYannick Fertre  */
is_xrgb(u32 drm)6108f2b5f6dSYannick Fertre static inline u32 is_xrgb(u32 drm)
6118f2b5f6dSYannick Fertre {
6128f2b5f6dSYannick Fertre 	return ((drm & 0xFF) == 'X' || ((drm >> 8) & 0xFF) == 'X');
613aefa8301SPhilippe CORNU }
614aefa8301SPhilippe CORNU 
ltdc_set_ycbcr_config(struct drm_plane * plane,u32 drm_pix_fmt)615484e72d3SYannick Fertre static inline void ltdc_set_ycbcr_config(struct drm_plane *plane, u32 drm_pix_fmt)
616484e72d3SYannick Fertre {
617484e72d3SYannick Fertre 	struct ltdc_device *ldev = plane_to_ltdc(plane);
618484e72d3SYannick Fertre 	struct drm_plane_state *state = plane->state;
619484e72d3SYannick Fertre 	u32 lofs = plane->index * LAY_OFS;
620484e72d3SYannick Fertre 	u32 val;
621484e72d3SYannick Fertre 
622484e72d3SYannick Fertre 	switch (drm_pix_fmt) {
623484e72d3SYannick Fertre 	case DRM_FORMAT_YUYV:
624484e72d3SYannick Fertre 		val = (YCM_I << 4) | LxPCR_YF | LxPCR_CBF;
625484e72d3SYannick Fertre 		break;
626484e72d3SYannick Fertre 	case DRM_FORMAT_YVYU:
627484e72d3SYannick Fertre 		val = (YCM_I << 4) | LxPCR_YF;
628484e72d3SYannick Fertre 		break;
629484e72d3SYannick Fertre 	case DRM_FORMAT_UYVY:
630484e72d3SYannick Fertre 		val = (YCM_I << 4) | LxPCR_CBF;
631484e72d3SYannick Fertre 		break;
632484e72d3SYannick Fertre 	case DRM_FORMAT_VYUY:
633484e72d3SYannick Fertre 		val = (YCM_I << 4);
634484e72d3SYannick Fertre 		break;
635484e72d3SYannick Fertre 	case DRM_FORMAT_NV12:
636484e72d3SYannick Fertre 		val = (YCM_SP << 4) | LxPCR_CBF;
637484e72d3SYannick Fertre 		break;
638484e72d3SYannick Fertre 	case DRM_FORMAT_NV21:
639484e72d3SYannick Fertre 		val = (YCM_SP << 4);
640484e72d3SYannick Fertre 		break;
641484e72d3SYannick Fertre 	case DRM_FORMAT_YUV420:
642484e72d3SYannick Fertre 	case DRM_FORMAT_YVU420:
643484e72d3SYannick Fertre 		val = (YCM_FP << 4);
644484e72d3SYannick Fertre 		break;
645484e72d3SYannick Fertre 	default:
646484e72d3SYannick Fertre 		/* RGB or not a YCbCr supported format */
6473b2f68f1SNathan Chancellor 		DRM_ERROR("Unsupported pixel format: %u\n", drm_pix_fmt);
6483b2f68f1SNathan Chancellor 		return;
649484e72d3SYannick Fertre 	}
650484e72d3SYannick Fertre 
651484e72d3SYannick Fertre 	/* Enable limited range */
652484e72d3SYannick Fertre 	if (state->color_range == DRM_COLOR_YCBCR_LIMITED_RANGE)
653484e72d3SYannick Fertre 		val |= LxPCR_YREN;
654484e72d3SYannick Fertre 
655484e72d3SYannick Fertre 	/* enable ycbcr conversion */
656484e72d3SYannick Fertre 	val |= LxPCR_YCEN;
657484e72d3SYannick Fertre 
658484e72d3SYannick Fertre 	regmap_write(ldev->regmap, LTDC_L1PCR + lofs, val);
659484e72d3SYannick Fertre }
660484e72d3SYannick Fertre 
ltdc_set_ycbcr_coeffs(struct drm_plane * plane)661484e72d3SYannick Fertre static inline void ltdc_set_ycbcr_coeffs(struct drm_plane *plane)
662484e72d3SYannick Fertre {
663484e72d3SYannick Fertre 	struct ltdc_device *ldev = plane_to_ltdc(plane);
664484e72d3SYannick Fertre 	struct drm_plane_state *state = plane->state;
665484e72d3SYannick Fertre 	enum drm_color_encoding enc = state->color_encoding;
666484e72d3SYannick Fertre 	enum drm_color_range ran = state->color_range;
667484e72d3SYannick Fertre 	u32 lofs = plane->index * LAY_OFS;
668484e72d3SYannick Fertre 
669484e72d3SYannick Fertre 	if (enc != DRM_COLOR_YCBCR_BT601 && enc != DRM_COLOR_YCBCR_BT709) {
670484e72d3SYannick Fertre 		DRM_ERROR("color encoding %d not supported, use bt601 by default\n", enc);
671484e72d3SYannick Fertre 		/* set by default color encoding to DRM_COLOR_YCBCR_BT601 */
672484e72d3SYannick Fertre 		enc = DRM_COLOR_YCBCR_BT601;
673484e72d3SYannick Fertre 	}
674484e72d3SYannick Fertre 
675484e72d3SYannick Fertre 	if (ran != DRM_COLOR_YCBCR_LIMITED_RANGE && ran != DRM_COLOR_YCBCR_FULL_RANGE) {
676484e72d3SYannick Fertre 		DRM_ERROR("color range %d not supported, use limited range by default\n", ran);
677484e72d3SYannick Fertre 		/* set by default color range to DRM_COLOR_YCBCR_LIMITED_RANGE */
678484e72d3SYannick Fertre 		ran = DRM_COLOR_YCBCR_LIMITED_RANGE;
679484e72d3SYannick Fertre 	}
680484e72d3SYannick Fertre 
681484e72d3SYannick Fertre 	DRM_DEBUG_DRIVER("Color encoding=%d, range=%d\n", enc, ran);
682484e72d3SYannick Fertre 	regmap_write(ldev->regmap, LTDC_L1CYR0R + lofs,
683484e72d3SYannick Fertre 		     ltdc_ycbcr2rgb_coeffs[enc][ran][0]);
684484e72d3SYannick Fertre 	regmap_write(ldev->regmap, LTDC_L1CYR1R + lofs,
685484e72d3SYannick Fertre 		     ltdc_ycbcr2rgb_coeffs[enc][ran][1]);
686484e72d3SYannick Fertre }
687484e72d3SYannick Fertre 
ltdc_irq_crc_handle(struct ltdc_device * ldev,struct drm_crtc * crtc)68879b44684SRaphael Gallais-Pou static inline void ltdc_irq_crc_handle(struct ltdc_device *ldev,
68979b44684SRaphael Gallais-Pou 				       struct drm_crtc *crtc)
69079b44684SRaphael Gallais-Pou {
69179b44684SRaphael Gallais-Pou 	u32 crc;
69279b44684SRaphael Gallais-Pou 	int ret;
69379b44684SRaphael Gallais-Pou 
69479b44684SRaphael Gallais-Pou 	if (ldev->crc_skip_count < CRC_SKIP_FRAMES) {
69579b44684SRaphael Gallais-Pou 		ldev->crc_skip_count++;
69679b44684SRaphael Gallais-Pou 		return;
69779b44684SRaphael Gallais-Pou 	}
69879b44684SRaphael Gallais-Pou 
69979b44684SRaphael Gallais-Pou 	/* Get the CRC of the frame */
70079b44684SRaphael Gallais-Pou 	ret = regmap_read(ldev->regmap, LTDC_CCRCR, &crc);
70179b44684SRaphael Gallais-Pou 	if (ret)
70279b44684SRaphael Gallais-Pou 		return;
70379b44684SRaphael Gallais-Pou 
70479b44684SRaphael Gallais-Pou 	/* Report to DRM the CRC (hw dependent feature) */
70579b44684SRaphael Gallais-Pou 	drm_crtc_add_crc_entry(crtc, true, drm_crtc_accurate_vblank_count(crtc), &crc);
70679b44684SRaphael Gallais-Pou }
70779b44684SRaphael Gallais-Pou 
ltdc_irq_thread(int irq,void * arg)708b759012cSYannick Fertre static irqreturn_t ltdc_irq_thread(int irq, void *arg)
709b759012cSYannick Fertre {
710b759012cSYannick Fertre 	struct drm_device *ddev = arg;
711b759012cSYannick Fertre 	struct ltdc_device *ldev = ddev->dev_private;
712b759012cSYannick Fertre 	struct drm_crtc *crtc = drm_crtc_from_index(ddev, 0);
713b759012cSYannick Fertre 
714b759012cSYannick Fertre 	/* Line IRQ : trigger the vblank event */
71579b44684SRaphael Gallais-Pou 	if (ldev->irq_status & ISR_LIF) {
716b759012cSYannick Fertre 		drm_crtc_handle_vblank(crtc);
717b759012cSYannick Fertre 
71879b44684SRaphael Gallais-Pou 		/* Early return if CRC is not active */
71979b44684SRaphael Gallais-Pou 		if (ldev->crc_active)
72079b44684SRaphael Gallais-Pou 			ltdc_irq_crc_handle(ldev, crtc);
72179b44684SRaphael Gallais-Pou 	}
72279b44684SRaphael Gallais-Pou 
723b759012cSYannick Fertre 	mutex_lock(&ldev->err_lock);
724b759012cSYannick Fertre 	if (ldev->irq_status & ISR_TERRIF)
7257d008eecSYannick Fertre 		ldev->transfer_err++;
7267d008eecSYannick Fertre 	if (ldev->irq_status & ISR_FUEIF)
7277d008eecSYannick Fertre 		ldev->fifo_err++;
7287d008eecSYannick Fertre 	if (ldev->irq_status & ISR_FUWIF)
7297d008eecSYannick Fertre 		ldev->fifo_warn++;
730b759012cSYannick Fertre 	mutex_unlock(&ldev->err_lock);
731b759012cSYannick Fertre 
732b759012cSYannick Fertre 	return IRQ_HANDLED;
733b759012cSYannick Fertre }
734b759012cSYannick Fertre 
ltdc_irq(int irq,void * arg)735b759012cSYannick Fertre static irqreturn_t ltdc_irq(int irq, void *arg)
736b759012cSYannick Fertre {
737b759012cSYannick Fertre 	struct drm_device *ddev = arg;
738b759012cSYannick Fertre 	struct ltdc_device *ldev = ddev->dev_private;
739b759012cSYannick Fertre 
740734c2645SYannick Fertre 	/*
741734c2645SYannick Fertre 	 *  Read & Clear the interrupt status
742734c2645SYannick Fertre 	 *  In order to write / read registers in this critical section
743734c2645SYannick Fertre 	 *  very quickly, the regmap functions are not used.
744734c2645SYannick Fertre 	 */
745734c2645SYannick Fertre 	ldev->irq_status = readl_relaxed(ldev->regs + LTDC_ISR);
746734c2645SYannick Fertre 	writel_relaxed(ldev->irq_status, ldev->regs + LTDC_ICR);
747b759012cSYannick Fertre 
748b759012cSYannick Fertre 	return IRQ_WAKE_THREAD;
749b759012cSYannick Fertre }
750b759012cSYannick Fertre 
751b759012cSYannick Fertre /*
752b759012cSYannick Fertre  * DRM_CRTC
753b759012cSYannick Fertre  */
754b759012cSYannick Fertre 
ltdc_crtc_update_clut(struct drm_crtc * crtc)755b706a25eSPhilippe CORNU static void ltdc_crtc_update_clut(struct drm_crtc *crtc)
756b706a25eSPhilippe CORNU {
757b706a25eSPhilippe CORNU 	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
758b706a25eSPhilippe CORNU 	struct drm_color_lut *lut;
759b706a25eSPhilippe CORNU 	u32 val;
760b706a25eSPhilippe CORNU 	int i;
761b706a25eSPhilippe CORNU 
762b706a25eSPhilippe CORNU 	if (!crtc->state->color_mgmt_changed || !crtc->state->gamma_lut)
763b706a25eSPhilippe CORNU 		return;
764b706a25eSPhilippe CORNU 
765b706a25eSPhilippe CORNU 	lut = (struct drm_color_lut *)crtc->state->gamma_lut->data;
766b706a25eSPhilippe CORNU 
767b706a25eSPhilippe CORNU 	for (i = 0; i < CLUT_SIZE; i++, lut++) {
768b706a25eSPhilippe CORNU 		val = ((lut->red << 8) & 0xff0000) | (lut->green & 0xff00) |
769b706a25eSPhilippe CORNU 			(lut->blue >> 8) | (i << 24);
770734c2645SYannick Fertre 		regmap_write(ldev->regmap, LTDC_L1CLUTWR, val);
771b706a25eSPhilippe CORNU 	}
772b706a25eSPhilippe CORNU }
773b706a25eSPhilippe CORNU 
ltdc_crtc_atomic_enable(struct drm_crtc * crtc,struct drm_atomic_state * state)7740b20a0f8SLaurent Pinchart static void ltdc_crtc_atomic_enable(struct drm_crtc *crtc,
775351f950dSMaxime Ripard 				    struct drm_atomic_state *state)
776b759012cSYannick Fertre {
777b759012cSYannick Fertre 	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
778ebd267b2SMarek Vasut 	struct drm_device *ddev = crtc->dev;
779b759012cSYannick Fertre 
780b759012cSYannick Fertre 	DRM_DEBUG_DRIVER("\n");
781b759012cSYannick Fertre 
782ebd267b2SMarek Vasut 	pm_runtime_get_sync(ddev->dev);
783ebd267b2SMarek Vasut 
784b759012cSYannick Fertre 	/* Sets the background color value */
785734c2645SYannick Fertre 	regmap_write(ldev->regmap, LTDC_BCCR, BCCR_BCBLACK);
786b759012cSYannick Fertre 
787b759012cSYannick Fertre 	/* Enable IRQ */
7887d008eecSYannick Fertre 	regmap_set_bits(ldev->regmap, LTDC_IER, IER_FUWIE | IER_FUEIE | IER_RRIE | IER_TERRIE);
789b759012cSYannick Fertre 
7908ceb8568SYannick Fertré 	/* Commit shadow registers = update planes at next vblank */
791a55d08e0SYannick Fertre 	if (!ldev->caps.plane_reg_shadow)
792734c2645SYannick Fertre 		regmap_set_bits(ldev->regmap, LTDC_SRCR, SRCR_VBR);
793b759012cSYannick Fertre 
794b759012cSYannick Fertre 	drm_crtc_vblank_on(crtc);
795b759012cSYannick Fertre }
796b759012cSYannick Fertre 
ltdc_crtc_atomic_disable(struct drm_crtc * crtc,struct drm_atomic_state * state)79764581714SLaurent Pinchart static void ltdc_crtc_atomic_disable(struct drm_crtc *crtc,
798351f950dSMaxime Ripard 				     struct drm_atomic_state *state)
799b759012cSYannick Fertre {
800b759012cSYannick Fertre 	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
80135ab6cfbSYannick Fertré 	struct drm_device *ddev = crtc->dev;
802c4f218d4SYannick Fertre 	int layer_index = 0;
803b759012cSYannick Fertre 
804b759012cSYannick Fertre 	DRM_DEBUG_DRIVER("\n");
805b759012cSYannick Fertre 
806b759012cSYannick Fertre 	drm_crtc_vblank_off(crtc);
807b759012cSYannick Fertre 
808c4f218d4SYannick Fertre 	/* Disable all layers */
809c4f218d4SYannick Fertre 	for (layer_index = 0; layer_index < ldev->caps.nb_layers; layer_index++)
810c4f218d4SYannick Fertre 		regmap_write_bits(ldev->regmap, LTDC_L1CR + layer_index * LAY_OFS,
811c4f218d4SYannick Fertre 				  LXCR_CLUTEN | LXCR_LEN, 0);
812c4f218d4SYannick Fertre 
813b759012cSYannick Fertre 	/* disable IRQ */
8147d008eecSYannick Fertre 	regmap_clear_bits(ldev->regmap, LTDC_IER, IER_FUWIE | IER_FUEIE | IER_RRIE | IER_TERRIE);
815b759012cSYannick Fertre 
816b759012cSYannick Fertre 	/* immediately commit disable of layers before switching off LTDC */
817a55d08e0SYannick Fertre 	if (!ldev->caps.plane_reg_shadow)
818734c2645SYannick Fertre 		regmap_set_bits(ldev->regmap, LTDC_SRCR, SRCR_IMR);
81935ab6cfbSYannick Fertré 
82035ab6cfbSYannick Fertré 	pm_runtime_put_sync(ddev->dev);
8217d008eecSYannick Fertre 
8227d008eecSYannick Fertre 	/*  clear interrupt error counters */
8237d008eecSYannick Fertre 	mutex_lock(&ldev->err_lock);
8247d008eecSYannick Fertre 	ldev->transfer_err = 0;
8257d008eecSYannick Fertre 	ldev->fifo_err = 0;
8267d008eecSYannick Fertre 	ldev->fifo_warn = 0;
8277d008eecSYannick Fertre 	mutex_unlock(&ldev->err_lock);
828b759012cSYannick Fertre }
829b759012cSYannick Fertre 
8300cefff96SPhilippe CORNU #define CLK_TOLERANCE_HZ 50
8310cefff96SPhilippe CORNU 
8320cefff96SPhilippe CORNU static enum drm_mode_status
ltdc_crtc_mode_valid(struct drm_crtc * crtc,const struct drm_display_mode * mode)8330cefff96SPhilippe CORNU ltdc_crtc_mode_valid(struct drm_crtc *crtc,
8340cefff96SPhilippe CORNU 		     const struct drm_display_mode *mode)
8350cefff96SPhilippe CORNU {
8360cefff96SPhilippe CORNU 	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
8370cefff96SPhilippe CORNU 	int target = mode->clock * 1000;
8380cefff96SPhilippe CORNU 	int target_min = target - CLK_TOLERANCE_HZ;
8390cefff96SPhilippe CORNU 	int target_max = target + CLK_TOLERANCE_HZ;
8400cefff96SPhilippe CORNU 	int result;
8410cefff96SPhilippe CORNU 
8427868e507SYannick Fertre 	result = clk_round_rate(ldev->pixel_clk, target);
8437868e507SYannick Fertre 
8447868e507SYannick Fertre 	DRM_DEBUG_DRIVER("clk rate target %d, available %d\n", target, result);
8457868e507SYannick Fertre 
8467868e507SYannick Fertre 	/* Filter modes according to the max frequency supported by the pads */
8477868e507SYannick Fertre 	if (result > ldev->caps.pad_max_freq_hz)
8487868e507SYannick Fertre 		return MODE_CLOCK_HIGH;
8497868e507SYannick Fertre 
8500cefff96SPhilippe CORNU 	/*
8510cefff96SPhilippe CORNU 	 * Accept all "preferred" modes:
8520cefff96SPhilippe CORNU 	 * - this is important for panels because panel clock tolerances are
8530cefff96SPhilippe CORNU 	 *   bigger than hdmi ones and there is no reason to not accept them
8540cefff96SPhilippe CORNU 	 *   (the fps may vary a little but it is not a problem).
8550cefff96SPhilippe CORNU 	 * - the hdmi preferred mode will be accepted too, but userland will
8560cefff96SPhilippe CORNU 	 *   be able to use others hdmi "valid" modes if necessary.
8570cefff96SPhilippe CORNU 	 */
8580cefff96SPhilippe CORNU 	if (mode->type & DRM_MODE_TYPE_PREFERRED)
8590cefff96SPhilippe CORNU 		return MODE_OK;
8600cefff96SPhilippe CORNU 
8610cefff96SPhilippe CORNU 	/*
8620cefff96SPhilippe CORNU 	 * Filter modes according to the clock value, particularly useful for
8630cefff96SPhilippe CORNU 	 * hdmi modes that require precise pixel clocks.
8640cefff96SPhilippe CORNU 	 */
8650cefff96SPhilippe CORNU 	if (result < target_min || result > target_max)
8660cefff96SPhilippe CORNU 		return MODE_CLOCK_RANGE;
8670cefff96SPhilippe CORNU 
8680cefff96SPhilippe CORNU 	return MODE_OK;
8690cefff96SPhilippe CORNU }
8700cefff96SPhilippe CORNU 
ltdc_crtc_mode_fixup(struct drm_crtc * crtc,const struct drm_display_mode * mode,struct drm_display_mode * adjusted_mode)8711a32a938SPhilippe CORNU static bool ltdc_crtc_mode_fixup(struct drm_crtc *crtc,
8721a32a938SPhilippe CORNU 				 const struct drm_display_mode *mode,
8731a32a938SPhilippe CORNU 				 struct drm_display_mode *adjusted_mode)
8741a32a938SPhilippe CORNU {
8751a32a938SPhilippe CORNU 	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
8761a32a938SPhilippe CORNU 	int rate = mode->clock * 1000;
87735ab6cfbSYannick Fertré 
8781a32a938SPhilippe CORNU 	if (clk_set_rate(ldev->pixel_clk, rate) < 0) {
8791a32a938SPhilippe CORNU 		DRM_ERROR("Cannot set rate (%dHz) for pixel clk\n", rate);
8801a32a938SPhilippe CORNU 		return false;
8811a32a938SPhilippe CORNU 	}
8821a32a938SPhilippe CORNU 
8831a32a938SPhilippe CORNU 	adjusted_mode->clock = clk_get_rate(ldev->pixel_clk) / 1000;
8841a32a938SPhilippe CORNU 
885fd6905fcSYannick Fertré 	DRM_DEBUG_DRIVER("requested clock %dkHz, adjusted clock %dkHz\n",
886fd6905fcSYannick Fertré 			 mode->clock, adjusted_mode->clock);
887fd6905fcSYannick Fertré 
8881a32a938SPhilippe CORNU 	return true;
8891a32a938SPhilippe CORNU }
8901a32a938SPhilippe CORNU 
ltdc_crtc_mode_set_nofb(struct drm_crtc * crtc)891b759012cSYannick Fertre static void ltdc_crtc_mode_set_nofb(struct drm_crtc *crtc)
892b759012cSYannick Fertre {
893b759012cSYannick Fertre 	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
89435ab6cfbSYannick Fertré 	struct drm_device *ddev = crtc->dev;
89599e36044SMarek Vasut 	struct drm_connector_list_iter iter;
89699e36044SMarek Vasut 	struct drm_connector *connector = NULL;
8972e6c86beSXiaomeng Tong 	struct drm_encoder *encoder = NULL, *en_iter;
8982e6c86beSXiaomeng Tong 	struct drm_bridge *bridge = NULL, *br_iter;
899b759012cSYannick Fertre 	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
900b759012cSYannick Fertre 	u32 hsync, vsync, accum_hbp, accum_vbp, accum_act_w, accum_act_h;
901b759012cSYannick Fertre 	u32 total_width, total_height;
902fb998edfSYannick Fertre 	u32 bus_formats = MEDIA_BUS_FMT_RGB888_1X24;
90399e36044SMarek Vasut 	u32 bus_flags = 0;
904b759012cSYannick Fertre 	u32 val;
90535ab6cfbSYannick Fertré 	int ret;
90635ab6cfbSYannick Fertré 
90799e36044SMarek Vasut 	/* get encoder from crtc */
9082e6c86beSXiaomeng Tong 	drm_for_each_encoder(en_iter, ddev)
9092e6c86beSXiaomeng Tong 		if (en_iter->crtc == crtc) {
9102e6c86beSXiaomeng Tong 			encoder = en_iter;
91199e36044SMarek Vasut 			break;
9122e6c86beSXiaomeng Tong 		}
91399e36044SMarek Vasut 
91499e36044SMarek Vasut 	if (encoder) {
91599e36044SMarek Vasut 		/* get bridge from encoder */
9162e6c86beSXiaomeng Tong 		list_for_each_entry(br_iter, &encoder->bridge_chain, chain_node)
9172e6c86beSXiaomeng Tong 			if (br_iter->encoder == encoder) {
9182e6c86beSXiaomeng Tong 				bridge = br_iter;
91999e36044SMarek Vasut 				break;
9202e6c86beSXiaomeng Tong 			}
92199e36044SMarek Vasut 
92299e36044SMarek Vasut 		/* Get the connector from encoder */
92399e36044SMarek Vasut 		drm_connector_list_iter_begin(ddev, &iter);
92499e36044SMarek Vasut 		drm_for_each_connector_iter(connector, &iter)
92599e36044SMarek Vasut 			if (connector->encoder == encoder)
92699e36044SMarek Vasut 				break;
92799e36044SMarek Vasut 		drm_connector_list_iter_end(&iter);
92899e36044SMarek Vasut 	}
92999e36044SMarek Vasut 
93044b4e728SYannick Fertre 	if (bridge && bridge->timings) {
93199e36044SMarek Vasut 		bus_flags = bridge->timings->input_bus_flags;
93244b4e728SYannick Fertre 	} else if (connector) {
93399e36044SMarek Vasut 		bus_flags = connector->display_info.bus_flags;
934fb998edfSYannick Fertre 		if (connector->display_info.num_bus_formats)
935fb998edfSYannick Fertre 			bus_formats = connector->display_info.bus_formats[0];
936fb998edfSYannick Fertre 	}
93799e36044SMarek Vasut 
93835ab6cfbSYannick Fertré 	if (!pm_runtime_active(ddev->dev)) {
93935ab6cfbSYannick Fertré 		ret = pm_runtime_get_sync(ddev->dev);
94035ab6cfbSYannick Fertré 		if (ret) {
94135ab6cfbSYannick Fertré 			DRM_ERROR("Failed to set mode, cannot get sync\n");
94235ab6cfbSYannick Fertré 			return;
94335ab6cfbSYannick Fertré 		}
94435ab6cfbSYannick Fertré 	}
945b759012cSYannick Fertre 
946b759012cSYannick Fertre 	DRM_DEBUG_DRIVER("CRTC:%d mode:%s\n", crtc->base.id, mode->name);
947e99168f9SMarek Vasut 	DRM_DEBUG_DRIVER("Video mode: %dx%d", mode->hdisplay, mode->vdisplay);
948b759012cSYannick Fertre 	DRM_DEBUG_DRIVER(" hfp %d hbp %d hsl %d vfp %d vbp %d vsl %d\n",
949e99168f9SMarek Vasut 			 mode->hsync_start - mode->hdisplay,
950e99168f9SMarek Vasut 			 mode->htotal - mode->hsync_end,
951e99168f9SMarek Vasut 			 mode->hsync_end - mode->hsync_start,
952e99168f9SMarek Vasut 			 mode->vsync_start - mode->vdisplay,
953e99168f9SMarek Vasut 			 mode->vtotal - mode->vsync_end,
954e99168f9SMarek Vasut 			 mode->vsync_end - mode->vsync_start);
955b759012cSYannick Fertre 
956b759012cSYannick Fertre 	/* Convert video timings to ltdc timings */
957e99168f9SMarek Vasut 	hsync = mode->hsync_end - mode->hsync_start - 1;
958e99168f9SMarek Vasut 	vsync = mode->vsync_end - mode->vsync_start - 1;
959e99168f9SMarek Vasut 	accum_hbp = mode->htotal - mode->hsync_start - 1;
960e99168f9SMarek Vasut 	accum_vbp = mode->vtotal - mode->vsync_start - 1;
961e99168f9SMarek Vasut 	accum_act_w = accum_hbp + mode->hdisplay;
962e99168f9SMarek Vasut 	accum_act_h = accum_vbp + mode->vdisplay;
963e99168f9SMarek Vasut 	total_width = mode->htotal - 1;
964e99168f9SMarek Vasut 	total_height = mode->vtotal - 1;
965b759012cSYannick Fertre 
966444d0db5SPhilippe CORNU 	/* Configures the HS, VS, DE and PC polarities. Default Active Low */
967444d0db5SPhilippe CORNU 	val = 0;
968b759012cSYannick Fertre 
969e99168f9SMarek Vasut 	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
970444d0db5SPhilippe CORNU 		val |= GCR_HSPOL;
971b759012cSYannick Fertre 
972e99168f9SMarek Vasut 	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
973444d0db5SPhilippe CORNU 		val |= GCR_VSPOL;
974b759012cSYannick Fertre 
97599e36044SMarek Vasut 	if (bus_flags & DRM_BUS_FLAG_DE_LOW)
976444d0db5SPhilippe CORNU 		val |= GCR_DEPOL;
977b759012cSYannick Fertre 
97899e36044SMarek Vasut 	if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
979444d0db5SPhilippe CORNU 		val |= GCR_PCPOL;
980b759012cSYannick Fertre 
981734c2645SYannick Fertre 	regmap_update_bits(ldev->regmap, LTDC_GCR,
982b759012cSYannick Fertre 			   GCR_HSPOL | GCR_VSPOL | GCR_DEPOL | GCR_PCPOL, val);
983b759012cSYannick Fertre 
984b759012cSYannick Fertre 	/* Set Synchronization size */
985b759012cSYannick Fertre 	val = (hsync << 16) | vsync;
986734c2645SYannick Fertre 	regmap_update_bits(ldev->regmap, LTDC_SSCR, SSCR_VSH | SSCR_HSW, val);
987b759012cSYannick Fertre 
988b759012cSYannick Fertre 	/* Set Accumulated Back porch */
989b759012cSYannick Fertre 	val = (accum_hbp << 16) | accum_vbp;
990734c2645SYannick Fertre 	regmap_update_bits(ldev->regmap, LTDC_BPCR, BPCR_AVBP | BPCR_AHBP, val);
991b759012cSYannick Fertre 
992b759012cSYannick Fertre 	/* Set Accumulated Active Width */
993b759012cSYannick Fertre 	val = (accum_act_w << 16) | accum_act_h;
994734c2645SYannick Fertre 	regmap_update_bits(ldev->regmap, LTDC_AWCR, AWCR_AAW | AWCR_AAH, val);
995b759012cSYannick Fertre 
996b759012cSYannick Fertre 	/* Set total width & height */
997b759012cSYannick Fertre 	val = (total_width << 16) | total_height;
998734c2645SYannick Fertre 	regmap_update_bits(ldev->regmap, LTDC_TWCR, TWCR_TOTALH | TWCR_TOTALW, val);
999b759012cSYannick Fertre 
1000734c2645SYannick Fertre 	regmap_write(ldev->regmap, LTDC_LIPCR, (accum_act_h + 1));
1001fb998edfSYannick Fertre 
1002fb998edfSYannick Fertre 	/* Configure the output format (hw version dependent) */
1003fb998edfSYannick Fertre 	if (ldev->caps.ycbcr_output) {
1004fb998edfSYannick Fertre 		/* Input video dynamic_range & colorimetry */
1005fb998edfSYannick Fertre 		int vic = drm_match_cea_mode(mode);
1006fb998edfSYannick Fertre 		u32 val;
1007fb998edfSYannick Fertre 
1008fb998edfSYannick Fertre 		if (vic == 6 || vic == 7 || vic == 21 || vic == 22 ||
1009fb998edfSYannick Fertre 		    vic == 2 || vic == 3 || vic == 17 || vic == 18)
1010fb998edfSYannick Fertre 			/* ITU-R BT.601 */
1011fb998edfSYannick Fertre 			val = 0;
1012fb998edfSYannick Fertre 		else
1013fb998edfSYannick Fertre 			/* ITU-R BT.709 */
1014fb998edfSYannick Fertre 			val = EDCR_OCYSEL;
1015fb998edfSYannick Fertre 
1016fb998edfSYannick Fertre 		switch (bus_formats) {
1017fb998edfSYannick Fertre 		case MEDIA_BUS_FMT_YUYV8_1X16:
1018fb998edfSYannick Fertre 			/* enable ycbcr output converter */
1019fb998edfSYannick Fertre 			regmap_write(ldev->regmap, LTDC_EDCR, EDCR_OCYEN | val);
1020fb998edfSYannick Fertre 			break;
1021fb998edfSYannick Fertre 		case MEDIA_BUS_FMT_YVYU8_1X16:
1022fb998edfSYannick Fertre 			/* enable ycbcr output converter & invert chrominance order */
1023fb998edfSYannick Fertre 			regmap_write(ldev->regmap, LTDC_EDCR, EDCR_OCYEN | EDCR_OCYCO | val);
1024fb998edfSYannick Fertre 			break;
1025fb998edfSYannick Fertre 		default:
1026fb998edfSYannick Fertre 			/* disable ycbcr output converter */
1027fb998edfSYannick Fertre 			regmap_write(ldev->regmap, LTDC_EDCR, 0);
1028fb998edfSYannick Fertre 			break;
1029fb998edfSYannick Fertre 		}
1030fb998edfSYannick Fertre 	}
1031b759012cSYannick Fertre }
1032b759012cSYannick Fertre 
ltdc_crtc_atomic_flush(struct drm_crtc * crtc,struct drm_atomic_state * state)1033b759012cSYannick Fertre static void ltdc_crtc_atomic_flush(struct drm_crtc *crtc,
1034f6ebe9f9SMaxime Ripard 				   struct drm_atomic_state *state)
1035b759012cSYannick Fertre {
1036b759012cSYannick Fertre 	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
103735ab6cfbSYannick Fertré 	struct drm_device *ddev = crtc->dev;
1038b759012cSYannick Fertre 	struct drm_pending_vblank_event *event = crtc->state->event;
1039b759012cSYannick Fertre 
1040b759012cSYannick Fertre 	DRM_DEBUG_ATOMIC("\n");
1041b759012cSYannick Fertre 
1042b706a25eSPhilippe CORNU 	ltdc_crtc_update_clut(crtc);
1043b706a25eSPhilippe CORNU 
1044b759012cSYannick Fertre 	/* Commit shadow registers = update planes at next vblank */
1045a55d08e0SYannick Fertre 	if (!ldev->caps.plane_reg_shadow)
1046734c2645SYannick Fertre 		regmap_set_bits(ldev->regmap, LTDC_SRCR, SRCR_VBR);
1047b759012cSYannick Fertre 
1048b759012cSYannick Fertre 	if (event) {
1049b759012cSYannick Fertre 		crtc->state->event = NULL;
1050b759012cSYannick Fertre 
105135ab6cfbSYannick Fertré 		spin_lock_irq(&ddev->event_lock);
1052b759012cSYannick Fertre 		if (drm_crtc_vblank_get(crtc) == 0)
1053b759012cSYannick Fertre 			drm_crtc_arm_vblank_event(crtc, event);
1054b759012cSYannick Fertre 		else
1055b759012cSYannick Fertre 			drm_crtc_send_vblank_event(crtc, event);
105635ab6cfbSYannick Fertré 		spin_unlock_irq(&ddev->event_lock);
1057b759012cSYannick Fertre 	}
1058b759012cSYannick Fertre }
1059b759012cSYannick 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)1060b70fbfc7SThomas Zimmermann static bool ltdc_crtc_get_scanout_position(struct drm_crtc *crtc,
1061b70fbfc7SThomas Zimmermann 					   bool in_vblank_irq,
1062b70fbfc7SThomas Zimmermann 					   int *vpos, int *hpos,
106353273b52SBenjamin Gaignard 					   ktime_t *stime, ktime_t *etime,
106453273b52SBenjamin Gaignard 					   const struct drm_display_mode *mode)
106553273b52SBenjamin Gaignard {
1066b70fbfc7SThomas Zimmermann 	struct drm_device *ddev = crtc->dev;
106753273b52SBenjamin Gaignard 	struct ltdc_device *ldev = ddev->dev_private;
106853273b52SBenjamin Gaignard 	int line, vactive_start, vactive_end, vtotal;
106953273b52SBenjamin Gaignard 
107053273b52SBenjamin Gaignard 	if (stime)
107153273b52SBenjamin Gaignard 		*stime = ktime_get();
107253273b52SBenjamin Gaignard 
107353273b52SBenjamin Gaignard 	/* The active area starts after vsync + front porch and ends
107453273b52SBenjamin Gaignard 	 * at vsync + front porc + display size.
107553273b52SBenjamin Gaignard 	 * The total height also include back porch.
107653273b52SBenjamin Gaignard 	 * We have 3 possible cases to handle:
107753273b52SBenjamin Gaignard 	 * - line < vactive_start: vpos = line - vactive_start and will be
107853273b52SBenjamin Gaignard 	 * negative
107953273b52SBenjamin Gaignard 	 * - vactive_start < line < vactive_end: vpos = line - vactive_start
108053273b52SBenjamin Gaignard 	 * and will be positive
108153273b52SBenjamin Gaignard 	 * - line > vactive_end: vpos = line - vtotal - vactive_start
108253273b52SBenjamin Gaignard 	 * and will negative
108353273b52SBenjamin Gaignard 	 *
108453273b52SBenjamin Gaignard 	 * Computation for the two first cases are identical so we can
108553273b52SBenjamin Gaignard 	 * simplify the code and only test if line > vactive_end
108653273b52SBenjamin Gaignard 	 */
108735ab6cfbSYannick Fertré 	if (pm_runtime_active(ddev->dev)) {
1088734c2645SYannick Fertre 		regmap_read(ldev->regmap, LTDC_CPSR, &line);
1089734c2645SYannick Fertre 		line &= CPSR_CYPOS;
1090734c2645SYannick Fertre 		regmap_read(ldev->regmap, LTDC_BPCR, &vactive_start);
1091734c2645SYannick Fertre 		vactive_start &= BPCR_AVBP;
1092734c2645SYannick Fertre 		regmap_read(ldev->regmap, LTDC_AWCR, &vactive_end);
1093734c2645SYannick Fertre 		vactive_end &= AWCR_AAH;
1094734c2645SYannick Fertre 		regmap_read(ldev->regmap, LTDC_TWCR, &vtotal);
1095734c2645SYannick Fertre 		vtotal &= TWCR_TOTALH;
109653273b52SBenjamin Gaignard 
109753273b52SBenjamin Gaignard 		if (line > vactive_end)
109853273b52SBenjamin Gaignard 			*vpos = line - vtotal - vactive_start;
109953273b52SBenjamin Gaignard 		else
110053273b52SBenjamin Gaignard 			*vpos = line - vactive_start;
110135ab6cfbSYannick Fertré 	} else {
110235ab6cfbSYannick Fertré 		*vpos = 0;
110335ab6cfbSYannick Fertré 	}
110453273b52SBenjamin Gaignard 
110553273b52SBenjamin Gaignard 	*hpos = 0;
110653273b52SBenjamin Gaignard 
110753273b52SBenjamin Gaignard 	if (etime)
110853273b52SBenjamin Gaignard 		*etime = ktime_get();
110953273b52SBenjamin Gaignard 
111053273b52SBenjamin Gaignard 	return true;
111153273b52SBenjamin Gaignard }
111253273b52SBenjamin Gaignard 
1113b70fbfc7SThomas Zimmermann static const struct drm_crtc_helper_funcs ltdc_crtc_helper_funcs = {
1114b70fbfc7SThomas Zimmermann 	.mode_valid = ltdc_crtc_mode_valid,
1115b70fbfc7SThomas Zimmermann 	.mode_fixup = ltdc_crtc_mode_fixup,
1116b70fbfc7SThomas Zimmermann 	.mode_set_nofb = ltdc_crtc_mode_set_nofb,
1117b70fbfc7SThomas Zimmermann 	.atomic_flush = ltdc_crtc_atomic_flush,
1118b70fbfc7SThomas Zimmermann 	.atomic_enable = ltdc_crtc_atomic_enable,
1119b70fbfc7SThomas Zimmermann 	.atomic_disable = ltdc_crtc_atomic_disable,
1120b70fbfc7SThomas Zimmermann 	.get_scanout_position = ltdc_crtc_get_scanout_position,
1121b70fbfc7SThomas Zimmermann };
1122b70fbfc7SThomas Zimmermann 
ltdc_crtc_enable_vblank(struct drm_crtc * crtc)1123b70fbfc7SThomas Zimmermann static int ltdc_crtc_enable_vblank(struct drm_crtc *crtc)
1124b70fbfc7SThomas Zimmermann {
1125b70fbfc7SThomas Zimmermann 	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
1126b70fbfc7SThomas Zimmermann 	struct drm_crtc_state *state = crtc->state;
1127b70fbfc7SThomas Zimmermann 
1128b70fbfc7SThomas Zimmermann 	DRM_DEBUG_DRIVER("\n");
1129b70fbfc7SThomas Zimmermann 
1130b70fbfc7SThomas Zimmermann 	if (state->enable)
1131734c2645SYannick Fertre 		regmap_set_bits(ldev->regmap, LTDC_IER, IER_LIE);
1132b70fbfc7SThomas Zimmermann 	else
1133b70fbfc7SThomas Zimmermann 		return -EPERM;
1134b70fbfc7SThomas Zimmermann 
1135b70fbfc7SThomas Zimmermann 	return 0;
1136b70fbfc7SThomas Zimmermann }
1137b70fbfc7SThomas Zimmermann 
ltdc_crtc_disable_vblank(struct drm_crtc * crtc)1138b70fbfc7SThomas Zimmermann static void ltdc_crtc_disable_vblank(struct drm_crtc *crtc)
1139b70fbfc7SThomas Zimmermann {
1140b70fbfc7SThomas Zimmermann 	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
1141b70fbfc7SThomas Zimmermann 
1142b70fbfc7SThomas Zimmermann 	DRM_DEBUG_DRIVER("\n");
1143734c2645SYannick Fertre 	regmap_clear_bits(ldev->regmap, LTDC_IER, IER_LIE);
1144b70fbfc7SThomas Zimmermann }
1145b70fbfc7SThomas Zimmermann 
ltdc_crtc_set_crc_source(struct drm_crtc * crtc,const char * source)114679b44684SRaphael Gallais-Pou static int ltdc_crtc_set_crc_source(struct drm_crtc *crtc, const char *source)
114779b44684SRaphael Gallais-Pou {
1148898a9e3fSRaphael Gallais-Pou 	struct ltdc_device *ldev;
114979b44684SRaphael Gallais-Pou 	int ret;
115079b44684SRaphael Gallais-Pou 
115179b44684SRaphael Gallais-Pou 	DRM_DEBUG_DRIVER("\n");
115279b44684SRaphael Gallais-Pou 
115379b44684SRaphael Gallais-Pou 	if (!crtc)
115479b44684SRaphael Gallais-Pou 		return -ENODEV;
115579b44684SRaphael Gallais-Pou 
1156898a9e3fSRaphael Gallais-Pou 	ldev = crtc_to_ltdc(crtc);
1157898a9e3fSRaphael Gallais-Pou 
115879b44684SRaphael Gallais-Pou 	if (source && strcmp(source, "auto") == 0) {
115979b44684SRaphael Gallais-Pou 		ldev->crc_active = true;
116079b44684SRaphael Gallais-Pou 		ret = regmap_set_bits(ldev->regmap, LTDC_GCR, GCR_CRCEN);
116179b44684SRaphael Gallais-Pou 	} else if (!source) {
116279b44684SRaphael Gallais-Pou 		ldev->crc_active = false;
116379b44684SRaphael Gallais-Pou 		ret = regmap_clear_bits(ldev->regmap, LTDC_GCR, GCR_CRCEN);
116479b44684SRaphael Gallais-Pou 	} else {
116579b44684SRaphael Gallais-Pou 		ret = -EINVAL;
116679b44684SRaphael Gallais-Pou 	}
116779b44684SRaphael Gallais-Pou 
116879b44684SRaphael Gallais-Pou 	ldev->crc_skip_count = 0;
116979b44684SRaphael Gallais-Pou 	return ret;
117079b44684SRaphael Gallais-Pou }
117179b44684SRaphael Gallais-Pou 
ltdc_crtc_verify_crc_source(struct drm_crtc * crtc,const char * source,size_t * values_cnt)117279b44684SRaphael Gallais-Pou static int ltdc_crtc_verify_crc_source(struct drm_crtc *crtc,
117379b44684SRaphael Gallais-Pou 				       const char *source, size_t *values_cnt)
117479b44684SRaphael Gallais-Pou {
117579b44684SRaphael Gallais-Pou 	DRM_DEBUG_DRIVER("\n");
117679b44684SRaphael Gallais-Pou 
117779b44684SRaphael Gallais-Pou 	if (!crtc)
117879b44684SRaphael Gallais-Pou 		return -ENODEV;
117979b44684SRaphael Gallais-Pou 
118079b44684SRaphael Gallais-Pou 	if (source && strcmp(source, "auto") != 0) {
118179b44684SRaphael Gallais-Pou 		DRM_DEBUG_DRIVER("Unknown CRC source %s for %s\n",
118279b44684SRaphael Gallais-Pou 				 source, crtc->name);
118379b44684SRaphael Gallais-Pou 		return -EINVAL;
118479b44684SRaphael Gallais-Pou 	}
118579b44684SRaphael Gallais-Pou 
118679b44684SRaphael Gallais-Pou 	*values_cnt = 1;
118779b44684SRaphael Gallais-Pou 	return 0;
118879b44684SRaphael Gallais-Pou }
118979b44684SRaphael Gallais-Pou 
ltdc_crtc_atomic_print_state(struct drm_printer * p,const struct drm_crtc_state * state)11907d008eecSYannick Fertre static void ltdc_crtc_atomic_print_state(struct drm_printer *p,
11917d008eecSYannick Fertre 					 const struct drm_crtc_state *state)
11927d008eecSYannick Fertre {
11937d008eecSYannick Fertre 	struct drm_crtc *crtc = state->crtc;
11947d008eecSYannick Fertre 	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
11957d008eecSYannick Fertre 
11967d008eecSYannick Fertre 	drm_printf(p, "\ttransfer_error=%d\n", ldev->transfer_err);
11977d008eecSYannick Fertre 	drm_printf(p, "\tfifo_underrun_error=%d\n", ldev->fifo_err);
11987d008eecSYannick Fertre 	drm_printf(p, "\tfifo_underrun_warning=%d\n", ldev->fifo_warn);
11997d008eecSYannick Fertre 	drm_printf(p, "\tfifo_underrun_threshold=%d\n", ldev->fifo_threshold);
12007d008eecSYannick Fertre }
12017d008eecSYannick Fertre 
1202c994796fSPhilippe CORNU static const struct drm_crtc_funcs ltdc_crtc_funcs = {
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 	.set_config = drm_atomic_helper_set_config,
121679b44684SRaphael Gallais-Pou 	.page_flip = drm_atomic_helper_page_flip,
121779b44684SRaphael Gallais-Pou 	.reset = drm_atomic_helper_crtc_reset,
121879b44684SRaphael Gallais-Pou 	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
121979b44684SRaphael Gallais-Pou 	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
122079b44684SRaphael Gallais-Pou 	.enable_vblank = ltdc_crtc_enable_vblank,
122179b44684SRaphael Gallais-Pou 	.disable_vblank = ltdc_crtc_disable_vblank,
122279b44684SRaphael Gallais-Pou 	.get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,
122379b44684SRaphael Gallais-Pou 	.set_crc_source = ltdc_crtc_set_crc_source,
122479b44684SRaphael Gallais-Pou 	.verify_crc_source = ltdc_crtc_verify_crc_source,
12257d008eecSYannick Fertre 	.atomic_print_state = ltdc_crtc_atomic_print_state,
122679b44684SRaphael Gallais-Pou };
122779b44684SRaphael Gallais-Pou 
1228b759012cSYannick Fertre /*
1229b759012cSYannick Fertre  * DRM_PLANE
1230b759012cSYannick Fertre  */
1231b759012cSYannick Fertre 
ltdc_plane_atomic_check(struct drm_plane * plane,struct drm_atomic_state * state)1232b759012cSYannick Fertre static int ltdc_plane_atomic_check(struct drm_plane *plane,
12337c11b99aSMaxime Ripard 				   struct drm_atomic_state *state)
1234b759012cSYannick Fertre {
12357c11b99aSMaxime Ripard 	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
12367c11b99aSMaxime Ripard 										 plane);
1237ba5c1649SMaxime Ripard 	struct drm_framebuffer *fb = new_plane_state->fb;
1238a236a669SYueHaibing 	u32 src_w, src_h;
1239b759012cSYannick Fertre 
1240b759012cSYannick Fertre 	DRM_DEBUG_DRIVER("\n");
1241b759012cSYannick Fertre 
1242b759012cSYannick Fertre 	if (!fb)
1243b759012cSYannick Fertre 		return 0;
1244b759012cSYannick Fertre 
1245b759012cSYannick Fertre 	/* convert src_ from 16:16 format */
1246ba5c1649SMaxime Ripard 	src_w = new_plane_state->src_w >> 16;
1247ba5c1649SMaxime Ripard 	src_h = new_plane_state->src_h >> 16;
1248b759012cSYannick Fertre 
1249b759012cSYannick Fertre 	/* Reject scaling */
1250ba5c1649SMaxime Ripard 	if (src_w != new_plane_state->crtc_w || src_h != new_plane_state->crtc_h) {
1251fb37cfa0SYannick Fertre 		DRM_DEBUG_DRIVER("Scaling is not supported");
1252fb37cfa0SYannick Fertre 
1253b759012cSYannick Fertre 		return -EINVAL;
1254b759012cSYannick Fertre 	}
1255b759012cSYannick Fertre 
1256b759012cSYannick Fertre 	return 0;
1257b759012cSYannick Fertre }
1258b759012cSYannick Fertre 
ltdc_plane_atomic_update(struct drm_plane * plane,struct drm_atomic_state * state)1259b759012cSYannick Fertre static void ltdc_plane_atomic_update(struct drm_plane *plane,
1260977697e2SMaxime Ripard 				     struct drm_atomic_state *state)
1261b759012cSYannick Fertre {
1262b759012cSYannick Fertre 	struct ltdc_device *ldev = plane_to_ltdc(plane);
126337418bf1SMaxime Ripard 	struct drm_plane_state *newstate = drm_atomic_get_new_plane_state(state,
126437418bf1SMaxime Ripard 									  plane);
126541016fe1SMaxime Ripard 	struct drm_framebuffer *fb = newstate->fb;
1266b759012cSYannick Fertre 	u32 lofs = plane->index * LAY_OFS;
126741016fe1SMaxime Ripard 	u32 x0 = newstate->crtc_x;
126841016fe1SMaxime Ripard 	u32 x1 = newstate->crtc_x + newstate->crtc_w - 1;
126941016fe1SMaxime Ripard 	u32 y0 = newstate->crtc_y;
127041016fe1SMaxime Ripard 	u32 y1 = newstate->crtc_y + newstate->crtc_h - 1;
1271b759012cSYannick Fertre 	u32 src_x, src_y, src_w, src_h;
1272c6193dc5SYannick Fertre 	u32 val, pitch_in_bytes, line_length, line_number, ahbp, avbp, bpcr;
1273c6193dc5SYannick Fertre 	u32 paddr, paddr1, paddr2;
1274b759012cSYannick Fertre 	enum ltdc_pix_fmt pf;
1275b759012cSYannick Fertre 
127641016fe1SMaxime Ripard 	if (!newstate->crtc || !fb) {
1277b759012cSYannick Fertre 		DRM_DEBUG_DRIVER("fb or crtc NULL");
1278b759012cSYannick Fertre 		return;
1279b759012cSYannick Fertre 	}
1280b759012cSYannick Fertre 
1281b759012cSYannick Fertre 	/* convert src_ from 16:16 format */
128241016fe1SMaxime Ripard 	src_x = newstate->src_x >> 16;
128341016fe1SMaxime Ripard 	src_y = newstate->src_y >> 16;
128441016fe1SMaxime Ripard 	src_w = newstate->src_w >> 16;
128541016fe1SMaxime Ripard 	src_h = newstate->src_h >> 16;
1286b759012cSYannick Fertre 
12870e21e3b0SPhilippe CORNU 	DRM_DEBUG_DRIVER("plane:%d fb:%d (%dx%d)@(%d,%d) -> (%dx%d)@(%d,%d)\n",
1288b759012cSYannick Fertre 			 plane->base.id, fb->base.id,
1289b759012cSYannick Fertre 			 src_w, src_h, src_x, src_y,
129041016fe1SMaxime Ripard 			 newstate->crtc_w, newstate->crtc_h,
129141016fe1SMaxime Ripard 			 newstate->crtc_x, newstate->crtc_y);
1292b759012cSYannick Fertre 
1293734c2645SYannick Fertre 	regmap_read(ldev->regmap, LTDC_BPCR, &bpcr);
1294734c2645SYannick Fertre 
1295b759012cSYannick Fertre 	ahbp = (bpcr & BPCR_AHBP) >> 16;
1296b759012cSYannick Fertre 	avbp = bpcr & BPCR_AVBP;
1297b759012cSYannick Fertre 
1298b759012cSYannick Fertre 	/* Configures the horizontal start and stop position */
1299b759012cSYannick Fertre 	val = ((x1 + 1 + ahbp) << 16) + (x0 + 1 + ahbp);
1300734c2645SYannick Fertre 	regmap_write_bits(ldev->regmap, LTDC_L1WHPCR + lofs,
1301b759012cSYannick Fertre 			  LXWHPCR_WHSTPOS | LXWHPCR_WHSPPOS, val);
1302b759012cSYannick Fertre 
1303b759012cSYannick Fertre 	/* Configures the vertical start and stop position */
1304b759012cSYannick Fertre 	val = ((y1 + 1 + avbp) << 16) + (y0 + 1 + avbp);
1305734c2645SYannick Fertre 	regmap_write_bits(ldev->regmap, LTDC_L1WVPCR + lofs,
1306b759012cSYannick Fertre 			  LXWVPCR_WVSTPOS | LXWVPCR_WVSPPOS, val);
1307b759012cSYannick Fertre 
1308b759012cSYannick Fertre 	/* Specifies the pixel format */
1309b759012cSYannick Fertre 	pf = to_ltdc_pixelformat(fb->format->format);
1310b759012cSYannick Fertre 	for (val = 0; val < NB_PF; val++)
1311b759012cSYannick Fertre 		if (ldev->caps.pix_fmt_hw[val] == pf)
1312b759012cSYannick Fertre 			break;
1313b759012cSYannick Fertre 
13148f2b5f6dSYannick Fertre 	/* Use the flexible color format feature if necessary and available */
13158f2b5f6dSYannick Fertre 	if (ldev->caps.pix_fmt_flex && val == NB_PF)
13168f2b5f6dSYannick Fertre 		val = ltdc_set_flexible_pixel_format(plane, pf);
13178f2b5f6dSYannick Fertre 
1318b759012cSYannick Fertre 	if (val == NB_PF) {
1319b759012cSYannick Fertre 		DRM_ERROR("Pixel format %.4s not supported\n",
1320b759012cSYannick Fertre 			  (char *)&fb->format->format);
1321b759012cSYannick Fertre 		val = 0;	/* set by default ARGB 32 bits */
1322b759012cSYannick Fertre 	}
1323734c2645SYannick Fertre 	regmap_write_bits(ldev->regmap, LTDC_L1PFCR + lofs, LXPFCR_PF, val);
1324b759012cSYannick Fertre 
1325b759012cSYannick Fertre 	/* Specifies the constant alpha value */
1326c20351adSRaphael Gallais-Pou 	val = newstate->alpha >> 8;
1327734c2645SYannick Fertre 	regmap_write_bits(ldev->regmap, LTDC_L1CACR + lofs, LXCACR_CONSTA, val);
1328b759012cSYannick Fertre 
1329b759012cSYannick Fertre 	/* Specifies the blending factors */
1330b759012cSYannick Fertre 	val = BF1_PAXCA | BF2_1PAXCA;
1331aefa8301SPhilippe CORNU 	if (!fb->format->has_alpha)
1332aefa8301SPhilippe CORNU 		val = BF1_CA | BF2_1CA;
1333aefa8301SPhilippe CORNU 
13349569002aSPhilippe CORNU 	/* Manage hw-specific capabilities */
13359569002aSPhilippe CORNU 	if (ldev->caps.non_alpha_only_l1 &&
13369569002aSPhilippe CORNU 	    plane->type != DRM_PLANE_TYPE_PRIMARY)
13379569002aSPhilippe CORNU 		val = BF1_PAXCA | BF2_1PAXCA;
13389569002aSPhilippe CORNU 
133962467fccSYannick Fertre 	if (ldev->caps.dynamic_zorder) {
134062467fccSYannick Fertre 		val |= (newstate->normalized_zpos << 16);
134162467fccSYannick Fertre 		regmap_write_bits(ldev->regmap, LTDC_L1BFCR + lofs,
134262467fccSYannick Fertre 				  LXBFCR_BF2 | LXBFCR_BF1 | LXBFCR_BOR, val);
134362467fccSYannick Fertre 	} else {
134462467fccSYannick Fertre 		regmap_write_bits(ldev->regmap, LTDC_L1BFCR + lofs,
134562467fccSYannick Fertre 				  LXBFCR_BF2 | LXBFCR_BF1, val);
134662467fccSYannick Fertre 	}
1347b759012cSYannick Fertre 
1348b759012cSYannick Fertre 	/* Sets the FB address */
13496bcfe8eaSDanilo Krummrich 	paddr = (u32)drm_fb_dma_get_gem_addr(fb, newstate, 0);
1350b759012cSYannick Fertre 
1351c6193dc5SYannick Fertre 	if (newstate->rotation & DRM_MODE_REFLECT_X)
1352c6193dc5SYannick Fertre 		paddr += (fb->format->cpp[0] * (x1 - x0 + 1)) - 1;
1353c6193dc5SYannick Fertre 
1354c6193dc5SYannick Fertre 	if (newstate->rotation & DRM_MODE_REFLECT_Y)
1355c6193dc5SYannick Fertre 		paddr += (fb->pitches[0] * (y1 - y0));
1356c6193dc5SYannick Fertre 
1357b759012cSYannick Fertre 	DRM_DEBUG_DRIVER("fb: phys 0x%08x", paddr);
1358734c2645SYannick Fertre 	regmap_write(ldev->regmap, LTDC_L1CFBAR + lofs, paddr);
1359b759012cSYannick Fertre 
1360c6193dc5SYannick Fertre 	/* Configures the color frame buffer pitch in bytes & line length */
1361c6193dc5SYannick Fertre 	line_length = fb->format->cpp[0] *
1362c6193dc5SYannick Fertre 		      (x1 - x0 + 1) + (ldev->caps.bus_width >> 3) - 1;
1363c6193dc5SYannick Fertre 
1364c6193dc5SYannick Fertre 	if (newstate->rotation & DRM_MODE_REFLECT_Y)
1365c6193dc5SYannick Fertre 		/* Compute negative value (signed on 16 bits) for the picth */
1366c6193dc5SYannick Fertre 		pitch_in_bytes = 0x10000 - fb->pitches[0];
1367c6193dc5SYannick Fertre 	else
1368c6193dc5SYannick Fertre 		pitch_in_bytes = fb->pitches[0];
1369c6193dc5SYannick Fertre 
1370c6193dc5SYannick Fertre 	val = (pitch_in_bytes << 16) | line_length;
1371c6193dc5SYannick Fertre 	regmap_write_bits(ldev->regmap, LTDC_L1CFBLR + lofs, LXCFBLR_CFBLL | LXCFBLR_CFBP, val);
1372c6193dc5SYannick Fertre 
1373c6193dc5SYannick Fertre 	/* Configures the frame buffer line number */
1374c6193dc5SYannick Fertre 	line_number = y1 - y0 + 1;
1375c6193dc5SYannick Fertre 	regmap_write_bits(ldev->regmap, LTDC_L1CFBLNR + lofs, LXCFBLNR_CFBLN, line_number);
1376c6193dc5SYannick Fertre 
1377484e72d3SYannick Fertre 	if (ldev->caps.ycbcr_input) {
1378484e72d3SYannick Fertre 		if (fb->format->is_yuv) {
1379484e72d3SYannick Fertre 			switch (fb->format->format) {
1380484e72d3SYannick Fertre 			case DRM_FORMAT_NV12:
1381484e72d3SYannick Fertre 			case DRM_FORMAT_NV21:
1382c6193dc5SYannick Fertre 			/* Configure the auxiliary frame buffer address 0 */
13836bcfe8eaSDanilo Krummrich 			paddr1 = (u32)drm_fb_dma_get_gem_addr(fb, newstate, 1);
1384484e72d3SYannick Fertre 
1385c6193dc5SYannick Fertre 			if (newstate->rotation & DRM_MODE_REFLECT_X)
1386c6193dc5SYannick Fertre 				paddr1 += ((fb->format->cpp[1] * (x1 - x0 + 1)) >> 1) - 1;
1387484e72d3SYannick Fertre 
1388c6193dc5SYannick Fertre 			if (newstate->rotation & DRM_MODE_REFLECT_Y)
1389c6193dc5SYannick Fertre 				paddr1 += (fb->pitches[1] * (y1 - y0 - 1)) >> 1;
1390c6193dc5SYannick Fertre 
1391c6193dc5SYannick Fertre 			regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr1);
1392484e72d3SYannick Fertre 			break;
1393484e72d3SYannick Fertre 			case DRM_FORMAT_YUV420:
1394c6193dc5SYannick Fertre 			/* Configure the auxiliary frame buffer address 0 & 1 */
13956bcfe8eaSDanilo Krummrich 			paddr1 = (u32)drm_fb_dma_get_gem_addr(fb, newstate, 1);
13966bcfe8eaSDanilo Krummrich 			paddr2 = (u32)drm_fb_dma_get_gem_addr(fb, newstate, 2);
1397484e72d3SYannick Fertre 
1398c6193dc5SYannick Fertre 			if (newstate->rotation & DRM_MODE_REFLECT_X) {
1399c6193dc5SYannick Fertre 				paddr1 += ((fb->format->cpp[1] * (x1 - x0 + 1)) >> 1) - 1;
1400c6193dc5SYannick Fertre 				paddr2 += ((fb->format->cpp[2] * (x1 - x0 + 1)) >> 1) - 1;
1401c6193dc5SYannick Fertre 			}
1402484e72d3SYannick Fertre 
1403c6193dc5SYannick Fertre 			if (newstate->rotation & DRM_MODE_REFLECT_Y) {
1404c6193dc5SYannick Fertre 				paddr1 += (fb->pitches[1] * (y1 - y0 - 1)) >> 1;
1405c6193dc5SYannick Fertre 				paddr2 += (fb->pitches[2] * (y1 - y0 - 1)) >> 1;
1406c6193dc5SYannick Fertre 			}
1407484e72d3SYannick Fertre 
1408c6193dc5SYannick Fertre 			regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr1);
1409c6193dc5SYannick Fertre 			regmap_write(ldev->regmap, LTDC_L1AFBA1R + lofs, paddr2);
1410484e72d3SYannick Fertre 			break;
1411484e72d3SYannick Fertre 			case DRM_FORMAT_YVU420:
1412c6193dc5SYannick Fertre 			/* Configure the auxiliary frame buffer address 0 & 1 */
14136bcfe8eaSDanilo Krummrich 			paddr1 = (u32)drm_fb_dma_get_gem_addr(fb, newstate, 2);
14146bcfe8eaSDanilo Krummrich 			paddr2 = (u32)drm_fb_dma_get_gem_addr(fb, newstate, 1);
1415484e72d3SYannick Fertre 
1416c6193dc5SYannick Fertre 			if (newstate->rotation & DRM_MODE_REFLECT_X) {
1417c6193dc5SYannick Fertre 				paddr1 += ((fb->format->cpp[1] * (x1 - x0 + 1)) >> 1) - 1;
1418c6193dc5SYannick Fertre 				paddr2 += ((fb->format->cpp[2] * (x1 - x0 + 1)) >> 1) - 1;
1419c6193dc5SYannick Fertre 			}
1420484e72d3SYannick Fertre 
1421c6193dc5SYannick Fertre 			if (newstate->rotation & DRM_MODE_REFLECT_Y) {
1422c6193dc5SYannick Fertre 				paddr1 += (fb->pitches[1] * (y1 - y0 - 1)) >> 1;
1423c6193dc5SYannick Fertre 				paddr2 += (fb->pitches[2] * (y1 - y0 - 1)) >> 1;
1424c6193dc5SYannick Fertre 			}
1425c6193dc5SYannick Fertre 
1426c6193dc5SYannick Fertre 			regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr1);
1427c6193dc5SYannick Fertre 			regmap_write(ldev->regmap, LTDC_L1AFBA1R + lofs, paddr2);
1428c6193dc5SYannick Fertre 			break;
1429c6193dc5SYannick Fertre 			}
1430c6193dc5SYannick Fertre 
1431c6193dc5SYannick Fertre 			/*
1432c6193dc5SYannick Fertre 			 * Set the length and the number of lines of the auxiliary
1433c6193dc5SYannick Fertre 			 * buffers if the framebuffer contains more than one plane.
1434c6193dc5SYannick Fertre 			 */
1435c6193dc5SYannick Fertre 			if (fb->format->num_planes > 1) {
1436c6193dc5SYannick Fertre 				if (newstate->rotation & DRM_MODE_REFLECT_Y)
1437c6193dc5SYannick Fertre 					/*
1438c6193dc5SYannick Fertre 					 * Compute negative value (signed on 16 bits)
1439c6193dc5SYannick Fertre 					 * for the picth
1440c6193dc5SYannick Fertre 					 */
1441c6193dc5SYannick Fertre 					pitch_in_bytes = 0x10000 - fb->pitches[1];
1442c6193dc5SYannick Fertre 				else
1443c6193dc5SYannick Fertre 					pitch_in_bytes = fb->pitches[1];
1444c6193dc5SYannick Fertre 
1445c6193dc5SYannick Fertre 				line_length = ((fb->format->cpp[1] * (x1 - x0 + 1)) >> 1) +
1446484e72d3SYannick Fertre 					      (ldev->caps.bus_width >> 3) - 1;
1447484e72d3SYannick Fertre 
1448c6193dc5SYannick Fertre 				/* Configure the auxiliary buffer length */
1449c6193dc5SYannick Fertre 				val = (pitch_in_bytes << 16) | line_length;
1450484e72d3SYannick Fertre 				regmap_write(ldev->regmap, LTDC_L1AFBLR + lofs, val);
1451484e72d3SYannick Fertre 
1452c6193dc5SYannick Fertre 				/* Configure the auxiliary frame buffer line number */
1453c6193dc5SYannick Fertre 				val = line_number >> 1;
1454484e72d3SYannick Fertre 				regmap_write(ldev->regmap, LTDC_L1AFBLNR + lofs, val);
1455484e72d3SYannick Fertre 			}
1456484e72d3SYannick Fertre 
1457484e72d3SYannick Fertre 			/* Configure YCbC conversion coefficient */
1458484e72d3SYannick Fertre 			ltdc_set_ycbcr_coeffs(plane);
1459484e72d3SYannick Fertre 
1460484e72d3SYannick Fertre 			/* Configure YCbCr format and enable/disable conversion */
1461484e72d3SYannick Fertre 			ltdc_set_ycbcr_config(plane, fb->format->format);
1462484e72d3SYannick Fertre 		} else {
1463484e72d3SYannick Fertre 			/* disable ycbcr conversion */
1464484e72d3SYannick Fertre 			regmap_write(ldev->regmap, LTDC_L1PCR + lofs, 0);
1465484e72d3SYannick Fertre 		}
1466484e72d3SYannick Fertre 	}
1467484e72d3SYannick Fertre 
1468b759012cSYannick Fertre 	/* Enable layer and CLUT if needed */
1469b759012cSYannick Fertre 	val = fb->format->format == DRM_FORMAT_C8 ? LXCR_CLUTEN : 0;
1470b759012cSYannick Fertre 	val |= LXCR_LEN;
1471c6193dc5SYannick Fertre 
1472c6193dc5SYannick Fertre 	/* Enable horizontal mirroring if requested */
1473c6193dc5SYannick Fertre 	if (newstate->rotation & DRM_MODE_REFLECT_X)
1474c6193dc5SYannick Fertre 		val |= LXCR_HMEN;
1475c6193dc5SYannick Fertre 
1476c6193dc5SYannick Fertre 	regmap_write_bits(ldev->regmap, LTDC_L1CR + lofs, LXCR_LEN | LXCR_CLUTEN | LXCR_HMEN, val);
1477b759012cSYannick Fertre 
1478a55d08e0SYannick Fertre 	/* Commit shadow registers = update plane at next vblank */
1479a55d08e0SYannick Fertre 	if (ldev->caps.plane_reg_shadow)
1480a55d08e0SYannick Fertre 		regmap_write_bits(ldev->regmap, LTDC_L1RCR + lofs,
1481a55d08e0SYannick Fertre 				  LXRCR_IMR | LXRCR_VBR | LXRCR_GRMSK, LXRCR_VBR);
1482a55d08e0SYannick Fertre 
148325bb1a9dSPhilippe CORNU 	ldev->plane_fpsi[plane->index].counter++;
148425bb1a9dSPhilippe CORNU 
1485b759012cSYannick Fertre 	mutex_lock(&ldev->err_lock);
14867d008eecSYannick Fertre 	if (ldev->transfer_err) {
14877d008eecSYannick Fertre 		DRM_WARN("ltdc transfer error: %d\n", ldev->transfer_err);
14887d008eecSYannick Fertre 		ldev->transfer_err = 0;
1489b759012cSYannick Fertre 	}
14907d008eecSYannick Fertre 
14917d008eecSYannick Fertre 	if (ldev->caps.fifo_threshold) {
14927d008eecSYannick Fertre 		if (ldev->fifo_err) {
14937d008eecSYannick Fertre 			DRM_WARN("ltdc fifo underrun: please verify display mode\n");
14947d008eecSYannick Fertre 			ldev->fifo_err = 0;
14957d008eecSYannick Fertre 		}
14967d008eecSYannick Fertre 	} else {
14977d008eecSYannick Fertre 		if (ldev->fifo_warn >= ldev->fifo_threshold) {
14987d008eecSYannick Fertre 			DRM_WARN("ltdc fifo underrun: please verify display mode\n");
14997d008eecSYannick Fertre 			ldev->fifo_warn = 0;
15007d008eecSYannick Fertre 		}
1501b759012cSYannick Fertre 	}
1502b759012cSYannick Fertre 	mutex_unlock(&ldev->err_lock);
1503b759012cSYannick Fertre }
1504b759012cSYannick Fertre 
ltdc_plane_atomic_disable(struct drm_plane * plane,struct drm_atomic_state * state)1505b759012cSYannick Fertre static void ltdc_plane_atomic_disable(struct drm_plane *plane,
1506977697e2SMaxime Ripard 				      struct drm_atomic_state *state)
1507b759012cSYannick Fertre {
1508977697e2SMaxime Ripard 	struct drm_plane_state *oldstate = drm_atomic_get_old_plane_state(state,
1509977697e2SMaxime Ripard 									  plane);
1510b759012cSYannick Fertre 	struct ltdc_device *ldev = plane_to_ltdc(plane);
1511b759012cSYannick Fertre 	u32 lofs = plane->index * LAY_OFS;
1512b759012cSYannick Fertre 
1513c6193dc5SYannick Fertre 	/* Disable layer */
1514c6193dc5SYannick Fertre 	regmap_write_bits(ldev->regmap, LTDC_L1CR + lofs, LXCR_LEN | LXCR_CLUTEN |  LXCR_HMEN, 0);
1515b759012cSYannick Fertre 
1516*fb557a36SYannick Fertre 	/* Reset the layer transparency to hide any related background color */
1517*fb557a36SYannick Fertre 	regmap_write_bits(ldev->regmap, LTDC_L1CACR + lofs, LXCACR_CONSTA, 0x00);
1518*fb557a36SYannick Fertre 
1519a55d08e0SYannick Fertre 	/* Commit shadow registers = update plane at next vblank */
1520a55d08e0SYannick Fertre 	if (ldev->caps.plane_reg_shadow)
1521a55d08e0SYannick Fertre 		regmap_write_bits(ldev->regmap, LTDC_L1RCR + lofs,
1522a55d08e0SYannick Fertre 				  LXRCR_IMR | LXRCR_VBR | LXRCR_GRMSK, LXRCR_VBR);
1523a55d08e0SYannick Fertre 
1524b759012cSYannick Fertre 	DRM_DEBUG_DRIVER("CRTC:%d plane:%d\n",
1525b759012cSYannick Fertre 			 oldstate->crtc->base.id, plane->base.id);
1526b759012cSYannick Fertre }
1527b759012cSYannick Fertre 
ltdc_plane_atomic_print_state(struct drm_printer * p,const struct drm_plane_state * state)152825bb1a9dSPhilippe CORNU static void ltdc_plane_atomic_print_state(struct drm_printer *p,
152925bb1a9dSPhilippe CORNU 					  const struct drm_plane_state *state)
153025bb1a9dSPhilippe CORNU {
153125bb1a9dSPhilippe CORNU 	struct drm_plane *plane = state->plane;
153225bb1a9dSPhilippe CORNU 	struct ltdc_device *ldev = plane_to_ltdc(plane);
153325bb1a9dSPhilippe CORNU 	struct fps_info *fpsi = &ldev->plane_fpsi[plane->index];
153425bb1a9dSPhilippe CORNU 	int ms_since_last;
153525bb1a9dSPhilippe CORNU 	ktime_t now;
153625bb1a9dSPhilippe CORNU 
153725bb1a9dSPhilippe CORNU 	now = ktime_get();
153825bb1a9dSPhilippe CORNU 	ms_since_last = ktime_to_ms(ktime_sub(now, fpsi->last_timestamp));
153925bb1a9dSPhilippe CORNU 
154025bb1a9dSPhilippe CORNU 	drm_printf(p, "\tuser_updates=%dfps\n",
154125bb1a9dSPhilippe CORNU 		   DIV_ROUND_CLOSEST(fpsi->counter * 1000, ms_since_last));
154225bb1a9dSPhilippe CORNU 
154325bb1a9dSPhilippe CORNU 	fpsi->last_timestamp = now;
154425bb1a9dSPhilippe CORNU 	fpsi->counter = 0;
154525bb1a9dSPhilippe CORNU }
154625bb1a9dSPhilippe CORNU 
1547c994796fSPhilippe CORNU static const struct drm_plane_funcs ltdc_plane_funcs = {
1548b759012cSYannick Fertre 	.update_plane = drm_atomic_helper_update_plane,
1549b759012cSYannick Fertre 	.disable_plane = drm_atomic_helper_disable_plane,
1550b759012cSYannick Fertre 	.reset = drm_atomic_helper_plane_reset,
1551b759012cSYannick Fertre 	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
1552b759012cSYannick Fertre 	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
155325bb1a9dSPhilippe CORNU 	.atomic_print_state = ltdc_plane_atomic_print_state,
1554b759012cSYannick Fertre };
1555b759012cSYannick Fertre 
1556b759012cSYannick Fertre static const struct drm_plane_helper_funcs ltdc_plane_helper_funcs = {
1557b759012cSYannick Fertre 	.atomic_check = ltdc_plane_atomic_check,
1558b759012cSYannick Fertre 	.atomic_update = ltdc_plane_atomic_update,
1559b759012cSYannick Fertre 	.atomic_disable = ltdc_plane_atomic_disable,
1560b759012cSYannick Fertre };
1561b759012cSYannick Fertre 
ltdc_plane_create(struct drm_device * ddev,enum drm_plane_type type,int index)1562b759012cSYannick Fertre static struct drm_plane *ltdc_plane_create(struct drm_device *ddev,
1563484e72d3SYannick Fertre 					   enum drm_plane_type type,
1564484e72d3SYannick Fertre 					   int index)
1565b759012cSYannick Fertre {
1566b759012cSYannick Fertre 	unsigned long possible_crtcs = CRTC_MASK;
1567b759012cSYannick Fertre 	struct ltdc_device *ldev = ddev->dev_private;
1568b759012cSYannick Fertre 	struct device *dev = ddev->dev;
1569b759012cSYannick Fertre 	struct drm_plane *plane;
1570b759012cSYannick Fertre 	unsigned int i, nb_fmt = 0;
15718f2b5f6dSYannick Fertre 	u32 *formats;
15728f2b5f6dSYannick Fertre 	u32 drm_fmt;
1573e7c03dbaSYannick Fertré 	const u64 *modifiers = ltdc_format_modifiers;
1574484e72d3SYannick Fertre 	u32 lofs = index * LAY_OFS;
1575484e72d3SYannick Fertre 	u32 val;
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);
1583fc8b0b8dSClaudiu Beznea 	if (!formats)
1584fc8b0b8dSClaudiu Beznea 		return NULL;
1585aefa8301SPhilippe CORNU 
15868f2b5f6dSYannick Fertre 	for (i = 0; i < ldev->caps.pix_fmt_nb; i++) {
15878f2b5f6dSYannick Fertre 		drm_fmt = ldev->caps.pix_fmt_drm[i];
15889569002aSPhilippe CORNU 
15899569002aSPhilippe CORNU 		/* Manage hw-specific capabilities */
15908f2b5f6dSYannick Fertre 		if (ldev->caps.non_alpha_only_l1)
15918f2b5f6dSYannick Fertre 			/* XR24 & RX24 like formats supported only on primary layer */
15928f2b5f6dSYannick Fertre 			if (type != DRM_PLANE_TYPE_PRIMARY && is_xrgb(drm_fmt))
15939569002aSPhilippe CORNU 				continue;
15949569002aSPhilippe CORNU 
15958f2b5f6dSYannick Fertre 		formats[nb_fmt++] = drm_fmt;
1596b759012cSYannick Fertre 	}
1597b759012cSYannick Fertre 
1598484e72d3SYannick Fertre 	/* Add YCbCr supported pixel formats */
1599484e72d3SYannick Fertre 	if (ldev->caps.ycbcr_input) {
1600484e72d3SYannick Fertre 		regmap_read(ldev->regmap, LTDC_L1C1R + lofs, &val);
1601484e72d3SYannick Fertre 		if (val & LXCR_C1R_YIA) {
1602484e72d3SYannick Fertre 			memcpy(&formats[nb_fmt], ltdc_drm_fmt_ycbcr_cp,
1603484e72d3SYannick Fertre 			       ARRAY_SIZE(ltdc_drm_fmt_ycbcr_cp) * sizeof(*formats));
1604484e72d3SYannick Fertre 			nb_fmt += ARRAY_SIZE(ltdc_drm_fmt_ycbcr_cp);
1605484e72d3SYannick Fertre 		}
1606484e72d3SYannick Fertre 		if (val & LXCR_C1R_YSPA) {
1607484e72d3SYannick Fertre 			memcpy(&formats[nb_fmt], ltdc_drm_fmt_ycbcr_sp,
1608484e72d3SYannick Fertre 			       ARRAY_SIZE(ltdc_drm_fmt_ycbcr_sp) * sizeof(*formats));
1609484e72d3SYannick Fertre 			nb_fmt += ARRAY_SIZE(ltdc_drm_fmt_ycbcr_sp);
1610484e72d3SYannick Fertre 		}
1611484e72d3SYannick Fertre 		if (val & LXCR_C1R_YFPA) {
1612484e72d3SYannick Fertre 			memcpy(&formats[nb_fmt], ltdc_drm_fmt_ycbcr_fp,
1613484e72d3SYannick Fertre 			       ARRAY_SIZE(ltdc_drm_fmt_ycbcr_fp) * sizeof(*formats));
1614484e72d3SYannick Fertre 			nb_fmt += ARRAY_SIZE(ltdc_drm_fmt_ycbcr_fp);
1615484e72d3SYannick Fertre 		}
1616484e72d3SYannick Fertre 	}
1617484e72d3SYannick Fertre 
16180a1741d1SKatya Orlova 	plane = drmm_universal_plane_alloc(ddev, struct drm_plane, dev,
16190a1741d1SKatya Orlova 					   possible_crtcs, &ltdc_plane_funcs, formats,
16200a1741d1SKatya Orlova 					   nb_fmt, modifiers, type, NULL);
16210a1741d1SKatya Orlova 	if (IS_ERR(plane))
1622cccb57d8SPhilippe CORNU 		return NULL;
1623b759012cSYannick Fertre 
1624484e72d3SYannick Fertre 	if (ldev->caps.ycbcr_input) {
1625484e72d3SYannick Fertre 		if (val & (LXCR_C1R_YIA | LXCR_C1R_YSPA | LXCR_C1R_YFPA))
1626484e72d3SYannick Fertre 			drm_plane_create_color_properties(plane,
1627484e72d3SYannick Fertre 							  BIT(DRM_COLOR_YCBCR_BT601) |
1628484e72d3SYannick Fertre 							  BIT(DRM_COLOR_YCBCR_BT709),
1629484e72d3SYannick Fertre 							  BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) |
1630484e72d3SYannick Fertre 							  BIT(DRM_COLOR_YCBCR_FULL_RANGE),
1631484e72d3SYannick Fertre 							  DRM_COLOR_YCBCR_BT601,
1632484e72d3SYannick Fertre 							  DRM_COLOR_YCBCR_LIMITED_RANGE);
1633484e72d3SYannick Fertre 	}
1634484e72d3SYannick Fertre 
1635b759012cSYannick Fertre 	drm_plane_helper_add(plane, &ltdc_plane_helper_funcs);
1636b759012cSYannick Fertre 
1637c20351adSRaphael Gallais-Pou 	drm_plane_create_alpha_property(plane);
1638c20351adSRaphael Gallais-Pou 
1639b759012cSYannick Fertre 	DRM_DEBUG_DRIVER("plane:%d created\n", plane->base.id);
1640b759012cSYannick Fertre 
1641b759012cSYannick Fertre 	return plane;
1642b759012cSYannick Fertre }
1643b759012cSYannick Fertre 
ltdc_crtc_init(struct drm_device * ddev,struct drm_crtc * crtc)1644b759012cSYannick Fertre static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc)
1645b759012cSYannick Fertre {
1646b759012cSYannick Fertre 	struct ltdc_device *ldev = ddev->dev_private;
1647b759012cSYannick Fertre 	struct drm_plane *primary, *overlay;
1648c6193dc5SYannick Fertre 	int supported_rotations = DRM_MODE_ROTATE_0 | DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y;
1649b759012cSYannick Fertre 	unsigned int i;
1650dc5e0cd2SPhilippe CORNU 	int ret;
1651b759012cSYannick Fertre 
1652484e72d3SYannick Fertre 	primary = ltdc_plane_create(ddev, DRM_PLANE_TYPE_PRIMARY, 0);
1653b759012cSYannick Fertre 	if (!primary) {
1654b759012cSYannick Fertre 		DRM_ERROR("Can not create primary plane\n");
1655b759012cSYannick Fertre 		return -EINVAL;
1656b759012cSYannick Fertre 	}
1657b759012cSYannick Fertre 
165862467fccSYannick Fertre 	if (ldev->caps.dynamic_zorder)
165962467fccSYannick Fertre 		drm_plane_create_zpos_property(primary, 0, 0, ldev->caps.nb_layers - 1);
166062467fccSYannick Fertre 	else
1661ee2cda7bSRaphael Gallais-Pou 		drm_plane_create_zpos_immutable_property(primary, 0);
1662ee2cda7bSRaphael Gallais-Pou 
1663c6193dc5SYannick Fertre 	if (ldev->caps.plane_rotation)
1664c6193dc5SYannick Fertre 		drm_plane_create_rotation_property(primary, DRM_MODE_ROTATE_0,
1665c6193dc5SYannick Fertre 						   supported_rotations);
1666c6193dc5SYannick Fertre 
166779b44684SRaphael Gallais-Pou 	/* Init CRTC according to its hardware features */
166879b44684SRaphael Gallais-Pou 	if (ldev->caps.crc)
16690a1741d1SKatya Orlova 		ret = drmm_crtc_init_with_planes(ddev, crtc, primary, NULL,
167079b44684SRaphael Gallais-Pou 						 &ltdc_crtc_with_crc_support_funcs, NULL);
167179b44684SRaphael Gallais-Pou 	else
16720a1741d1SKatya Orlova 		ret = drmm_crtc_init_with_planes(ddev, crtc, primary, NULL,
1673b759012cSYannick Fertre 						 &ltdc_crtc_funcs, NULL);
1674dc5e0cd2SPhilippe CORNU 	if (ret) {
1675b759012cSYannick Fertre 		DRM_ERROR("Can not initialize CRTC\n");
16760a1741d1SKatya Orlova 		return ret;
1677b759012cSYannick Fertre 	}
1678b759012cSYannick Fertre 
1679b759012cSYannick Fertre 	drm_crtc_helper_add(crtc, &ltdc_crtc_helper_funcs);
1680b759012cSYannick Fertre 
1681b706a25eSPhilippe CORNU 	drm_mode_crtc_set_gamma_size(crtc, CLUT_SIZE);
1682b706a25eSPhilippe CORNU 	drm_crtc_enable_color_mgmt(crtc, 0, false, CLUT_SIZE);
1683b706a25eSPhilippe CORNU 
1684b759012cSYannick Fertre 	DRM_DEBUG_DRIVER("CRTC:%d created\n", crtc->base.id);
1685b759012cSYannick Fertre 
1686b759012cSYannick Fertre 	/* Add planes. Note : the first layer is used by primary plane */
1687b759012cSYannick Fertre 	for (i = 1; i < ldev->caps.nb_layers; i++) {
1688484e72d3SYannick Fertre 		overlay = ltdc_plane_create(ddev, DRM_PLANE_TYPE_OVERLAY, i);
1689b759012cSYannick Fertre 		if (!overlay) {
1690b759012cSYannick Fertre 			DRM_ERROR("Can not create overlay plane %d\n", i);
16910a1741d1SKatya Orlova 			return -ENOMEM;
1692b759012cSYannick Fertre 		}
169362467fccSYannick Fertre 		if (ldev->caps.dynamic_zorder)
169462467fccSYannick Fertre 			drm_plane_create_zpos_property(overlay, i, 0, ldev->caps.nb_layers - 1);
169562467fccSYannick Fertre 		else
1696ee2cda7bSRaphael Gallais-Pou 			drm_plane_create_zpos_immutable_property(overlay, i);
1697c6193dc5SYannick Fertre 
1698c6193dc5SYannick Fertre 		if (ldev->caps.plane_rotation)
1699c6193dc5SYannick Fertre 			drm_plane_create_rotation_property(overlay, DRM_MODE_ROTATE_0,
1700c6193dc5SYannick Fertre 							   supported_rotations);
1701b759012cSYannick Fertre 	}
1702b759012cSYannick Fertre 
1703b759012cSYannick Fertre 	return 0;
1704b759012cSYannick Fertre }
1705b759012cSYannick Fertre 
ltdc_encoder_disable(struct drm_encoder * encoder)170692a57b3fSYannick Fertré static void ltdc_encoder_disable(struct drm_encoder *encoder)
170792a57b3fSYannick Fertré {
170892a57b3fSYannick Fertré 	struct drm_device *ddev = encoder->dev;
1709f412af18SYannick Fertré 	struct ltdc_device *ldev = ddev->dev_private;
171092a57b3fSYannick Fertré 
171192a57b3fSYannick Fertré 	DRM_DEBUG_DRIVER("\n");
171292a57b3fSYannick Fertré 
1713f412af18SYannick Fertré 	/* Disable LTDC */
1714734c2645SYannick Fertre 	regmap_clear_bits(ldev->regmap, LTDC_GCR, GCR_LTDCEN);
1715f412af18SYannick Fertré 
171692a57b3fSYannick Fertré 	/* Set to sleep state the pinctrl whatever type of encoder */
171792a57b3fSYannick Fertré 	pinctrl_pm_select_sleep_state(ddev->dev);
171892a57b3fSYannick Fertré }
171992a57b3fSYannick Fertré 
ltdc_encoder_enable(struct drm_encoder * encoder)172092a57b3fSYannick Fertré static void ltdc_encoder_enable(struct drm_encoder *encoder)
172192a57b3fSYannick Fertré {
172292a57b3fSYannick Fertré 	struct drm_device *ddev = encoder->dev;
1723f412af18SYannick Fertré 	struct ltdc_device *ldev = ddev->dev_private;
1724f412af18SYannick Fertré 
1725f412af18SYannick Fertré 	DRM_DEBUG_DRIVER("\n");
1726f412af18SYannick Fertré 
17277d008eecSYannick Fertre 	/* set fifo underrun threshold register */
17287d008eecSYannick Fertre 	if (ldev->caps.fifo_threshold)
17297d008eecSYannick Fertre 		regmap_write(ldev->regmap, LTDC_FUT, ldev->fifo_threshold);
17307d008eecSYannick Fertre 
1731f412af18SYannick Fertré 	/* Enable LTDC */
1732734c2645SYannick Fertre 	regmap_set_bits(ldev->regmap, LTDC_GCR, GCR_LTDCEN);
1733f412af18SYannick Fertré }
1734f412af18SYannick Fertré 
ltdc_encoder_mode_set(struct drm_encoder * encoder,struct drm_display_mode * mode,struct drm_display_mode * adjusted_mode)1735f412af18SYannick Fertré static void ltdc_encoder_mode_set(struct drm_encoder *encoder,
1736f412af18SYannick Fertré 				  struct drm_display_mode *mode,
1737f412af18SYannick Fertré 				  struct drm_display_mode *adjusted_mode)
1738f412af18SYannick Fertré {
1739f412af18SYannick Fertré 	struct drm_device *ddev = encoder->dev;
174092a57b3fSYannick Fertré 
174192a57b3fSYannick Fertré 	DRM_DEBUG_DRIVER("\n");
174292a57b3fSYannick Fertré 
174392a57b3fSYannick Fertré 	/*
174492a57b3fSYannick Fertré 	 * Set to default state the pinctrl only with DPI type.
174592a57b3fSYannick Fertré 	 * Others types like DSI, don't need pinctrl due to
174692a57b3fSYannick Fertré 	 * internal bridge (the signals do not come out of the chipset).
174792a57b3fSYannick Fertré 	 */
174892a57b3fSYannick Fertré 	if (encoder->encoder_type == DRM_MODE_ENCODER_DPI)
174992a57b3fSYannick Fertré 		pinctrl_pm_select_default_state(ddev->dev);
175092a57b3fSYannick Fertré }
175192a57b3fSYannick Fertré 
175292a57b3fSYannick Fertré static const struct drm_encoder_helper_funcs ltdc_encoder_helper_funcs = {
175392a57b3fSYannick Fertré 	.disable = ltdc_encoder_disable,
175492a57b3fSYannick Fertré 	.enable = ltdc_encoder_enable,
1755f412af18SYannick Fertré 	.mode_set = ltdc_encoder_mode_set,
175692a57b3fSYannick Fertré };
175792a57b3fSYannick Fertré 
ltdc_encoder_init(struct drm_device * ddev,struct drm_bridge * bridge)175808de7afaSbenjamin.gaignard@linaro.org static int ltdc_encoder_init(struct drm_device *ddev, struct drm_bridge *bridge)
1759b759012cSYannick Fertre {
1760b759012cSYannick Fertre 	struct drm_encoder *encoder;
1761bdf31bcfSPhilippe CORNU 	int ret;
1762b759012cSYannick Fertre 
17630a1741d1SKatya Orlova 	encoder = drmm_simple_encoder_alloc(ddev, struct drm_encoder, dev,
17640a1741d1SKatya Orlova 					    DRM_MODE_ENCODER_DPI);
17650a1741d1SKatya Orlova 	if (IS_ERR(encoder))
17660a1741d1SKatya Orlova 		return PTR_ERR(encoder);
1767b759012cSYannick Fertre 
1768b759012cSYannick Fertre 	encoder->possible_crtcs = CRTC_MASK;
1769b759012cSYannick Fertre 	encoder->possible_clones = 0;	/* No cloning support */
1770b759012cSYannick Fertre 
177192a57b3fSYannick Fertré 	drm_encoder_helper_add(encoder, &ltdc_encoder_helper_funcs);
177292a57b3fSYannick Fertré 
1773a25b988fSLaurent Pinchart 	ret = drm_bridge_attach(encoder, bridge, NULL, 0);
17740a1741d1SKatya Orlova 	if (ret)
1775648ce7fdSJagan Teki 		return ret;
1776b759012cSYannick Fertre 
1777bdf31bcfSPhilippe CORNU 	DRM_DEBUG_DRIVER("Bridge encoder:%d created\n", encoder->base.id);
1778b759012cSYannick Fertre 
1779bdf31bcfSPhilippe CORNU 	return 0;
1780b759012cSYannick Fertre }
1781b759012cSYannick Fertre 
ltdc_get_caps(struct drm_device * ddev)1782b759012cSYannick Fertre static int ltdc_get_caps(struct drm_device *ddev)
1783b759012cSYannick Fertre {
1784b759012cSYannick Fertre 	struct ltdc_device *ldev = ddev->dev_private;
1785b759012cSYannick Fertre 	u32 bus_width_log2, lcr, gc2r;
1786b759012cSYannick Fertre 
178777756ad6SYannick Fertré 	/*
178877756ad6SYannick Fertré 	 * at least 1 layer must be managed & the number of layers
178977756ad6SYannick Fertré 	 * must not exceed LTDC_MAX_LAYER
179077756ad6SYannick Fertré 	 */
1791734c2645SYannick Fertre 	regmap_read(ldev->regmap, LTDC_LCR, &lcr);
1792b759012cSYannick Fertre 
179377756ad6SYannick Fertré 	ldev->caps.nb_layers = clamp((int)lcr, 1, LTDC_MAX_LAYER);
1794b759012cSYannick Fertre 
1795b759012cSYannick Fertre 	/* set data bus width */
1796734c2645SYannick Fertre 	regmap_read(ldev->regmap, LTDC_GC2R, &gc2r);
1797b759012cSYannick Fertre 	bus_width_log2 = (gc2r & GC2R_BW) >> 4;
1798b759012cSYannick Fertre 	ldev->caps.bus_width = 8 << bus_width_log2;
1799734c2645SYannick Fertre 	regmap_read(ldev->regmap, LTDC_IDR, &ldev->caps.hw_version);
1800b759012cSYannick Fertre 
1801b759012cSYannick Fertre 	switch (ldev->caps.hw_version) {
1802b759012cSYannick Fertre 	case HWVER_10200:
1803b759012cSYannick Fertre 	case HWVER_10300:
18041726cee3SYannick Fertre 		ldev->caps.layer_ofs = LAY_OFS_0;
18051726cee3SYannick Fertre 		ldev->caps.layer_regs = ltdc_layer_regs_a0;
1806b759012cSYannick Fertre 		ldev->caps.pix_fmt_hw = ltdc_pix_fmt_a0;
18078f2b5f6dSYannick Fertre 		ldev->caps.pix_fmt_drm = ltdc_drm_fmt_a0;
18088f2b5f6dSYannick Fertre 		ldev->caps.pix_fmt_nb = ARRAY_SIZE(ltdc_drm_fmt_a0);
18098f2b5f6dSYannick Fertre 		ldev->caps.pix_fmt_flex = false;
18109569002aSPhilippe CORNU 		/*
18119569002aSPhilippe CORNU 		 * Hw older versions support non-alpha color formats derived
18129569002aSPhilippe CORNU 		 * from native alpha color formats only on the primary layer.
18139569002aSPhilippe CORNU 		 * For instance, RG16 native format without alpha works fine
18149569002aSPhilippe CORNU 		 * on 2nd layer but XR24 (derived color format from AR24)
18159569002aSPhilippe CORNU 		 * does not work on 2nd layer.
18169569002aSPhilippe CORNU 		 */
18179569002aSPhilippe CORNU 		ldev->caps.non_alpha_only_l1 = true;
18187868e507SYannick Fertre 		ldev->caps.pad_max_freq_hz = 90000000;
18197868e507SYannick Fertre 		if (ldev->caps.hw_version == HWVER_10200)
18207868e507SYannick Fertre 			ldev->caps.pad_max_freq_hz = 65000000;
1821544aa6ceSYannick Fertre 		ldev->caps.nb_irq = 2;
1822484e72d3SYannick Fertre 		ldev->caps.ycbcr_input = false;
1823fb998edfSYannick Fertre 		ldev->caps.ycbcr_output = false;
1824a55d08e0SYannick Fertre 		ldev->caps.plane_reg_shadow = false;
182579b44684SRaphael Gallais-Pou 		ldev->caps.crc = false;
182662467fccSYannick Fertre 		ldev->caps.dynamic_zorder = false;
1827c6193dc5SYannick Fertre 		ldev->caps.plane_rotation = false;
18287d008eecSYannick Fertre 		ldev->caps.fifo_threshold = false;
1829b759012cSYannick Fertre 		break;
1830b759012cSYannick Fertre 	case HWVER_20101:
18311726cee3SYannick Fertre 		ldev->caps.layer_ofs = LAY_OFS_0;
18321726cee3SYannick Fertre 		ldev->caps.layer_regs = ltdc_layer_regs_a1;
1833b759012cSYannick Fertre 		ldev->caps.pix_fmt_hw = ltdc_pix_fmt_a1;
18348f2b5f6dSYannick Fertre 		ldev->caps.pix_fmt_drm = ltdc_drm_fmt_a1;
18358f2b5f6dSYannick Fertre 		ldev->caps.pix_fmt_nb = ARRAY_SIZE(ltdc_drm_fmt_a1);
18368f2b5f6dSYannick Fertre 		ldev->caps.pix_fmt_flex = false;
18379569002aSPhilippe CORNU 		ldev->caps.non_alpha_only_l1 = false;
18387868e507SYannick Fertre 		ldev->caps.pad_max_freq_hz = 150000000;
1839544aa6ceSYannick Fertre 		ldev->caps.nb_irq = 4;
1840484e72d3SYannick Fertre 		ldev->caps.ycbcr_input = false;
1841fb998edfSYannick Fertre 		ldev->caps.ycbcr_output = false;
1842a55d08e0SYannick Fertre 		ldev->caps.plane_reg_shadow = false;
184379b44684SRaphael Gallais-Pou 		ldev->caps.crc = false;
184462467fccSYannick Fertre 		ldev->caps.dynamic_zorder = false;
1845c6193dc5SYannick Fertre 		ldev->caps.plane_rotation = false;
18467d008eecSYannick Fertre 		ldev->caps.fifo_threshold = false;
1847b759012cSYannick Fertre 		break;
18481726cee3SYannick Fertre 	case HWVER_40100:
18491726cee3SYannick Fertre 		ldev->caps.layer_ofs = LAY_OFS_1;
18501726cee3SYannick Fertre 		ldev->caps.layer_regs = ltdc_layer_regs_a2;
18511726cee3SYannick Fertre 		ldev->caps.pix_fmt_hw = ltdc_pix_fmt_a2;
18528f2b5f6dSYannick Fertre 		ldev->caps.pix_fmt_drm = ltdc_drm_fmt_a2;
18538f2b5f6dSYannick Fertre 		ldev->caps.pix_fmt_nb = ARRAY_SIZE(ltdc_drm_fmt_a2);
18548f2b5f6dSYannick Fertre 		ldev->caps.pix_fmt_flex = true;
18551726cee3SYannick Fertre 		ldev->caps.non_alpha_only_l1 = false;
18561726cee3SYannick Fertre 		ldev->caps.pad_max_freq_hz = 90000000;
18571726cee3SYannick Fertre 		ldev->caps.nb_irq = 2;
1858484e72d3SYannick Fertre 		ldev->caps.ycbcr_input = true;
1859fb998edfSYannick Fertre 		ldev->caps.ycbcr_output = true;
1860a55d08e0SYannick Fertre 		ldev->caps.plane_reg_shadow = true;
186179b44684SRaphael Gallais-Pou 		ldev->caps.crc = true;
186262467fccSYannick Fertre 		ldev->caps.dynamic_zorder = true;
1863c6193dc5SYannick Fertre 		ldev->caps.plane_rotation = true;
18647d008eecSYannick Fertre 		ldev->caps.fifo_threshold = true;
18651726cee3SYannick Fertre 		break;
1866b759012cSYannick Fertre 	default:
1867b759012cSYannick Fertre 		return -ENODEV;
1868b759012cSYannick Fertre 	}
1869b759012cSYannick Fertre 
1870b759012cSYannick Fertre 	return 0;
1871b759012cSYannick Fertre }
1872b759012cSYannick Fertre 
ltdc_suspend(struct drm_device * ddev)1873df61c776SYannick Fertré void ltdc_suspend(struct drm_device *ddev)
1874df61c776SYannick Fertré {
1875df61c776SYannick Fertré 	struct ltdc_device *ldev = ddev->dev_private;
1876df61c776SYannick Fertré 
1877df61c776SYannick Fertré 	DRM_DEBUG_DRIVER("\n");
1878df61c776SYannick Fertré 	clk_disable_unprepare(ldev->pixel_clk);
1879df61c776SYannick Fertré }
1880df61c776SYannick Fertré 
ltdc_resume(struct drm_device * ddev)1881df61c776SYannick Fertré int ltdc_resume(struct drm_device *ddev)
1882df61c776SYannick Fertré {
1883df61c776SYannick Fertré 	struct ltdc_device *ldev = ddev->dev_private;
1884df61c776SYannick Fertré 	int ret;
1885df61c776SYannick Fertré 
1886df61c776SYannick Fertré 	DRM_DEBUG_DRIVER("\n");
1887df61c776SYannick Fertré 
1888df61c776SYannick Fertré 	ret = clk_prepare_enable(ldev->pixel_clk);
1889df61c776SYannick Fertré 	if (ret) {
1890df61c776SYannick Fertré 		DRM_ERROR("failed to enable pixel clock (%d)\n", ret);
1891df61c776SYannick Fertré 		return ret;
1892df61c776SYannick Fertré 	}
1893df61c776SYannick Fertré 
1894df61c776SYannick Fertré 	return 0;
1895df61c776SYannick Fertré }
1896df61c776SYannick Fertré 
ltdc_load(struct drm_device * ddev)1897b759012cSYannick Fertre int ltdc_load(struct drm_device *ddev)
1898b759012cSYannick Fertre {
1899b759012cSYannick Fertre 	struct platform_device *pdev = to_platform_device(ddev->dev);
1900b759012cSYannick Fertre 	struct ltdc_device *ldev = ddev->dev_private;
1901b759012cSYannick Fertre 	struct device *dev = ddev->dev;
1902b759012cSYannick Fertre 	struct device_node *np = dev->of_node;
1903b430ff7eSYannick Fertre 	struct drm_bridge *bridge;
1904b430ff7eSYannick Fertre 	struct drm_panel *panel;
1905b759012cSYannick Fertre 	struct drm_crtc *crtc;
1906b759012cSYannick Fertre 	struct reset_control *rstc;
1907589b6482SPhilippe CORNU 	struct resource *res;
1908b430ff7eSYannick Fertre 	int irq, i, nb_endpoints;
1909b430ff7eSYannick Fertre 	int ret = -ENODEV;
1910b759012cSYannick Fertre 
1911b759012cSYannick Fertre 	DRM_DEBUG_DRIVER("\n");
1912b759012cSYannick Fertre 
1913b430ff7eSYannick Fertre 	/* Get number of endpoints */
1914b430ff7eSYannick Fertre 	nb_endpoints = of_graph_get_endpoint_count(np);
1915b430ff7eSYannick Fertre 	if (!nb_endpoints)
1916b430ff7eSYannick Fertre 		return -ENODEV;
1917b759012cSYannick Fertre 
1918b759012cSYannick Fertre 	ldev->pixel_clk = devm_clk_get(dev, "lcd");
1919b759012cSYannick Fertre 	if (IS_ERR(ldev->pixel_clk)) {
19201f358bc6SFabien Dessenne 		if (PTR_ERR(ldev->pixel_clk) != -EPROBE_DEFER)
1921b759012cSYannick Fertre 			DRM_ERROR("Unable to get lcd clock\n");
19221f358bc6SFabien Dessenne 		return PTR_ERR(ldev->pixel_clk);
1923b759012cSYannick Fertre 	}
1924b759012cSYannick Fertre 
1925b759012cSYannick Fertre 	if (clk_prepare_enable(ldev->pixel_clk)) {
1926b759012cSYannick Fertre 		DRM_ERROR("Unable to prepare pixel clock\n");
1927b759012cSYannick Fertre 		return -ENODEV;
1928b759012cSYannick Fertre 	}
1929b759012cSYannick Fertre 
1930b430ff7eSYannick Fertre 	/* Get endpoints if any */
1931b430ff7eSYannick Fertre 	for (i = 0; i < nb_endpoints; i++) {
1932b430ff7eSYannick Fertre 		ret = drm_of_find_panel_or_bridge(np, 0, i, &panel, &bridge);
1933b430ff7eSYannick Fertre 
1934b430ff7eSYannick Fertre 		/*
1935b430ff7eSYannick Fertre 		 * If at least one endpoint is -ENODEV, continue probing,
1936b430ff7eSYannick Fertre 		 * else if at least one endpoint returned an error
1937b430ff7eSYannick Fertre 		 * (ie -EPROBE_DEFER) then stop probing.
1938b430ff7eSYannick Fertre 		 */
1939b430ff7eSYannick Fertre 		if (ret == -ENODEV)
1940b430ff7eSYannick Fertre 			continue;
1941b430ff7eSYannick Fertre 		else if (ret)
1942b430ff7eSYannick Fertre 			goto err;
1943b430ff7eSYannick Fertre 
1944b430ff7eSYannick Fertre 		if (panel) {
19450a1741d1SKatya Orlova 			bridge = drmm_panel_bridge_add(ddev, panel);
1946b430ff7eSYannick Fertre 			if (IS_ERR(bridge)) {
1947b430ff7eSYannick Fertre 				DRM_ERROR("panel-bridge endpoint %d\n", i);
1948b430ff7eSYannick Fertre 				ret = PTR_ERR(bridge);
1949b430ff7eSYannick Fertre 				goto err;
1950b430ff7eSYannick Fertre 			}
1951b430ff7eSYannick Fertre 		}
1952b430ff7eSYannick Fertre 
1953b430ff7eSYannick Fertre 		if (bridge) {
1954b430ff7eSYannick Fertre 			ret = ltdc_encoder_init(ddev, bridge);
1955b430ff7eSYannick Fertre 			if (ret) {
1956648ce7fdSJagan Teki 				if (ret != -EPROBE_DEFER)
1957b430ff7eSYannick Fertre 					DRM_ERROR("init encoder endpoint %d\n", i);
1958b430ff7eSYannick Fertre 				goto err;
1959b430ff7eSYannick Fertre 			}
1960b430ff7eSYannick Fertre 		}
1961b430ff7eSYannick Fertre 	}
1962b430ff7eSYannick Fertre 
1963b430ff7eSYannick Fertre 	rstc = devm_reset_control_get_exclusive(dev, NULL);
1964b430ff7eSYannick Fertre 
1965b430ff7eSYannick Fertre 	mutex_init(&ldev->err_lock);
1966b430ff7eSYannick Fertre 
1967f42f540bSYannick Fertré 	if (!IS_ERR(rstc)) {
1968f42f540bSYannick Fertré 		reset_control_assert(rstc);
1969f42f540bSYannick Fertré 		usleep_range(10, 20);
1970f42f540bSYannick Fertré 		reset_control_deassert(rstc);
1971f42f540bSYannick Fertré 	}
1972f42f540bSYannick Fertré 
1973589b6482SPhilippe CORNU 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1974589b6482SPhilippe CORNU 	ldev->regs = devm_ioremap_resource(dev, res);
1975b759012cSYannick Fertre 	if (IS_ERR(ldev->regs)) {
1976b759012cSYannick Fertre 		DRM_ERROR("Unable to get ltdc registers\n");
1977cea3a330SPhilippe CORNU 		ret = PTR_ERR(ldev->regs);
1978cea3a330SPhilippe CORNU 		goto err;
1979b759012cSYannick Fertre 	}
1980b759012cSYannick Fertre 
1981734c2645SYannick Fertre 	ldev->regmap = devm_regmap_init_mmio(&pdev->dev, ldev->regs, &stm32_ltdc_regmap_cfg);
1982734c2645SYannick Fertre 	if (IS_ERR(ldev->regmap)) {
1983734c2645SYannick Fertre 		DRM_ERROR("Unable to regmap ltdc registers\n");
1984734c2645SYannick Fertre 		ret = PTR_ERR(ldev->regmap);
1985734c2645SYannick Fertre 		goto err;
1986734c2645SYannick Fertre 	}
1987734c2645SYannick Fertre 
1988544aa6ceSYannick Fertre 	ret = ltdc_get_caps(ddev);
1989544aa6ceSYannick Fertre 	if (ret) {
1990544aa6ceSYannick Fertre 		DRM_ERROR("hardware identifier (0x%08x) not supported!\n",
1991544aa6ceSYannick Fertre 			  ldev->caps.hw_version);
19929e759fc7SFabien Dessenne 		goto err;
1993544aa6ceSYannick Fertre 	}
19949e759fc7SFabien Dessenne 
19957d008eecSYannick Fertre 	/* Disable interrupts */
19967d008eecSYannick Fertre 	if (ldev->caps.fifo_threshold)
19977d008eecSYannick Fertre 		regmap_clear_bits(ldev->regmap, LTDC_IER, IER_LIE | IER_RRIE | IER_FUWIE |
19987d008eecSYannick Fertre 				  IER_TERRIE);
19997d008eecSYannick Fertre 	else
20007d008eecSYannick Fertre 		regmap_clear_bits(ldev->regmap, LTDC_IER, IER_LIE | IER_RRIE | IER_FUWIE |
20017d008eecSYannick Fertre 				  IER_TERRIE | IER_FUEIE);
20027d008eecSYannick Fertre 
2003544aa6ceSYannick Fertre 	DRM_DEBUG_DRIVER("ltdc hw version 0x%08x\n", ldev->caps.hw_version);
2004544aa6ceSYannick Fertre 
20057d008eecSYannick Fertre 	/* initialize default value for fifo underrun threshold & clear interrupt error counters */
20067d008eecSYannick Fertre 	ldev->transfer_err = 0;
20077d008eecSYannick Fertre 	ldev->fifo_err = 0;
20087d008eecSYannick Fertre 	ldev->fifo_warn = 0;
20097d008eecSYannick Fertre 	ldev->fifo_threshold = FUT_DFT;
20107d008eecSYannick Fertre 
2011544aa6ceSYannick Fertre 	for (i = 0; i < ldev->caps.nb_irq; i++) {
2012544aa6ceSYannick Fertre 		irq = platform_get_irq(pdev, i);
2013544aa6ceSYannick Fertre 		if (irq < 0) {
2014544aa6ceSYannick Fertre 			ret = irq;
2015544aa6ceSYannick Fertre 			goto err;
2016544aa6ceSYannick Fertre 		}
2017b759012cSYannick Fertre 
2018b759012cSYannick Fertre 		ret = devm_request_threaded_irq(dev, irq, ltdc_irq,
2019b759012cSYannick Fertre 						ltdc_irq_thread, IRQF_ONESHOT,
2020b759012cSYannick Fertre 						dev_name(dev), ddev);
2021b759012cSYannick Fertre 		if (ret) {
2022b759012cSYannick Fertre 			DRM_ERROR("Failed to register LTDC interrupt\n");
2023cea3a330SPhilippe CORNU 			goto err;
2024b759012cSYannick Fertre 		}
2025c188d7ebSPhilippe CORNU 	}
2026b759012cSYannick Fertre 
20270a1741d1SKatya Orlova 	crtc = drmm_kzalloc(ddev, sizeof(*crtc), GFP_KERNEL);
2028b759012cSYannick Fertre 	if (!crtc) {
2029b759012cSYannick Fertre 		DRM_ERROR("Failed to allocate crtc\n");
2030b759012cSYannick Fertre 		ret = -ENOMEM;
2031b759012cSYannick Fertre 		goto err;
2032b759012cSYannick Fertre 	}
2033b759012cSYannick Fertre 
2034b759012cSYannick Fertre 	ret = ltdc_crtc_init(ddev, crtc);
2035b759012cSYannick Fertre 	if (ret) {
2036b759012cSYannick Fertre 		DRM_ERROR("Failed to init crtc\n");
2037b759012cSYannick Fertre 		goto err;
2038b759012cSYannick Fertre 	}
2039b759012cSYannick Fertre 
2040b759012cSYannick Fertre 	ret = drm_vblank_init(ddev, NB_CRTC);
2041b759012cSYannick Fertre 	if (ret) {
2042b759012cSYannick Fertre 		DRM_ERROR("Failed calling drm_vblank_init()\n");
2043b759012cSYannick Fertre 		goto err;
2044b759012cSYannick Fertre 	}
2045b759012cSYannick Fertre 
204635ab6cfbSYannick Fertré 	clk_disable_unprepare(ldev->pixel_clk);
2047bdf31bcfSPhilippe CORNU 
204892a57b3fSYannick Fertré 	pinctrl_pm_select_sleep_state(ddev->dev);
204992a57b3fSYannick Fertré 
205035ab6cfbSYannick Fertré 	pm_runtime_enable(ddev->dev);
205135ab6cfbSYannick Fertré 
205235ab6cfbSYannick Fertré 	return 0;
2053b759012cSYannick Fertre err:
2054b759012cSYannick Fertre 	clk_disable_unprepare(ldev->pixel_clk);
2055b759012cSYannick Fertre 
2056b759012cSYannick Fertre 	return ret;
2057b759012cSYannick Fertre }
2058b759012cSYannick Fertre 
ltdc_unload(struct drm_device * ddev)2059b759012cSYannick Fertre void ltdc_unload(struct drm_device *ddev)
2060b759012cSYannick Fertre {
2061b759012cSYannick Fertre 	DRM_DEBUG_DRIVER("\n");
2062b759012cSYannick Fertre 
206335ab6cfbSYannick Fertré 	pm_runtime_disable(ddev->dev);
2064b759012cSYannick Fertre }
2065b759012cSYannick Fertre 
2066b759012cSYannick Fertre MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>");
2067b759012cSYannick Fertre MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
2068b759012cSYannick Fertre MODULE_AUTHOR("Fabien Dessenne <fabien.dessenne@st.com>");
2069b759012cSYannick Fertre MODULE_AUTHOR("Mickael Reulier <mickael.reulier@st.com>");
2070b759012cSYannick Fertre MODULE_DESCRIPTION("STMicroelectronics ST DRM LTDC driver");
2071b759012cSYannick Fertre MODULE_LICENSE("GPL v2");
2072