blob: 6787201bf5525475f6a5e7f686736982c5cd5528 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Stefano Babic42f151a2010-10-13 12:17:14 +02002/*
3 * Porting to u-boot:
4 *
5 * (C) Copyright 2010
6 * Stefano Babic, DENX Software Engineering, sbabic@denx.de
7 *
8 * MX51 Linux framebuffer:
9 *
10 * (C) Copyright 2004-2010 Freescale Semiconductor, Inc.
Stefano Babic42f151a2010-10-13 12:17:14 +020011 */
12
Stefano Babic42f151a2010-10-13 12:17:14 +020013#include <common.h>
Simon Glass0f2af882020-05-10 11:40:05 -060014#include <log.h>
Simon Glass655306c2020-05-10 11:39:58 -060015#include <part.h>
Simon Glass274e0b02020-05-10 11:39:56 -060016#include <asm/cache.h>
Masahiro Yamada56a931c2016-09-21 11:28:55 +090017#include <linux/errno.h>
Eric Nelsonc9e2be62014-04-29 14:37:56 -070018#include <asm/global_data.h>
Stefano Babic42f151a2010-10-13 12:17:14 +020019#include <linux/string.h>
20#include <linux/list.h>
21#include <linux/fb.h>
22#include <asm/io.h>
Anatolij Gustschin983e2f2a2019-03-18 23:29:31 +010023#include <asm/mach-imx/video.h>
Stefano Babic42f151a2010-10-13 12:17:14 +020024#include <malloc.h>
Stefano Babic61852442011-09-28 11:21:15 +020025#include <video_fb.h>
Anatolij Gustschin411e73d2019-03-18 23:29:32 +010026#include "../videomodes.h"
Stefano Babic42f151a2010-10-13 12:17:14 +020027#include "ipu.h"
28#include "mxcfb.h"
Eric Nelson988ad422012-09-23 07:30:54 +000029#include "ipu_regs.h"
Heiko Schocher3c787842019-07-22 06:49:07 +020030#include "display.h"
Heiko Schocher92afc9b2019-07-22 06:49:08 +020031#include <panel.h>
Stefano Babic42f151a2010-10-13 12:17:14 +020032
Anatolij Gustschin983e2f2a2019-03-18 23:29:31 +010033#include <dm.h>
34#include <video.h>
35
Eric Nelsonc9e2be62014-04-29 14:37:56 -070036DECLARE_GLOBAL_DATA_PTR;
37
Stefano Babic42f151a2010-10-13 12:17:14 +020038static int mxcfb_map_video_memory(struct fb_info *fbi);
39static int mxcfb_unmap_video_memory(struct fb_info *fbi);
40
Anatolij Gustschin8329ce32020-05-25 14:34:17 +020041#if !CONFIG_IS_ENABLED(DM_VIDEO)
Stefano Babic61852442011-09-28 11:21:15 +020042/* graphics setup */
43static GraphicDevice panel;
Anatolij Gustschin8329ce32020-05-25 14:34:17 +020044#endif
Eric Nelson10a6cd12012-10-03 07:27:38 +000045static struct fb_videomode const *gmode;
Marek Vasutff5c108c2011-10-06 00:25:03 +020046static uint8_t gdisp;
47static uint32_t gpixfmt;
Stefano Babic42f151a2010-10-13 12:17:14 +020048
Jeroen Hofsteea175d302014-10-08 22:57:47 +020049static void fb_videomode_to_var(struct fb_var_screeninfo *var,
Stefano Babic42f151a2010-10-13 12:17:14 +020050 const struct fb_videomode *mode)
51{
52 var->xres = mode->xres;
53 var->yres = mode->yres;
54 var->xres_virtual = mode->xres;
55 var->yres_virtual = mode->yres;
56 var->xoffset = 0;
57 var->yoffset = 0;
58 var->pixclock = mode->pixclock;
59 var->left_margin = mode->left_margin;
60 var->right_margin = mode->right_margin;
61 var->upper_margin = mode->upper_margin;
62 var->lower_margin = mode->lower_margin;
63 var->hsync_len = mode->hsync_len;
64 var->vsync_len = mode->vsync_len;
65 var->sync = mode->sync;
66 var->vmode = mode->vmode & FB_VMODE_MASK;
67}
68
69/*
70 * Structure containing the MXC specific framebuffer information.
71 */
72struct mxcfb_info {
73 int blank;
74 ipu_channel_t ipu_ch;
75 int ipu_di;
76 u32 ipu_di_pix_fmt;
77 unsigned char overlay;
78 unsigned char alpha_chan_en;
79 dma_addr_t alpha_phy_addr0;
80 dma_addr_t alpha_phy_addr1;
81 void *alpha_virt_addr0;
82 void *alpha_virt_addr1;
83 uint32_t alpha_mem_len;
84 uint32_t cur_ipu_buf;
85 uint32_t cur_ipu_alpha_buf;
86
87 u32 pseudo_palette[16];
88};
89
90enum {
91 BOTH_ON,
92 SRC_ON,
93 TGT_ON,
94 BOTH_OFF
95};
96
97static unsigned long default_bpp = 16;
98static unsigned char g_dp_in_use;
99static struct fb_info *mxcfb_info[3];
100static int ext_clk_used;
101
102static uint32_t bpp_to_pixfmt(struct fb_info *fbi)
103{
104 uint32_t pixfmt = 0;
105
106 debug("bpp_to_pixfmt: %d\n", fbi->var.bits_per_pixel);
107
108 if (fbi->var.nonstd)
109 return fbi->var.nonstd;
110
111 switch (fbi->var.bits_per_pixel) {
112 case 24:
113 pixfmt = IPU_PIX_FMT_BGR24;
114 break;
115 case 32:
116 pixfmt = IPU_PIX_FMT_BGR32;
117 break;
118 case 16:
119 pixfmt = IPU_PIX_FMT_RGB565;
120 break;
121 }
122 return pixfmt;
123}
124
Stefano Babic42f151a2010-10-13 12:17:14 +0200125static int setup_disp_channel1(struct fb_info *fbi)
126{
127 ipu_channel_params_t params;
128 struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
129
130 memset(&params, 0, sizeof(params));
131 params.mem_dp_bg_sync.di = mxc_fbi->ipu_di;
132
133 debug("%s called\n", __func__);
134 /*
135 * Assuming interlaced means yuv output, below setting also
136 * valid for mem_dc_sync. FG should have the same vmode as BG.
137 */
138 if (fbi->var.vmode & FB_VMODE_INTERLACED) {
139 params.mem_dp_bg_sync.interlaced = 1;
140 params.mem_dp_bg_sync.out_pixel_fmt =
141 IPU_PIX_FMT_YUV444;
142 } else {
143 if (mxc_fbi->ipu_di_pix_fmt) {
144 params.mem_dp_bg_sync.out_pixel_fmt =
145 mxc_fbi->ipu_di_pix_fmt;
146 } else {
147 params.mem_dp_bg_sync.out_pixel_fmt =
148 IPU_PIX_FMT_RGB666;
149 }
150 }
151 params.mem_dp_bg_sync.in_pixel_fmt = bpp_to_pixfmt(fbi);
152 if (mxc_fbi->alpha_chan_en)
153 params.mem_dp_bg_sync.alpha_chan_en = 1;
154
155 ipu_init_channel(mxc_fbi->ipu_ch, &params);
156
157 return 0;
158}
159
160static int setup_disp_channel2(struct fb_info *fbi)
161{
162 int retval = 0;
163 struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
164
165 mxc_fbi->cur_ipu_buf = 1;
166 if (mxc_fbi->alpha_chan_en)
167 mxc_fbi->cur_ipu_alpha_buf = 1;
168
169 fbi->var.xoffset = fbi->var.yoffset = 0;
170
171 debug("%s: %x %d %d %d %lx %lx\n",
172 __func__,
173 mxc_fbi->ipu_ch,
174 fbi->var.xres,
175 fbi->var.yres,
176 fbi->fix.line_length,
177 fbi->fix.smem_start,
178 fbi->fix.smem_start +
179 (fbi->fix.line_length * fbi->var.yres));
180
181 retval = ipu_init_channel_buffer(mxc_fbi->ipu_ch, IPU_INPUT_BUFFER,
182 bpp_to_pixfmt(fbi),
183 fbi->var.xres, fbi->var.yres,
184 fbi->fix.line_length,
185 fbi->fix.smem_start +
186 (fbi->fix.line_length * fbi->var.yres),
187 fbi->fix.smem_start,
188 0, 0);
189 if (retval)
190 printf("ipu_init_channel_buffer error %d\n", retval);
191
192 return retval;
193}
194
195/*
196 * Set framebuffer parameters and change the operating mode.
197 *
198 * @param info framebuffer information pointer
199 */
200static int mxcfb_set_par(struct fb_info *fbi)
201{
202 int retval = 0;
203 u32 mem_len;
204 ipu_di_signal_cfg_t sig_cfg;
205 struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
206 uint32_t out_pixel_fmt;
207
208 ipu_disable_channel(mxc_fbi->ipu_ch);
209 ipu_uninit_channel(mxc_fbi->ipu_ch);
Stefano Babic42f151a2010-10-13 12:17:14 +0200210
211 mem_len = fbi->var.yres_virtual * fbi->fix.line_length;
212 if (!fbi->fix.smem_start || (mem_len > fbi->fix.smem_len)) {
213 if (fbi->fix.smem_start)
214 mxcfb_unmap_video_memory(fbi);
215
216 if (mxcfb_map_video_memory(fbi) < 0)
217 return -ENOMEM;
218 }
219
220 setup_disp_channel1(fbi);
221
222 memset(&sig_cfg, 0, sizeof(sig_cfg));
223 if (fbi->var.vmode & FB_VMODE_INTERLACED) {
224 sig_cfg.interlaced = 1;
225 out_pixel_fmt = IPU_PIX_FMT_YUV444;
226 } else {
227 if (mxc_fbi->ipu_di_pix_fmt)
228 out_pixel_fmt = mxc_fbi->ipu_di_pix_fmt;
229 else
230 out_pixel_fmt = IPU_PIX_FMT_RGB666;
231 }
232 if (fbi->var.vmode & FB_VMODE_ODD_FLD_FIRST) /* PAL */
233 sig_cfg.odd_field_first = 1;
234 if ((fbi->var.sync & FB_SYNC_EXT) || ext_clk_used)
235 sig_cfg.ext_clk = 1;
236 if (fbi->var.sync & FB_SYNC_HOR_HIGH_ACT)
237 sig_cfg.Hsync_pol = 1;
238 if (fbi->var.sync & FB_SYNC_VERT_HIGH_ACT)
239 sig_cfg.Vsync_pol = 1;
240 if (!(fbi->var.sync & FB_SYNC_CLK_LAT_FALL))
241 sig_cfg.clk_pol = 1;
242 if (fbi->var.sync & FB_SYNC_DATA_INVERT)
243 sig_cfg.data_pol = 1;
244 if (!(fbi->var.sync & FB_SYNC_OE_LOW_ACT))
245 sig_cfg.enable_pol = 1;
246 if (fbi->var.sync & FB_SYNC_CLK_IDLE_EN)
247 sig_cfg.clkidle_en = 1;
248
Jeroen Hofstee98276212014-10-14 20:37:14 +0200249 debug("pixclock = %lu Hz\n", PICOS2KHZ(fbi->var.pixclock) * 1000UL);
Stefano Babic42f151a2010-10-13 12:17:14 +0200250
251 if (ipu_init_sync_panel(mxc_fbi->ipu_di,
252 (PICOS2KHZ(fbi->var.pixclock)) * 1000UL,
253 fbi->var.xres, fbi->var.yres,
254 out_pixel_fmt,
255 fbi->var.left_margin,
256 fbi->var.hsync_len,
257 fbi->var.right_margin,
258 fbi->var.upper_margin,
259 fbi->var.vsync_len,
260 fbi->var.lower_margin,
261 0, sig_cfg) != 0) {
262 puts("mxcfb: Error initializing panel.\n");
263 return -EINVAL;
264 }
265
266 retval = setup_disp_channel2(fbi);
267 if (retval)
268 return retval;
269
270 if (mxc_fbi->blank == FB_BLANK_UNBLANK)
271 ipu_enable_channel(mxc_fbi->ipu_ch);
272
273 return retval;
274}
275
276/*
277 * Check framebuffer variable parameters and adjust to valid values.
278 *
279 * @param var framebuffer variable parameters
280 *
281 * @param info framebuffer information pointer
282 */
283static int mxcfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
284{
285 u32 vtotal;
286 u32 htotal;
287
288 if (var->xres_virtual < var->xres)
289 var->xres_virtual = var->xres;
290 if (var->yres_virtual < var->yres)
291 var->yres_virtual = var->yres;
292
293 if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) &&
294 (var->bits_per_pixel != 16) && (var->bits_per_pixel != 8))
295 var->bits_per_pixel = default_bpp;
296
297 switch (var->bits_per_pixel) {
298 case 8:
299 var->red.length = 3;
300 var->red.offset = 5;
301 var->red.msb_right = 0;
302
303 var->green.length = 3;
304 var->green.offset = 2;
305 var->green.msb_right = 0;
306
307 var->blue.length = 2;
308 var->blue.offset = 0;
309 var->blue.msb_right = 0;
310
311 var->transp.length = 0;
312 var->transp.offset = 0;
313 var->transp.msb_right = 0;
314 break;
315 case 16:
316 var->red.length = 5;
317 var->red.offset = 11;
318 var->red.msb_right = 0;
319
320 var->green.length = 6;
321 var->green.offset = 5;
322 var->green.msb_right = 0;
323
324 var->blue.length = 5;
325 var->blue.offset = 0;
326 var->blue.msb_right = 0;
327
328 var->transp.length = 0;
329 var->transp.offset = 0;
330 var->transp.msb_right = 0;
331 break;
332 case 24:
333 var->red.length = 8;
334 var->red.offset = 16;
335 var->red.msb_right = 0;
336
337 var->green.length = 8;
338 var->green.offset = 8;
339 var->green.msb_right = 0;
340
341 var->blue.length = 8;
342 var->blue.offset = 0;
343 var->blue.msb_right = 0;
344
345 var->transp.length = 0;
346 var->transp.offset = 0;
347 var->transp.msb_right = 0;
348 break;
349 case 32:
350 var->red.length = 8;
351 var->red.offset = 16;
352 var->red.msb_right = 0;
353
354 var->green.length = 8;
355 var->green.offset = 8;
356 var->green.msb_right = 0;
357
358 var->blue.length = 8;
359 var->blue.offset = 0;
360 var->blue.msb_right = 0;
361
362 var->transp.length = 8;
363 var->transp.offset = 24;
364 var->transp.msb_right = 0;
365 break;
366 }
367
368 if (var->pixclock < 1000) {
369 htotal = var->xres + var->right_margin + var->hsync_len +
370 var->left_margin;
371 vtotal = var->yres + var->lower_margin + var->vsync_len +
372 var->upper_margin;
373 var->pixclock = (vtotal * htotal * 6UL) / 100UL;
374 var->pixclock = KHZ2PICOS(var->pixclock);
375 printf("pixclock set for 60Hz refresh = %u ps\n",
376 var->pixclock);
377 }
378
379 var->height = -1;
380 var->width = -1;
381 var->grayscale = 0;
382
383 return 0;
384}
385
386static int mxcfb_map_video_memory(struct fb_info *fbi)
387{
388 if (fbi->fix.smem_len < fbi->var.yres_virtual * fbi->fix.line_length) {
389 fbi->fix.smem_len = fbi->var.yres_virtual *
390 fbi->fix.line_length;
391 }
Eric Nelsonfe6296f2013-07-26 17:53:45 -0700392 fbi->fix.smem_len = roundup(fbi->fix.smem_len, ARCH_DMA_MINALIGN);
Anatolij Gustschin983e2f2a2019-03-18 23:29:31 +0100393
394#if CONFIG_IS_ENABLED(DM_VIDEO)
395 fbi->screen_base = (char *)gd->video_bottom;
396#else
Eric Nelsonfe6296f2013-07-26 17:53:45 -0700397 fbi->screen_base = (char *)memalign(ARCH_DMA_MINALIGN,
398 fbi->fix.smem_len);
Anatolij Gustschin983e2f2a2019-03-18 23:29:31 +0100399#endif
400
Stefano Babic61852442011-09-28 11:21:15 +0200401 fbi->fix.smem_start = (unsigned long)fbi->screen_base;
Stefano Babic42f151a2010-10-13 12:17:14 +0200402 if (fbi->screen_base == 0) {
403 puts("Unable to allocate framebuffer memory\n");
404 fbi->fix.smem_len = 0;
405 fbi->fix.smem_start = 0;
406 return -EBUSY;
407 }
408
409 debug("allocated fb @ paddr=0x%08X, size=%d.\n",
410 (uint32_t) fbi->fix.smem_start, fbi->fix.smem_len);
411
412 fbi->screen_size = fbi->fix.smem_len;
413
Anatolij Gustschin983e2f2a2019-03-18 23:29:31 +0100414#if CONFIG_IS_ENABLED(VIDEO)
Eric Nelsonc9e2be62014-04-29 14:37:56 -0700415 gd->fb_base = fbi->fix.smem_start;
Anatolij Gustschin983e2f2a2019-03-18 23:29:31 +0100416#endif
Eric Nelsonc9e2be62014-04-29 14:37:56 -0700417
Stefano Babic42f151a2010-10-13 12:17:14 +0200418 /* Clear the screen */
419 memset((char *)fbi->screen_base, 0, fbi->fix.smem_len);
420
421 return 0;
422}
423
424static int mxcfb_unmap_video_memory(struct fb_info *fbi)
425{
426 fbi->screen_base = 0;
427 fbi->fix.smem_start = 0;
428 fbi->fix.smem_len = 0;
429 return 0;
430}
431
432/*
433 * Initializes the framebuffer information pointer. After allocating
434 * sufficient memory for the framebuffer structure, the fields are
435 * filled with custom information passed in from the configurable
436 * structures. This includes information such as bits per pixel,
437 * color maps, screen width/height and RGBA offsets.
438 *
439 * @return Framebuffer structure initialized with our information
440 */
441static struct fb_info *mxcfb_init_fbinfo(void)
442{
443#define BYTES_PER_LONG 4
444#define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG))
445 struct fb_info *fbi;
446 struct mxcfb_info *mxcfbi;
447 char *p;
448 int size = sizeof(struct mxcfb_info) + PADDING +
449 sizeof(struct fb_info);
450
451 debug("%s: %d %d %d %d\n",
452 __func__,
453 PADDING,
454 size,
455 sizeof(struct mxcfb_info),
456 sizeof(struct fb_info));
457 /*
458 * Allocate sufficient memory for the fb structure
459 */
460
461 p = malloc(size);
462 if (!p)
463 return NULL;
464
465 memset(p, 0, size);
466
467 fbi = (struct fb_info *)p;
468 fbi->par = p + sizeof(struct fb_info) + PADDING;
469
470 mxcfbi = (struct mxcfb_info *)fbi->par;
471 debug("Framebuffer structures at: fbi=0x%x mxcfbi=0x%x\n",
472 (unsigned int)fbi, (unsigned int)mxcfbi);
473
474 fbi->var.activate = FB_ACTIVATE_NOW;
475
476 fbi->flags = FBINFO_FLAG_DEFAULT;
477 fbi->pseudo_palette = mxcfbi->pseudo_palette;
478
479 return fbi;
480}
481
Anatolij Gustschin8329ce32020-05-25 14:34:17 +0200482extern struct clk *g_ipu_clk;
483
Stefano Babic42f151a2010-10-13 12:17:14 +0200484/*
485 * Probe routine for the framebuffer driver. It is called during the
Jeroen Hofstee98276212014-10-14 20:37:14 +0200486 * driver binding process. The following functions are performed in
Stefano Babic42f151a2010-10-13 12:17:14 +0200487 * this routine: Framebuffer initialization, Memory allocation and
488 * mapping, Framebuffer registration, IPU initialization.
489 *
490 * @return Appropriate error code to the kernel common code
491 */
Marek Vasutff5c108c2011-10-06 00:25:03 +0200492static int mxcfb_probe(u32 interface_pix_fmt, uint8_t disp,
Eric Nelson10a6cd12012-10-03 07:27:38 +0000493 struct fb_videomode const *mode)
Stefano Babic42f151a2010-10-13 12:17:14 +0200494{
495 struct fb_info *fbi;
496 struct mxcfb_info *mxcfbi;
Stefano Babic42f151a2010-10-13 12:17:14 +0200497
498 /*
499 * Initialize FB structures
500 */
501 fbi = mxcfb_init_fbinfo();
Anatolij Gustschin8329ce32020-05-25 14:34:17 +0200502 if (!fbi)
503 return -ENOMEM;
504
Stefano Babic42f151a2010-10-13 12:17:14 +0200505 mxcfbi = (struct mxcfb_info *)fbi->par;
506
507 if (!g_dp_in_use) {
508 mxcfbi->ipu_ch = MEM_BG_SYNC;
509 mxcfbi->blank = FB_BLANK_UNBLANK;
510 } else {
511 mxcfbi->ipu_ch = MEM_DC_SYNC;
512 mxcfbi->blank = FB_BLANK_POWERDOWN;
513 }
514
Marek Vasutff5c108c2011-10-06 00:25:03 +0200515 mxcfbi->ipu_di = disp;
Stefano Babic42f151a2010-10-13 12:17:14 +0200516
Anatolij Gustschin8329ce32020-05-25 14:34:17 +0200517 if (!ipu_clk_enabled())
518 clk_enable(g_ipu_clk);
519
Stefano Babic42f151a2010-10-13 12:17:14 +0200520 ipu_disp_set_global_alpha(mxcfbi->ipu_ch, 1, 0x80);
521 ipu_disp_set_color_key(mxcfbi->ipu_ch, 0, 0);
Stefano Babic42f151a2010-10-13 12:17:14 +0200522
523 g_dp_in_use = 1;
524
525 mxcfb_info[mxcfbi->ipu_di] = fbi;
526
527 /* Need dummy values until real panel is configured */
Stefano Babic42f151a2010-10-13 12:17:14 +0200528
529 mxcfbi->ipu_di_pix_fmt = interface_pix_fmt;
530 fb_videomode_to_var(&fbi->var, mode);
Stefano Babic61852442011-09-28 11:21:15 +0200531 fbi->var.bits_per_pixel = 16;
Anatolij Gustschin8329ce32020-05-25 14:34:17 +0200532 fbi->fix.line_length = fbi->var.xres_virtual *
533 (fbi->var.bits_per_pixel / 8);
Stefano Babic61852442011-09-28 11:21:15 +0200534 fbi->fix.smem_len = fbi->var.yres_virtual * fbi->fix.line_length;
Stefano Babic42f151a2010-10-13 12:17:14 +0200535
536 mxcfb_check_var(&fbi->var, fbi);
537
538 /* Default Y virtual size is 2x panel size */
539 fbi->var.yres_virtual = fbi->var.yres * 2;
540
Jeroen Hofstee98276212014-10-14 20:37:14 +0200541 /* allocate fb first */
Stefano Babic42f151a2010-10-13 12:17:14 +0200542 if (mxcfb_map_video_memory(fbi) < 0)
543 return -ENOMEM;
544
545 mxcfb_set_par(fbi);
546
Anatolij Gustschin8329ce32020-05-25 14:34:17 +0200547#if !CONFIG_IS_ENABLED(DM_VIDEO)
Stefano Babic61852442011-09-28 11:21:15 +0200548 panel.winSizeX = mode->xres;
549 panel.winSizeY = mode->yres;
550 panel.plnSizeX = mode->xres;
551 panel.plnSizeY = mode->yres;
Stefano Babic42f151a2010-10-13 12:17:14 +0200552
Stefano Babic61852442011-09-28 11:21:15 +0200553 panel.frameAdrs = (u32)fbi->screen_base;
554 panel.memSize = fbi->screen_size;
Stefano Babic42f151a2010-10-13 12:17:14 +0200555
Stefano Babic61852442011-09-28 11:21:15 +0200556 panel.gdfBytesPP = 2;
557 panel.gdfIndex = GDF_16BIT_565RGB;
Anatolij Gustschin8329ce32020-05-25 14:34:17 +0200558#endif
559#ifdef DEBUG
Stefano Babic42f151a2010-10-13 12:17:14 +0200560 ipu_dump_registers();
Anatolij Gustschin8329ce32020-05-25 14:34:17 +0200561#endif
Stefano Babic42f151a2010-10-13 12:17:14 +0200562
563 return 0;
Stefano Babic42f151a2010-10-13 12:17:14 +0200564}
565
Eric Nelson988ad422012-09-23 07:30:54 +0000566void ipuv3_fb_shutdown(void)
567{
Anatolij Gustschinfa4211c2017-08-25 15:10:43 +0200568 int i;
Tom Rini47dce762017-09-01 16:17:17 -0400569 struct ipu_stat *stat = (struct ipu_stat *)IPU_STAT;
Eric Nelson988ad422012-09-23 07:30:54 +0000570
Anatolij Gustschin3e7ad7d2017-09-04 23:33:45 +0200571 if (!ipu_clk_enabled())
572 return;
573
Eric Nelson988ad422012-09-23 07:30:54 +0000574 for (i = 0; i < ARRAY_SIZE(mxcfb_info); i++) {
575 struct fb_info *fbi = mxcfb_info[i];
576 if (fbi) {
577 struct mxcfb_info *mxc_fbi = fbi->par;
578 ipu_disable_channel(mxc_fbi->ipu_ch);
579 ipu_uninit_channel(mxc_fbi->ipu_ch);
580 }
581 }
582 for (i = 0; i < ARRAY_SIZE(stat->int_stat); i++) {
583 __raw_writel(__raw_readl(&stat->int_stat[i]),
584 &stat->int_stat[i]);
585 }
586}
587
Anatolij Gustschin8329ce32020-05-25 14:34:17 +0200588#if !CONFIG_IS_ENABLED(DM_VIDEO)
Stefano Babic61852442011-09-28 11:21:15 +0200589void *video_hw_init(void)
Stefano Babic42f151a2010-10-13 12:17:14 +0200590{
591 int ret;
592
593 ret = ipu_probe();
594 if (ret)
595 puts("Error initializing IPU\n");
596
Marek Vasutff5c108c2011-10-06 00:25:03 +0200597 ret = mxcfb_probe(gpixfmt, gdisp, gmode);
Stefano Babic61852442011-09-28 11:21:15 +0200598 debug("Framebuffer at 0x%x\n", (unsigned int)panel.frameAdrs);
Heiko Schocherabb59422019-07-22 06:49:05 +0200599 gd->fb_base = panel.frameAdrs;
Stefano Babic42f151a2010-10-13 12:17:14 +0200600
Stefano Babic61852442011-09-28 11:21:15 +0200601 return (void *)&panel;
602}
Anatolij Gustschin8329ce32020-05-25 14:34:17 +0200603#endif
Stefano Babic42f151a2010-10-13 12:17:14 +0200604
Eric Nelson10a6cd12012-10-03 07:27:38 +0000605int ipuv3_fb_init(struct fb_videomode const *mode,
606 uint8_t disp,
607 uint32_t pixfmt)
Stefano Babic61852442011-09-28 11:21:15 +0200608{
609 gmode = mode;
Marek Vasutff5c108c2011-10-06 00:25:03 +0200610 gdisp = disp;
611 gpixfmt = pixfmt;
Stefano Babic61852442011-09-28 11:21:15 +0200612
613 return 0;
Stefano Babic42f151a2010-10-13 12:17:14 +0200614}
Anatolij Gustschin983e2f2a2019-03-18 23:29:31 +0100615
616#if CONFIG_IS_ENABLED(DM_VIDEO)
617enum {
618 /* Maximum display size we support */
619 LCD_MAX_WIDTH = 1920,
620 LCD_MAX_HEIGHT = 1080,
621 LCD_MAX_LOG2_BPP = VIDEO_BPP16,
622};
623
624static int ipuv3_video_probe(struct udevice *dev)
625{
626 struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
627 struct video_priv *uc_priv = dev_get_uclass_priv(dev);
Heiko Schocher3c787842019-07-22 06:49:07 +0200628#if defined(CONFIG_DISPLAY)
629 struct udevice *disp_dev;
630#endif
Anatolij Gustschin983e2f2a2019-03-18 23:29:31 +0100631 u32 fb_start, fb_end;
632 int ret;
633
634 debug("%s() plat: base 0x%lx, size 0x%x\n",
635 __func__, plat->base, plat->size);
636
637 ret = ipu_probe();
638 if (ret)
639 return ret;
640
641 ret = ipu_displays_init();
642 if (ret < 0)
643 return ret;
644
645 ret = mxcfb_probe(gpixfmt, gdisp, gmode);
646 if (ret < 0)
647 return ret;
648
Heiko Schocher3c787842019-07-22 06:49:07 +0200649#if defined(CONFIG_DISPLAY)
650 ret = uclass_first_device(UCLASS_DISPLAY, &disp_dev);
651 if (disp_dev) {
652 ret = display_enable(disp_dev, 16, NULL);
653 if (ret < 0)
654 return ret;
655 }
656#endif
Anatolij Gustschin00878362020-05-26 00:09:22 +0200657 if (CONFIG_IS_ENABLED(PANEL)) {
658 struct udevice *panel_dev;
659
660 ret = uclass_get_device(UCLASS_PANEL, 0, &panel_dev);
661 if (panel_dev)
662 panel_enable_backlight(panel_dev);
663 }
Heiko Schocher3c787842019-07-22 06:49:07 +0200664
Anatolij Gustschin983e2f2a2019-03-18 23:29:31 +0100665 uc_priv->xsize = gmode->xres;
666 uc_priv->ysize = gmode->yres;
667 uc_priv->bpix = LCD_MAX_LOG2_BPP;
668
669 /* Enable dcache for the frame buffer */
670 fb_start = plat->base & ~(MMU_SECTION_SIZE - 1);
671 fb_end = plat->base + plat->size;
672 fb_end = ALIGN(fb_end, 1 << MMU_SECTION_SHIFT);
673 mmu_set_region_dcache_behaviour(fb_start, fb_end - fb_start,
674 DCACHE_WRITEBACK);
675 video_set_flush_dcache(dev, true);
Heiko Schocherabb59422019-07-22 06:49:05 +0200676 gd->fb_base = fb_start;
Anatolij Gustschin983e2f2a2019-03-18 23:29:31 +0100677
678 return 0;
679}
680
681struct ipuv3_video_priv {
682 ulong regs;
683};
684
685static int ipuv3_video_bind(struct udevice *dev)
686{
687 struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
688
689 plat->size = LCD_MAX_WIDTH * LCD_MAX_HEIGHT *
Marek Vasut28bdd932019-05-06 01:11:32 +0200690 (1 << VIDEO_BPP32) / 8;
Anatolij Gustschin983e2f2a2019-03-18 23:29:31 +0100691
692 return 0;
693}
694
695static const struct udevice_id ipuv3_video_ids[] = {
Anatolij Gustschin8329ce32020-05-25 14:34:17 +0200696#ifdef CONFIG_ARCH_MX6
Anatolij Gustschin983e2f2a2019-03-18 23:29:31 +0100697 { .compatible = "fsl,imx6q-ipu" },
Anatolij Gustschin8329ce32020-05-25 14:34:17 +0200698#endif
699#ifdef CONFIG_ARCH_MX5
Steffen Dirkwinkel4e40f1a2019-04-17 13:57:15 +0200700 { .compatible = "fsl,imx53-ipu" },
Anatolij Gustschin8329ce32020-05-25 14:34:17 +0200701#endif
Anatolij Gustschin983e2f2a2019-03-18 23:29:31 +0100702 { }
703};
704
705U_BOOT_DRIVER(ipuv3_video) = {
706 .name = "ipuv3_video",
707 .id = UCLASS_VIDEO,
708 .of_match = ipuv3_video_ids,
709 .bind = ipuv3_video_bind,
710 .probe = ipuv3_video_probe,
711 .priv_auto_alloc_size = sizeof(struct ipuv3_video_priv),
712 .flags = DM_FLAG_PRE_RELOC,
713};
714#endif /* CONFIG_DM_VIDEO */