diff options
Diffstat (limited to 'drivers/gpu/drm/stm/ltdc.c')
-rw-r--r-- | drivers/gpu/drm/stm/ltdc.c | 1160 |
1 files changed, 1160 insertions, 0 deletions
diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c new file mode 100644 index 000000000000..a40418cda74a --- /dev/null +++ b/drivers/gpu/drm/stm/ltdc.c | |||
@@ -0,0 +1,1160 @@ | |||
1 | /* | ||
2 | * Copyright (C) STMicroelectronics SA 2017 | ||
3 | * | ||
4 | * Authors: Philippe Cornu <philippe.cornu@st.com> | ||
5 | * Yannick Fertre <yannick.fertre@st.com> | ||
6 | * Fabien Dessenne <fabien.dessenne@st.com> | ||
7 | * Mickael Reulier <mickael.reulier@st.com> | ||
8 | * | ||
9 | * License terms: GNU General Public License (GPL), version 2 | ||
10 | */ | ||
11 | |||
12 | #include <linux/clk.h> | ||
13 | #include <linux/component.h> | ||
14 | #include <linux/of_address.h> | ||
15 | #include <linux/of_graph.h> | ||
16 | #include <linux/reset.h> | ||
17 | |||
18 | #include <drm/drm_atomic.h> | ||
19 | #include <drm/drm_atomic_helper.h> | ||
20 | #include <drm/drm_crtc_helper.h> | ||
21 | #include <drm/drm_fb_cma_helper.h> | ||
22 | #include <drm/drm_gem_cma_helper.h> | ||
23 | #include <drm/drm_of.h> | ||
24 | #include <drm/drm_panel.h> | ||
25 | #include <drm/drm_plane_helper.h> | ||
26 | |||
27 | #include <video/videomode.h> | ||
28 | |||
29 | #include "ltdc.h" | ||
30 | |||
31 | #define NB_CRTC 1 | ||
32 | #define CRTC_MASK GENMASK(NB_CRTC - 1, 0) | ||
33 | |||
34 | #define MAX_IRQ 4 | ||
35 | |||
36 | #define HWVER_10200 0x010200 | ||
37 | #define HWVER_10300 0x010300 | ||
38 | #define HWVER_20101 0x020101 | ||
39 | |||
40 | /* | ||
41 | * The address of some registers depends on the HW version: such registers have | ||
42 | * an extra offset specified with reg_ofs. | ||
43 | */ | ||
44 | #define REG_OFS_NONE 0 | ||
45 | #define REG_OFS_4 4 /* Insertion of "Layer Configuration 2" reg */ | ||
46 | #define REG_OFS (ldev->caps.reg_ofs) | ||
47 | #define LAY_OFS 0x80 /* Register Offset between 2 layers */ | ||
48 | |||
49 | /* Global register offsets */ | ||
50 | #define LTDC_IDR 0x0000 /* IDentification */ | ||
51 | #define LTDC_LCR 0x0004 /* Layer Count */ | ||
52 | #define LTDC_SSCR 0x0008 /* Synchronization Size Configuration */ | ||
53 | #define LTDC_BPCR 0x000C /* Back Porch Configuration */ | ||
54 | #define LTDC_AWCR 0x0010 /* Active Width Configuration */ | ||
55 | #define LTDC_TWCR 0x0014 /* Total Width Configuration */ | ||
56 | #define LTDC_GCR 0x0018 /* Global Control */ | ||
57 | #define LTDC_GC1R 0x001C /* Global Configuration 1 */ | ||
58 | #define LTDC_GC2R 0x0020 /* Global Configuration 2 */ | ||
59 | #define LTDC_SRCR 0x0024 /* Shadow Reload Configuration */ | ||
60 | #define LTDC_GACR 0x0028 /* GAmma Correction */ | ||
61 | #define LTDC_BCCR 0x002C /* Background Color Configuration */ | ||
62 | #define LTDC_IER 0x0034 /* Interrupt Enable */ | ||
63 | #define LTDC_ISR 0x0038 /* Interrupt Status */ | ||
64 | #define LTDC_ICR 0x003C /* Interrupt Clear */ | ||
65 | #define LTDC_LIPCR 0x0040 /* Line Interrupt Position Configuration */ | ||
66 | #define LTDC_CPSR 0x0044 /* Current Position Status */ | ||
67 | #define LTDC_CDSR 0x0048 /* Current Display Status */ | ||
68 | |||
69 | /* Layer register offsets */ | ||
70 | #define LTDC_L1LC1R (0x0080) /* L1 Layer Configuration 1 */ | ||
71 | #define LTDC_L1LC2R (0x0084) /* L1 Layer Configuration 2 */ | ||
72 | #define LTDC_L1CR (0x0084 + REG_OFS) /* L1 Control */ | ||
73 | #define LTDC_L1WHPCR (0x0088 + REG_OFS) /* L1 Window Hor Position Config */ | ||
74 | #define LTDC_L1WVPCR (0x008C + REG_OFS) /* L1 Window Vert Position Config */ | ||
75 | #define LTDC_L1CKCR (0x0090 + REG_OFS) /* L1 Color Keying Configuration */ | ||
76 | #define LTDC_L1PFCR (0x0094 + REG_OFS) /* L1 Pixel Format Configuration */ | ||
77 | #define LTDC_L1CACR (0x0098 + REG_OFS) /* L1 Constant Alpha Config */ | ||
78 | #define LTDC_L1DCCR (0x009C + REG_OFS) /* L1 Default Color Configuration */ | ||
79 | #define LTDC_L1BFCR (0x00A0 + REG_OFS) /* L1 Blend Factors Configuration */ | ||
80 | #define LTDC_L1FBBCR (0x00A4 + REG_OFS) /* L1 FrameBuffer Bus Control */ | ||
81 | #define LTDC_L1AFBCR (0x00A8 + REG_OFS) /* L1 AuxFB Control */ | ||
82 | #define LTDC_L1CFBAR (0x00AC + REG_OFS) /* L1 Color FrameBuffer Address */ | ||
83 | #define LTDC_L1CFBLR (0x00B0 + REG_OFS) /* L1 Color FrameBuffer Length */ | ||
84 | #define LTDC_L1CFBLNR (0x00B4 + REG_OFS) /* L1 Color FrameBuffer Line Nb */ | ||
85 | #define LTDC_L1AFBAR (0x00B8 + REG_OFS) /* L1 AuxFB Address */ | ||
86 | #define LTDC_L1AFBLR (0x00BC + REG_OFS) /* L1 AuxFB Length */ | ||
87 | #define LTDC_L1AFBLNR (0x00C0 + REG_OFS) /* L1 AuxFB Line Number */ | ||
88 | #define LTDC_L1CLUTWR (0x00C4 + REG_OFS) /* L1 CLUT Write */ | ||
89 | #define LTDC_L1YS1R (0x00E0 + REG_OFS) /* L1 YCbCr Scale 1 */ | ||
90 | #define LTDC_L1YS2R (0x00E4 + REG_OFS) /* L1 YCbCr Scale 2 */ | ||
91 | |||
92 | /* Bit definitions */ | ||
93 | #define SSCR_VSH GENMASK(10, 0) /* Vertical Synchronization Height */ | ||
94 | #define SSCR_HSW GENMASK(27, 16) /* Horizontal Synchronization Width */ | ||
95 | |||
96 | #define BPCR_AVBP GENMASK(10, 0) /* Accumulated Vertical Back Porch */ | ||
97 | #define BPCR_AHBP GENMASK(27, 16) /* Accumulated Horizontal Back Porch */ | ||
98 | |||
99 | #define AWCR_AAH GENMASK(10, 0) /* Accumulated Active Height */ | ||
100 | #define AWCR_AAW GENMASK(27, 16) /* Accumulated Active Width */ | ||
101 | |||
102 | #define TWCR_TOTALH GENMASK(10, 0) /* TOTAL Height */ | ||
103 | #define TWCR_TOTALW GENMASK(27, 16) /* TOTAL Width */ | ||
104 | |||
105 | #define GCR_LTDCEN BIT(0) /* LTDC ENable */ | ||
106 | #define GCR_DEN BIT(16) /* Dither ENable */ | ||
107 | #define GCR_PCPOL BIT(28) /* Pixel Clock POLarity */ | ||
108 | #define GCR_DEPOL BIT(29) /* Data Enable POLarity */ | ||
109 | #define GCR_VSPOL BIT(30) /* Vertical Synchro POLarity */ | ||
110 | #define GCR_HSPOL BIT(31) /* Horizontal Synchro POLarity */ | ||
111 | |||
112 | #define GC1R_WBCH GENMASK(3, 0) /* Width of Blue CHannel output */ | ||
113 | #define GC1R_WGCH GENMASK(7, 4) /* Width of Green Channel output */ | ||
114 | #define GC1R_WRCH GENMASK(11, 8) /* Width of Red Channel output */ | ||
115 | #define GC1R_PBEN BIT(12) /* Precise Blending ENable */ | ||
116 | #define GC1R_DT GENMASK(15, 14) /* Dithering Technique */ | ||
117 | #define GC1R_GCT GENMASK(19, 17) /* Gamma Correction Technique */ | ||
118 | #define GC1R_SHREN BIT(21) /* SHadow Registers ENabled */ | ||
119 | #define GC1R_BCP BIT(22) /* Background Colour Programmable */ | ||
120 | #define GC1R_BBEN BIT(23) /* Background Blending ENabled */ | ||
121 | #define GC1R_LNIP BIT(24) /* Line Number IRQ Position */ | ||
122 | #define GC1R_TP BIT(25) /* Timing Programmable */ | ||
123 | #define GC1R_IPP BIT(26) /* IRQ Polarity Programmable */ | ||
124 | #define GC1R_SPP BIT(27) /* Sync Polarity Programmable */ | ||
125 | #define GC1R_DWP BIT(28) /* Dither Width Programmable */ | ||
126 | #define GC1R_STREN BIT(29) /* STatus Registers ENabled */ | ||
127 | #define GC1R_BMEN BIT(31) /* Blind Mode ENabled */ | ||
128 | |||
129 | #define GC2R_EDCA BIT(0) /* External Display Control Ability */ | ||
130 | #define GC2R_STSAEN BIT(1) /* Slave Timing Sync Ability ENabled */ | ||
131 | #define GC2R_DVAEN BIT(2) /* Dual-View Ability ENabled */ | ||
132 | #define GC2R_DPAEN BIT(3) /* Dual-Port Ability ENabled */ | ||
133 | #define GC2R_BW GENMASK(6, 4) /* Bus Width (log2 of nb of bytes) */ | ||
134 | #define GC2R_EDCEN BIT(7) /* External Display Control ENabled */ | ||
135 | |||
136 | #define SRCR_IMR BIT(0) /* IMmediate Reload */ | ||
137 | #define SRCR_VBR BIT(1) /* Vertical Blanking Reload */ | ||
138 | |||
139 | #define BCCR_BCBLACK 0x00 /* Background Color BLACK */ | ||
140 | #define BCCR_BCBLUE GENMASK(7, 0) /* Background Color BLUE */ | ||
141 | #define BCCR_BCGREEN GENMASK(15, 8) /* Background Color GREEN */ | ||
142 | #define BCCR_BCRED GENMASK(23, 16) /* Background Color RED */ | ||
143 | #define BCCR_BCWHITE GENMASK(23, 0) /* Background Color WHITE */ | ||
144 | |||
145 | #define IER_LIE BIT(0) /* Line Interrupt Enable */ | ||
146 | #define IER_FUIE BIT(1) /* Fifo Underrun Interrupt Enable */ | ||
147 | #define IER_TERRIE BIT(2) /* Transfer ERRor Interrupt Enable */ | ||
148 | #define IER_RRIE BIT(3) /* Register Reload Interrupt enable */ | ||
149 | |||
150 | #define ISR_LIF BIT(0) /* Line Interrupt Flag */ | ||
151 | #define ISR_FUIF BIT(1) /* Fifo Underrun Interrupt Flag */ | ||
152 | #define ISR_TERRIF BIT(2) /* Transfer ERRor Interrupt Flag */ | ||
153 | #define ISR_RRIF BIT(3) /* Register Reload Interrupt Flag */ | ||
154 | |||
155 | #define LXCR_LEN BIT(0) /* Layer ENable */ | ||
156 | #define LXCR_COLKEN BIT(1) /* Color Keying Enable */ | ||
157 | #define LXCR_CLUTEN BIT(4) /* Color Look-Up Table ENable */ | ||
158 | |||
159 | #define LXWHPCR_WHSTPOS GENMASK(11, 0) /* Window Horizontal StarT POSition */ | ||
160 | #define LXWHPCR_WHSPPOS GENMASK(27, 16) /* Window Horizontal StoP POSition */ | ||
161 | |||
162 | #define LXWVPCR_WVSTPOS GENMASK(10, 0) /* Window Vertical StarT POSition */ | ||
163 | #define LXWVPCR_WVSPPOS GENMASK(26, 16) /* Window Vertical StoP POSition */ | ||
164 | |||
165 | #define LXPFCR_PF GENMASK(2, 0) /* Pixel Format */ | ||
166 | |||
167 | #define LXCACR_CONSTA GENMASK(7, 0) /* CONSTant Alpha */ | ||
168 | |||
169 | #define LXBFCR_BF2 GENMASK(2, 0) /* Blending Factor 2 */ | ||
170 | #define LXBFCR_BF1 GENMASK(10, 8) /* Blending Factor 1 */ | ||
171 | |||
172 | #define LXCFBLR_CFBLL GENMASK(12, 0) /* Color Frame Buffer Line Length */ | ||
173 | #define LXCFBLR_CFBP GENMASK(28, 16) /* Color Frame Buffer Pitch in bytes */ | ||
174 | |||
175 | #define LXCFBLNR_CFBLN GENMASK(10, 0) /* Color Frame Buffer Line Number */ | ||
176 | |||
177 | #define HSPOL_AL 0 /* Horizontal Sync POLarity Active Low */ | ||
178 | #define VSPOL_AL 0 /* Vertical Sync POLarity Active Low */ | ||
179 | #define DEPOL_AL 0 /* Data Enable POLarity Active Low */ | ||
180 | #define PCPOL_IPC 0 /* Input Pixel Clock */ | ||
181 | #define HSPOL_AH GCR_HSPOL /* Horizontal Sync POLarity Active High */ | ||
182 | #define VSPOL_AH GCR_VSPOL /* Vertical Sync POLarity Active High */ | ||
183 | #define DEPOL_AH GCR_DEPOL /* Data Enable POLarity Active High */ | ||
184 | #define PCPOL_IIPC GCR_PCPOL /* Inverted Input Pixel Clock */ | ||
185 | #define CONSTA_MAX 0xFF /* CONSTant Alpha MAX= 1.0 */ | ||
186 | #define BF1_PAXCA 0x600 /* Pixel Alpha x Constant Alpha */ | ||
187 | #define BF1_CA 0x400 /* Constant Alpha */ | ||
188 | #define BF2_1PAXCA 0x007 /* 1 - (Pixel Alpha x Constant Alpha) */ | ||
189 | #define BF2_1CA 0x005 /* 1 - Constant Alpha */ | ||
190 | |||
191 | #define NB_PF 8 /* Max nb of HW pixel format */ | ||
192 | |||
193 | enum ltdc_pix_fmt { | ||
194 | PF_NONE, | ||
195 | /* RGB formats */ | ||
196 | PF_ARGB8888, /* ARGB [32 bits] */ | ||
197 | PF_RGBA8888, /* RGBA [32 bits] */ | ||
198 | PF_RGB888, /* RGB [24 bits] */ | ||
199 | PF_RGB565, /* RGB [16 bits] */ | ||
200 | PF_ARGB1555, /* ARGB A:1 bit RGB:15 bits [16 bits] */ | ||
201 | PF_ARGB4444, /* ARGB A:4 bits R/G/B: 4 bits each [16 bits] */ | ||
202 | /* Indexed formats */ | ||
203 | PF_L8, /* Indexed 8 bits [8 bits] */ | ||
204 | PF_AL44, /* Alpha:4 bits + indexed 4 bits [8 bits] */ | ||
205 | PF_AL88 /* Alpha:8 bits + indexed 8 bits [16 bits] */ | ||
206 | }; | ||
207 | |||
208 | /* The index gives the encoding of the pixel format for an HW version */ | ||
209 | static const enum ltdc_pix_fmt ltdc_pix_fmt_a0[NB_PF] = { | ||
210 | PF_ARGB8888, /* 0x00 */ | ||
211 | PF_RGB888, /* 0x01 */ | ||
212 | PF_RGB565, /* 0x02 */ | ||
213 | PF_ARGB1555, /* 0x03 */ | ||
214 | PF_ARGB4444, /* 0x04 */ | ||
215 | PF_L8, /* 0x05 */ | ||
216 | PF_AL44, /* 0x06 */ | ||
217 | PF_AL88 /* 0x07 */ | ||
218 | }; | ||
219 | |||
220 | static const enum ltdc_pix_fmt ltdc_pix_fmt_a1[NB_PF] = { | ||
221 | PF_ARGB8888, /* 0x00 */ | ||
222 | PF_RGB888, /* 0x01 */ | ||
223 | PF_RGB565, /* 0x02 */ | ||
224 | PF_RGBA8888, /* 0x03 */ | ||
225 | PF_AL44, /* 0x04 */ | ||
226 | PF_L8, /* 0x05 */ | ||
227 | PF_ARGB1555, /* 0x06 */ | ||
228 | PF_ARGB4444 /* 0x07 */ | ||
229 | }; | ||
230 | |||
231 | static inline u32 reg_read(void __iomem *base, u32 reg) | ||
232 | { | ||
233 | return readl_relaxed(base + reg); | ||
234 | } | ||
235 | |||
236 | static inline void reg_write(void __iomem *base, u32 reg, u32 val) | ||
237 | { | ||
238 | writel_relaxed(val, base + reg); | ||
239 | } | ||
240 | |||
241 | static inline void reg_set(void __iomem *base, u32 reg, u32 mask) | ||
242 | { | ||
243 | reg_write(base, reg, reg_read(base, reg) | mask); | ||
244 | } | ||
245 | |||
246 | static inline void reg_clear(void __iomem *base, u32 reg, u32 mask) | ||
247 | { | ||
248 | reg_write(base, reg, reg_read(base, reg) & ~mask); | ||
249 | } | ||
250 | |||
251 | static inline void reg_update_bits(void __iomem *base, u32 reg, u32 mask, | ||
252 | u32 val) | ||
253 | { | ||
254 | reg_write(base, reg, (reg_read(base, reg) & ~mask) | val); | ||
255 | } | ||
256 | |||
257 | static inline struct ltdc_device *crtc_to_ltdc(struct drm_crtc *crtc) | ||
258 | { | ||
259 | return (struct ltdc_device *)crtc->dev->dev_private; | ||
260 | } | ||
261 | |||
262 | static inline struct ltdc_device *plane_to_ltdc(struct drm_plane *plane) | ||
263 | { | ||
264 | return (struct ltdc_device *)plane->dev->dev_private; | ||
265 | } | ||
266 | |||
267 | static inline struct ltdc_device *encoder_to_ltdc(struct drm_encoder *enc) | ||
268 | { | ||
269 | return (struct ltdc_device *)enc->dev->dev_private; | ||
270 | } | ||
271 | |||
272 | static inline struct ltdc_device *connector_to_ltdc(struct drm_connector *con) | ||
273 | { | ||
274 | return (struct ltdc_device *)con->dev->dev_private; | ||
275 | } | ||
276 | |||
277 | static inline enum ltdc_pix_fmt to_ltdc_pixelformat(u32 drm_fmt) | ||
278 | { | ||
279 | enum ltdc_pix_fmt pf; | ||
280 | |||
281 | switch (drm_fmt) { | ||
282 | case DRM_FORMAT_ARGB8888: | ||
283 | case DRM_FORMAT_XRGB8888: | ||
284 | pf = PF_ARGB8888; | ||
285 | break; | ||
286 | case DRM_FORMAT_RGBA8888: | ||
287 | case DRM_FORMAT_RGBX8888: | ||
288 | pf = PF_RGBA8888; | ||
289 | break; | ||
290 | case DRM_FORMAT_RGB888: | ||
291 | pf = PF_RGB888; | ||
292 | break; | ||
293 | case DRM_FORMAT_RGB565: | ||
294 | pf = PF_RGB565; | ||
295 | break; | ||
296 | case DRM_FORMAT_ARGB1555: | ||
297 | case DRM_FORMAT_XRGB1555: | ||
298 | pf = PF_ARGB1555; | ||
299 | break; | ||
300 | case DRM_FORMAT_ARGB4444: | ||
301 | case DRM_FORMAT_XRGB4444: | ||
302 | pf = PF_ARGB4444; | ||
303 | break; | ||
304 | case DRM_FORMAT_C8: | ||
305 | pf = PF_L8; | ||
306 | break; | ||
307 | default: | ||
308 | pf = PF_NONE; | ||
309 | break; | ||
310 | /* Note: There are no DRM_FORMAT for AL44 and AL88 */ | ||
311 | } | ||
312 | |||
313 | return pf; | ||
314 | } | ||
315 | |||
316 | static inline u32 to_drm_pixelformat(enum ltdc_pix_fmt pf) | ||
317 | { | ||
318 | switch (pf) { | ||
319 | case PF_ARGB8888: | ||
320 | return DRM_FORMAT_ARGB8888; | ||
321 | case PF_RGBA8888: | ||
322 | return DRM_FORMAT_RGBA8888; | ||
323 | case PF_RGB888: | ||
324 | return DRM_FORMAT_RGB888; | ||
325 | case PF_RGB565: | ||
326 | return DRM_FORMAT_RGB565; | ||
327 | case PF_ARGB1555: | ||
328 | return DRM_FORMAT_ARGB1555; | ||
329 | case PF_ARGB4444: | ||
330 | return DRM_FORMAT_ARGB4444; | ||
331 | case PF_L8: | ||
332 | return DRM_FORMAT_C8; | ||
333 | case PF_AL44: /* No DRM support */ | ||
334 | case PF_AL88: /* No DRM support */ | ||
335 | case PF_NONE: | ||
336 | default: | ||
337 | return 0; | ||
338 | } | ||
339 | } | ||
340 | |||
341 | static irqreturn_t ltdc_irq_thread(int irq, void *arg) | ||
342 | { | ||
343 | struct drm_device *ddev = arg; | ||
344 | struct ltdc_device *ldev = ddev->dev_private; | ||
345 | struct drm_crtc *crtc = drm_crtc_from_index(ddev, 0); | ||
346 | |||
347 | /* Line IRQ : trigger the vblank event */ | ||
348 | if (ldev->irq_status & ISR_LIF) | ||
349 | drm_crtc_handle_vblank(crtc); | ||
350 | |||
351 | /* Save FIFO Underrun & Transfer Error status */ | ||
352 | mutex_lock(&ldev->err_lock); | ||
353 | if (ldev->irq_status & ISR_FUIF) | ||
354 | ldev->error_status |= ISR_FUIF; | ||
355 | if (ldev->irq_status & ISR_TERRIF) | ||
356 | ldev->error_status |= ISR_TERRIF; | ||
357 | mutex_unlock(&ldev->err_lock); | ||
358 | |||
359 | return IRQ_HANDLED; | ||
360 | } | ||
361 | |||
362 | static irqreturn_t ltdc_irq(int irq, void *arg) | ||
363 | { | ||
364 | struct drm_device *ddev = arg; | ||
365 | struct ltdc_device *ldev = ddev->dev_private; | ||
366 | |||
367 | /* Read & Clear the interrupt status */ | ||
368 | ldev->irq_status = reg_read(ldev->regs, LTDC_ISR); | ||
369 | reg_write(ldev->regs, LTDC_ICR, ldev->irq_status); | ||
370 | |||
371 | return IRQ_WAKE_THREAD; | ||
372 | } | ||
373 | |||
374 | /* | ||
375 | * DRM_CRTC | ||
376 | */ | ||
377 | |||
378 | static void ltdc_crtc_load_lut(struct drm_crtc *crtc) | ||
379 | { | ||
380 | struct ltdc_device *ldev = crtc_to_ltdc(crtc); | ||
381 | unsigned int i, lay; | ||
382 | |||
383 | for (lay = 0; lay < ldev->caps.nb_layers; lay++) | ||
384 | for (i = 0; i < 256; i++) | ||
385 | reg_write(ldev->regs, LTDC_L1CLUTWR + lay * LAY_OFS, | ||
386 | ldev->clut[i]); | ||
387 | } | ||
388 | |||
389 | static void ltdc_crtc_enable(struct drm_crtc *crtc) | ||
390 | { | ||
391 | struct ltdc_device *ldev = crtc_to_ltdc(crtc); | ||
392 | |||
393 | DRM_DEBUG_DRIVER("\n"); | ||
394 | |||
395 | /* Sets the background color value */ | ||
396 | reg_write(ldev->regs, LTDC_BCCR, BCCR_BCBLACK); | ||
397 | |||
398 | /* Enable IRQ */ | ||
399 | reg_set(ldev->regs, LTDC_IER, IER_RRIE | IER_FUIE | IER_TERRIE); | ||
400 | |||
401 | /* Immediately commit the planes */ | ||
402 | reg_set(ldev->regs, LTDC_SRCR, SRCR_IMR); | ||
403 | |||
404 | /* Enable LTDC */ | ||
405 | reg_set(ldev->regs, LTDC_GCR, GCR_LTDCEN); | ||
406 | |||
407 | drm_crtc_vblank_on(crtc); | ||
408 | } | ||
409 | |||
410 | static void ltdc_crtc_disable(struct drm_crtc *crtc) | ||
411 | { | ||
412 | struct ltdc_device *ldev = crtc_to_ltdc(crtc); | ||
413 | |||
414 | DRM_DEBUG_DRIVER("\n"); | ||
415 | |||
416 | drm_crtc_vblank_off(crtc); | ||
417 | |||
418 | /* disable LTDC */ | ||
419 | reg_clear(ldev->regs, LTDC_GCR, GCR_LTDCEN); | ||
420 | |||
421 | /* disable IRQ */ | ||
422 | reg_clear(ldev->regs, LTDC_IER, IER_RRIE | IER_FUIE | IER_TERRIE); | ||
423 | |||
424 | /* immediately commit disable of layers before switching off LTDC */ | ||
425 | reg_set(ldev->regs, LTDC_SRCR, SRCR_IMR); | ||
426 | } | ||
427 | |||
428 | static void ltdc_crtc_mode_set_nofb(struct drm_crtc *crtc) | ||
429 | { | ||
430 | struct ltdc_device *ldev = crtc_to_ltdc(crtc); | ||
431 | struct drm_display_mode *mode = &crtc->state->adjusted_mode; | ||
432 | struct videomode vm; | ||
433 | int rate = mode->clock * 1000; | ||
434 | u32 hsync, vsync, accum_hbp, accum_vbp, accum_act_w, accum_act_h; | ||
435 | u32 total_width, total_height; | ||
436 | u32 val; | ||
437 | |||
438 | drm_display_mode_to_videomode(mode, &vm); | ||
439 | |||
440 | DRM_DEBUG_DRIVER("CRTC:%d mode:%s\n", crtc->base.id, mode->name); | ||
441 | DRM_DEBUG_DRIVER("Video mode: %dx%d", vm.hactive, vm.vactive); | ||
442 | DRM_DEBUG_DRIVER(" hfp %d hbp %d hsl %d vfp %d vbp %d vsl %d\n", | ||
443 | vm.hfront_porch, vm.hback_porch, vm.hsync_len, | ||
444 | vm.vfront_porch, vm.vback_porch, vm.vsync_len); | ||
445 | |||
446 | /* Convert video timings to ltdc timings */ | ||
447 | hsync = vm.hsync_len - 1; | ||
448 | vsync = vm.vsync_len - 1; | ||
449 | accum_hbp = hsync + vm.hback_porch; | ||
450 | accum_vbp = vsync + vm.vback_porch; | ||
451 | accum_act_w = accum_hbp + vm.hactive; | ||
452 | accum_act_h = accum_vbp + vm.vactive; | ||
453 | total_width = accum_act_w + vm.hfront_porch; | ||
454 | total_height = accum_act_h + vm.vfront_porch; | ||
455 | |||
456 | clk_disable(ldev->pixel_clk); | ||
457 | |||
458 | if (clk_set_rate(ldev->pixel_clk, rate) < 0) { | ||
459 | DRM_ERROR("Cannot set rate (%dHz) for pixel clk\n", rate); | ||
460 | return; | ||
461 | } | ||
462 | |||
463 | clk_enable(ldev->pixel_clk); | ||
464 | |||
465 | /* Configures the HS, VS, DE and PC polarities. */ | ||
466 | val = HSPOL_AL | HSPOL_AL | DEPOL_AL | PCPOL_IPC; | ||
467 | |||
468 | if (vm.flags & DISPLAY_FLAGS_HSYNC_HIGH) | ||
469 | val |= HSPOL_AH; | ||
470 | |||
471 | if (vm.flags & DISPLAY_FLAGS_VSYNC_HIGH) | ||
472 | val |= VSPOL_AH; | ||
473 | |||
474 | if (vm.flags & DISPLAY_FLAGS_DE_HIGH) | ||
475 | val |= DEPOL_AH; | ||
476 | |||
477 | if (vm.flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE) | ||
478 | val |= PCPOL_IIPC; | ||
479 | |||
480 | reg_update_bits(ldev->regs, LTDC_GCR, | ||
481 | GCR_HSPOL | GCR_VSPOL | GCR_DEPOL | GCR_PCPOL, val); | ||
482 | |||
483 | /* Set Synchronization size */ | ||
484 | val = (hsync << 16) | vsync; | ||
485 | reg_update_bits(ldev->regs, LTDC_SSCR, SSCR_VSH | SSCR_HSW, val); | ||
486 | |||
487 | /* Set Accumulated Back porch */ | ||
488 | val = (accum_hbp << 16) | accum_vbp; | ||
489 | reg_update_bits(ldev->regs, LTDC_BPCR, BPCR_AVBP | BPCR_AHBP, val); | ||
490 | |||
491 | /* Set Accumulated Active Width */ | ||
492 | val = (accum_act_w << 16) | accum_act_h; | ||
493 | reg_update_bits(ldev->regs, LTDC_AWCR, AWCR_AAW | AWCR_AAH, val); | ||
494 | |||
495 | /* Set total width & height */ | ||
496 | val = (total_width << 16) | total_height; | ||
497 | reg_update_bits(ldev->regs, LTDC_TWCR, TWCR_TOTALH | TWCR_TOTALW, val); | ||
498 | |||
499 | reg_write(ldev->regs, LTDC_LIPCR, (accum_act_h + 1)); | ||
500 | } | ||
501 | |||
502 | static void ltdc_crtc_atomic_flush(struct drm_crtc *crtc, | ||
503 | struct drm_crtc_state *old_crtc_state) | ||
504 | { | ||
505 | struct ltdc_device *ldev = crtc_to_ltdc(crtc); | ||
506 | struct drm_pending_vblank_event *event = crtc->state->event; | ||
507 | |||
508 | DRM_DEBUG_ATOMIC("\n"); | ||
509 | |||
510 | /* Commit shadow registers = update planes at next vblank */ | ||
511 | reg_set(ldev->regs, LTDC_SRCR, SRCR_VBR); | ||
512 | |||
513 | if (event) { | ||
514 | crtc->state->event = NULL; | ||
515 | |||
516 | spin_lock_irq(&crtc->dev->event_lock); | ||
517 | if (drm_crtc_vblank_get(crtc) == 0) | ||
518 | drm_crtc_arm_vblank_event(crtc, event); | ||
519 | else | ||
520 | drm_crtc_send_vblank_event(crtc, event); | ||
521 | spin_unlock_irq(&crtc->dev->event_lock); | ||
522 | } | ||
523 | } | ||
524 | |||
525 | static struct drm_crtc_helper_funcs ltdc_crtc_helper_funcs = { | ||
526 | .load_lut = ltdc_crtc_load_lut, | ||
527 | .enable = ltdc_crtc_enable, | ||
528 | .disable = ltdc_crtc_disable, | ||
529 | .mode_set_nofb = ltdc_crtc_mode_set_nofb, | ||
530 | .atomic_flush = ltdc_crtc_atomic_flush, | ||
531 | }; | ||
532 | |||
533 | int ltdc_crtc_enable_vblank(struct drm_device *ddev, unsigned int pipe) | ||
534 | { | ||
535 | struct ltdc_device *ldev = ddev->dev_private; | ||
536 | |||
537 | DRM_DEBUG_DRIVER("\n"); | ||
538 | reg_set(ldev->regs, LTDC_IER, IER_LIE); | ||
539 | |||
540 | return 0; | ||
541 | } | ||
542 | |||
543 | void ltdc_crtc_disable_vblank(struct drm_device *ddev, unsigned int pipe) | ||
544 | { | ||
545 | struct ltdc_device *ldev = ddev->dev_private; | ||
546 | |||
547 | DRM_DEBUG_DRIVER("\n"); | ||
548 | reg_clear(ldev->regs, LTDC_IER, IER_LIE); | ||
549 | } | ||
550 | |||
551 | static struct drm_crtc_funcs ltdc_crtc_funcs = { | ||
552 | .destroy = drm_crtc_cleanup, | ||
553 | .set_config = drm_atomic_helper_set_config, | ||
554 | .page_flip = drm_atomic_helper_page_flip, | ||
555 | .reset = drm_atomic_helper_crtc_reset, | ||
556 | .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, | ||
557 | .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, | ||
558 | }; | ||
559 | |||
560 | /* | ||
561 | * DRM_PLANE | ||
562 | */ | ||
563 | |||
564 | static int ltdc_plane_atomic_check(struct drm_plane *plane, | ||
565 | struct drm_plane_state *state) | ||
566 | { | ||
567 | struct drm_framebuffer *fb = state->fb; | ||
568 | u32 src_x, src_y, src_w, src_h; | ||
569 | |||
570 | DRM_DEBUG_DRIVER("\n"); | ||
571 | |||
572 | if (!fb) | ||
573 | return 0; | ||
574 | |||
575 | /* convert src_ from 16:16 format */ | ||
576 | src_x = state->src_x >> 16; | ||
577 | src_y = state->src_y >> 16; | ||
578 | src_w = state->src_w >> 16; | ||
579 | src_h = state->src_h >> 16; | ||
580 | |||
581 | /* Reject scaling */ | ||
582 | if ((src_w != state->crtc_w) || (src_h != state->crtc_h)) { | ||
583 | DRM_ERROR("Scaling is not supported"); | ||
584 | return -EINVAL; | ||
585 | } | ||
586 | |||
587 | return 0; | ||
588 | } | ||
589 | |||
590 | static void ltdc_plane_atomic_update(struct drm_plane *plane, | ||
591 | struct drm_plane_state *oldstate) | ||
592 | { | ||
593 | struct ltdc_device *ldev = plane_to_ltdc(plane); | ||
594 | struct drm_plane_state *state = plane->state; | ||
595 | struct drm_framebuffer *fb = state->fb; | ||
596 | u32 lofs = plane->index * LAY_OFS; | ||
597 | u32 x0 = state->crtc_x; | ||
598 | u32 x1 = state->crtc_x + state->crtc_w - 1; | ||
599 | u32 y0 = state->crtc_y; | ||
600 | u32 y1 = state->crtc_y + state->crtc_h - 1; | ||
601 | u32 src_x, src_y, src_w, src_h; | ||
602 | u32 val, pitch_in_bytes, line_length, paddr, ahbp, avbp, bpcr; | ||
603 | enum ltdc_pix_fmt pf; | ||
604 | |||
605 | if (!state->crtc || !fb) { | ||
606 | DRM_DEBUG_DRIVER("fb or crtc NULL"); | ||
607 | return; | ||
608 | } | ||
609 | |||
610 | /* convert src_ from 16:16 format */ | ||
611 | src_x = state->src_x >> 16; | ||
612 | src_y = state->src_y >> 16; | ||
613 | src_w = state->src_w >> 16; | ||
614 | src_h = state->src_h >> 16; | ||
615 | |||
616 | DRM_DEBUG_DRIVER( | ||
617 | "plane:%d fb:%d (%dx%d)@(%d,%d) -> (%dx%d)@(%d,%d)\n", | ||
618 | plane->base.id, fb->base.id, | ||
619 | src_w, src_h, src_x, src_y, | ||
620 | state->crtc_w, state->crtc_h, state->crtc_x, state->crtc_y); | ||
621 | |||
622 | bpcr = reg_read(ldev->regs, LTDC_BPCR); | ||
623 | ahbp = (bpcr & BPCR_AHBP) >> 16; | ||
624 | avbp = bpcr & BPCR_AVBP; | ||
625 | |||
626 | /* Configures the horizontal start and stop position */ | ||
627 | val = ((x1 + 1 + ahbp) << 16) + (x0 + 1 + ahbp); | ||
628 | reg_update_bits(ldev->regs, LTDC_L1WHPCR + lofs, | ||
629 | LXWHPCR_WHSTPOS | LXWHPCR_WHSPPOS, val); | ||
630 | |||
631 | /* Configures the vertical start and stop position */ | ||
632 | val = ((y1 + 1 + avbp) << 16) + (y0 + 1 + avbp); | ||
633 | reg_update_bits(ldev->regs, LTDC_L1WVPCR + lofs, | ||
634 | LXWVPCR_WVSTPOS | LXWVPCR_WVSPPOS, val); | ||
635 | |||
636 | /* Specifies the pixel format */ | ||
637 | pf = to_ltdc_pixelformat(fb->format->format); | ||
638 | for (val = 0; val < NB_PF; val++) | ||
639 | if (ldev->caps.pix_fmt_hw[val] == pf) | ||
640 | break; | ||
641 | |||
642 | if (val == NB_PF) { | ||
643 | DRM_ERROR("Pixel format %.4s not supported\n", | ||
644 | (char *)&fb->format->format); | ||
645 | val = 0; /* set by default ARGB 32 bits */ | ||
646 | } | ||
647 | reg_update_bits(ldev->regs, LTDC_L1PFCR + lofs, LXPFCR_PF, val); | ||
648 | |||
649 | /* Configures the color frame buffer pitch in bytes & line length */ | ||
650 | pitch_in_bytes = fb->pitches[0]; | ||
651 | line_length = drm_format_plane_cpp(fb->format->format, 0) * | ||
652 | (x1 - x0 + 1) + (ldev->caps.bus_width >> 3) - 1; | ||
653 | val = ((pitch_in_bytes << 16) | line_length); | ||
654 | reg_update_bits(ldev->regs, LTDC_L1CFBLR + lofs, | ||
655 | LXCFBLR_CFBLL | LXCFBLR_CFBP, val); | ||
656 | |||
657 | /* Specifies the constant alpha value */ | ||
658 | val = CONSTA_MAX; | ||
659 | reg_update_bits(ldev->regs, LTDC_L1CACR + lofs, | ||
660 | LXCACR_CONSTA, val); | ||
661 | |||
662 | /* Specifies the blending factors */ | ||
663 | val = BF1_PAXCA | BF2_1PAXCA; | ||
664 | reg_update_bits(ldev->regs, LTDC_L1BFCR + lofs, | ||
665 | LXBFCR_BF2 | LXBFCR_BF1, val); | ||
666 | |||
667 | /* Configures the frame buffer line number */ | ||
668 | val = y1 - y0 + 1; | ||
669 | reg_update_bits(ldev->regs, LTDC_L1CFBLNR + lofs, | ||
670 | LXCFBLNR_CFBLN, val); | ||
671 | |||
672 | /* Sets the FB address */ | ||
673 | paddr = (u32)drm_fb_cma_get_gem_addr(fb, state, 0); | ||
674 | |||
675 | DRM_DEBUG_DRIVER("fb: phys 0x%08x", paddr); | ||
676 | reg_write(ldev->regs, LTDC_L1CFBAR + lofs, paddr); | ||
677 | |||
678 | /* Enable layer and CLUT if needed */ | ||
679 | val = fb->format->format == DRM_FORMAT_C8 ? LXCR_CLUTEN : 0; | ||
680 | val |= LXCR_LEN; | ||
681 | reg_update_bits(ldev->regs, LTDC_L1CR + lofs, | ||
682 | LXCR_LEN | LXCR_CLUTEN, val); | ||
683 | |||
684 | mutex_lock(&ldev->err_lock); | ||
685 | if (ldev->error_status & ISR_FUIF) { | ||
686 | DRM_DEBUG_DRIVER("Fifo underrun\n"); | ||
687 | ldev->error_status &= ~ISR_FUIF; | ||
688 | } | ||
689 | if (ldev->error_status & ISR_TERRIF) { | ||
690 | DRM_DEBUG_DRIVER("Transfer error\n"); | ||
691 | ldev->error_status &= ~ISR_TERRIF; | ||
692 | } | ||
693 | mutex_unlock(&ldev->err_lock); | ||
694 | } | ||
695 | |||
696 | static void ltdc_plane_atomic_disable(struct drm_plane *plane, | ||
697 | struct drm_plane_state *oldstate) | ||
698 | { | ||
699 | struct ltdc_device *ldev = plane_to_ltdc(plane); | ||
700 | u32 lofs = plane->index * LAY_OFS; | ||
701 | |||
702 | /* disable layer */ | ||
703 | reg_clear(ldev->regs, LTDC_L1CR + lofs, LXCR_LEN); | ||
704 | |||
705 | DRM_DEBUG_DRIVER("CRTC:%d plane:%d\n", | ||
706 | oldstate->crtc->base.id, plane->base.id); | ||
707 | } | ||
708 | |||
709 | static struct drm_plane_funcs ltdc_plane_funcs = { | ||
710 | .update_plane = drm_atomic_helper_update_plane, | ||
711 | .disable_plane = drm_atomic_helper_disable_plane, | ||
712 | .destroy = drm_plane_cleanup, | ||
713 | .set_property = drm_atomic_helper_plane_set_property, | ||
714 | .reset = drm_atomic_helper_plane_reset, | ||
715 | .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, | ||
716 | .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, | ||
717 | }; | ||
718 | |||
719 | static const struct drm_plane_helper_funcs ltdc_plane_helper_funcs = { | ||
720 | .atomic_check = ltdc_plane_atomic_check, | ||
721 | .atomic_update = ltdc_plane_atomic_update, | ||
722 | .atomic_disable = ltdc_plane_atomic_disable, | ||
723 | }; | ||
724 | |||
725 | static struct drm_plane *ltdc_plane_create(struct drm_device *ddev, | ||
726 | enum drm_plane_type type) | ||
727 | { | ||
728 | unsigned long possible_crtcs = CRTC_MASK; | ||
729 | struct ltdc_device *ldev = ddev->dev_private; | ||
730 | struct device *dev = ddev->dev; | ||
731 | struct drm_plane *plane; | ||
732 | unsigned int i, nb_fmt = 0; | ||
733 | u32 formats[NB_PF]; | ||
734 | u32 drm_fmt; | ||
735 | int ret; | ||
736 | |||
737 | /* Get supported pixel formats */ | ||
738 | for (i = 0; i < NB_PF; i++) { | ||
739 | drm_fmt = to_drm_pixelformat(ldev->caps.pix_fmt_hw[i]); | ||
740 | if (!drm_fmt) | ||
741 | continue; | ||
742 | formats[nb_fmt++] = drm_fmt; | ||
743 | } | ||
744 | |||
745 | plane = devm_kzalloc(dev, sizeof(*plane), GFP_KERNEL); | ||
746 | if (!plane) | ||
747 | return 0; | ||
748 | |||
749 | ret = drm_universal_plane_init(ddev, plane, possible_crtcs, | ||
750 | <dc_plane_funcs, formats, nb_fmt, | ||
751 | type, NULL); | ||
752 | if (ret < 0) | ||
753 | return 0; | ||
754 | |||
755 | drm_plane_helper_add(plane, <dc_plane_helper_funcs); | ||
756 | |||
757 | DRM_DEBUG_DRIVER("plane:%d created\n", plane->base.id); | ||
758 | |||
759 | return plane; | ||
760 | } | ||
761 | |||
762 | static void ltdc_plane_destroy_all(struct drm_device *ddev) | ||
763 | { | ||
764 | struct drm_plane *plane, *plane_temp; | ||
765 | |||
766 | list_for_each_entry_safe(plane, plane_temp, | ||
767 | &ddev->mode_config.plane_list, head) | ||
768 | drm_plane_cleanup(plane); | ||
769 | } | ||
770 | |||
771 | static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc) | ||
772 | { | ||
773 | struct ltdc_device *ldev = ddev->dev_private; | ||
774 | struct drm_plane *primary, *overlay; | ||
775 | unsigned int i; | ||
776 | int res; | ||
777 | |||
778 | primary = ltdc_plane_create(ddev, DRM_PLANE_TYPE_PRIMARY); | ||
779 | if (!primary) { | ||
780 | DRM_ERROR("Can not create primary plane\n"); | ||
781 | return -EINVAL; | ||
782 | } | ||
783 | |||
784 | res = drm_crtc_init_with_planes(ddev, crtc, primary, NULL, | ||
785 | <dc_crtc_funcs, NULL); | ||
786 | if (res) { | ||
787 | DRM_ERROR("Can not initialize CRTC\n"); | ||
788 | goto cleanup; | ||
789 | } | ||
790 | |||
791 | drm_crtc_helper_add(crtc, <dc_crtc_helper_funcs); | ||
792 | |||
793 | DRM_DEBUG_DRIVER("CRTC:%d created\n", crtc->base.id); | ||
794 | |||
795 | /* Add planes. Note : the first layer is used by primary plane */ | ||
796 | for (i = 1; i < ldev->caps.nb_layers; i++) { | ||
797 | overlay = ltdc_plane_create(ddev, DRM_PLANE_TYPE_OVERLAY); | ||
798 | if (!overlay) { | ||
799 | res = -ENOMEM; | ||
800 | DRM_ERROR("Can not create overlay plane %d\n", i); | ||
801 | goto cleanup; | ||
802 | } | ||
803 | } | ||
804 | |||
805 | return 0; | ||
806 | |||
807 | cleanup: | ||
808 | ltdc_plane_destroy_all(ddev); | ||
809 | return res; | ||
810 | } | ||
811 | |||
812 | /* | ||
813 | * DRM_ENCODER | ||
814 | */ | ||
815 | |||
816 | static void ltdc_rgb_encoder_enable(struct drm_encoder *encoder) | ||
817 | { | ||
818 | struct ltdc_device *ldev = encoder_to_ltdc(encoder); | ||
819 | |||
820 | DRM_DEBUG_DRIVER("\n"); | ||
821 | |||
822 | drm_panel_prepare(ldev->panel); | ||
823 | drm_panel_enable(ldev->panel); | ||
824 | } | ||
825 | |||
826 | static void ltdc_rgb_encoder_disable(struct drm_encoder *encoder) | ||
827 | { | ||
828 | struct ltdc_device *ldev = encoder_to_ltdc(encoder); | ||
829 | |||
830 | DRM_DEBUG_DRIVER("\n"); | ||
831 | |||
832 | drm_panel_disable(ldev->panel); | ||
833 | drm_panel_unprepare(ldev->panel); | ||
834 | } | ||
835 | |||
836 | static const struct drm_encoder_helper_funcs ltdc_rgb_encoder_helper_funcs = { | ||
837 | .enable = ltdc_rgb_encoder_enable, | ||
838 | .disable = ltdc_rgb_encoder_disable, | ||
839 | }; | ||
840 | |||
841 | static const struct drm_encoder_funcs ltdc_rgb_encoder_funcs = { | ||
842 | .destroy = drm_encoder_cleanup, | ||
843 | }; | ||
844 | |||
845 | static struct drm_encoder *ltdc_rgb_encoder_create(struct drm_device *ddev) | ||
846 | { | ||
847 | struct drm_encoder *encoder; | ||
848 | |||
849 | encoder = devm_kzalloc(ddev->dev, sizeof(*encoder), GFP_KERNEL); | ||
850 | if (!encoder) | ||
851 | return NULL; | ||
852 | |||
853 | encoder->possible_crtcs = CRTC_MASK; | ||
854 | encoder->possible_clones = 0; /* No cloning support */ | ||
855 | |||
856 | drm_encoder_init(ddev, encoder, <dc_rgb_encoder_funcs, | ||
857 | DRM_MODE_ENCODER_DPI, NULL); | ||
858 | |||
859 | drm_encoder_helper_add(encoder, <dc_rgb_encoder_helper_funcs); | ||
860 | |||
861 | DRM_DEBUG_DRIVER("RGB encoder:%d created\n", encoder->base.id); | ||
862 | |||
863 | return encoder; | ||
864 | } | ||
865 | |||
866 | /* | ||
867 | * DRM_CONNECTOR | ||
868 | */ | ||
869 | |||
870 | static int ltdc_rgb_connector_get_modes(struct drm_connector *connector) | ||
871 | { | ||
872 | struct drm_device *ddev = connector->dev; | ||
873 | struct ltdc_device *ldev = ddev->dev_private; | ||
874 | int ret = 0; | ||
875 | |||
876 | DRM_DEBUG_DRIVER("\n"); | ||
877 | |||
878 | if (ldev->panel) | ||
879 | ret = drm_panel_get_modes(ldev->panel); | ||
880 | |||
881 | return ret < 0 ? 0 : ret; | ||
882 | } | ||
883 | |||
884 | static struct drm_connector_helper_funcs ltdc_rgb_connector_helper_funcs = { | ||
885 | .get_modes = ltdc_rgb_connector_get_modes, | ||
886 | }; | ||
887 | |||
888 | static enum drm_connector_status | ||
889 | ltdc_rgb_connector_detect(struct drm_connector *connector, bool force) | ||
890 | { | ||
891 | struct ltdc_device *ldev = connector_to_ltdc(connector); | ||
892 | |||
893 | return ldev->panel ? connector_status_connected : | ||
894 | connector_status_disconnected; | ||
895 | } | ||
896 | |||
897 | static void ltdc_rgb_connector_destroy(struct drm_connector *connector) | ||
898 | { | ||
899 | DRM_DEBUG_DRIVER("\n"); | ||
900 | |||
901 | drm_connector_unregister(connector); | ||
902 | drm_connector_cleanup(connector); | ||
903 | } | ||
904 | |||
905 | static const struct drm_connector_funcs ltdc_rgb_connector_funcs = { | ||
906 | .dpms = drm_atomic_helper_connector_dpms, | ||
907 | .fill_modes = drm_helper_probe_single_connector_modes, | ||
908 | .detect = ltdc_rgb_connector_detect, | ||
909 | .destroy = ltdc_rgb_connector_destroy, | ||
910 | .reset = drm_atomic_helper_connector_reset, | ||
911 | .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, | ||
912 | .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, | ||
913 | }; | ||
914 | |||
915 | struct drm_connector *ltdc_rgb_connector_create(struct drm_device *ddev) | ||
916 | { | ||
917 | struct drm_connector *connector; | ||
918 | int err; | ||
919 | |||
920 | connector = devm_kzalloc(ddev->dev, sizeof(*connector), GFP_KERNEL); | ||
921 | if (!connector) { | ||
922 | DRM_ERROR("Failed to allocate connector\n"); | ||
923 | return NULL; | ||
924 | } | ||
925 | |||
926 | connector->polled = DRM_CONNECTOR_POLL_HPD; | ||
927 | |||
928 | err = drm_connector_init(ddev, connector, <dc_rgb_connector_funcs, | ||
929 | DRM_MODE_CONNECTOR_DPI); | ||
930 | if (err) { | ||
931 | DRM_ERROR("Failed to initialize connector\n"); | ||
932 | return NULL; | ||
933 | } | ||
934 | |||
935 | drm_connector_helper_add(connector, <dc_rgb_connector_helper_funcs); | ||
936 | |||
937 | DRM_DEBUG_DRIVER("RGB connector:%d created\n", connector->base.id); | ||
938 | |||
939 | return connector; | ||
940 | } | ||
941 | |||
942 | static int ltdc_get_caps(struct drm_device *ddev) | ||
943 | { | ||
944 | struct ltdc_device *ldev = ddev->dev_private; | ||
945 | u32 bus_width_log2, lcr, gc2r; | ||
946 | |||
947 | /* at least 1 layer must be managed */ | ||
948 | lcr = reg_read(ldev->regs, LTDC_LCR); | ||
949 | |||
950 | ldev->caps.nb_layers = max_t(int, lcr, 1); | ||
951 | |||
952 | /* set data bus width */ | ||
953 | gc2r = reg_read(ldev->regs, LTDC_GC2R); | ||
954 | bus_width_log2 = (gc2r & GC2R_BW) >> 4; | ||
955 | ldev->caps.bus_width = 8 << bus_width_log2; | ||
956 | ldev->caps.hw_version = reg_read(ldev->regs, LTDC_IDR); | ||
957 | |||
958 | switch (ldev->caps.hw_version) { | ||
959 | case HWVER_10200: | ||
960 | case HWVER_10300: | ||
961 | ldev->caps.reg_ofs = REG_OFS_NONE; | ||
962 | ldev->caps.pix_fmt_hw = ltdc_pix_fmt_a0; | ||
963 | break; | ||
964 | case HWVER_20101: | ||
965 | ldev->caps.reg_ofs = REG_OFS_4; | ||
966 | ldev->caps.pix_fmt_hw = ltdc_pix_fmt_a1; | ||
967 | break; | ||
968 | default: | ||
969 | return -ENODEV; | ||
970 | } | ||
971 | |||
972 | return 0; | ||
973 | } | ||
974 | |||
975 | static struct drm_panel *ltdc_get_panel(struct drm_device *ddev) | ||
976 | { | ||
977 | struct device *dev = ddev->dev; | ||
978 | struct device_node *np = dev->of_node; | ||
979 | struct device_node *entity, *port = NULL; | ||
980 | struct drm_panel *panel = NULL; | ||
981 | |||
982 | DRM_DEBUG_DRIVER("\n"); | ||
983 | |||
984 | /* | ||
985 | * Parse ltdc node to get remote port and find RGB panel / HDMI slave | ||
986 | * If a dsi or a bridge (hdmi, lvds...) is connected to ltdc, | ||
987 | * a remote port & RGB panel will not be found. | ||
988 | */ | ||
989 | for_each_endpoint_of_node(np, entity) { | ||
990 | if (!of_device_is_available(entity)) | ||
991 | continue; | ||
992 | |||
993 | port = of_graph_get_remote_port_parent(entity); | ||
994 | if (port) { | ||
995 | panel = of_drm_find_panel(port); | ||
996 | of_node_put(port); | ||
997 | if (panel) { | ||
998 | DRM_DEBUG_DRIVER("remote panel %s\n", | ||
999 | port->full_name); | ||
1000 | } else { | ||
1001 | DRM_DEBUG_DRIVER("panel missing\n"); | ||
1002 | of_node_put(entity); | ||
1003 | } | ||
1004 | } | ||
1005 | } | ||
1006 | |||
1007 | return panel; | ||
1008 | } | ||
1009 | |||
1010 | int ltdc_load(struct drm_device *ddev) | ||
1011 | { | ||
1012 | struct platform_device *pdev = to_platform_device(ddev->dev); | ||
1013 | struct ltdc_device *ldev = ddev->dev_private; | ||
1014 | struct device *dev = ddev->dev; | ||
1015 | struct device_node *np = dev->of_node; | ||
1016 | struct drm_encoder *encoder; | ||
1017 | struct drm_connector *connector = NULL; | ||
1018 | struct drm_crtc *crtc; | ||
1019 | struct reset_control *rstc; | ||
1020 | struct resource res; | ||
1021 | int irq, ret, i; | ||
1022 | |||
1023 | DRM_DEBUG_DRIVER("\n"); | ||
1024 | |||
1025 | ldev->panel = ltdc_get_panel(ddev); | ||
1026 | if (!ldev->panel) | ||
1027 | return -EPROBE_DEFER; | ||
1028 | |||
1029 | rstc = of_reset_control_get(np, NULL); | ||
1030 | |||
1031 | mutex_init(&ldev->err_lock); | ||
1032 | |||
1033 | ldev->pixel_clk = devm_clk_get(dev, "lcd"); | ||
1034 | if (IS_ERR(ldev->pixel_clk)) { | ||
1035 | DRM_ERROR("Unable to get lcd clock\n"); | ||
1036 | return -ENODEV; | ||
1037 | } | ||
1038 | |||
1039 | if (clk_prepare_enable(ldev->pixel_clk)) { | ||
1040 | DRM_ERROR("Unable to prepare pixel clock\n"); | ||
1041 | return -ENODEV; | ||
1042 | } | ||
1043 | |||
1044 | if (of_address_to_resource(np, 0, &res)) { | ||
1045 | DRM_ERROR("Unable to get resource\n"); | ||
1046 | return -ENODEV; | ||
1047 | } | ||
1048 | |||
1049 | ldev->regs = devm_ioremap_resource(dev, &res); | ||
1050 | if (IS_ERR(ldev->regs)) { | ||
1051 | DRM_ERROR("Unable to get ltdc registers\n"); | ||
1052 | return PTR_ERR(ldev->regs); | ||
1053 | } | ||
1054 | |||
1055 | for (i = 0; i < MAX_IRQ; i++) { | ||
1056 | irq = platform_get_irq(pdev, i); | ||
1057 | if (irq < 0) | ||
1058 | continue; | ||
1059 | |||
1060 | ret = devm_request_threaded_irq(dev, irq, ltdc_irq, | ||
1061 | ltdc_irq_thread, IRQF_ONESHOT, | ||
1062 | dev_name(dev), ddev); | ||
1063 | if (ret) { | ||
1064 | DRM_ERROR("Failed to register LTDC interrupt\n"); | ||
1065 | return ret; | ||
1066 | } | ||
1067 | } | ||
1068 | |||
1069 | if (!IS_ERR(rstc)) | ||
1070 | reset_control_deassert(rstc); | ||
1071 | |||
1072 | /* Disable interrupts */ | ||
1073 | reg_clear(ldev->regs, LTDC_IER, | ||
1074 | IER_LIE | IER_RRIE | IER_FUIE | IER_TERRIE); | ||
1075 | |||
1076 | ret = ltdc_get_caps(ddev); | ||
1077 | if (ret) { | ||
1078 | DRM_ERROR("hardware identifier (0x%08x) not supported!\n", | ||
1079 | ldev->caps.hw_version); | ||
1080 | return ret; | ||
1081 | } | ||
1082 | |||
1083 | DRM_INFO("ltdc hw version 0x%08x - ready\n", ldev->caps.hw_version); | ||
1084 | |||
1085 | if (ldev->panel) { | ||
1086 | encoder = ltdc_rgb_encoder_create(ddev); | ||
1087 | if (!encoder) { | ||
1088 | DRM_ERROR("Failed to create RGB encoder\n"); | ||
1089 | ret = -EINVAL; | ||
1090 | goto err; | ||
1091 | } | ||
1092 | |||
1093 | connector = ltdc_rgb_connector_create(ddev); | ||
1094 | if (!connector) { | ||
1095 | DRM_ERROR("Failed to create RGB connector\n"); | ||
1096 | ret = -EINVAL; | ||
1097 | goto err; | ||
1098 | } | ||
1099 | |||
1100 | ret = drm_mode_connector_attach_encoder(connector, encoder); | ||
1101 | if (ret) { | ||
1102 | DRM_ERROR("Failed to attach connector to encoder\n"); | ||
1103 | goto err; | ||
1104 | } | ||
1105 | |||
1106 | drm_panel_attach(ldev->panel, connector); | ||
1107 | } | ||
1108 | |||
1109 | crtc = devm_kzalloc(dev, sizeof(*crtc), GFP_KERNEL); | ||
1110 | if (!crtc) { | ||
1111 | DRM_ERROR("Failed to allocate crtc\n"); | ||
1112 | ret = -ENOMEM; | ||
1113 | goto err; | ||
1114 | } | ||
1115 | |||
1116 | ret = ltdc_crtc_init(ddev, crtc); | ||
1117 | if (ret) { | ||
1118 | DRM_ERROR("Failed to init crtc\n"); | ||
1119 | goto err; | ||
1120 | } | ||
1121 | |||
1122 | ret = drm_vblank_init(ddev, NB_CRTC); | ||
1123 | if (ret) { | ||
1124 | DRM_ERROR("Failed calling drm_vblank_init()\n"); | ||
1125 | goto err; | ||
1126 | } | ||
1127 | |||
1128 | /* Allow usage of vblank without having to call drm_irq_install */ | ||
1129 | ddev->irq_enabled = 1; | ||
1130 | |||
1131 | return 0; | ||
1132 | err: | ||
1133 | if (ldev->panel) | ||
1134 | drm_panel_detach(ldev->panel); | ||
1135 | |||
1136 | clk_disable_unprepare(ldev->pixel_clk); | ||
1137 | |||
1138 | return ret; | ||
1139 | } | ||
1140 | |||
1141 | void ltdc_unload(struct drm_device *ddev) | ||
1142 | { | ||
1143 | struct ltdc_device *ldev = ddev->dev_private; | ||
1144 | |||
1145 | DRM_DEBUG_DRIVER("\n"); | ||
1146 | |||
1147 | drm_vblank_cleanup(ddev); | ||
1148 | |||
1149 | if (ldev->panel) | ||
1150 | drm_panel_detach(ldev->panel); | ||
1151 | |||
1152 | clk_disable_unprepare(ldev->pixel_clk); | ||
1153 | } | ||
1154 | |||
1155 | MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>"); | ||
1156 | MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>"); | ||
1157 | MODULE_AUTHOR("Fabien Dessenne <fabien.dessenne@st.com>"); | ||
1158 | MODULE_AUTHOR("Mickael Reulier <mickael.reulier@st.com>"); | ||
1159 | MODULE_DESCRIPTION("STMicroelectronics ST DRM LTDC driver"); | ||
1160 | MODULE_LICENSE("GPL v2"); | ||