]>
Commit | Line | Data |
---|---|---|
b759012c YF |
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. */ | |
c4d3fd46 | 466 | val = HSPOL_AL | VSPOL_AL | DEPOL_AL | PCPOL_IPC; |
b759012c YF |
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"); |