1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) 2018 Linus Walleij <linus.walleij@linaro.org> |
4 | * Parts of this file were based on the MCDE driver by Marcus Lorentzon |
5 | * (C) ST-Ericsson SA 2013 |
6 | */ |
7 | #include <linux/clk.h> |
8 | #include <linux/delay.h> |
9 | #include <linux/dma-buf.h> |
10 | #include <linux/regulator/consumer.h> |
11 | #include <linux/media-bus-format.h> |
12 | |
13 | #include <drm/drm_device.h> |
14 | #include <drm/drm_fb_dma_helper.h> |
15 | #include <drm/drm_fourcc.h> |
16 | #include <drm/drm_framebuffer.h> |
17 | #include <drm/drm_gem_atomic_helper.h> |
18 | #include <drm/drm_gem_dma_helper.h> |
19 | #include <drm/drm_mipi_dsi.h> |
20 | #include <drm/drm_simple_kms_helper.h> |
21 | #include <drm/drm_bridge.h> |
22 | #include <drm/drm_vblank.h> |
23 | #include <video/mipi_display.h> |
24 | |
25 | #include "mcde_drm.h" |
26 | #include "mcde_display_regs.h" |
27 | |
28 | enum mcde_fifo { |
29 | MCDE_FIFO_A, |
30 | MCDE_FIFO_B, |
31 | /* TODO: implement FIFO C0 and FIFO C1 */ |
32 | }; |
33 | |
34 | enum mcde_channel { |
35 | MCDE_CHANNEL_0 = 0, |
36 | MCDE_CHANNEL_1, |
37 | MCDE_CHANNEL_2, |
38 | MCDE_CHANNEL_3, |
39 | }; |
40 | |
41 | enum mcde_extsrc { |
42 | MCDE_EXTSRC_0 = 0, |
43 | MCDE_EXTSRC_1, |
44 | MCDE_EXTSRC_2, |
45 | MCDE_EXTSRC_3, |
46 | MCDE_EXTSRC_4, |
47 | MCDE_EXTSRC_5, |
48 | MCDE_EXTSRC_6, |
49 | MCDE_EXTSRC_7, |
50 | MCDE_EXTSRC_8, |
51 | MCDE_EXTSRC_9, |
52 | }; |
53 | |
54 | enum mcde_overlay { |
55 | MCDE_OVERLAY_0 = 0, |
56 | MCDE_OVERLAY_1, |
57 | MCDE_OVERLAY_2, |
58 | MCDE_OVERLAY_3, |
59 | MCDE_OVERLAY_4, |
60 | MCDE_OVERLAY_5, |
61 | }; |
62 | |
63 | enum mcde_formatter { |
64 | MCDE_DSI_FORMATTER_0 = 0, |
65 | MCDE_DSI_FORMATTER_1, |
66 | MCDE_DSI_FORMATTER_2, |
67 | MCDE_DSI_FORMATTER_3, |
68 | MCDE_DSI_FORMATTER_4, |
69 | MCDE_DSI_FORMATTER_5, |
70 | MCDE_DPI_FORMATTER_0, |
71 | MCDE_DPI_FORMATTER_1, |
72 | }; |
73 | |
74 | void mcde_display_irq(struct mcde *mcde) |
75 | { |
76 | u32 mispp, misovl, mischnl; |
77 | bool vblank = false; |
78 | |
79 | /* Handle display IRQs */ |
80 | mispp = readl(addr: mcde->regs + MCDE_MISPP); |
81 | misovl = readl(addr: mcde->regs + MCDE_MISOVL); |
82 | mischnl = readl(addr: mcde->regs + MCDE_MISCHNL); |
83 | |
84 | /* |
85 | * Handle IRQs from the DSI link. All IRQs from the DSI links |
86 | * are just latched onto the MCDE IRQ line, so we need to traverse |
87 | * any active DSI masters and check if an IRQ is originating from |
88 | * them. |
89 | * |
90 | * TODO: Currently only one DSI link is supported. |
91 | */ |
92 | if (!mcde->dpi_output && mcde_dsi_irq(mdsi: mcde->mdsi)) { |
93 | u32 val; |
94 | |
95 | /* |
96 | * In oneshot mode we do not send continuous updates |
97 | * to the display, instead we only push out updates when |
98 | * the update function is called, then we disable the |
99 | * flow on the channel once we get the TE IRQ. |
100 | */ |
101 | if (mcde->flow_mode == MCDE_COMMAND_ONESHOT_FLOW) { |
102 | spin_lock(lock: &mcde->flow_lock); |
103 | if (--mcde->flow_active == 0) { |
104 | dev_dbg(mcde->dev, "TE0 IRQ\n" ); |
105 | /* Disable FIFO A flow */ |
106 | val = readl(addr: mcde->regs + MCDE_CRA0); |
107 | val &= ~MCDE_CRX0_FLOEN; |
108 | writel(val, addr: mcde->regs + MCDE_CRA0); |
109 | } |
110 | spin_unlock(lock: &mcde->flow_lock); |
111 | } |
112 | } |
113 | |
114 | /* Vblank from one of the channels */ |
115 | if (mispp & MCDE_PP_VCMPA) { |
116 | dev_dbg(mcde->dev, "chnl A vblank IRQ\n" ); |
117 | vblank = true; |
118 | } |
119 | if (mispp & MCDE_PP_VCMPB) { |
120 | dev_dbg(mcde->dev, "chnl B vblank IRQ\n" ); |
121 | vblank = true; |
122 | } |
123 | if (mispp & MCDE_PP_VCMPC0) |
124 | dev_dbg(mcde->dev, "chnl C0 vblank IRQ\n" ); |
125 | if (mispp & MCDE_PP_VCMPC1) |
126 | dev_dbg(mcde->dev, "chnl C1 vblank IRQ\n" ); |
127 | if (mispp & MCDE_PP_VSCC0) |
128 | dev_dbg(mcde->dev, "chnl C0 TE IRQ\n" ); |
129 | if (mispp & MCDE_PP_VSCC1) |
130 | dev_dbg(mcde->dev, "chnl C1 TE IRQ\n" ); |
131 | writel(val: mispp, addr: mcde->regs + MCDE_RISPP); |
132 | |
133 | if (vblank) |
134 | drm_crtc_handle_vblank(crtc: &mcde->pipe.crtc); |
135 | |
136 | if (misovl) |
137 | dev_info(mcde->dev, "some stray overlay IRQ %08x\n" , misovl); |
138 | writel(val: misovl, addr: mcde->regs + MCDE_RISOVL); |
139 | |
140 | if (mischnl) |
141 | dev_info(mcde->dev, "some stray channel error IRQ %08x\n" , |
142 | mischnl); |
143 | writel(val: mischnl, addr: mcde->regs + MCDE_RISCHNL); |
144 | } |
145 | |
146 | void mcde_display_disable_irqs(struct mcde *mcde) |
147 | { |
148 | /* Disable all IRQs */ |
149 | writel(val: 0, addr: mcde->regs + MCDE_IMSCPP); |
150 | writel(val: 0, addr: mcde->regs + MCDE_IMSCOVL); |
151 | writel(val: 0, addr: mcde->regs + MCDE_IMSCCHNL); |
152 | |
153 | /* Clear any pending IRQs */ |
154 | writel(val: 0xFFFFFFFF, addr: mcde->regs + MCDE_RISPP); |
155 | writel(val: 0xFFFFFFFF, addr: mcde->regs + MCDE_RISOVL); |
156 | writel(val: 0xFFFFFFFF, addr: mcde->regs + MCDE_RISCHNL); |
157 | } |
158 | |
159 | static int mcde_display_check(struct drm_simple_display_pipe *pipe, |
160 | struct drm_plane_state *pstate, |
161 | struct drm_crtc_state *cstate) |
162 | { |
163 | const struct drm_display_mode *mode = &cstate->mode; |
164 | struct drm_framebuffer *old_fb = pipe->plane.state->fb; |
165 | struct drm_framebuffer *fb = pstate->fb; |
166 | |
167 | if (fb) { |
168 | u32 offset = drm_fb_dma_get_gem_addr(fb, state: pstate, plane: 0); |
169 | |
170 | /* FB base address must be dword aligned. */ |
171 | if (offset & 3) { |
172 | DRM_DEBUG_KMS("FB not 32-bit aligned\n" ); |
173 | return -EINVAL; |
174 | } |
175 | |
176 | /* |
177 | * There's no pitch register, the mode's hdisplay |
178 | * controls this. |
179 | */ |
180 | if (fb->pitches[0] != mode->hdisplay * fb->format->cpp[0]) { |
181 | DRM_DEBUG_KMS("can't handle pitches\n" ); |
182 | return -EINVAL; |
183 | } |
184 | |
185 | /* |
186 | * We can't change the FB format in a flicker-free |
187 | * manner (and only update it during CRTC enable). |
188 | */ |
189 | if (old_fb && old_fb->format != fb->format) |
190 | cstate->mode_changed = true; |
191 | } |
192 | |
193 | return 0; |
194 | } |
195 | |
196 | static int mcde_configure_extsrc(struct mcde *mcde, enum mcde_extsrc src, |
197 | u32 format) |
198 | { |
199 | u32 val; |
200 | u32 conf; |
201 | u32 cr; |
202 | |
203 | switch (src) { |
204 | case MCDE_EXTSRC_0: |
205 | conf = MCDE_EXTSRC0CONF; |
206 | cr = MCDE_EXTSRC0CR; |
207 | break; |
208 | case MCDE_EXTSRC_1: |
209 | conf = MCDE_EXTSRC1CONF; |
210 | cr = MCDE_EXTSRC1CR; |
211 | break; |
212 | case MCDE_EXTSRC_2: |
213 | conf = MCDE_EXTSRC2CONF; |
214 | cr = MCDE_EXTSRC2CR; |
215 | break; |
216 | case MCDE_EXTSRC_3: |
217 | conf = MCDE_EXTSRC3CONF; |
218 | cr = MCDE_EXTSRC3CR; |
219 | break; |
220 | case MCDE_EXTSRC_4: |
221 | conf = MCDE_EXTSRC4CONF; |
222 | cr = MCDE_EXTSRC4CR; |
223 | break; |
224 | case MCDE_EXTSRC_5: |
225 | conf = MCDE_EXTSRC5CONF; |
226 | cr = MCDE_EXTSRC5CR; |
227 | break; |
228 | case MCDE_EXTSRC_6: |
229 | conf = MCDE_EXTSRC6CONF; |
230 | cr = MCDE_EXTSRC6CR; |
231 | break; |
232 | case MCDE_EXTSRC_7: |
233 | conf = MCDE_EXTSRC7CONF; |
234 | cr = MCDE_EXTSRC7CR; |
235 | break; |
236 | case MCDE_EXTSRC_8: |
237 | conf = MCDE_EXTSRC8CONF; |
238 | cr = MCDE_EXTSRC8CR; |
239 | break; |
240 | case MCDE_EXTSRC_9: |
241 | conf = MCDE_EXTSRC9CONF; |
242 | cr = MCDE_EXTSRC9CR; |
243 | break; |
244 | } |
245 | |
246 | /* |
247 | * Configure external source 0 one buffer (buffer 0) |
248 | * primary overlay ID 0. |
249 | * From mcde_hw.c ovly_update_registers() in the vendor tree |
250 | */ |
251 | val = 0 << MCDE_EXTSRCXCONF_BUF_ID_SHIFT; |
252 | val |= 1 << MCDE_EXTSRCXCONF_BUF_NB_SHIFT; |
253 | val |= 0 << MCDE_EXTSRCXCONF_PRI_OVLID_SHIFT; |
254 | |
255 | switch (format) { |
256 | case DRM_FORMAT_ARGB8888: |
257 | val |= MCDE_EXTSRCXCONF_BPP_ARGB8888 << |
258 | MCDE_EXTSRCXCONF_BPP_SHIFT; |
259 | break; |
260 | case DRM_FORMAT_ABGR8888: |
261 | val |= MCDE_EXTSRCXCONF_BPP_ARGB8888 << |
262 | MCDE_EXTSRCXCONF_BPP_SHIFT; |
263 | val |= MCDE_EXTSRCXCONF_BGR; |
264 | break; |
265 | case DRM_FORMAT_XRGB8888: |
266 | val |= MCDE_EXTSRCXCONF_BPP_XRGB8888 << |
267 | MCDE_EXTSRCXCONF_BPP_SHIFT; |
268 | break; |
269 | case DRM_FORMAT_XBGR8888: |
270 | val |= MCDE_EXTSRCXCONF_BPP_XRGB8888 << |
271 | MCDE_EXTSRCXCONF_BPP_SHIFT; |
272 | val |= MCDE_EXTSRCXCONF_BGR; |
273 | break; |
274 | case DRM_FORMAT_RGB888: |
275 | val |= MCDE_EXTSRCXCONF_BPP_RGB888 << |
276 | MCDE_EXTSRCXCONF_BPP_SHIFT; |
277 | break; |
278 | case DRM_FORMAT_BGR888: |
279 | val |= MCDE_EXTSRCXCONF_BPP_RGB888 << |
280 | MCDE_EXTSRCXCONF_BPP_SHIFT; |
281 | val |= MCDE_EXTSRCXCONF_BGR; |
282 | break; |
283 | case DRM_FORMAT_ARGB4444: |
284 | val |= MCDE_EXTSRCXCONF_BPP_ARGB4444 << |
285 | MCDE_EXTSRCXCONF_BPP_SHIFT; |
286 | break; |
287 | case DRM_FORMAT_ABGR4444: |
288 | val |= MCDE_EXTSRCXCONF_BPP_ARGB4444 << |
289 | MCDE_EXTSRCXCONF_BPP_SHIFT; |
290 | val |= MCDE_EXTSRCXCONF_BGR; |
291 | break; |
292 | case DRM_FORMAT_XRGB4444: |
293 | val |= MCDE_EXTSRCXCONF_BPP_RGB444 << |
294 | MCDE_EXTSRCXCONF_BPP_SHIFT; |
295 | break; |
296 | case DRM_FORMAT_XBGR4444: |
297 | val |= MCDE_EXTSRCXCONF_BPP_RGB444 << |
298 | MCDE_EXTSRCXCONF_BPP_SHIFT; |
299 | val |= MCDE_EXTSRCXCONF_BGR; |
300 | break; |
301 | case DRM_FORMAT_XRGB1555: |
302 | val |= MCDE_EXTSRCXCONF_BPP_IRGB1555 << |
303 | MCDE_EXTSRCXCONF_BPP_SHIFT; |
304 | break; |
305 | case DRM_FORMAT_XBGR1555: |
306 | val |= MCDE_EXTSRCXCONF_BPP_IRGB1555 << |
307 | MCDE_EXTSRCXCONF_BPP_SHIFT; |
308 | val |= MCDE_EXTSRCXCONF_BGR; |
309 | break; |
310 | case DRM_FORMAT_RGB565: |
311 | val |= MCDE_EXTSRCXCONF_BPP_RGB565 << |
312 | MCDE_EXTSRCXCONF_BPP_SHIFT; |
313 | break; |
314 | case DRM_FORMAT_BGR565: |
315 | val |= MCDE_EXTSRCXCONF_BPP_RGB565 << |
316 | MCDE_EXTSRCXCONF_BPP_SHIFT; |
317 | val |= MCDE_EXTSRCXCONF_BGR; |
318 | break; |
319 | case DRM_FORMAT_YUV422: |
320 | val |= MCDE_EXTSRCXCONF_BPP_YCBCR422 << |
321 | MCDE_EXTSRCXCONF_BPP_SHIFT; |
322 | break; |
323 | default: |
324 | dev_err(mcde->dev, "Unknown pixel format 0x%08x\n" , |
325 | format); |
326 | return -EINVAL; |
327 | } |
328 | writel(val, addr: mcde->regs + conf); |
329 | |
330 | /* Software select, primary */ |
331 | val = MCDE_EXTSRCXCR_SEL_MOD_SOFTWARE_SEL; |
332 | val |= MCDE_EXTSRCXCR_MULTIOVL_CTRL_PRIMARY; |
333 | writel(val, addr: mcde->regs + cr); |
334 | |
335 | return 0; |
336 | } |
337 | |
338 | static void mcde_configure_overlay(struct mcde *mcde, enum mcde_overlay ovl, |
339 | enum mcde_extsrc src, |
340 | enum mcde_channel ch, |
341 | const struct drm_display_mode *mode, |
342 | u32 format, int cpp) |
343 | { |
344 | u32 val; |
345 | u32 conf1; |
346 | u32 conf2; |
347 | u32 crop; |
348 | u32 ljinc; |
349 | u32 cr; |
350 | u32 comp; |
351 | u32 pixel_fetcher_watermark; |
352 | |
353 | switch (ovl) { |
354 | case MCDE_OVERLAY_0: |
355 | conf1 = MCDE_OVL0CONF; |
356 | conf2 = MCDE_OVL0CONF2; |
357 | crop = MCDE_OVL0CROP; |
358 | ljinc = MCDE_OVL0LJINC; |
359 | cr = MCDE_OVL0CR; |
360 | comp = MCDE_OVL0COMP; |
361 | break; |
362 | case MCDE_OVERLAY_1: |
363 | conf1 = MCDE_OVL1CONF; |
364 | conf2 = MCDE_OVL1CONF2; |
365 | crop = MCDE_OVL1CROP; |
366 | ljinc = MCDE_OVL1LJINC; |
367 | cr = MCDE_OVL1CR; |
368 | comp = MCDE_OVL1COMP; |
369 | break; |
370 | case MCDE_OVERLAY_2: |
371 | conf1 = MCDE_OVL2CONF; |
372 | conf2 = MCDE_OVL2CONF2; |
373 | crop = MCDE_OVL2CROP; |
374 | ljinc = MCDE_OVL2LJINC; |
375 | cr = MCDE_OVL2CR; |
376 | comp = MCDE_OVL2COMP; |
377 | break; |
378 | case MCDE_OVERLAY_3: |
379 | conf1 = MCDE_OVL3CONF; |
380 | conf2 = MCDE_OVL3CONF2; |
381 | crop = MCDE_OVL3CROP; |
382 | ljinc = MCDE_OVL3LJINC; |
383 | cr = MCDE_OVL3CR; |
384 | comp = MCDE_OVL3COMP; |
385 | break; |
386 | case MCDE_OVERLAY_4: |
387 | conf1 = MCDE_OVL4CONF; |
388 | conf2 = MCDE_OVL4CONF2; |
389 | crop = MCDE_OVL4CROP; |
390 | ljinc = MCDE_OVL4LJINC; |
391 | cr = MCDE_OVL4CR; |
392 | comp = MCDE_OVL4COMP; |
393 | break; |
394 | case MCDE_OVERLAY_5: |
395 | conf1 = MCDE_OVL5CONF; |
396 | conf2 = MCDE_OVL5CONF2; |
397 | crop = MCDE_OVL5CROP; |
398 | ljinc = MCDE_OVL5LJINC; |
399 | cr = MCDE_OVL5CR; |
400 | comp = MCDE_OVL5COMP; |
401 | break; |
402 | } |
403 | |
404 | val = mode->hdisplay << MCDE_OVLXCONF_PPL_SHIFT; |
405 | val |= mode->vdisplay << MCDE_OVLXCONF_LPF_SHIFT; |
406 | /* Use external source 0 that we just configured */ |
407 | val |= src << MCDE_OVLXCONF_EXTSRC_ID_SHIFT; |
408 | writel(val, addr: mcde->regs + conf1); |
409 | |
410 | val = MCDE_OVLXCONF2_BP_PER_PIXEL_ALPHA; |
411 | val |= 0xff << MCDE_OVLXCONF2_ALPHAVALUE_SHIFT; |
412 | /* OPQ: overlay is opaque */ |
413 | switch (format) { |
414 | case DRM_FORMAT_ARGB8888: |
415 | case DRM_FORMAT_ABGR8888: |
416 | case DRM_FORMAT_ARGB4444: |
417 | case DRM_FORMAT_ABGR4444: |
418 | case DRM_FORMAT_XRGB1555: |
419 | case DRM_FORMAT_XBGR1555: |
420 | /* No OPQ */ |
421 | break; |
422 | case DRM_FORMAT_XRGB8888: |
423 | case DRM_FORMAT_XBGR8888: |
424 | case DRM_FORMAT_RGB888: |
425 | case DRM_FORMAT_BGR888: |
426 | case DRM_FORMAT_RGB565: |
427 | case DRM_FORMAT_BGR565: |
428 | case DRM_FORMAT_YUV422: |
429 | val |= MCDE_OVLXCONF2_OPQ; |
430 | break; |
431 | default: |
432 | dev_err(mcde->dev, "Unknown pixel format 0x%08x\n" , |
433 | format); |
434 | break; |
435 | } |
436 | |
437 | /* |
438 | * Pixel fetch watermark level is max 0x1FFF pixels. |
439 | * Two basic rules should be followed: |
440 | * 1. The value should be at least 256 bits. |
441 | * 2. The sum of all active overlays pixelfetch watermark level |
442 | * multiplied with bits per pixel, should be lower than the |
443 | * size of input_fifo_size in bits. |
444 | * 3. The value should be a multiple of a line (256 bits). |
445 | */ |
446 | switch (cpp) { |
447 | case 2: |
448 | pixel_fetcher_watermark = 128; |
449 | break; |
450 | case 3: |
451 | pixel_fetcher_watermark = 96; |
452 | break; |
453 | case 4: |
454 | pixel_fetcher_watermark = 48; |
455 | break; |
456 | default: |
457 | pixel_fetcher_watermark = 48; |
458 | break; |
459 | } |
460 | dev_dbg(mcde->dev, "pixel fetcher watermark level %d pixels\n" , |
461 | pixel_fetcher_watermark); |
462 | val |= pixel_fetcher_watermark << MCDE_OVLXCONF2_PIXELFETCHERWATERMARKLEVEL_SHIFT; |
463 | writel(val, addr: mcde->regs + conf2); |
464 | |
465 | /* Number of bytes to fetch per line */ |
466 | writel(val: mcde->stride, addr: mcde->regs + ljinc); |
467 | /* No cropping */ |
468 | writel(val: 0, addr: mcde->regs + crop); |
469 | |
470 | /* Set up overlay control register */ |
471 | val = MCDE_OVLXCR_OVLEN; |
472 | val |= MCDE_OVLXCR_COLCCTRL_DISABLED; |
473 | val |= MCDE_OVLXCR_BURSTSIZE_8W << |
474 | MCDE_OVLXCR_BURSTSIZE_SHIFT; |
475 | val |= MCDE_OVLXCR_MAXOUTSTANDING_8_REQ << |
476 | MCDE_OVLXCR_MAXOUTSTANDING_SHIFT; |
477 | /* Not using rotation but set it up anyways */ |
478 | val |= MCDE_OVLXCR_ROTBURSTSIZE_8W << |
479 | MCDE_OVLXCR_ROTBURSTSIZE_SHIFT; |
480 | writel(val, addr: mcde->regs + cr); |
481 | |
482 | /* |
483 | * Set up the overlay compositor to route the overlay out to |
484 | * the desired channel |
485 | */ |
486 | val = ch << MCDE_OVLXCOMP_CH_ID_SHIFT; |
487 | writel(val, addr: mcde->regs + comp); |
488 | } |
489 | |
490 | static void mcde_configure_channel(struct mcde *mcde, enum mcde_channel ch, |
491 | enum mcde_fifo fifo, |
492 | const struct drm_display_mode *mode) |
493 | { |
494 | u32 val; |
495 | u32 conf; |
496 | u32 sync; |
497 | u32 stat; |
498 | u32 bgcol; |
499 | u32 mux; |
500 | |
501 | switch (ch) { |
502 | case MCDE_CHANNEL_0: |
503 | conf = MCDE_CHNL0CONF; |
504 | sync = MCDE_CHNL0SYNCHMOD; |
505 | stat = MCDE_CHNL0STAT; |
506 | bgcol = MCDE_CHNL0BCKGNDCOL; |
507 | mux = MCDE_CHNL0MUXING; |
508 | break; |
509 | case MCDE_CHANNEL_1: |
510 | conf = MCDE_CHNL1CONF; |
511 | sync = MCDE_CHNL1SYNCHMOD; |
512 | stat = MCDE_CHNL1STAT; |
513 | bgcol = MCDE_CHNL1BCKGNDCOL; |
514 | mux = MCDE_CHNL1MUXING; |
515 | break; |
516 | case MCDE_CHANNEL_2: |
517 | conf = MCDE_CHNL2CONF; |
518 | sync = MCDE_CHNL2SYNCHMOD; |
519 | stat = MCDE_CHNL2STAT; |
520 | bgcol = MCDE_CHNL2BCKGNDCOL; |
521 | mux = MCDE_CHNL2MUXING; |
522 | break; |
523 | case MCDE_CHANNEL_3: |
524 | conf = MCDE_CHNL3CONF; |
525 | sync = MCDE_CHNL3SYNCHMOD; |
526 | stat = MCDE_CHNL3STAT; |
527 | bgcol = MCDE_CHNL3BCKGNDCOL; |
528 | mux = MCDE_CHNL3MUXING; |
529 | return; |
530 | } |
531 | |
532 | /* Set up channel 0 sync (based on chnl_update_registers()) */ |
533 | switch (mcde->flow_mode) { |
534 | case MCDE_COMMAND_ONESHOT_FLOW: |
535 | /* Oneshot is achieved with software sync */ |
536 | val = MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SOFTWARE |
537 | << MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SHIFT; |
538 | break; |
539 | case MCDE_COMMAND_TE_FLOW: |
540 | val = MCDE_CHNLXSYNCHMOD_SRC_SYNCH_HARDWARE |
541 | << MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SHIFT; |
542 | val |= MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_TE0 |
543 | << MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_SHIFT; |
544 | break; |
545 | case MCDE_COMMAND_BTA_TE_FLOW: |
546 | val = MCDE_CHNLXSYNCHMOD_SRC_SYNCH_HARDWARE |
547 | << MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SHIFT; |
548 | /* |
549 | * TODO: |
550 | * The vendor driver uses the formatter as sync source |
551 | * for BTA TE mode. Test to use TE if you have a panel |
552 | * that uses this mode. |
553 | */ |
554 | val |= MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_FORMATTER |
555 | << MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_SHIFT; |
556 | break; |
557 | case MCDE_VIDEO_TE_FLOW: |
558 | val = MCDE_CHNLXSYNCHMOD_SRC_SYNCH_HARDWARE |
559 | << MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SHIFT; |
560 | val |= MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_TE0 |
561 | << MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_SHIFT; |
562 | break; |
563 | case MCDE_VIDEO_FORMATTER_FLOW: |
564 | case MCDE_DPI_FORMATTER_FLOW: |
565 | val = MCDE_CHNLXSYNCHMOD_SRC_SYNCH_HARDWARE |
566 | << MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SHIFT; |
567 | val |= MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_FORMATTER |
568 | << MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_SHIFT; |
569 | break; |
570 | default: |
571 | dev_err(mcde->dev, "unknown flow mode %d\n" , |
572 | mcde->flow_mode); |
573 | return; |
574 | } |
575 | |
576 | writel(val, addr: mcde->regs + sync); |
577 | |
578 | /* Set up pixels per line and lines per frame */ |
579 | val = (mode->hdisplay - 1) << MCDE_CHNLXCONF_PPL_SHIFT; |
580 | val |= (mode->vdisplay - 1) << MCDE_CHNLXCONF_LPF_SHIFT; |
581 | writel(val, addr: mcde->regs + conf); |
582 | |
583 | /* |
584 | * Normalize color conversion: |
585 | * black background, OLED conversion disable on channel |
586 | */ |
587 | val = MCDE_CHNLXSTAT_CHNLBLBCKGND_EN | |
588 | MCDE_CHNLXSTAT_CHNLRD; |
589 | writel(val, addr: mcde->regs + stat); |
590 | writel(val: 0, addr: mcde->regs + bgcol); |
591 | |
592 | /* Set up muxing: connect the channel to the desired FIFO */ |
593 | switch (fifo) { |
594 | case MCDE_FIFO_A: |
595 | writel(MCDE_CHNLXMUXING_FIFO_ID_FIFO_A, |
596 | addr: mcde->regs + mux); |
597 | break; |
598 | case MCDE_FIFO_B: |
599 | writel(MCDE_CHNLXMUXING_FIFO_ID_FIFO_B, |
600 | addr: mcde->regs + mux); |
601 | break; |
602 | } |
603 | |
604 | /* |
605 | * If using DPI configure the sync event. |
606 | * TODO: this is for LCD only, it does not cover TV out. |
607 | */ |
608 | if (mcde->dpi_output) { |
609 | u32 stripwidth; |
610 | |
611 | stripwidth = 0xF000 / (mode->vdisplay * 4); |
612 | dev_info(mcde->dev, "stripwidth: %d\n" , stripwidth); |
613 | |
614 | val = MCDE_SYNCHCONF_HWREQVEVENT_ACTIVE_VIDEO | |
615 | (mode->hdisplay - 1 - stripwidth) << MCDE_SYNCHCONF_HWREQVCNT_SHIFT | |
616 | MCDE_SYNCHCONF_SWINTVEVENT_ACTIVE_VIDEO | |
617 | (mode->hdisplay - 1 - stripwidth) << MCDE_SYNCHCONF_SWINTVCNT_SHIFT; |
618 | |
619 | switch (fifo) { |
620 | case MCDE_FIFO_A: |
621 | writel(val, addr: mcde->regs + MCDE_SYNCHCONFA); |
622 | break; |
623 | case MCDE_FIFO_B: |
624 | writel(val, addr: mcde->regs + MCDE_SYNCHCONFB); |
625 | break; |
626 | } |
627 | } |
628 | } |
629 | |
630 | static void mcde_configure_fifo(struct mcde *mcde, enum mcde_fifo fifo, |
631 | enum mcde_formatter fmt, |
632 | int fifo_wtrmrk) |
633 | { |
634 | u32 val; |
635 | u32 ctrl; |
636 | u32 cr0, cr1; |
637 | |
638 | switch (fifo) { |
639 | case MCDE_FIFO_A: |
640 | ctrl = MCDE_CTRLA; |
641 | cr0 = MCDE_CRA0; |
642 | cr1 = MCDE_CRA1; |
643 | break; |
644 | case MCDE_FIFO_B: |
645 | ctrl = MCDE_CTRLB; |
646 | cr0 = MCDE_CRB0; |
647 | cr1 = MCDE_CRB1; |
648 | break; |
649 | } |
650 | |
651 | val = fifo_wtrmrk << MCDE_CTRLX_FIFOWTRMRK_SHIFT; |
652 | |
653 | /* |
654 | * Select the formatter to use for this FIFO |
655 | * |
656 | * The register definitions imply that different IDs should be used |
657 | * by the DSI formatters depending on if they are in VID or CMD |
658 | * mode, and the manual says they are dedicated but identical. |
659 | * The vendor code uses them as it seems fit. |
660 | */ |
661 | switch (fmt) { |
662 | case MCDE_DSI_FORMATTER_0: |
663 | val |= MCDE_CTRLX_FORMTYPE_DSI << MCDE_CTRLX_FORMTYPE_SHIFT; |
664 | val |= MCDE_CTRLX_FORMID_DSI0VID << MCDE_CTRLX_FORMID_SHIFT; |
665 | break; |
666 | case MCDE_DSI_FORMATTER_1: |
667 | val |= MCDE_CTRLX_FORMTYPE_DSI << MCDE_CTRLX_FORMTYPE_SHIFT; |
668 | val |= MCDE_CTRLX_FORMID_DSI0CMD << MCDE_CTRLX_FORMID_SHIFT; |
669 | break; |
670 | case MCDE_DSI_FORMATTER_2: |
671 | val |= MCDE_CTRLX_FORMTYPE_DSI << MCDE_CTRLX_FORMTYPE_SHIFT; |
672 | val |= MCDE_CTRLX_FORMID_DSI1VID << MCDE_CTRLX_FORMID_SHIFT; |
673 | break; |
674 | case MCDE_DSI_FORMATTER_3: |
675 | val |= MCDE_CTRLX_FORMTYPE_DSI << MCDE_CTRLX_FORMTYPE_SHIFT; |
676 | val |= MCDE_CTRLX_FORMID_DSI1CMD << MCDE_CTRLX_FORMID_SHIFT; |
677 | break; |
678 | case MCDE_DSI_FORMATTER_4: |
679 | val |= MCDE_CTRLX_FORMTYPE_DSI << MCDE_CTRLX_FORMTYPE_SHIFT; |
680 | val |= MCDE_CTRLX_FORMID_DSI2VID << MCDE_CTRLX_FORMID_SHIFT; |
681 | break; |
682 | case MCDE_DSI_FORMATTER_5: |
683 | val |= MCDE_CTRLX_FORMTYPE_DSI << MCDE_CTRLX_FORMTYPE_SHIFT; |
684 | val |= MCDE_CTRLX_FORMID_DSI2CMD << MCDE_CTRLX_FORMID_SHIFT; |
685 | break; |
686 | case MCDE_DPI_FORMATTER_0: |
687 | val |= MCDE_CTRLX_FORMTYPE_DPITV << MCDE_CTRLX_FORMTYPE_SHIFT; |
688 | val |= MCDE_CTRLX_FORMID_DPIA << MCDE_CTRLX_FORMID_SHIFT; |
689 | break; |
690 | case MCDE_DPI_FORMATTER_1: |
691 | val |= MCDE_CTRLX_FORMTYPE_DPITV << MCDE_CTRLX_FORMTYPE_SHIFT; |
692 | val |= MCDE_CTRLX_FORMID_DPIB << MCDE_CTRLX_FORMID_SHIFT; |
693 | break; |
694 | } |
695 | writel(val, addr: mcde->regs + ctrl); |
696 | |
697 | /* Blend source with Alpha 0xff on FIFO */ |
698 | val = MCDE_CRX0_BLENDEN | |
699 | 0xff << MCDE_CRX0_ALPHABLEND_SHIFT; |
700 | writel(val, addr: mcde->regs + cr0); |
701 | |
702 | spin_lock(lock: &mcde->fifo_crx1_lock); |
703 | val = readl(addr: mcde->regs + cr1); |
704 | /* |
705 | * Set-up from mcde_fmtr_dsi.c, fmtr_dsi_enable_video() |
706 | * FIXME: a different clock needs to be selected for TV out. |
707 | */ |
708 | if (mcde->dpi_output) { |
709 | struct drm_connector *connector = drm_panel_bridge_connector(bridge: mcde->bridge); |
710 | u32 bus_format; |
711 | |
712 | /* Assume RGB888 24 bit if we have no further info */ |
713 | if (!connector->display_info.num_bus_formats) { |
714 | dev_info(mcde->dev, "panel does not specify bus format, assume RGB888\n" ); |
715 | bus_format = MEDIA_BUS_FMT_RGB888_1X24; |
716 | } else { |
717 | bus_format = connector->display_info.bus_formats[0]; |
718 | } |
719 | |
720 | /* |
721 | * Set up the CDWIN and OUTBPP for the LCD |
722 | * |
723 | * FIXME: fill this in if you know the correspondance between the MIPI |
724 | * DPI specification and the media bus formats. |
725 | */ |
726 | val &= ~MCDE_CRX1_CDWIN_MASK; |
727 | val &= ~MCDE_CRX1_OUTBPP_MASK; |
728 | switch (bus_format) { |
729 | case MEDIA_BUS_FMT_RGB888_1X24: |
730 | val |= MCDE_CRX1_CDWIN_24BPP << MCDE_CRX1_CDWIN_SHIFT; |
731 | val |= MCDE_CRX1_OUTBPP_24BPP << MCDE_CRX1_OUTBPP_SHIFT; |
732 | break; |
733 | default: |
734 | dev_err(mcde->dev, "unknown bus format, assume RGB888\n" ); |
735 | val |= MCDE_CRX1_CDWIN_24BPP << MCDE_CRX1_CDWIN_SHIFT; |
736 | val |= MCDE_CRX1_OUTBPP_24BPP << MCDE_CRX1_OUTBPP_SHIFT; |
737 | break; |
738 | } |
739 | } else { |
740 | /* Use the MCDE clock for DSI */ |
741 | val &= ~MCDE_CRX1_CLKSEL_MASK; |
742 | val |= MCDE_CRX1_CLKSEL_MCDECLK << MCDE_CRX1_CLKSEL_SHIFT; |
743 | } |
744 | writel(val, addr: mcde->regs + cr1); |
745 | spin_unlock(lock: &mcde->fifo_crx1_lock); |
746 | }; |
747 | |
748 | static void mcde_configure_dsi_formatter(struct mcde *mcde, |
749 | enum mcde_formatter fmt, |
750 | u32 formatter_frame, |
751 | int pkt_size) |
752 | { |
753 | u32 val; |
754 | u32 conf0; |
755 | u32 frame; |
756 | u32 pkt; |
757 | u32 sync; |
758 | u32 cmdw; |
759 | u32 delay0, delay1; |
760 | |
761 | switch (fmt) { |
762 | case MCDE_DSI_FORMATTER_0: |
763 | conf0 = MCDE_DSIVID0CONF0; |
764 | frame = MCDE_DSIVID0FRAME; |
765 | pkt = MCDE_DSIVID0PKT; |
766 | sync = MCDE_DSIVID0SYNC; |
767 | cmdw = MCDE_DSIVID0CMDW; |
768 | delay0 = MCDE_DSIVID0DELAY0; |
769 | delay1 = MCDE_DSIVID0DELAY1; |
770 | break; |
771 | case MCDE_DSI_FORMATTER_1: |
772 | conf0 = MCDE_DSIVID1CONF0; |
773 | frame = MCDE_DSIVID1FRAME; |
774 | pkt = MCDE_DSIVID1PKT; |
775 | sync = MCDE_DSIVID1SYNC; |
776 | cmdw = MCDE_DSIVID1CMDW; |
777 | delay0 = MCDE_DSIVID1DELAY0; |
778 | delay1 = MCDE_DSIVID1DELAY1; |
779 | break; |
780 | case MCDE_DSI_FORMATTER_2: |
781 | conf0 = MCDE_DSIVID2CONF0; |
782 | frame = MCDE_DSIVID2FRAME; |
783 | pkt = MCDE_DSIVID2PKT; |
784 | sync = MCDE_DSIVID2SYNC; |
785 | cmdw = MCDE_DSIVID2CMDW; |
786 | delay0 = MCDE_DSIVID2DELAY0; |
787 | delay1 = MCDE_DSIVID2DELAY1; |
788 | break; |
789 | default: |
790 | dev_err(mcde->dev, "tried to configure a non-DSI formatter as DSI\n" ); |
791 | return; |
792 | } |
793 | |
794 | /* |
795 | * Enable formatter |
796 | * 8 bit commands and DCS commands (notgen = not generic) |
797 | */ |
798 | val = MCDE_DSICONF0_CMD8 | MCDE_DSICONF0_DCSVID_NOTGEN; |
799 | if (mcde->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) |
800 | val |= MCDE_DSICONF0_VID_MODE_VID; |
801 | switch (mcde->mdsi->format) { |
802 | case MIPI_DSI_FMT_RGB888: |
803 | val |= MCDE_DSICONF0_PACKING_RGB888 << |
804 | MCDE_DSICONF0_PACKING_SHIFT; |
805 | break; |
806 | case MIPI_DSI_FMT_RGB666: |
807 | val |= MCDE_DSICONF0_PACKING_RGB666 << |
808 | MCDE_DSICONF0_PACKING_SHIFT; |
809 | break; |
810 | case MIPI_DSI_FMT_RGB666_PACKED: |
811 | dev_err(mcde->dev, |
812 | "we cannot handle the packed RGB666 format\n" ); |
813 | val |= MCDE_DSICONF0_PACKING_RGB666 << |
814 | MCDE_DSICONF0_PACKING_SHIFT; |
815 | break; |
816 | case MIPI_DSI_FMT_RGB565: |
817 | val |= MCDE_DSICONF0_PACKING_RGB565 << |
818 | MCDE_DSICONF0_PACKING_SHIFT; |
819 | break; |
820 | default: |
821 | dev_err(mcde->dev, "unknown DSI format\n" ); |
822 | return; |
823 | } |
824 | writel(val, addr: mcde->regs + conf0); |
825 | |
826 | writel(val: formatter_frame, addr: mcde->regs + frame); |
827 | writel(val: pkt_size, addr: mcde->regs + pkt); |
828 | writel(val: 0, addr: mcde->regs + sync); |
829 | /* Define the MIPI command: we want to write into display memory */ |
830 | val = MIPI_DCS_WRITE_MEMORY_CONTINUE << |
831 | MCDE_DSIVIDXCMDW_CMDW_CONTINUE_SHIFT; |
832 | val |= MIPI_DCS_WRITE_MEMORY_START << |
833 | MCDE_DSIVIDXCMDW_CMDW_START_SHIFT; |
834 | writel(val, addr: mcde->regs + cmdw); |
835 | |
836 | /* |
837 | * FIXME: the vendor driver has some hack around this value in |
838 | * CMD mode with autotrig. |
839 | */ |
840 | writel(val: 0, addr: mcde->regs + delay0); |
841 | writel(val: 0, addr: mcde->regs + delay1); |
842 | } |
843 | |
844 | static void mcde_enable_fifo(struct mcde *mcde, enum mcde_fifo fifo) |
845 | { |
846 | u32 val; |
847 | u32 cr; |
848 | |
849 | switch (fifo) { |
850 | case MCDE_FIFO_A: |
851 | cr = MCDE_CRA0; |
852 | break; |
853 | case MCDE_FIFO_B: |
854 | cr = MCDE_CRB0; |
855 | break; |
856 | default: |
857 | dev_err(mcde->dev, "cannot enable FIFO %c\n" , |
858 | 'A' + fifo); |
859 | return; |
860 | } |
861 | |
862 | spin_lock(lock: &mcde->flow_lock); |
863 | val = readl(addr: mcde->regs + cr); |
864 | val |= MCDE_CRX0_FLOEN; |
865 | writel(val, addr: mcde->regs + cr); |
866 | mcde->flow_active++; |
867 | spin_unlock(lock: &mcde->flow_lock); |
868 | } |
869 | |
870 | static void mcde_disable_fifo(struct mcde *mcde, enum mcde_fifo fifo, |
871 | bool wait_for_drain) |
872 | { |
873 | int timeout = 100; |
874 | u32 val; |
875 | u32 cr; |
876 | |
877 | switch (fifo) { |
878 | case MCDE_FIFO_A: |
879 | cr = MCDE_CRA0; |
880 | break; |
881 | case MCDE_FIFO_B: |
882 | cr = MCDE_CRB0; |
883 | break; |
884 | default: |
885 | dev_err(mcde->dev, "cannot disable FIFO %c\n" , |
886 | 'A' + fifo); |
887 | return; |
888 | } |
889 | |
890 | spin_lock(lock: &mcde->flow_lock); |
891 | val = readl(addr: mcde->regs + cr); |
892 | val &= ~MCDE_CRX0_FLOEN; |
893 | writel(val, addr: mcde->regs + cr); |
894 | mcde->flow_active = 0; |
895 | spin_unlock(lock: &mcde->flow_lock); |
896 | |
897 | if (!wait_for_drain) |
898 | return; |
899 | |
900 | /* Check that we really drained and stopped the flow */ |
901 | while (readl(addr: mcde->regs + cr) & MCDE_CRX0_FLOEN) { |
902 | usleep_range(min: 1000, max: 1500); |
903 | if (!--timeout) { |
904 | dev_err(mcde->dev, |
905 | "FIFO timeout while clearing FIFO %c\n" , |
906 | 'A' + fifo); |
907 | return; |
908 | } |
909 | } |
910 | } |
911 | |
912 | /* |
913 | * This drains a pipe i.e. a FIFO connected to a certain channel |
914 | */ |
915 | static void mcde_drain_pipe(struct mcde *mcde, enum mcde_fifo fifo, |
916 | enum mcde_channel ch) |
917 | { |
918 | u32 val; |
919 | u32 ctrl; |
920 | u32 synsw; |
921 | |
922 | switch (fifo) { |
923 | case MCDE_FIFO_A: |
924 | ctrl = MCDE_CTRLA; |
925 | break; |
926 | case MCDE_FIFO_B: |
927 | ctrl = MCDE_CTRLB; |
928 | break; |
929 | } |
930 | |
931 | switch (ch) { |
932 | case MCDE_CHANNEL_0: |
933 | synsw = MCDE_CHNL0SYNCHSW; |
934 | break; |
935 | case MCDE_CHANNEL_1: |
936 | synsw = MCDE_CHNL1SYNCHSW; |
937 | break; |
938 | case MCDE_CHANNEL_2: |
939 | synsw = MCDE_CHNL2SYNCHSW; |
940 | break; |
941 | case MCDE_CHANNEL_3: |
942 | synsw = MCDE_CHNL3SYNCHSW; |
943 | return; |
944 | } |
945 | |
946 | val = readl(addr: mcde->regs + ctrl); |
947 | if (!(val & MCDE_CTRLX_FIFOEMPTY)) { |
948 | dev_err(mcde->dev, "Channel A FIFO not empty (handover)\n" ); |
949 | /* Attempt to clear the FIFO */ |
950 | mcde_enable_fifo(mcde, fifo); |
951 | /* Trigger a software sync out on respective channel (0-3) */ |
952 | writel(MCDE_CHNLXSYNCHSW_SW_TRIG, addr: mcde->regs + synsw); |
953 | /* Disable FIFO A flow again */ |
954 | mcde_disable_fifo(mcde, fifo, wait_for_drain: true); |
955 | } |
956 | } |
957 | |
958 | static int mcde_dsi_get_pkt_div(int ppl, int fifo_size) |
959 | { |
960 | /* |
961 | * DSI command mode line packets should be split into an even number of |
962 | * packets smaller than or equal to the fifo size. |
963 | */ |
964 | int div; |
965 | const int max_div = DIV_ROUND_UP(MCDE_MAX_WIDTH, fifo_size); |
966 | |
967 | for (div = 1; div < max_div; div++) |
968 | if (ppl % div == 0 && ppl / div <= fifo_size) |
969 | return div; |
970 | return 1; |
971 | } |
972 | |
973 | static void mcde_setup_dpi(struct mcde *mcde, const struct drm_display_mode *mode, |
974 | int *fifo_wtrmrk_lvl) |
975 | { |
976 | struct drm_connector *connector = drm_panel_bridge_connector(bridge: mcde->bridge); |
977 | u32 hsw, hfp, hbp; |
978 | u32 vsw, vfp, vbp; |
979 | u32 val; |
980 | |
981 | /* FIXME: we only support LCD, implement TV out */ |
982 | hsw = mode->hsync_end - mode->hsync_start; |
983 | hfp = mode->hsync_start - mode->hdisplay; |
984 | hbp = mode->htotal - mode->hsync_end; |
985 | vsw = mode->vsync_end - mode->vsync_start; |
986 | vfp = mode->vsync_start - mode->vdisplay; |
987 | vbp = mode->vtotal - mode->vsync_end; |
988 | |
989 | dev_info(mcde->dev, "output on DPI LCD from channel A\n" ); |
990 | /* Display actual values */ |
991 | dev_info(mcde->dev, "HSW: %d, HFP: %d, HBP: %d, VSW: %d, VFP: %d, VBP: %d\n" , |
992 | hsw, hfp, hbp, vsw, vfp, vbp); |
993 | |
994 | /* |
995 | * The pixel fetcher is 128 64-bit words deep = 1024 bytes. |
996 | * One overlay of 32bpp (4 cpp) assumed, fetch 160 pixels. |
997 | * 160 * 4 = 640 bytes. |
998 | */ |
999 | *fifo_wtrmrk_lvl = 640; |
1000 | |
1001 | /* Set up the main control, watermark level at 7 */ |
1002 | val = 7 << MCDE_CONF0_IFIFOCTRLWTRMRKLVL_SHIFT; |
1003 | |
1004 | /* |
1005 | * This sets up the internal silicon muxing of the DPI |
1006 | * lines. This is how the silicon connects out to the |
1007 | * external pins, then the pins need to be further |
1008 | * configured into "alternate functions" using pin control |
1009 | * to actually get the signals out. |
1010 | * |
1011 | * FIXME: this is hardcoded to the only setting found in |
1012 | * the wild. If we need to use different settings for |
1013 | * different DPI displays, make this parameterizable from |
1014 | * the device tree. |
1015 | */ |
1016 | /* 24 bits DPI: connect Ch A LSB to D[0:7] */ |
1017 | val |= 0 << MCDE_CONF0_OUTMUX0_SHIFT; |
1018 | /* 24 bits DPI: connect Ch A MID to D[8:15] */ |
1019 | val |= 1 << MCDE_CONF0_OUTMUX1_SHIFT; |
1020 | /* Don't care about this muxing */ |
1021 | val |= 0 << MCDE_CONF0_OUTMUX2_SHIFT; |
1022 | /* Don't care about this muxing */ |
1023 | val |= 0 << MCDE_CONF0_OUTMUX3_SHIFT; |
1024 | /* 24 bits DPI: connect Ch A MSB to D[32:39] */ |
1025 | val |= 2 << MCDE_CONF0_OUTMUX4_SHIFT; |
1026 | /* Syncmux bits zero: DPI channel A */ |
1027 | writel(val, addr: mcde->regs + MCDE_CONF0); |
1028 | |
1029 | /* This hammers us into LCD mode */ |
1030 | writel(val: 0, addr: mcde->regs + MCDE_TVCRA); |
1031 | |
1032 | /* Front porch and sync width */ |
1033 | val = (vsw << MCDE_TVBL1_BEL1_SHIFT); |
1034 | val |= (vfp << MCDE_TVBL1_BSL1_SHIFT); |
1035 | writel(val, addr: mcde->regs + MCDE_TVBL1A); |
1036 | /* The vendor driver sets the same value into TVBL2A */ |
1037 | writel(val, addr: mcde->regs + MCDE_TVBL2A); |
1038 | |
1039 | /* Vertical back porch */ |
1040 | val = (vbp << MCDE_TVDVO_DVO1_SHIFT); |
1041 | /* The vendor drivers sets the same value into TVDVOA */ |
1042 | val |= (vbp << MCDE_TVDVO_DVO2_SHIFT); |
1043 | writel(val, addr: mcde->regs + MCDE_TVDVOA); |
1044 | |
1045 | /* Horizontal back porch, as 0 = 1 cycle we need to subtract 1 */ |
1046 | writel(val: (hbp - 1), addr: mcde->regs + MCDE_TVTIM1A); |
1047 | |
1048 | /* Horizongal sync width and horizonal front porch, 0 = 1 cycle */ |
1049 | val = ((hsw - 1) << MCDE_TVLBALW_LBW_SHIFT); |
1050 | val |= ((hfp - 1) << MCDE_TVLBALW_ALW_SHIFT); |
1051 | writel(val, addr: mcde->regs + MCDE_TVLBALWA); |
1052 | |
1053 | /* Blank some TV registers we don't use */ |
1054 | writel(val: 0, addr: mcde->regs + MCDE_TVISLA); |
1055 | writel(val: 0, addr: mcde->regs + MCDE_TVBLUA); |
1056 | |
1057 | /* Set up sync inversion etc */ |
1058 | val = 0; |
1059 | if (mode->flags & DRM_MODE_FLAG_NHSYNC) |
1060 | val |= MCDE_LCDTIM1B_IHS; |
1061 | if (mode->flags & DRM_MODE_FLAG_NVSYNC) |
1062 | val |= MCDE_LCDTIM1B_IVS; |
1063 | if (connector->display_info.bus_flags & DRM_BUS_FLAG_DE_LOW) |
1064 | val |= MCDE_LCDTIM1B_IOE; |
1065 | if (connector->display_info.bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE) |
1066 | val |= MCDE_LCDTIM1B_IPC; |
1067 | writel(val, addr: mcde->regs + MCDE_LCDTIM1A); |
1068 | } |
1069 | |
1070 | static void mcde_setup_dsi(struct mcde *mcde, const struct drm_display_mode *mode, |
1071 | int cpp, int *fifo_wtrmrk_lvl, int *dsi_formatter_frame, |
1072 | int *dsi_pkt_size) |
1073 | { |
1074 | u32 formatter_ppl = mode->hdisplay; /* pixels per line */ |
1075 | u32 formatter_lpf = mode->vdisplay; /* lines per frame */ |
1076 | int formatter_frame; |
1077 | int formatter_cpp; |
1078 | int fifo_wtrmrk; |
1079 | u32 pkt_div; |
1080 | int pkt_size; |
1081 | u32 val; |
1082 | |
1083 | dev_info(mcde->dev, "output in %s mode, format %dbpp\n" , |
1084 | (mcde->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) ? |
1085 | "VIDEO" : "CMD" , |
1086 | mipi_dsi_pixel_format_to_bpp(mcde->mdsi->format)); |
1087 | formatter_cpp = |
1088 | mipi_dsi_pixel_format_to_bpp(fmt: mcde->mdsi->format) / 8; |
1089 | dev_info(mcde->dev, "Overlay CPP: %d bytes, DSI formatter CPP %d bytes\n" , |
1090 | cpp, formatter_cpp); |
1091 | |
1092 | /* Set up the main control, watermark level at 7 */ |
1093 | val = 7 << MCDE_CONF0_IFIFOCTRLWTRMRKLVL_SHIFT; |
1094 | |
1095 | /* |
1096 | * This is the internal silicon muxing of the DPI |
1097 | * (parallell display) lines. Since we are not using |
1098 | * this at all (we are using DSI) these are just |
1099 | * dummy values from the vendor tree. |
1100 | */ |
1101 | val |= 3 << MCDE_CONF0_OUTMUX0_SHIFT; |
1102 | val |= 3 << MCDE_CONF0_OUTMUX1_SHIFT; |
1103 | val |= 0 << MCDE_CONF0_OUTMUX2_SHIFT; |
1104 | val |= 4 << MCDE_CONF0_OUTMUX3_SHIFT; |
1105 | val |= 5 << MCDE_CONF0_OUTMUX4_SHIFT; |
1106 | writel(val, addr: mcde->regs + MCDE_CONF0); |
1107 | |
1108 | /* Calculations from mcde_fmtr_dsi.c, fmtr_dsi_enable_video() */ |
1109 | |
1110 | /* |
1111 | * Set up FIFO A watermark level: |
1112 | * 128 for LCD 32bpp video mode |
1113 | * 48 for LCD 32bpp command mode |
1114 | * 128 for LCD 16bpp video mode |
1115 | * 64 for LCD 16bpp command mode |
1116 | * 128 for HDMI 32bpp |
1117 | * 192 for HDMI 16bpp |
1118 | */ |
1119 | fifo_wtrmrk = mode->hdisplay; |
1120 | if (mcde->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) { |
1121 | fifo_wtrmrk = min(fifo_wtrmrk, 128); |
1122 | pkt_div = 1; |
1123 | } else { |
1124 | fifo_wtrmrk = min(fifo_wtrmrk, 48); |
1125 | /* The FIFO is 640 entries deep on this v3 hardware */ |
1126 | pkt_div = mcde_dsi_get_pkt_div(ppl: mode->hdisplay, fifo_size: 640); |
1127 | } |
1128 | dev_dbg(mcde->dev, "FIFO watermark after flooring: %d bytes\n" , |
1129 | fifo_wtrmrk); |
1130 | dev_dbg(mcde->dev, "Packet divisor: %d bytes\n" , pkt_div); |
1131 | |
1132 | /* NOTE: pkt_div is 1 for video mode */ |
1133 | pkt_size = (formatter_ppl * formatter_cpp) / pkt_div; |
1134 | /* Commands CMD8 need one extra byte */ |
1135 | if (!(mcde->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO)) |
1136 | pkt_size++; |
1137 | |
1138 | dev_dbg(mcde->dev, "DSI packet size: %d * %d bytes per line\n" , |
1139 | pkt_size, pkt_div); |
1140 | dev_dbg(mcde->dev, "Overlay frame size: %u bytes\n" , |
1141 | mode->hdisplay * mode->vdisplay * cpp); |
1142 | /* NOTE: pkt_div is 1 for video mode */ |
1143 | formatter_frame = pkt_size * pkt_div * formatter_lpf; |
1144 | dev_dbg(mcde->dev, "Formatter frame size: %u bytes\n" , formatter_frame); |
1145 | |
1146 | *fifo_wtrmrk_lvl = fifo_wtrmrk; |
1147 | *dsi_pkt_size = pkt_size; |
1148 | *dsi_formatter_frame = formatter_frame; |
1149 | } |
1150 | |
1151 | static void mcde_display_enable(struct drm_simple_display_pipe *pipe, |
1152 | struct drm_crtc_state *cstate, |
1153 | struct drm_plane_state *plane_state) |
1154 | { |
1155 | struct drm_crtc *crtc = &pipe->crtc; |
1156 | struct drm_plane *plane = &pipe->plane; |
1157 | struct drm_device *drm = crtc->dev; |
1158 | struct mcde *mcde = to_mcde(drm); |
1159 | const struct drm_display_mode *mode = &cstate->mode; |
1160 | struct drm_framebuffer *fb = plane->state->fb; |
1161 | u32 format = fb->format->format; |
1162 | int dsi_pkt_size; |
1163 | int fifo_wtrmrk; |
1164 | int cpp = fb->format->cpp[0]; |
1165 | u32 dsi_formatter_frame; |
1166 | u32 val; |
1167 | int ret; |
1168 | |
1169 | /* This powers up the entire MCDE block and the DSI hardware */ |
1170 | ret = regulator_enable(regulator: mcde->epod); |
1171 | if (ret) { |
1172 | dev_err(drm->dev, "can't re-enable EPOD regulator\n" ); |
1173 | return; |
1174 | } |
1175 | |
1176 | dev_info(drm->dev, "enable MCDE, %d x %d format %p4cc\n" , |
1177 | mode->hdisplay, mode->vdisplay, &format); |
1178 | |
1179 | |
1180 | /* Clear any pending interrupts */ |
1181 | mcde_display_disable_irqs(mcde); |
1182 | writel(val: 0, addr: mcde->regs + MCDE_IMSCERR); |
1183 | writel(val: 0xFFFFFFFF, addr: mcde->regs + MCDE_RISERR); |
1184 | |
1185 | if (mcde->dpi_output) |
1186 | mcde_setup_dpi(mcde, mode, fifo_wtrmrk_lvl: &fifo_wtrmrk); |
1187 | else |
1188 | mcde_setup_dsi(mcde, mode, cpp, fifo_wtrmrk_lvl: &fifo_wtrmrk, |
1189 | dsi_formatter_frame: &dsi_formatter_frame, dsi_pkt_size: &dsi_pkt_size); |
1190 | |
1191 | mcde->stride = mode->hdisplay * cpp; |
1192 | dev_dbg(drm->dev, "Overlay line stride: %u bytes\n" , |
1193 | mcde->stride); |
1194 | |
1195 | /* Drain the FIFO A + channel 0 pipe so we have a clean slate */ |
1196 | mcde_drain_pipe(mcde, fifo: MCDE_FIFO_A, ch: MCDE_CHANNEL_0); |
1197 | |
1198 | /* |
1199 | * We set up our display pipeline: |
1200 | * EXTSRC 0 -> OVERLAY 0 -> CHANNEL 0 -> FIFO A -> DSI FORMATTER 0 |
1201 | * |
1202 | * First configure the external source (memory) on external source 0 |
1203 | * using the desired bitstream/bitmap format |
1204 | */ |
1205 | mcde_configure_extsrc(mcde, src: MCDE_EXTSRC_0, format); |
1206 | |
1207 | /* |
1208 | * Configure overlay 0 according to format and mode and take input |
1209 | * from external source 0 and route the output of this overlay to |
1210 | * channel 0 |
1211 | */ |
1212 | mcde_configure_overlay(mcde, ovl: MCDE_OVERLAY_0, src: MCDE_EXTSRC_0, |
1213 | ch: MCDE_CHANNEL_0, mode, format, cpp); |
1214 | |
1215 | /* |
1216 | * Configure pixel-per-line and line-per-frame for channel 0 and then |
1217 | * route channel 0 to FIFO A |
1218 | */ |
1219 | mcde_configure_channel(mcde, ch: MCDE_CHANNEL_0, fifo: MCDE_FIFO_A, mode); |
1220 | |
1221 | if (mcde->dpi_output) { |
1222 | unsigned long lcd_freq; |
1223 | |
1224 | /* Configure FIFO A to use DPI formatter 0 */ |
1225 | mcde_configure_fifo(mcde, fifo: MCDE_FIFO_A, fmt: MCDE_DPI_FORMATTER_0, |
1226 | fifo_wtrmrk); |
1227 | |
1228 | /* Set up and enable the LCD clock */ |
1229 | lcd_freq = clk_round_rate(clk: mcde->fifoa_clk, rate: mode->clock * 1000); |
1230 | ret = clk_set_rate(clk: mcde->fifoa_clk, rate: lcd_freq); |
1231 | if (ret) |
1232 | dev_err(mcde->dev, "failed to set LCD clock rate %lu Hz\n" , |
1233 | lcd_freq); |
1234 | ret = clk_prepare_enable(clk: mcde->fifoa_clk); |
1235 | if (ret) { |
1236 | dev_err(mcde->dev, "failed to enable FIFO A DPI clock\n" ); |
1237 | return; |
1238 | } |
1239 | dev_info(mcde->dev, "LCD FIFO A clk rate %lu Hz\n" , |
1240 | clk_get_rate(mcde->fifoa_clk)); |
1241 | } else { |
1242 | /* Configure FIFO A to use DSI formatter 0 */ |
1243 | mcde_configure_fifo(mcde, fifo: MCDE_FIFO_A, fmt: MCDE_DSI_FORMATTER_0, |
1244 | fifo_wtrmrk); |
1245 | |
1246 | /* |
1247 | * This brings up the DSI bridge which is tightly connected |
1248 | * to the MCDE DSI formatter. |
1249 | */ |
1250 | mcde_dsi_enable(bridge: mcde->bridge); |
1251 | |
1252 | /* Configure the DSI formatter 0 for the DSI panel output */ |
1253 | mcde_configure_dsi_formatter(mcde, fmt: MCDE_DSI_FORMATTER_0, |
1254 | formatter_frame: dsi_formatter_frame, pkt_size: dsi_pkt_size); |
1255 | } |
1256 | |
1257 | switch (mcde->flow_mode) { |
1258 | case MCDE_COMMAND_TE_FLOW: |
1259 | case MCDE_COMMAND_BTA_TE_FLOW: |
1260 | case MCDE_VIDEO_TE_FLOW: |
1261 | /* We are using TE in some combination */ |
1262 | if (mode->flags & DRM_MODE_FLAG_NVSYNC) |
1263 | val = MCDE_VSCRC_VSPOL; |
1264 | else |
1265 | val = 0; |
1266 | writel(val, addr: mcde->regs + MCDE_VSCRC0); |
1267 | /* Enable VSYNC capture on TE0 */ |
1268 | val = readl(addr: mcde->regs + MCDE_CRC); |
1269 | val |= MCDE_CRC_SYCEN0; |
1270 | writel(val, addr: mcde->regs + MCDE_CRC); |
1271 | break; |
1272 | default: |
1273 | /* No TE capture */ |
1274 | break; |
1275 | } |
1276 | |
1277 | drm_crtc_vblank_on(crtc); |
1278 | |
1279 | /* |
1280 | * If we're using oneshot mode we don't start the flow |
1281 | * until each time the display is given an update, and |
1282 | * then we disable it immediately after. For all other |
1283 | * modes (command or video) we start the FIFO flow |
1284 | * right here. This is necessary for the hardware to |
1285 | * behave right. |
1286 | */ |
1287 | if (mcde->flow_mode != MCDE_COMMAND_ONESHOT_FLOW) { |
1288 | mcde_enable_fifo(mcde, fifo: MCDE_FIFO_A); |
1289 | dev_dbg(mcde->dev, "started MCDE video FIFO flow\n" ); |
1290 | } |
1291 | |
1292 | /* Enable MCDE with automatic clock gating */ |
1293 | val = readl(addr: mcde->regs + MCDE_CR); |
1294 | val |= MCDE_CR_MCDEEN | MCDE_CR_AUTOCLKG_EN; |
1295 | writel(val, addr: mcde->regs + MCDE_CR); |
1296 | |
1297 | dev_info(drm->dev, "MCDE display is enabled\n" ); |
1298 | } |
1299 | |
1300 | static void mcde_display_disable(struct drm_simple_display_pipe *pipe) |
1301 | { |
1302 | struct drm_crtc *crtc = &pipe->crtc; |
1303 | struct drm_device *drm = crtc->dev; |
1304 | struct mcde *mcde = to_mcde(drm); |
1305 | struct drm_pending_vblank_event *event; |
1306 | int ret; |
1307 | |
1308 | drm_crtc_vblank_off(crtc); |
1309 | |
1310 | /* Disable FIFO A flow */ |
1311 | mcde_disable_fifo(mcde, fifo: MCDE_FIFO_A, wait_for_drain: true); |
1312 | |
1313 | if (mcde->dpi_output) { |
1314 | clk_disable_unprepare(clk: mcde->fifoa_clk); |
1315 | } else { |
1316 | /* This disables the DSI bridge */ |
1317 | mcde_dsi_disable(bridge: mcde->bridge); |
1318 | } |
1319 | |
1320 | event = crtc->state->event; |
1321 | if (event) { |
1322 | crtc->state->event = NULL; |
1323 | |
1324 | spin_lock_irq(lock: &crtc->dev->event_lock); |
1325 | drm_crtc_send_vblank_event(crtc, e: event); |
1326 | spin_unlock_irq(lock: &crtc->dev->event_lock); |
1327 | } |
1328 | |
1329 | ret = regulator_disable(regulator: mcde->epod); |
1330 | if (ret) |
1331 | dev_err(drm->dev, "can't disable EPOD regulator\n" ); |
1332 | /* Make sure we are powered down (before we may power up again) */ |
1333 | usleep_range(min: 50000, max: 70000); |
1334 | |
1335 | dev_info(drm->dev, "MCDE display is disabled\n" ); |
1336 | } |
1337 | |
1338 | static void mcde_start_flow(struct mcde *mcde) |
1339 | { |
1340 | /* Request a TE ACK only in TE+BTA mode */ |
1341 | if (mcde->flow_mode == MCDE_COMMAND_BTA_TE_FLOW) |
1342 | mcde_dsi_te_request(mdsi: mcde->mdsi); |
1343 | |
1344 | /* Enable FIFO A flow */ |
1345 | mcde_enable_fifo(mcde, fifo: MCDE_FIFO_A); |
1346 | |
1347 | /* |
1348 | * If oneshot mode is enabled, the flow will be disabled |
1349 | * when the TE0 IRQ arrives in the interrupt handler. Otherwise |
1350 | * updates are continuously streamed to the display after this |
1351 | * point. |
1352 | */ |
1353 | |
1354 | if (mcde->flow_mode == MCDE_COMMAND_ONESHOT_FLOW) { |
1355 | /* Trigger a software sync out on channel 0 */ |
1356 | writel(MCDE_CHNLXSYNCHSW_SW_TRIG, |
1357 | addr: mcde->regs + MCDE_CHNL0SYNCHSW); |
1358 | |
1359 | /* |
1360 | * Disable FIFO A flow again: since we are using TE sync we |
1361 | * need to wait for the FIFO to drain before we continue |
1362 | * so repeated calls to this function will not cause a mess |
1363 | * in the hardware by pushing updates will updates are going |
1364 | * on already. |
1365 | */ |
1366 | mcde_disable_fifo(mcde, fifo: MCDE_FIFO_A, wait_for_drain: true); |
1367 | } |
1368 | |
1369 | dev_dbg(mcde->dev, "started MCDE FIFO flow\n" ); |
1370 | } |
1371 | |
1372 | static void mcde_set_extsrc(struct mcde *mcde, u32 buffer_address) |
1373 | { |
1374 | /* Write bitmap base address to register */ |
1375 | writel(val: buffer_address, addr: mcde->regs + MCDE_EXTSRCXA0); |
1376 | /* |
1377 | * Base address for next line this is probably only used |
1378 | * in interlace modes. |
1379 | */ |
1380 | writel(val: buffer_address + mcde->stride, addr: mcde->regs + MCDE_EXTSRCXA1); |
1381 | } |
1382 | |
1383 | static void mcde_display_update(struct drm_simple_display_pipe *pipe, |
1384 | struct drm_plane_state *old_pstate) |
1385 | { |
1386 | struct drm_crtc *crtc = &pipe->crtc; |
1387 | struct drm_device *drm = crtc->dev; |
1388 | struct mcde *mcde = to_mcde(drm); |
1389 | struct drm_pending_vblank_event *event = crtc->state->event; |
1390 | struct drm_plane *plane = &pipe->plane; |
1391 | struct drm_plane_state *pstate = plane->state; |
1392 | struct drm_framebuffer *fb = pstate->fb; |
1393 | |
1394 | /* |
1395 | * Handle any pending event first, we need to arm the vblank |
1396 | * interrupt before sending any update to the display so we don't |
1397 | * miss the interrupt. |
1398 | */ |
1399 | if (event) { |
1400 | crtc->state->event = NULL; |
1401 | |
1402 | spin_lock_irq(lock: &crtc->dev->event_lock); |
1403 | /* |
1404 | * Hardware must be on before we can arm any vblank event, |
1405 | * this is not a scanout controller where there is always |
1406 | * some periodic update going on, it is completely frozen |
1407 | * until we get an update. If MCDE output isn't yet enabled, |
1408 | * we just send a vblank dummy event back. |
1409 | */ |
1410 | if (crtc->state->active && drm_crtc_vblank_get(crtc) == 0) { |
1411 | dev_dbg(mcde->dev, "arm vblank event\n" ); |
1412 | drm_crtc_arm_vblank_event(crtc, e: event); |
1413 | } else { |
1414 | dev_dbg(mcde->dev, "insert fake vblank event\n" ); |
1415 | drm_crtc_send_vblank_event(crtc, e: event); |
1416 | } |
1417 | |
1418 | spin_unlock_irq(lock: &crtc->dev->event_lock); |
1419 | } |
1420 | |
1421 | /* |
1422 | * We do not start sending framebuffer updates before the |
1423 | * display is enabled. Update events will however be dispatched |
1424 | * from the DRM core before the display is enabled. |
1425 | */ |
1426 | if (fb) { |
1427 | mcde_set_extsrc(mcde, buffer_address: drm_fb_dma_get_gem_addr(fb, state: pstate, plane: 0)); |
1428 | dev_info_once(mcde->dev, "first update of display contents\n" ); |
1429 | /* |
1430 | * Usually the flow is already active, unless we are in |
1431 | * oneshot mode, then we need to kick the flow right here. |
1432 | */ |
1433 | if (mcde->flow_active == 0) |
1434 | mcde_start_flow(mcde); |
1435 | } else { |
1436 | /* |
1437 | * If an update is receieved before the MCDE is enabled |
1438 | * (before mcde_display_enable() is called) we can't really |
1439 | * do much with that buffer. |
1440 | */ |
1441 | dev_info(mcde->dev, "ignored a display update\n" ); |
1442 | } |
1443 | } |
1444 | |
1445 | static int mcde_display_enable_vblank(struct drm_simple_display_pipe *pipe) |
1446 | { |
1447 | struct drm_crtc *crtc = &pipe->crtc; |
1448 | struct drm_device *drm = crtc->dev; |
1449 | struct mcde *mcde = to_mcde(drm); |
1450 | u32 val; |
1451 | |
1452 | /* Enable all VBLANK IRQs */ |
1453 | val = MCDE_PP_VCMPA | |
1454 | MCDE_PP_VCMPB | |
1455 | MCDE_PP_VSCC0 | |
1456 | MCDE_PP_VSCC1 | |
1457 | MCDE_PP_VCMPC0 | |
1458 | MCDE_PP_VCMPC1; |
1459 | writel(val, addr: mcde->regs + MCDE_IMSCPP); |
1460 | |
1461 | return 0; |
1462 | } |
1463 | |
1464 | static void mcde_display_disable_vblank(struct drm_simple_display_pipe *pipe) |
1465 | { |
1466 | struct drm_crtc *crtc = &pipe->crtc; |
1467 | struct drm_device *drm = crtc->dev; |
1468 | struct mcde *mcde = to_mcde(drm); |
1469 | |
1470 | /* Disable all VBLANK IRQs */ |
1471 | writel(val: 0, addr: mcde->regs + MCDE_IMSCPP); |
1472 | /* Clear any pending IRQs */ |
1473 | writel(val: 0xFFFFFFFF, addr: mcde->regs + MCDE_RISPP); |
1474 | } |
1475 | |
1476 | static struct drm_simple_display_pipe_funcs mcde_display_funcs = { |
1477 | .check = mcde_display_check, |
1478 | .enable = mcde_display_enable, |
1479 | .disable = mcde_display_disable, |
1480 | .update = mcde_display_update, |
1481 | .enable_vblank = mcde_display_enable_vblank, |
1482 | .disable_vblank = mcde_display_disable_vblank, |
1483 | }; |
1484 | |
1485 | int mcde_display_init(struct drm_device *drm) |
1486 | { |
1487 | struct mcde *mcde = to_mcde(drm); |
1488 | int ret; |
1489 | static const u32 formats[] = { |
1490 | DRM_FORMAT_ARGB8888, |
1491 | DRM_FORMAT_ABGR8888, |
1492 | DRM_FORMAT_XRGB8888, |
1493 | DRM_FORMAT_XBGR8888, |
1494 | DRM_FORMAT_RGB888, |
1495 | DRM_FORMAT_BGR888, |
1496 | DRM_FORMAT_ARGB4444, |
1497 | DRM_FORMAT_ABGR4444, |
1498 | DRM_FORMAT_XRGB4444, |
1499 | DRM_FORMAT_XBGR4444, |
1500 | /* These are actually IRGB1555 so intensity bit is lost */ |
1501 | DRM_FORMAT_XRGB1555, |
1502 | DRM_FORMAT_XBGR1555, |
1503 | DRM_FORMAT_RGB565, |
1504 | DRM_FORMAT_BGR565, |
1505 | DRM_FORMAT_YUV422, |
1506 | }; |
1507 | |
1508 | ret = mcde_init_clock_divider(mcde); |
1509 | if (ret) |
1510 | return ret; |
1511 | |
1512 | ret = drm_simple_display_pipe_init(dev: drm, pipe: &mcde->pipe, |
1513 | funcs: &mcde_display_funcs, |
1514 | formats, ARRAY_SIZE(formats), |
1515 | NULL, |
1516 | connector: mcde->connector); |
1517 | if (ret) |
1518 | return ret; |
1519 | |
1520 | return 0; |
1521 | } |
1522 | EXPORT_SYMBOL_GPL(mcde_display_init); |
1523 | |