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, <dc_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, <dc_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 <dc_crtc_with_crc_support_funcs, NULL);
167179b44684SRaphael Gallais-Pou else
16720a1741d1SKatya Orlova ret = drmm_crtc_init_with_planes(ddev, crtc, primary, NULL,
1673b759012cSYannick Fertre <dc_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, <dc_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, <dc_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