2 * Display driver for Allwinner SoCs.
4 * (C) Copyright 2013-2014 Luc Verhaegen <libv@skynet.be>
5 * (C) Copyright 2014 Hans de Goede <hdegoede@redhat.com>
7 * SPDX-License-Identifier: GPL-2.0+
12 #include <asm/arch/clock.h>
13 #include <asm/arch/display.h>
14 #include <asm/arch/gpio.h>
15 #include <asm/global_data.h>
20 #include <fdt_support.h>
22 #include "videomodes.h"
23 #include "hitachi_tx18d42vm_lcd.h"
26 #ifdef CONFIG_VIDEO_LCD_BL_PWM_ACTIVE_LOW
34 DECLARE_GLOBAL_DATA_PTR;
43 #define SUNXI_MONITOR_LAST sunxi_monitor_vga
45 struct sunxi_display {
46 GraphicDevice graphic_device;
47 enum sunxi_monitor monitor;
51 #ifdef CONFIG_VIDEO_HDMI
54 * Wait up to 200ms for value to be set in given part of reg.
56 static int await_completion(u32 *reg, u32 mask, u32 val)
58 unsigned long tmo = timer_get_us() + 200000;
60 while ((readl(reg) & mask) != val) {
61 if (timer_get_us() > tmo) {
62 printf("DDC: timeout reading EDID\n");
69 static int sunxi_hdmi_hpd_detect(int hpd_delay)
71 struct sunxi_ccm_reg * const ccm =
72 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
73 struct sunxi_hdmi_reg * const hdmi =
74 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
75 unsigned long tmo = timer_get_us() + hpd_delay * 1000;
77 /* Set pll3 to 300MHz */
78 clock_set_pll3(300000000);
80 /* Set hdmi parent to pll3 */
81 clrsetbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_PLL_MASK,
84 /* Set ahb gating to pass */
85 #ifdef CONFIG_MACH_SUN6I
86 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_HDMI);
88 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_HDMI);
91 setbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_GATE);
93 writel(SUNXI_HDMI_CTRL_ENABLE, &hdmi->ctrl);
94 writel(SUNXI_HDMI_PAD_CTRL0_HDP, &hdmi->pad_ctrl0);
96 while (timer_get_us() < tmo) {
97 if (readl(&hdmi->hpd) & SUNXI_HDMI_HPD_DETECT)
104 static void sunxi_hdmi_shutdown(void)
106 struct sunxi_ccm_reg * const ccm =
107 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
108 struct sunxi_hdmi_reg * const hdmi =
109 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
111 clrbits_le32(&hdmi->ctrl, SUNXI_HDMI_CTRL_ENABLE);
112 clrbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_GATE);
113 clrbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_HDMI);
114 #ifdef CONFIG_MACH_SUN6I
115 clrbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_HDMI);
120 static int sunxi_hdmi_ddc_do_command(u32 cmnd, int offset, int n)
122 struct sunxi_hdmi_reg * const hdmi =
123 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
125 setbits_le32(&hdmi->ddc_fifo_ctrl, SUNXI_HDMI_DDC_FIFO_CTRL_CLEAR);
126 writel(SUNXI_HMDI_DDC_ADDR_EDDC_SEGMENT(offset >> 8) |
127 SUNXI_HMDI_DDC_ADDR_EDDC_ADDR |
128 SUNXI_HMDI_DDC_ADDR_OFFSET(offset) |
129 SUNXI_HMDI_DDC_ADDR_SLAVE_ADDR, &hdmi->ddc_addr);
130 #ifndef CONFIG_MACH_SUN6I
131 writel(n, &hdmi->ddc_byte_count);
132 writel(cmnd, &hdmi->ddc_cmnd);
134 writel(n << 16 | cmnd, &hdmi->ddc_cmnd);
136 setbits_le32(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_START);
138 return await_completion(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_START, 0);
141 static int sunxi_hdmi_ddc_read(int offset, u8 *buf, int count)
143 struct sunxi_hdmi_reg * const hdmi =
144 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
153 if (sunxi_hdmi_ddc_do_command(
154 SUNXI_HDMI_DDC_CMND_EXPLICIT_EDDC_READ,
158 for (i = 0; i < n; i++)
159 *buf++ = readb(&hdmi->ddc_fifo_data);
168 static int sunxi_hdmi_edid_get_block(int block, u8 *buf)
173 r = sunxi_hdmi_ddc_read(block * 128, buf, 128);
176 r = edid_check_checksum(buf);
178 printf("EDID block %d: checksum error%s\n",
179 block, retries ? ", retrying" : "");
181 } while (r && retries--);
186 static int sunxi_hdmi_edid_get_mode(struct ctfb_res_modes *mode)
188 struct edid1_info edid1;
189 struct edid_cea861_info cea681[4];
190 struct edid_detailed_timing *t =
191 (struct edid_detailed_timing *)edid1.monitor_details.timing;
192 struct sunxi_hdmi_reg * const hdmi =
193 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
194 struct sunxi_ccm_reg * const ccm =
195 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
196 int i, r, ext_blocks = 0;
198 /* SUNXI_HDMI_CTRL_ENABLE & PAD_CTRL0 are already set by hpd_detect */
199 writel(SUNXI_HDMI_PAD_CTRL1 | SUNXI_HDMI_PAD_CTRL1_HALVE,
201 writel(SUNXI_HDMI_PLL_CTRL | SUNXI_HDMI_PLL_CTRL_DIV(15),
203 writel(SUNXI_HDMI_PLL_DBG0_PLL3, &hdmi->pll_dbg0);
205 /* Reset i2c controller */
206 setbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_DDC_GATE);
207 writel(SUNXI_HMDI_DDC_CTRL_ENABLE |
208 SUNXI_HMDI_DDC_CTRL_SDA_ENABLE |
209 SUNXI_HMDI_DDC_CTRL_SCL_ENABLE |
210 SUNXI_HMDI_DDC_CTRL_RESET, &hdmi->ddc_ctrl);
211 if (await_completion(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_RESET, 0))
214 writel(SUNXI_HDMI_DDC_CLOCK, &hdmi->ddc_clock);
215 #ifndef CONFIG_MACH_SUN6I
216 writel(SUNXI_HMDI_DDC_LINE_CTRL_SDA_ENABLE |
217 SUNXI_HMDI_DDC_LINE_CTRL_SCL_ENABLE, &hdmi->ddc_line_ctrl);
220 r = sunxi_hdmi_edid_get_block(0, (u8 *)&edid1);
222 r = edid_check_info(&edid1);
224 printf("EDID: invalid EDID data\n");
229 ext_blocks = edid1.extension_flag;
232 for (i = 0; i < ext_blocks; i++) {
233 if (sunxi_hdmi_edid_get_block(1 + i,
234 (u8 *)&cea681[i]) != 0) {
241 /* Disable DDC engine, no longer needed */
242 clrbits_le32(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_ENABLE);
243 clrbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_DDC_GATE);
248 /* We want version 1.3 or 1.2 with detailed timing info */
249 if (edid1.version != 1 || (edid1.revision < 3 &&
250 !EDID1_INFO_FEATURE_PREFERRED_TIMING_MODE(edid1))) {
251 printf("EDID: unsupported version %d.%d\n",
252 edid1.version, edid1.revision);
256 /* Take the first usable detailed timing */
257 for (i = 0; i < 4; i++, t++) {
258 r = video_edid_dtd_to_ctfb_res_modes(t, mode);
263 printf("EDID: no usable detailed timing found\n");
267 /* Check for basic audio support, if found enable hdmi output */
268 sunxi_display.monitor = sunxi_monitor_dvi;
269 for (i = 0; i < ext_blocks; i++) {
270 if (cea681[i].extension_tag != EDID_CEA861_EXTENSION_TAG ||
271 cea681[i].revision < 2)
274 if (EDID_CEA861_SUPPORTS_BASIC_AUDIO(cea681[i]))
275 sunxi_display.monitor = sunxi_monitor_hdmi;
281 #endif /* CONFIG_VIDEO_HDMI */
283 #ifdef CONFIG_MACH_SUN4I
285 * Testing has shown that on sun4i the display backend engine does not have
286 * deep enough fifo-s causing flickering / tearing in full-hd mode due to
287 * fifo underruns. So on sun4i we use the display frontend engine to do the
288 * dma from memory, as the frontend does have deep enough fifo-s.
291 static const u32 sun4i_vert_coef[32] = {
292 0x00004000, 0x000140ff, 0x00033ffe, 0x00043ffd,
293 0x00063efc, 0xff083dfc, 0x000a3bfb, 0xff0d39fb,
294 0xff0f37fb, 0xff1136fa, 0xfe1433fb, 0xfe1631fb,
295 0xfd192ffb, 0xfd1c2cfb, 0xfd1f29fb, 0xfc2127fc,
296 0xfc2424fc, 0xfc2721fc, 0xfb291ffd, 0xfb2c1cfd,
297 0xfb2f19fd, 0xfb3116fe, 0xfb3314fe, 0xfa3611ff,
298 0xfb370fff, 0xfb390dff, 0xfb3b0a00, 0xfc3d08ff,
299 0xfc3e0600, 0xfd3f0400, 0xfe3f0300, 0xff400100,
302 static const u32 sun4i_horz_coef[64] = {
303 0x40000000, 0x00000000, 0x40fe0000, 0x0000ff03,
304 0x3ffd0000, 0x0000ff05, 0x3ffc0000, 0x0000ff06,
305 0x3efb0000, 0x0000ff08, 0x3dfb0000, 0x0000ff09,
306 0x3bfa0000, 0x0000fe0d, 0x39fa0000, 0x0000fe0f,
307 0x38fa0000, 0x0000fe10, 0x36fa0000, 0x0000fe12,
308 0x33fa0000, 0x0000fd16, 0x31fa0000, 0x0000fd18,
309 0x2ffa0000, 0x0000fd1a, 0x2cfa0000, 0x0000fc1e,
310 0x29fa0000, 0x0000fc21, 0x27fb0000, 0x0000fb23,
311 0x24fb0000, 0x0000fb26, 0x21fb0000, 0x0000fb29,
312 0x1ffc0000, 0x0000fa2b, 0x1cfc0000, 0x0000fa2e,
313 0x19fd0000, 0x0000fa30, 0x16fd0000, 0x0000fa33,
314 0x14fd0000, 0x0000fa35, 0x11fe0000, 0x0000fa37,
315 0x0ffe0000, 0x0000fa39, 0x0dfe0000, 0x0000fa3b,
316 0x0afe0000, 0x0000fa3e, 0x08ff0000, 0x0000fb3e,
317 0x06ff0000, 0x0000fb40, 0x05ff0000, 0x0000fc40,
318 0x03ff0000, 0x0000fd41, 0x01ff0000, 0x0000fe42,
321 static void sunxi_frontend_init(void)
323 struct sunxi_ccm_reg * const ccm =
324 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
325 struct sunxi_de_fe_reg * const de_fe =
326 (struct sunxi_de_fe_reg *)SUNXI_DE_FE0_BASE;
330 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_DE_FE0);
331 setbits_le32(&ccm->dram_clk_gate, 1 << CCM_DRAM_GATE_OFFSET_DE_FE0);
332 clock_set_de_mod_clock(&ccm->fe0_clk_cfg, 300000000);
334 setbits_le32(&de_fe->enable, SUNXI_DE_FE_ENABLE_EN);
336 for (i = 0; i < 32; i++) {
337 writel(sun4i_horz_coef[2 * i], &de_fe->ch0_horzcoef0[i]);
338 writel(sun4i_horz_coef[2 * i + 1], &de_fe->ch0_horzcoef1[i]);
339 writel(sun4i_vert_coef[i], &de_fe->ch0_vertcoef[i]);
340 writel(sun4i_horz_coef[2 * i], &de_fe->ch1_horzcoef0[i]);
341 writel(sun4i_horz_coef[2 * i + 1], &de_fe->ch1_horzcoef1[i]);
342 writel(sun4i_vert_coef[i], &de_fe->ch1_vertcoef[i]);
345 setbits_le32(&de_fe->frame_ctrl, SUNXI_DE_FE_FRAME_CTRL_COEF_RDY);
348 static void sunxi_frontend_mode_set(const struct ctfb_res_modes *mode,
349 unsigned int address)
351 struct sunxi_de_fe_reg * const de_fe =
352 (struct sunxi_de_fe_reg *)SUNXI_DE_FE0_BASE;
354 setbits_le32(&de_fe->bypass, SUNXI_DE_FE_BYPASS_CSC_BYPASS);
355 writel(CONFIG_SYS_SDRAM_BASE + address, &de_fe->ch0_addr);
356 writel(mode->xres * 4, &de_fe->ch0_stride);
357 writel(SUNXI_DE_FE_INPUT_FMT_ARGB8888, &de_fe->input_fmt);
358 writel(SUNXI_DE_FE_OUTPUT_FMT_ARGB8888, &de_fe->output_fmt);
360 writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
362 writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
363 &de_fe->ch0_outsize);
364 writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch0_horzfact);
365 writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch0_vertfact);
367 writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
369 writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
370 &de_fe->ch1_outsize);
371 writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch1_horzfact);
372 writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch1_vertfact);
374 setbits_le32(&de_fe->frame_ctrl, SUNXI_DE_FE_FRAME_CTRL_REG_RDY);
377 static void sunxi_frontend_enable(void)
379 struct sunxi_de_fe_reg * const de_fe =
380 (struct sunxi_de_fe_reg *)SUNXI_DE_FE0_BASE;
382 setbits_le32(&de_fe->frame_ctrl, SUNXI_DE_FE_FRAME_CTRL_FRM_START);
385 static void sunxi_frontend_init(void) {}
386 static void sunxi_frontend_mode_set(const struct ctfb_res_modes *mode,
387 unsigned int address) {}
388 static void sunxi_frontend_enable(void) {}
392 * This is the entity that mixes and matches the different layers and inputs.
393 * Allwinner calls it the back-end, but i like composer better.
395 static void sunxi_composer_init(void)
397 struct sunxi_ccm_reg * const ccm =
398 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
399 struct sunxi_de_be_reg * const de_be =
400 (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE;
403 sunxi_frontend_init();
405 #if defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I
407 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_DE_BE0);
411 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_DE_BE0);
412 #ifndef CONFIG_MACH_SUN4I /* On sun4i the frontend does the dma */
413 setbits_le32(&ccm->dram_clk_gate, 1 << CCM_DRAM_GATE_OFFSET_DE_BE0);
415 clock_set_de_mod_clock(&ccm->be0_clk_cfg, 300000000);
417 /* Engine bug, clear registers after reset */
418 for (i = 0x0800; i < 0x1000; i += 4)
419 writel(0, SUNXI_DE_BE0_BASE + i);
421 setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_ENABLE);
424 static void sunxi_composer_mode_set(const struct ctfb_res_modes *mode,
425 unsigned int address)
427 struct sunxi_de_be_reg * const de_be =
428 (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE;
430 sunxi_frontend_mode_set(mode, address);
432 writel(SUNXI_DE_BE_HEIGHT(mode->yres) | SUNXI_DE_BE_WIDTH(mode->xres),
434 writel(SUNXI_DE_BE_HEIGHT(mode->yres) | SUNXI_DE_BE_WIDTH(mode->xres),
435 &de_be->layer0_size);
436 #ifndef CONFIG_MACH_SUN4I /* On sun4i the frontend does the dma */
437 writel(SUNXI_DE_BE_LAYER_STRIDE(mode->xres), &de_be->layer0_stride);
438 writel(address << 3, &de_be->layer0_addr_low32b);
439 writel(address >> 29, &de_be->layer0_addr_high4b);
441 writel(SUNXI_DE_BE_LAYER_ATTR0_SRC_FE0, &de_be->layer0_attr0_ctrl);
443 writel(SUNXI_DE_BE_LAYER_ATTR1_FMT_XRGB8888, &de_be->layer0_attr1_ctrl);
445 setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_LAYER0_ENABLE);
448 static void sunxi_composer_enable(void)
450 struct sunxi_de_be_reg * const de_be =
451 (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE;
453 sunxi_frontend_enable();
455 setbits_le32(&de_be->reg_ctrl, SUNXI_DE_BE_REG_CTRL_LOAD_REGS);
456 setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_START);
460 * LCDC, what allwinner calls a CRTC, so timing controller and serializer.
462 static void sunxi_lcdc_pll_set(int tcon, int dotclock,
463 int *clk_div, int *clk_double)
465 struct sunxi_ccm_reg * const ccm =
466 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
467 int value, n, m, min_m, max_m, diff;
468 int best_n = 0, best_m = 0, best_diff = 0x0FFFFFFF;
472 #ifdef CONFIG_VIDEO_LCD_IF_PARALLEL
476 #ifdef CONFIG_VIDEO_LCD_IF_LVDS
485 * Find the lowest divider resulting in a matching clock, if there
486 * is no match, pick the closest lower clock, as monitors tend to
487 * not sync to higher frequencies.
489 for (m = min_m; m <= max_m; m++) {
490 n = (m * dotclock) / 3000;
492 if ((n >= 9) && (n <= 127)) {
493 value = (3000 * n) / m;
494 diff = dotclock - value;
495 if (diff < best_diff) {
503 /* These are just duplicates */
507 n = (m * dotclock) / 6000;
508 if ((n >= 9) && (n <= 127)) {
509 value = (6000 * n) / m;
510 diff = dotclock - value;
511 if (diff < best_diff) {
520 debug("dotclock: %dkHz = %dkHz: (%d * 3MHz * %d) / %d\n",
521 dotclock, (best_double + 1) * 3000 * best_n / best_m,
522 best_double + 1, best_n, best_m);
524 clock_set_pll3(best_n * 3000000);
527 writel(CCM_LCD_CH0_CTRL_GATE | CCM_LCD_CH0_CTRL_RST |
528 (best_double ? CCM_LCD_CH0_CTRL_PLL3_2X :
529 CCM_LCD_CH0_CTRL_PLL3),
530 &ccm->lcd0_ch0_clk_cfg);
532 writel(CCM_LCD_CH1_CTRL_GATE |
533 (best_double ? CCM_LCD_CH1_CTRL_PLL3_2X :
534 CCM_LCD_CH1_CTRL_PLL3) |
535 CCM_LCD_CH1_CTRL_M(best_m), &ccm->lcd0_ch1_clk_cfg);
539 *clk_double = best_double;
542 static void sunxi_lcdc_init(void)
544 struct sunxi_ccm_reg * const ccm =
545 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
546 struct sunxi_lcdc_reg * const lcdc =
547 (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
550 #if defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I
551 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_LCD0);
553 setbits_le32(&ccm->lcd0_ch0_clk_cfg, CCM_LCD_CH0_CTRL_RST);
557 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_LCD0);
558 #ifdef CONFIG_VIDEO_LCD_IF_LVDS
559 setbits_le32(&ccm->lvds_clk_cfg, CCM_LVDS_CTRL_RST);
563 writel(0, &lcdc->ctrl); /* Disable tcon */
564 writel(0, &lcdc->int0); /* Disable all interrupts */
566 /* Disable tcon0 dot clock */
567 clrbits_le32(&lcdc->tcon0_dclk, SUNXI_LCDC_TCON0_DCLK_ENABLE);
569 /* Set all io lines to tristate */
570 writel(0xffffffff, &lcdc->tcon0_io_tristate);
571 writel(0xffffffff, &lcdc->tcon1_io_tristate);
574 static void sunxi_lcdc_enable(void)
576 struct sunxi_lcdc_reg * const lcdc =
577 (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
579 setbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_TCON_ENABLE);
580 #ifdef CONFIG_VIDEO_LCD_IF_LVDS
581 setbits_le32(&lcdc->tcon0_lvds_intf, SUNXI_LCDC_TCON0_LVDS_INTF_ENABLE);
582 setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0);
583 setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_UPDATE);
584 udelay(2); /* delay at least 1200 ns */
585 setbits_le32(&lcdc->lvds_ana1, SUNXI_LCDC_LVDS_ANA1_INIT1);
586 udelay(1); /* delay at least 120 ns */
587 setbits_le32(&lcdc->lvds_ana1, SUNXI_LCDC_LVDS_ANA1_INIT2);
588 setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_UPDATE);
592 static void sunxi_lcdc_panel_enable(void)
597 * Start with backlight disabled to avoid the screen flashing to
598 * white while the lcd inits.
600 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_EN);
602 gpio_request(pin, "lcd_backlight_enable");
603 gpio_direction_output(pin, 0);
606 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_PWM);
608 gpio_request(pin, "lcd_backlight_pwm");
609 gpio_direction_output(pin, PWM_OFF);
612 /* Give the backlight some time to turn off and power up the panel. */
614 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_POWER);
616 gpio_request(pin, "lcd_power");
617 gpio_direction_output(pin, 1);
621 static void sunxi_lcdc_backlight_enable(void)
626 * We want to have scanned out at least one frame before enabling the
627 * backlight to avoid the screen flashing to white when we enable it.
631 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_EN);
633 gpio_direction_output(pin, 1);
635 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_PWM);
637 gpio_direction_output(pin, PWM_ON);
640 static int sunxi_lcdc_get_clk_delay(const struct ctfb_res_modes *mode)
644 delay = mode->lower_margin + mode->vsync_len + mode->upper_margin - 2;
645 return (delay > 30) ? 30 : delay;
648 static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode)
650 struct sunxi_lcdc_reg * const lcdc =
651 (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
652 int bp, clk_delay, clk_div, clk_double, pin, total, val;
654 for (pin = SUNXI_GPD(0); pin <= SUNXI_GPD(27); pin++)
655 #ifdef CONFIG_VIDEO_LCD_IF_PARALLEL
656 sunxi_gpio_set_cfgpin(pin, SUNXI_GPD0_LCD0);
658 #ifdef CONFIG_VIDEO_LCD_IF_LVDS
659 sunxi_gpio_set_cfgpin(pin, SUNXI_GPD0_LVDS0);
662 sunxi_lcdc_pll_set(0, mode->pixclock_khz, &clk_div, &clk_double);
665 clrsetbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_IO_MAP_MASK,
666 SUNXI_LCDC_CTRL_IO_MAP_TCON0);
668 clk_delay = sunxi_lcdc_get_clk_delay(mode);
669 writel(SUNXI_LCDC_TCON0_CTRL_ENABLE |
670 SUNXI_LCDC_TCON0_CTRL_CLK_DELAY(clk_delay), &lcdc->tcon0_ctrl);
672 writel(SUNXI_LCDC_TCON0_DCLK_ENABLE |
673 SUNXI_LCDC_TCON0_DCLK_DIV(clk_div), &lcdc->tcon0_dclk);
675 writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(mode->yres),
676 &lcdc->tcon0_timing_active);
678 bp = mode->hsync_len + mode->left_margin;
679 total = mode->xres + mode->right_margin + bp;
680 writel(SUNXI_LCDC_TCON0_TIMING_H_TOTAL(total) |
681 SUNXI_LCDC_TCON0_TIMING_H_BP(bp), &lcdc->tcon0_timing_h);
683 bp = mode->vsync_len + mode->upper_margin;
684 total = mode->yres + mode->lower_margin + bp;
685 writel(SUNXI_LCDC_TCON0_TIMING_V_TOTAL(total) |
686 SUNXI_LCDC_TCON0_TIMING_V_BP(bp), &lcdc->tcon0_timing_v);
688 #ifdef CONFIG_VIDEO_LCD_IF_PARALLEL
689 writel(SUNXI_LCDC_X(mode->hsync_len) | SUNXI_LCDC_Y(mode->vsync_len),
690 &lcdc->tcon0_timing_sync);
692 writel(0, &lcdc->tcon0_hv_intf);
693 writel(0, &lcdc->tcon0_cpu_intf);
695 #ifdef CONFIG_VIDEO_LCD_IF_LVDS
696 val = (sunxi_display.depth == 18) ? 1 : 0;
697 writel(SUNXI_LCDC_TCON0_LVDS_INTF_BITWIDTH(val), &lcdc->tcon0_lvds_intf);
700 if (sunxi_display.depth == 18 || sunxi_display.depth == 16) {
701 writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[0]);
702 writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[1]);
703 writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[2]);
704 writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[3]);
705 writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[4]);
706 writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[5]);
707 writel(SUNXI_LCDC_TCON0_FRM_TAB0, &lcdc->tcon0_frm_table[0]);
708 writel(SUNXI_LCDC_TCON0_FRM_TAB1, &lcdc->tcon0_frm_table[1]);
709 writel(SUNXI_LCDC_TCON0_FRM_TAB2, &lcdc->tcon0_frm_table[2]);
710 writel(SUNXI_LCDC_TCON0_FRM_TAB3, &lcdc->tcon0_frm_table[3]);
711 writel(((sunxi_display.depth == 18) ?
712 SUNXI_LCDC_TCON0_FRM_CTRL_RGB666 :
713 SUNXI_LCDC_TCON0_FRM_CTRL_RGB565),
714 &lcdc->tcon0_frm_ctrl);
717 val = SUNXI_LCDC_TCON0_IO_POL_DCLK_PHASE(CONFIG_VIDEO_LCD_DCLK_PHASE);
718 if (!(mode->sync & FB_SYNC_HOR_HIGH_ACT))
719 val |= SUNXI_LCDC_TCON_HSYNC_MASK;
720 if (!(mode->sync & FB_SYNC_VERT_HIGH_ACT))
721 val |= SUNXI_LCDC_TCON_VSYNC_MASK;
722 writel(val, &lcdc->tcon0_io_polarity);
724 writel(0, &lcdc->tcon0_io_tristate);
727 #if defined CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA
728 static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode,
729 int *clk_div, int *clk_double,
730 bool use_portd_hvsync)
732 struct sunxi_lcdc_reg * const lcdc =
733 (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
734 int bp, clk_delay, total, val;
737 clrsetbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_IO_MAP_MASK,
738 SUNXI_LCDC_CTRL_IO_MAP_TCON1);
740 clk_delay = sunxi_lcdc_get_clk_delay(mode);
741 writel(SUNXI_LCDC_TCON1_CTRL_ENABLE |
742 SUNXI_LCDC_TCON1_CTRL_CLK_DELAY(clk_delay), &lcdc->tcon1_ctrl);
744 writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(mode->yres),
745 &lcdc->tcon1_timing_source);
746 writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(mode->yres),
747 &lcdc->tcon1_timing_scale);
748 writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(mode->yres),
749 &lcdc->tcon1_timing_out);
751 bp = mode->hsync_len + mode->left_margin;
752 total = mode->xres + mode->right_margin + bp;
753 writel(SUNXI_LCDC_TCON1_TIMING_H_TOTAL(total) |
754 SUNXI_LCDC_TCON1_TIMING_H_BP(bp), &lcdc->tcon1_timing_h);
756 bp = mode->vsync_len + mode->upper_margin;
757 total = mode->yres + mode->lower_margin + bp;
758 writel(SUNXI_LCDC_TCON1_TIMING_V_TOTAL(total) |
759 SUNXI_LCDC_TCON1_TIMING_V_BP(bp), &lcdc->tcon1_timing_v);
761 writel(SUNXI_LCDC_X(mode->hsync_len) | SUNXI_LCDC_Y(mode->vsync_len),
762 &lcdc->tcon1_timing_sync);
764 if (use_portd_hvsync) {
765 sunxi_gpio_set_cfgpin(SUNXI_GPD(26), SUNXI_GPD0_LCD0);
766 sunxi_gpio_set_cfgpin(SUNXI_GPD(27), SUNXI_GPD0_LCD0);
769 if (mode->sync & FB_SYNC_HOR_HIGH_ACT)
770 val |= SUNXI_LCDC_TCON_HSYNC_MASK;
771 if (mode->sync & FB_SYNC_VERT_HIGH_ACT)
772 val |= SUNXI_LCDC_TCON_VSYNC_MASK;
773 writel(val, &lcdc->tcon1_io_polarity);
775 clrbits_le32(&lcdc->tcon1_io_tristate,
776 SUNXI_LCDC_TCON_VSYNC_MASK |
777 SUNXI_LCDC_TCON_HSYNC_MASK);
779 sunxi_lcdc_pll_set(1, mode->pixclock_khz, clk_div, clk_double);
781 #endif /* CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA */
783 #ifdef CONFIG_VIDEO_HDMI
785 static void sunxi_hdmi_setup_info_frames(const struct ctfb_res_modes *mode)
787 struct sunxi_hdmi_reg * const hdmi =
788 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
790 u8 avi_info_frame[17] = {
791 0x82, 0x02, 0x0d, 0x00, 0x12, 0x00, 0x88, 0x00,
792 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
795 u8 vendor_info_frame[19] = {
796 0x81, 0x01, 0x06, 0x29, 0x03, 0x0c, 0x00, 0x40,
797 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
802 if (mode->pixclock_khz <= 27000)
803 avi_info_frame[5] = 0x40; /* SD-modes, ITU601 colorspace */
805 avi_info_frame[5] = 0x80; /* HD-modes, ITU709 colorspace */
807 if (mode->xres * 100 / mode->yres < 156)
808 avi_info_frame[5] |= 0x18; /* 4 : 3 */
810 avi_info_frame[5] |= 0x28; /* 16 : 9 */
812 for (i = 0; i < ARRAY_SIZE(avi_info_frame); i++)
813 checksum += avi_info_frame[i];
815 avi_info_frame[3] = 0x100 - checksum;
817 for (i = 0; i < ARRAY_SIZE(avi_info_frame); i++)
818 writeb(avi_info_frame[i], &hdmi->avi_info_frame[i]);
820 writel(SUNXI_HDMI_QCP_PACKET0, &hdmi->qcp_packet0);
821 writel(SUNXI_HDMI_QCP_PACKET1, &hdmi->qcp_packet1);
823 for (i = 0; i < ARRAY_SIZE(vendor_info_frame); i++)
824 writeb(vendor_info_frame[i], &hdmi->vendor_info_frame[i]);
826 writel(SUNXI_HDMI_PKT_CTRL0, &hdmi->pkt_ctrl0);
827 writel(SUNXI_HDMI_PKT_CTRL1, &hdmi->pkt_ctrl1);
829 setbits_le32(&hdmi->video_ctrl, SUNXI_HDMI_VIDEO_CTRL_HDMI);
832 static void sunxi_hdmi_mode_set(const struct ctfb_res_modes *mode,
833 int clk_div, int clk_double)
835 struct sunxi_hdmi_reg * const hdmi =
836 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
839 /* Write clear interrupt status bits */
840 writel(SUNXI_HDMI_IRQ_STATUS_BITS, &hdmi->irq);
842 if (sunxi_display.monitor == sunxi_monitor_hdmi)
843 sunxi_hdmi_setup_info_frames(mode);
845 /* Set input sync enable */
846 writel(SUNXI_HDMI_UNKNOWN_INPUT_SYNC, &hdmi->unknown);
848 /* Init various registers, select pll3 as clock source */
849 writel(SUNXI_HDMI_VIDEO_POL_TX_CLK, &hdmi->video_polarity);
850 writel(SUNXI_HDMI_PAD_CTRL0_RUN, &hdmi->pad_ctrl0);
851 writel(SUNXI_HDMI_PAD_CTRL1, &hdmi->pad_ctrl1);
852 writel(SUNXI_HDMI_PLL_CTRL, &hdmi->pll_ctrl);
853 writel(SUNXI_HDMI_PLL_DBG0_PLL3, &hdmi->pll_dbg0);
855 /* Setup clk div and doubler */
856 clrsetbits_le32(&hdmi->pll_ctrl, SUNXI_HDMI_PLL_CTRL_DIV_MASK,
857 SUNXI_HDMI_PLL_CTRL_DIV(clk_div));
859 setbits_le32(&hdmi->pad_ctrl1, SUNXI_HDMI_PAD_CTRL1_HALVE);
861 /* Setup timing registers */
862 writel(SUNXI_HDMI_Y(mode->yres) | SUNXI_HDMI_X(mode->xres),
865 x = mode->hsync_len + mode->left_margin;
866 y = mode->vsync_len + mode->upper_margin;
867 writel(SUNXI_HDMI_Y(y) | SUNXI_HDMI_X(x), &hdmi->video_bp);
869 x = mode->right_margin;
870 y = mode->lower_margin;
871 writel(SUNXI_HDMI_Y(y) | SUNXI_HDMI_X(x), &hdmi->video_fp);
875 writel(SUNXI_HDMI_Y(y) | SUNXI_HDMI_X(x), &hdmi->video_spw);
877 if (mode->sync & FB_SYNC_HOR_HIGH_ACT)
878 setbits_le32(&hdmi->video_polarity, SUNXI_HDMI_VIDEO_POL_HOR);
880 if (mode->sync & FB_SYNC_VERT_HIGH_ACT)
881 setbits_le32(&hdmi->video_polarity, SUNXI_HDMI_VIDEO_POL_VER);
884 static void sunxi_hdmi_enable(void)
886 struct sunxi_hdmi_reg * const hdmi =
887 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
890 setbits_le32(&hdmi->video_ctrl, SUNXI_HDMI_VIDEO_CTRL_ENABLE);
893 #endif /* CONFIG_VIDEO_HDMI */
895 #ifdef CONFIG_VIDEO_VGA
897 static void sunxi_vga_mode_set(void)
899 struct sunxi_ccm_reg * const ccm =
900 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
901 struct sunxi_tve_reg * const tve =
902 (struct sunxi_tve_reg *)SUNXI_TVE0_BASE;
905 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_TVE0);
907 /* Set TVE in VGA mode */
908 writel(SUNXI_TVE_GCTRL_DAC_INPUT(0, 1) |
909 SUNXI_TVE_GCTRL_DAC_INPUT(1, 2) |
910 SUNXI_TVE_GCTRL_DAC_INPUT(2, 3), &tve->gctrl);
911 writel(SUNXI_TVE_GCTRL_CFG0_VGA, &tve->cfg0);
912 writel(SUNXI_TVE_GCTRL_DAC_CFG0_VGA, &tve->dac_cfg0);
913 writel(SUNXI_TVE_GCTRL_UNKNOWN1_VGA, &tve->unknown1);
916 static void sunxi_vga_enable(void)
918 struct sunxi_tve_reg * const tve =
919 (struct sunxi_tve_reg *)SUNXI_TVE0_BASE;
921 setbits_le32(&tve->gctrl, SUNXI_TVE_GCTRL_ENABLE);
924 #endif /* CONFIG_VIDEO_VGA */
926 static void sunxi_drc_init(void)
928 #if defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I
929 struct sunxi_ccm_reg * const ccm =
930 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
932 /* On sun6i the drc must be clocked even when in pass-through mode */
933 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_DRC0);
934 clock_set_de_mod_clock(&ccm->iep_drc0_clk_cfg, 300000000);
938 #ifdef CONFIG_VIDEO_VGA_VIA_LCD
939 static void sunxi_vga_external_dac_enable(void)
943 pin = sunxi_name_to_gpio(CONFIG_VIDEO_VGA_EXTERNAL_DAC_EN);
945 gpio_request(pin, "vga_enable");
946 gpio_direction_output(pin, 1);
949 #endif /* CONFIG_VIDEO_VGA_VIA_LCD */
951 #ifdef CONFIG_VIDEO_LCD_SSD2828
952 static int sunxi_ssd2828_init(const struct ctfb_res_modes *mode)
954 struct ssd2828_config cfg = {
955 .csx_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_CS),
956 .sck_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_SCLK),
957 .sdi_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_MOSI),
958 .sdo_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_MISO),
959 .reset_pin = name_to_gpio(CONFIG_VIDEO_LCD_SSD2828_RESET),
960 .ssd2828_tx_clk_khz = CONFIG_VIDEO_LCD_SSD2828_TX_CLK * 1000,
961 .ssd2828_color_depth = 24,
962 #ifdef CONFIG_VIDEO_LCD_PANEL_MIPI_4_LANE_513_MBPS_VIA_SSD2828
963 .mipi_dsi_number_of_data_lanes = 4,
964 .mipi_dsi_bitrate_per_data_lane_mbps = 513,
965 .mipi_dsi_delay_after_exit_sleep_mode_ms = 100,
966 .mipi_dsi_delay_after_set_display_on_ms = 200
968 #error MIPI LCD panel needs configuration parameters
972 if (cfg.csx_pin == -1 || cfg.sck_pin == -1 || cfg.sdi_pin == -1) {
973 printf("SSD2828: SPI pins are not properly configured\n");
976 if (cfg.reset_pin == -1) {
977 printf("SSD2828: Reset pin is not properly configured\n");
981 return ssd2828_init(&cfg, mode);
983 #endif /* CONFIG_VIDEO_LCD_SSD2828 */
985 static void sunxi_engines_init(void)
987 sunxi_composer_init();
992 static void sunxi_mode_set(const struct ctfb_res_modes *mode,
993 unsigned int address)
995 int __maybe_unused clk_div, clk_double;
997 switch (sunxi_display.monitor) {
998 case sunxi_monitor_none:
1000 case sunxi_monitor_dvi:
1001 case sunxi_monitor_hdmi:
1002 #ifdef CONFIG_VIDEO_HDMI
1003 sunxi_composer_mode_set(mode, address);
1004 sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0);
1005 sunxi_hdmi_mode_set(mode, clk_div, clk_double);
1006 sunxi_composer_enable();
1007 sunxi_lcdc_enable();
1008 sunxi_hdmi_enable();
1011 case sunxi_monitor_lcd:
1012 sunxi_lcdc_panel_enable();
1013 if (IS_ENABLED(CONFIG_VIDEO_LCD_HITACHI_TX18D42VM)) {
1014 mdelay(50); /* Wait for lcd controller power on */
1015 hitachi_tx18d42vm_init();
1017 sunxi_composer_mode_set(mode, address);
1018 sunxi_lcdc_tcon0_mode_set(mode);
1019 sunxi_composer_enable();
1020 sunxi_lcdc_enable();
1021 #ifdef CONFIG_VIDEO_LCD_SSD2828
1022 sunxi_ssd2828_init(mode);
1024 sunxi_lcdc_backlight_enable();
1026 case sunxi_monitor_vga:
1027 #ifdef CONFIG_VIDEO_VGA
1028 sunxi_composer_mode_set(mode, address);
1029 sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 1);
1030 sunxi_vga_mode_set();
1031 sunxi_composer_enable();
1032 sunxi_lcdc_enable();
1034 #elif defined CONFIG_VIDEO_VGA_VIA_LCD
1035 sunxi_composer_mode_set(mode, address);
1036 sunxi_lcdc_tcon0_mode_set(mode);
1037 sunxi_composer_enable();
1038 sunxi_lcdc_enable();
1039 sunxi_vga_external_dac_enable();
1045 static const char *sunxi_get_mon_desc(enum sunxi_monitor monitor)
1048 case sunxi_monitor_none: return "none";
1049 case sunxi_monitor_dvi: return "dvi";
1050 case sunxi_monitor_hdmi: return "hdmi";
1051 case sunxi_monitor_lcd: return "lcd";
1052 case sunxi_monitor_vga: return "vga";
1054 return NULL; /* never reached */
1057 void *video_hw_init(void)
1059 static GraphicDevice *graphic_device = &sunxi_display.graphic_device;
1060 const struct ctfb_res_modes *mode;
1061 struct ctfb_res_modes custom;
1062 const char *options;
1063 #ifdef CONFIG_VIDEO_HDMI
1064 int ret, hpd, hpd_delay, edid;
1067 char *lcd_mode = CONFIG_VIDEO_LCD_MODE;
1070 memset(&sunxi_display, 0, sizeof(struct sunxi_display));
1072 printf("Reserved %dkB of RAM for Framebuffer.\n",
1073 CONFIG_SUNXI_FB_SIZE >> 10);
1074 gd->fb_base = gd->ram_top;
1076 video_get_ctfb_res_modes(RES_MODE_1024x768, 24, &mode,
1077 &sunxi_display.depth, &options);
1078 #ifdef CONFIG_VIDEO_HDMI
1079 hpd = video_get_option_int(options, "hpd", 1);
1080 hpd_delay = video_get_option_int(options, "hpd_delay", 500);
1081 edid = video_get_option_int(options, "edid", 1);
1082 sunxi_display.monitor = sunxi_monitor_dvi;
1083 #elif defined CONFIG_VIDEO_VGA_VIA_LCD
1084 sunxi_display.monitor = sunxi_monitor_vga;
1086 sunxi_display.monitor = sunxi_monitor_lcd;
1088 video_get_option_string(options, "monitor", mon, sizeof(mon),
1089 sunxi_get_mon_desc(sunxi_display.monitor));
1090 for (i = 0; i <= SUNXI_MONITOR_LAST; i++) {
1091 if (strcmp(mon, sunxi_get_mon_desc(i)) == 0) {
1092 sunxi_display.monitor = i;
1096 if (i > SUNXI_MONITOR_LAST)
1097 printf("Unknown monitor: '%s', falling back to '%s'\n",
1098 mon, sunxi_get_mon_desc(sunxi_display.monitor));
1100 #ifdef CONFIG_VIDEO_HDMI
1101 /* If HDMI/DVI is selected do HPD & EDID, and handle fallback */
1102 if (sunxi_display.monitor == sunxi_monitor_dvi ||
1103 sunxi_display.monitor == sunxi_monitor_hdmi) {
1104 /* Always call hdp_detect, as it also enables clocks, etc. */
1105 ret = sunxi_hdmi_hpd_detect(hpd_delay);
1107 printf("HDMI connected: ");
1108 if (edid && sunxi_hdmi_edid_get_mode(&custom) == 0)
1111 sunxi_hdmi_shutdown();
1112 /* Fallback to lcd / vga / none */
1114 sunxi_display.monitor = sunxi_monitor_lcd;
1116 #if defined CONFIG_VIDEO_VGA_VIA_LCD || defined CONFIG_VIDEO_VGA
1117 sunxi_display.monitor = sunxi_monitor_vga;
1119 sunxi_display.monitor = sunxi_monitor_none;
1122 } /* else continue with hdmi/dvi without a cable connected */
1126 switch (sunxi_display.monitor) {
1127 case sunxi_monitor_none:
1129 case sunxi_monitor_dvi:
1130 case sunxi_monitor_hdmi:
1131 #ifdef CONFIG_VIDEO_HDMI
1134 printf("HDMI/DVI not supported on this board\n");
1135 sunxi_display.monitor = sunxi_monitor_none;
1138 case sunxi_monitor_lcd:
1140 sunxi_display.depth = video_get_params(&custom, lcd_mode);
1144 printf("LCD not supported on this board\n");
1145 sunxi_display.monitor = sunxi_monitor_none;
1147 case sunxi_monitor_vga:
1148 #if defined CONFIG_VIDEO_VGA_VIA_LCD || defined CONFIG_VIDEO_VGA
1149 sunxi_display.depth = 18;
1152 printf("VGA not supported on this board\n");
1153 sunxi_display.monitor = sunxi_monitor_none;
1158 if (mode->vmode != FB_VMODE_NONINTERLACED) {
1159 printf("Only non-interlaced modes supported, falling back to 1024x768\n");
1160 mode = &res_mode_init[RES_MODE_1024x768];
1162 printf("Setting up a %dx%d %s console\n", mode->xres,
1163 mode->yres, sunxi_get_mon_desc(sunxi_display.monitor));
1166 sunxi_engines_init();
1167 sunxi_mode_set(mode, gd->fb_base - CONFIG_SYS_SDRAM_BASE);
1170 * These are the only members of this structure that are used. All the
1171 * others are driver specific. There is nothing to decribe pitch or
1172 * stride, but we are lucky with our hw.
1174 graphic_device->frameAdrs = gd->fb_base;
1175 graphic_device->gdfIndex = GDF_32BIT_X888RGB;
1176 graphic_device->gdfBytesPP = 4;
1177 graphic_device->winSizeX = mode->xres;
1178 graphic_device->winSizeY = mode->yres;
1180 return graphic_device;
1186 #if defined(CONFIG_OF_BOARD_SETUP) && defined(CONFIG_VIDEO_DT_SIMPLEFB)
1187 int sunxi_simplefb_setup(void *blob)
1189 static GraphicDevice *graphic_device = &sunxi_display.graphic_device;
1191 const char *pipeline = NULL;
1193 #ifdef CONFIG_MACH_SUN4I
1194 #define PIPELINE_PREFIX "de_fe0-"
1196 #define PIPELINE_PREFIX
1199 switch (sunxi_display.monitor) {
1200 case sunxi_monitor_none:
1202 case sunxi_monitor_dvi:
1203 case sunxi_monitor_hdmi:
1204 pipeline = PIPELINE_PREFIX "de_be0-lcd0-hdmi";
1206 case sunxi_monitor_lcd:
1207 pipeline = PIPELINE_PREFIX "de_be0-lcd0";
1209 case sunxi_monitor_vga:
1210 #ifdef CONFIG_VIDEO_VGA
1211 pipeline = PIPELINE_PREFIX "de_be0-lcd0-tve0";
1212 #elif defined CONFIG_VIDEO_VGA_VIA_LCD
1213 pipeline = PIPELINE_PREFIX "de_be0-lcd0";
1218 /* Find a prefilled simpefb node, matching out pipeline config */
1219 offset = fdt_node_offset_by_compatible(blob, -1,
1220 "allwinner,simple-framebuffer");
1221 while (offset >= 0) {
1222 ret = fdt_find_string(blob, offset, "allwinner,pipeline",
1226 offset = fdt_node_offset_by_compatible(blob, offset,
1227 "allwinner,simple-framebuffer");
1230 eprintf("Cannot setup simplefb: node not found\n");
1231 return 0; /* Keep older kernels working */
1234 ret = fdt_setup_simplefb_node(blob, offset, gd->fb_base,
1235 graphic_device->winSizeX, graphic_device->winSizeY,
1236 graphic_device->winSizeX * graphic_device->gdfBytesPP,
1239 eprintf("Cannot setup simplefb: Error setting properties\n");
1243 #endif /* CONFIG_OF_BOARD_SETUP && CONFIG_VIDEO_DT_SIMPLEFB */