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 DECLARE_GLOBAL_DATA_PTR;
35 #define SUNXI_MONITOR_LAST sunxi_monitor_vga
37 struct sunxi_display {
38 GraphicDevice graphic_device;
39 enum sunxi_monitor monitor;
43 #ifdef CONFIG_VIDEO_HDMI
46 * Wait up to 200ms for value to be set in given part of reg.
48 static int await_completion(u32 *reg, u32 mask, u32 val)
50 unsigned long tmo = timer_get_us() + 200000;
52 while ((readl(reg) & mask) != val) {
53 if (timer_get_us() > tmo) {
54 printf("DDC: timeout reading EDID\n");
61 static int sunxi_hdmi_hpd_detect(int hpd_delay)
63 struct sunxi_ccm_reg * const ccm =
64 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
65 struct sunxi_hdmi_reg * const hdmi =
66 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
67 unsigned long tmo = timer_get_us() + hpd_delay * 1000;
69 /* Set pll3 to 300MHz */
70 clock_set_pll3(300000000);
72 /* Set hdmi parent to pll3 */
73 clrsetbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_PLL_MASK,
76 /* Set ahb gating to pass */
77 #ifdef CONFIG_MACH_SUN6I
78 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_HDMI);
80 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_HDMI);
83 setbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_GATE);
85 writel(SUNXI_HDMI_CTRL_ENABLE, &hdmi->ctrl);
86 writel(SUNXI_HDMI_PAD_CTRL0_HDP, &hdmi->pad_ctrl0);
88 while (timer_get_us() < tmo) {
89 if (readl(&hdmi->hpd) & SUNXI_HDMI_HPD_DETECT)
96 static void sunxi_hdmi_shutdown(void)
98 struct sunxi_ccm_reg * const ccm =
99 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
100 struct sunxi_hdmi_reg * const hdmi =
101 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
103 clrbits_le32(&hdmi->ctrl, SUNXI_HDMI_CTRL_ENABLE);
104 clrbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_GATE);
105 clrbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_HDMI);
106 #ifdef CONFIG_MACH_SUN6I
107 clrbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_HDMI);
112 static int sunxi_hdmi_ddc_do_command(u32 cmnd, int offset, int n)
114 struct sunxi_hdmi_reg * const hdmi =
115 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
117 setbits_le32(&hdmi->ddc_fifo_ctrl, SUNXI_HDMI_DDC_FIFO_CTRL_CLEAR);
118 writel(SUNXI_HMDI_DDC_ADDR_EDDC_SEGMENT(offset >> 8) |
119 SUNXI_HMDI_DDC_ADDR_EDDC_ADDR |
120 SUNXI_HMDI_DDC_ADDR_OFFSET(offset) |
121 SUNXI_HMDI_DDC_ADDR_SLAVE_ADDR, &hdmi->ddc_addr);
122 #ifndef CONFIG_MACH_SUN6I
123 writel(n, &hdmi->ddc_byte_count);
124 writel(cmnd, &hdmi->ddc_cmnd);
126 writel(n << 16 | cmnd, &hdmi->ddc_cmnd);
128 setbits_le32(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_START);
130 return await_completion(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_START, 0);
133 static int sunxi_hdmi_ddc_read(int offset, u8 *buf, int count)
135 struct sunxi_hdmi_reg * const hdmi =
136 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
145 if (sunxi_hdmi_ddc_do_command(
146 SUNXI_HDMI_DDC_CMND_EXPLICIT_EDDC_READ,
150 for (i = 0; i < n; i++)
151 *buf++ = readb(&hdmi->ddc_fifo_data);
160 static int sunxi_hdmi_edid_get_block(int block, u8 *buf)
165 r = sunxi_hdmi_ddc_read(block * 128, buf, 128);
168 r = edid_check_checksum(buf);
170 printf("EDID block %d: checksum error%s\n",
171 block, retries ? ", retrying" : "");
173 } while (r && retries--);
178 static int sunxi_hdmi_edid_get_mode(struct ctfb_res_modes *mode)
180 struct edid1_info edid1;
181 struct edid_cea861_info cea681[4];
182 struct edid_detailed_timing *t =
183 (struct edid_detailed_timing *)edid1.monitor_details.timing;
184 struct sunxi_hdmi_reg * const hdmi =
185 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
186 struct sunxi_ccm_reg * const ccm =
187 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
188 int i, r, ext_blocks = 0;
190 /* SUNXI_HDMI_CTRL_ENABLE & PAD_CTRL0 are already set by hpd_detect */
191 writel(SUNXI_HDMI_PAD_CTRL1 | SUNXI_HDMI_PAD_CTRL1_HALVE,
193 writel(SUNXI_HDMI_PLL_CTRL | SUNXI_HDMI_PLL_CTRL_DIV(15),
195 writel(SUNXI_HDMI_PLL_DBG0_PLL3, &hdmi->pll_dbg0);
197 /* Reset i2c controller */
198 setbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_DDC_GATE);
199 writel(SUNXI_HMDI_DDC_CTRL_ENABLE |
200 SUNXI_HMDI_DDC_CTRL_SDA_ENABLE |
201 SUNXI_HMDI_DDC_CTRL_SCL_ENABLE |
202 SUNXI_HMDI_DDC_CTRL_RESET, &hdmi->ddc_ctrl);
203 if (await_completion(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_RESET, 0))
206 writel(SUNXI_HDMI_DDC_CLOCK, &hdmi->ddc_clock);
207 #ifndef CONFIG_MACH_SUN6I
208 writel(SUNXI_HMDI_DDC_LINE_CTRL_SDA_ENABLE |
209 SUNXI_HMDI_DDC_LINE_CTRL_SCL_ENABLE, &hdmi->ddc_line_ctrl);
212 r = sunxi_hdmi_edid_get_block(0, (u8 *)&edid1);
214 r = edid_check_info(&edid1);
216 printf("EDID: invalid EDID data\n");
221 ext_blocks = edid1.extension_flag;
224 for (i = 0; i < ext_blocks; i++) {
225 if (sunxi_hdmi_edid_get_block(1 + i,
226 (u8 *)&cea681[i]) != 0) {
233 /* Disable DDC engine, no longer needed */
234 clrbits_le32(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_ENABLE);
235 clrbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_DDC_GATE);
240 /* We want version 1.3 or 1.2 with detailed timing info */
241 if (edid1.version != 1 || (edid1.revision < 3 &&
242 !EDID1_INFO_FEATURE_PREFERRED_TIMING_MODE(edid1))) {
243 printf("EDID: unsupported version %d.%d\n",
244 edid1.version, edid1.revision);
248 /* Take the first usable detailed timing */
249 for (i = 0; i < 4; i++, t++) {
250 r = video_edid_dtd_to_ctfb_res_modes(t, mode);
255 printf("EDID: no usable detailed timing found\n");
259 /* Check for basic audio support, if found enable hdmi output */
260 sunxi_display.monitor = sunxi_monitor_dvi;
261 for (i = 0; i < ext_blocks; i++) {
262 if (cea681[i].extension_tag != EDID_CEA861_EXTENSION_TAG ||
263 cea681[i].revision < 2)
266 if (EDID_CEA861_SUPPORTS_BASIC_AUDIO(cea681[i]))
267 sunxi_display.monitor = sunxi_monitor_hdmi;
273 #endif /* CONFIG_VIDEO_HDMI */
275 #ifdef CONFIG_MACH_SUN4I
277 * Testing has shown that on sun4i the display backend engine does not have
278 * deep enough fifo-s causing flickering / tearing in full-hd mode due to
279 * fifo underruns. So on sun4i we use the display frontend engine to do the
280 * dma from memory, as the frontend does have deep enough fifo-s.
283 static const u32 sun4i_vert_coef[32] = {
284 0x00004000, 0x000140ff, 0x00033ffe, 0x00043ffd,
285 0x00063efc, 0xff083dfc, 0x000a3bfb, 0xff0d39fb,
286 0xff0f37fb, 0xff1136fa, 0xfe1433fb, 0xfe1631fb,
287 0xfd192ffb, 0xfd1c2cfb, 0xfd1f29fb, 0xfc2127fc,
288 0xfc2424fc, 0xfc2721fc, 0xfb291ffd, 0xfb2c1cfd,
289 0xfb2f19fd, 0xfb3116fe, 0xfb3314fe, 0xfa3611ff,
290 0xfb370fff, 0xfb390dff, 0xfb3b0a00, 0xfc3d08ff,
291 0xfc3e0600, 0xfd3f0400, 0xfe3f0300, 0xff400100,
294 static const u32 sun4i_horz_coef[64] = {
295 0x40000000, 0x00000000, 0x40fe0000, 0x0000ff03,
296 0x3ffd0000, 0x0000ff05, 0x3ffc0000, 0x0000ff06,
297 0x3efb0000, 0x0000ff08, 0x3dfb0000, 0x0000ff09,
298 0x3bfa0000, 0x0000fe0d, 0x39fa0000, 0x0000fe0f,
299 0x38fa0000, 0x0000fe10, 0x36fa0000, 0x0000fe12,
300 0x33fa0000, 0x0000fd16, 0x31fa0000, 0x0000fd18,
301 0x2ffa0000, 0x0000fd1a, 0x2cfa0000, 0x0000fc1e,
302 0x29fa0000, 0x0000fc21, 0x27fb0000, 0x0000fb23,
303 0x24fb0000, 0x0000fb26, 0x21fb0000, 0x0000fb29,
304 0x1ffc0000, 0x0000fa2b, 0x1cfc0000, 0x0000fa2e,
305 0x19fd0000, 0x0000fa30, 0x16fd0000, 0x0000fa33,
306 0x14fd0000, 0x0000fa35, 0x11fe0000, 0x0000fa37,
307 0x0ffe0000, 0x0000fa39, 0x0dfe0000, 0x0000fa3b,
308 0x0afe0000, 0x0000fa3e, 0x08ff0000, 0x0000fb3e,
309 0x06ff0000, 0x0000fb40, 0x05ff0000, 0x0000fc40,
310 0x03ff0000, 0x0000fd41, 0x01ff0000, 0x0000fe42,
313 static void sunxi_frontend_init(void)
315 struct sunxi_ccm_reg * const ccm =
316 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
317 struct sunxi_de_fe_reg * const de_fe =
318 (struct sunxi_de_fe_reg *)SUNXI_DE_FE0_BASE;
322 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_DE_FE0);
323 setbits_le32(&ccm->dram_clk_gate, 1 << CCM_DRAM_GATE_OFFSET_DE_FE0);
324 clock_set_de_mod_clock(&ccm->fe0_clk_cfg, 300000000);
326 setbits_le32(&de_fe->enable, SUNXI_DE_FE_ENABLE_EN);
328 for (i = 0; i < 32; i++) {
329 writel(sun4i_horz_coef[2 * i], &de_fe->ch0_horzcoef0[i]);
330 writel(sun4i_horz_coef[2 * i + 1], &de_fe->ch0_horzcoef1[i]);
331 writel(sun4i_vert_coef[i], &de_fe->ch0_vertcoef[i]);
332 writel(sun4i_horz_coef[2 * i], &de_fe->ch1_horzcoef0[i]);
333 writel(sun4i_horz_coef[2 * i + 1], &de_fe->ch1_horzcoef1[i]);
334 writel(sun4i_vert_coef[i], &de_fe->ch1_vertcoef[i]);
337 setbits_le32(&de_fe->frame_ctrl, SUNXI_DE_FE_FRAME_CTRL_COEF_RDY);
340 static void sunxi_frontend_mode_set(const struct ctfb_res_modes *mode,
341 unsigned int address)
343 struct sunxi_de_fe_reg * const de_fe =
344 (struct sunxi_de_fe_reg *)SUNXI_DE_FE0_BASE;
346 setbits_le32(&de_fe->bypass, SUNXI_DE_FE_BYPASS_CSC_BYPASS);
347 writel(CONFIG_SYS_SDRAM_BASE + address, &de_fe->ch0_addr);
348 writel(mode->xres * 4, &de_fe->ch0_stride);
349 writel(SUNXI_DE_FE_INPUT_FMT_ARGB8888, &de_fe->input_fmt);
350 writel(SUNXI_DE_FE_OUTPUT_FMT_ARGB8888, &de_fe->output_fmt);
352 writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
354 writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
355 &de_fe->ch0_outsize);
356 writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch0_horzfact);
357 writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch0_vertfact);
359 writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
361 writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
362 &de_fe->ch1_outsize);
363 writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch1_horzfact);
364 writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch1_vertfact);
366 setbits_le32(&de_fe->frame_ctrl, SUNXI_DE_FE_FRAME_CTRL_REG_RDY);
369 static void sunxi_frontend_enable(void)
371 struct sunxi_de_fe_reg * const de_fe =
372 (struct sunxi_de_fe_reg *)SUNXI_DE_FE0_BASE;
374 setbits_le32(&de_fe->frame_ctrl, SUNXI_DE_FE_FRAME_CTRL_FRM_START);
377 static void sunxi_frontend_init(void) {}
378 static void sunxi_frontend_mode_set(const struct ctfb_res_modes *mode,
379 unsigned int address) {}
380 static void sunxi_frontend_enable(void) {}
384 * This is the entity that mixes and matches the different layers and inputs.
385 * Allwinner calls it the back-end, but i like composer better.
387 static void sunxi_composer_init(void)
389 struct sunxi_ccm_reg * const ccm =
390 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
391 struct sunxi_de_be_reg * const de_be =
392 (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE;
395 sunxi_frontend_init();
397 #if defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I
399 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_DE_BE0);
403 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_DE_BE0);
404 #ifndef CONFIG_MACH_SUN4I /* On sun4i the frontend does the dma */
405 setbits_le32(&ccm->dram_clk_gate, 1 << CCM_DRAM_GATE_OFFSET_DE_BE0);
407 clock_set_de_mod_clock(&ccm->be0_clk_cfg, 300000000);
409 /* Engine bug, clear registers after reset */
410 for (i = 0x0800; i < 0x1000; i += 4)
411 writel(0, SUNXI_DE_BE0_BASE + i);
413 setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_ENABLE);
416 static void sunxi_composer_mode_set(const struct ctfb_res_modes *mode,
417 unsigned int address)
419 struct sunxi_de_be_reg * const de_be =
420 (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE;
422 sunxi_frontend_mode_set(mode, address);
424 writel(SUNXI_DE_BE_HEIGHT(mode->yres) | SUNXI_DE_BE_WIDTH(mode->xres),
426 writel(SUNXI_DE_BE_HEIGHT(mode->yres) | SUNXI_DE_BE_WIDTH(mode->xres),
427 &de_be->layer0_size);
428 #ifndef CONFIG_MACH_SUN4I /* On sun4i the frontend does the dma */
429 writel(SUNXI_DE_BE_LAYER_STRIDE(mode->xres), &de_be->layer0_stride);
430 writel(address << 3, &de_be->layer0_addr_low32b);
431 writel(address >> 29, &de_be->layer0_addr_high4b);
433 writel(SUNXI_DE_BE_LAYER_ATTR0_SRC_FE0, &de_be->layer0_attr0_ctrl);
435 writel(SUNXI_DE_BE_LAYER_ATTR1_FMT_XRGB8888, &de_be->layer0_attr1_ctrl);
437 setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_LAYER0_ENABLE);
440 static void sunxi_composer_enable(void)
442 struct sunxi_de_be_reg * const de_be =
443 (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE;
445 sunxi_frontend_enable();
447 setbits_le32(&de_be->reg_ctrl, SUNXI_DE_BE_REG_CTRL_LOAD_REGS);
448 setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_START);
452 * LCDC, what allwinner calls a CRTC, so timing controller and serializer.
454 static void sunxi_lcdc_pll_set(int tcon, int dotclock,
455 int *clk_div, int *clk_double)
457 struct sunxi_ccm_reg * const ccm =
458 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
459 int value, n, m, min_m, max_m, diff;
460 int best_n = 0, best_m = 0, best_diff = 0x0FFFFFFF;
464 #ifdef CONFIG_VIDEO_LCD_IF_PARALLEL
468 #ifdef CONFIG_VIDEO_LCD_IF_LVDS
477 * Find the lowest divider resulting in a matching clock, if there
478 * is no match, pick the closest lower clock, as monitors tend to
479 * not sync to higher frequencies.
481 for (m = min_m; m <= max_m; m++) {
482 n = (m * dotclock) / 3000;
484 if ((n >= 9) && (n <= 127)) {
485 value = (3000 * n) / m;
486 diff = dotclock - value;
487 if (diff < best_diff) {
495 /* These are just duplicates */
499 n = (m * dotclock) / 6000;
500 if ((n >= 9) && (n <= 127)) {
501 value = (6000 * n) / m;
502 diff = dotclock - value;
503 if (diff < best_diff) {
512 debug("dotclock: %dkHz = %dkHz: (%d * 3MHz * %d) / %d\n",
513 dotclock, (best_double + 1) * 3000 * best_n / best_m,
514 best_double + 1, best_n, best_m);
516 clock_set_pll3(best_n * 3000000);
519 writel(CCM_LCD_CH0_CTRL_GATE | CCM_LCD_CH0_CTRL_RST |
520 (best_double ? CCM_LCD_CH0_CTRL_PLL3_2X :
521 CCM_LCD_CH0_CTRL_PLL3),
522 &ccm->lcd0_ch0_clk_cfg);
524 writel(CCM_LCD_CH1_CTRL_GATE |
525 (best_double ? CCM_LCD_CH1_CTRL_PLL3_2X :
526 CCM_LCD_CH1_CTRL_PLL3) |
527 CCM_LCD_CH1_CTRL_M(best_m), &ccm->lcd0_ch1_clk_cfg);
531 *clk_double = best_double;
534 static void sunxi_lcdc_init(void)
536 struct sunxi_ccm_reg * const ccm =
537 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
538 struct sunxi_lcdc_reg * const lcdc =
539 (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
542 #if defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I
543 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_LCD0);
545 setbits_le32(&ccm->lcd0_ch0_clk_cfg, CCM_LCD_CH0_CTRL_RST);
549 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_LCD0);
550 #ifdef CONFIG_VIDEO_LCD_IF_LVDS
551 setbits_le32(&ccm->lvds_clk_cfg, CCM_LVDS_CTRL_RST);
555 writel(0, &lcdc->ctrl); /* Disable tcon */
556 writel(0, &lcdc->int0); /* Disable all interrupts */
558 /* Disable tcon0 dot clock */
559 clrbits_le32(&lcdc->tcon0_dclk, SUNXI_LCDC_TCON0_DCLK_ENABLE);
561 /* Set all io lines to tristate */
562 writel(0xffffffff, &lcdc->tcon0_io_tristate);
563 writel(0xffffffff, &lcdc->tcon1_io_tristate);
566 static void sunxi_lcdc_enable(void)
568 struct sunxi_lcdc_reg * const lcdc =
569 (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
571 setbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_TCON_ENABLE);
572 #ifdef CONFIG_VIDEO_LCD_IF_LVDS
573 setbits_le32(&lcdc->tcon0_lvds_intf, SUNXI_LCDC_TCON0_LVDS_INTF_ENABLE);
574 setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0);
575 setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_UPDATE);
576 udelay(2); /* delay at least 1200 ns */
577 setbits_le32(&lcdc->lvds_ana1, SUNXI_LCDC_LVDS_ANA1_INIT1);
578 udelay(1); /* delay at least 120 ns */
579 setbits_le32(&lcdc->lvds_ana1, SUNXI_LCDC_LVDS_ANA1_INIT2);
580 setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_UPDATE);
584 static void sunxi_lcdc_panel_enable(void)
589 * Start with backlight disabled to avoid the screen flashing to
590 * white while the lcd inits.
592 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_EN);
594 gpio_request(pin, "lcd_backlight_enable");
595 gpio_direction_output(pin, 0);
598 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_PWM);
600 gpio_request(pin, "lcd_backlight_pwm");
601 /* backlight pwm is inverted, set to 1 to disable backlight */
602 gpio_direction_output(pin, 1);
605 /* Give the backlight some time to turn off and power up the panel. */
607 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_POWER);
609 gpio_request(pin, "lcd_power");
610 gpio_direction_output(pin, 1);
614 static void sunxi_lcdc_backlight_enable(void)
619 * We want to have scanned out at least one frame before enabling the
620 * backlight to avoid the screen flashing to white when we enable it.
624 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_EN);
626 gpio_direction_output(pin, 1);
628 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_PWM);
630 /* backlight pwm is inverted, set to 0 to enable backlight */
631 gpio_direction_output(pin, 0);
635 static int sunxi_lcdc_get_clk_delay(const struct ctfb_res_modes *mode)
639 delay = mode->lower_margin + mode->vsync_len + mode->upper_margin - 2;
640 return (delay > 30) ? 30 : delay;
643 static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode)
645 struct sunxi_lcdc_reg * const lcdc =
646 (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
647 int bp, clk_delay, clk_div, clk_double, pin, total, val;
649 for (pin = SUNXI_GPD(0); pin <= SUNXI_GPD(27); pin++)
650 #ifdef CONFIG_VIDEO_LCD_IF_PARALLEL
651 sunxi_gpio_set_cfgpin(pin, SUNXI_GPD0_LCD0);
653 #ifdef CONFIG_VIDEO_LCD_IF_LVDS
654 sunxi_gpio_set_cfgpin(pin, SUNXI_GPD0_LVDS0);
657 sunxi_lcdc_pll_set(0, mode->pixclock_khz, &clk_div, &clk_double);
660 clrsetbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_IO_MAP_MASK,
661 SUNXI_LCDC_CTRL_IO_MAP_TCON0);
663 clk_delay = sunxi_lcdc_get_clk_delay(mode);
664 writel(SUNXI_LCDC_TCON0_CTRL_ENABLE |
665 SUNXI_LCDC_TCON0_CTRL_CLK_DELAY(clk_delay), &lcdc->tcon0_ctrl);
667 writel(SUNXI_LCDC_TCON0_DCLK_ENABLE |
668 SUNXI_LCDC_TCON0_DCLK_DIV(clk_div), &lcdc->tcon0_dclk);
670 writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(mode->yres),
671 &lcdc->tcon0_timing_active);
673 bp = mode->hsync_len + mode->left_margin;
674 total = mode->xres + mode->right_margin + bp;
675 writel(SUNXI_LCDC_TCON0_TIMING_H_TOTAL(total) |
676 SUNXI_LCDC_TCON0_TIMING_H_BP(bp), &lcdc->tcon0_timing_h);
678 bp = mode->vsync_len + mode->upper_margin;
679 total = mode->yres + mode->lower_margin + bp;
680 writel(SUNXI_LCDC_TCON0_TIMING_V_TOTAL(total) |
681 SUNXI_LCDC_TCON0_TIMING_V_BP(bp), &lcdc->tcon0_timing_v);
683 #ifdef CONFIG_VIDEO_LCD_IF_PARALLEL
684 writel(SUNXI_LCDC_X(mode->hsync_len) | SUNXI_LCDC_Y(mode->vsync_len),
685 &lcdc->tcon0_timing_sync);
687 writel(0, &lcdc->tcon0_hv_intf);
688 writel(0, &lcdc->tcon0_cpu_intf);
690 #ifdef CONFIG_VIDEO_LCD_IF_LVDS
691 val = (sunxi_display.depth == 18) ? 1 : 0;
692 writel(SUNXI_LCDC_TCON0_LVDS_INTF_BITWIDTH(val), &lcdc->tcon0_lvds_intf);
695 if (sunxi_display.depth == 18 || sunxi_display.depth == 16) {
696 writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[0]);
697 writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[1]);
698 writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[2]);
699 writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[3]);
700 writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[4]);
701 writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[5]);
702 writel(SUNXI_LCDC_TCON0_FRM_TAB0, &lcdc->tcon0_frm_table[0]);
703 writel(SUNXI_LCDC_TCON0_FRM_TAB1, &lcdc->tcon0_frm_table[1]);
704 writel(SUNXI_LCDC_TCON0_FRM_TAB2, &lcdc->tcon0_frm_table[2]);
705 writel(SUNXI_LCDC_TCON0_FRM_TAB3, &lcdc->tcon0_frm_table[3]);
706 writel(((sunxi_display.depth == 18) ?
707 SUNXI_LCDC_TCON0_FRM_CTRL_RGB666 :
708 SUNXI_LCDC_TCON0_FRM_CTRL_RGB565),
709 &lcdc->tcon0_frm_ctrl);
712 val = SUNXI_LCDC_TCON0_IO_POL_DCLK_PHASE(CONFIG_VIDEO_LCD_DCLK_PHASE);
713 if (!(mode->sync & FB_SYNC_HOR_HIGH_ACT))
714 val |= SUNXI_LCDC_TCON_HSYNC_MASK;
715 if (!(mode->sync & FB_SYNC_VERT_HIGH_ACT))
716 val |= SUNXI_LCDC_TCON_VSYNC_MASK;
717 writel(val, &lcdc->tcon0_io_polarity);
719 writel(0, &lcdc->tcon0_io_tristate);
722 #if defined CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA
723 static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode,
724 int *clk_div, int *clk_double,
725 bool use_portd_hvsync)
727 struct sunxi_lcdc_reg * const lcdc =
728 (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
729 int bp, clk_delay, total, val;
732 clrsetbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_IO_MAP_MASK,
733 SUNXI_LCDC_CTRL_IO_MAP_TCON1);
735 clk_delay = sunxi_lcdc_get_clk_delay(mode);
736 writel(SUNXI_LCDC_TCON1_CTRL_ENABLE |
737 SUNXI_LCDC_TCON1_CTRL_CLK_DELAY(clk_delay), &lcdc->tcon1_ctrl);
739 writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(mode->yres),
740 &lcdc->tcon1_timing_source);
741 writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(mode->yres),
742 &lcdc->tcon1_timing_scale);
743 writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(mode->yres),
744 &lcdc->tcon1_timing_out);
746 bp = mode->hsync_len + mode->left_margin;
747 total = mode->xres + mode->right_margin + bp;
748 writel(SUNXI_LCDC_TCON1_TIMING_H_TOTAL(total) |
749 SUNXI_LCDC_TCON1_TIMING_H_BP(bp), &lcdc->tcon1_timing_h);
751 bp = mode->vsync_len + mode->upper_margin;
752 total = mode->yres + mode->lower_margin + bp;
753 writel(SUNXI_LCDC_TCON1_TIMING_V_TOTAL(total) |
754 SUNXI_LCDC_TCON1_TIMING_V_BP(bp), &lcdc->tcon1_timing_v);
756 writel(SUNXI_LCDC_X(mode->hsync_len) | SUNXI_LCDC_Y(mode->vsync_len),
757 &lcdc->tcon1_timing_sync);
759 if (use_portd_hvsync) {
760 sunxi_gpio_set_cfgpin(SUNXI_GPD(26), SUNXI_GPD0_LCD0);
761 sunxi_gpio_set_cfgpin(SUNXI_GPD(27), SUNXI_GPD0_LCD0);
764 if (mode->sync & FB_SYNC_HOR_HIGH_ACT)
765 val |= SUNXI_LCDC_TCON_HSYNC_MASK;
766 if (mode->sync & FB_SYNC_VERT_HIGH_ACT)
767 val |= SUNXI_LCDC_TCON_VSYNC_MASK;
768 writel(val, &lcdc->tcon1_io_polarity);
770 clrbits_le32(&lcdc->tcon1_io_tristate,
771 SUNXI_LCDC_TCON_VSYNC_MASK |
772 SUNXI_LCDC_TCON_HSYNC_MASK);
774 sunxi_lcdc_pll_set(1, mode->pixclock_khz, clk_div, clk_double);
776 #endif /* CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA */
778 #ifdef CONFIG_VIDEO_HDMI
780 static void sunxi_hdmi_setup_info_frames(const struct ctfb_res_modes *mode)
782 struct sunxi_hdmi_reg * const hdmi =
783 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
785 u8 avi_info_frame[17] = {
786 0x82, 0x02, 0x0d, 0x00, 0x12, 0x00, 0x88, 0x00,
787 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
790 u8 vendor_info_frame[19] = {
791 0x81, 0x01, 0x06, 0x29, 0x03, 0x0c, 0x00, 0x40,
792 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
797 if (mode->pixclock_khz <= 27000)
798 avi_info_frame[5] = 0x40; /* SD-modes, ITU601 colorspace */
800 avi_info_frame[5] = 0x80; /* HD-modes, ITU709 colorspace */
802 if (mode->xres * 100 / mode->yres < 156)
803 avi_info_frame[5] |= 0x18; /* 4 : 3 */
805 avi_info_frame[5] |= 0x28; /* 16 : 9 */
807 for (i = 0; i < ARRAY_SIZE(avi_info_frame); i++)
808 checksum += avi_info_frame[i];
810 avi_info_frame[3] = 0x100 - checksum;
812 for (i = 0; i < ARRAY_SIZE(avi_info_frame); i++)
813 writeb(avi_info_frame[i], &hdmi->avi_info_frame[i]);
815 writel(SUNXI_HDMI_QCP_PACKET0, &hdmi->qcp_packet0);
816 writel(SUNXI_HDMI_QCP_PACKET1, &hdmi->qcp_packet1);
818 for (i = 0; i < ARRAY_SIZE(vendor_info_frame); i++)
819 writeb(vendor_info_frame[i], &hdmi->vendor_info_frame[i]);
821 writel(SUNXI_HDMI_PKT_CTRL0, &hdmi->pkt_ctrl0);
822 writel(SUNXI_HDMI_PKT_CTRL1, &hdmi->pkt_ctrl1);
824 setbits_le32(&hdmi->video_ctrl, SUNXI_HDMI_VIDEO_CTRL_HDMI);
827 static void sunxi_hdmi_mode_set(const struct ctfb_res_modes *mode,
828 int clk_div, int clk_double)
830 struct sunxi_hdmi_reg * const hdmi =
831 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
834 /* Write clear interrupt status bits */
835 writel(SUNXI_HDMI_IRQ_STATUS_BITS, &hdmi->irq);
837 if (sunxi_display.monitor == sunxi_monitor_hdmi)
838 sunxi_hdmi_setup_info_frames(mode);
840 /* Set input sync enable */
841 writel(SUNXI_HDMI_UNKNOWN_INPUT_SYNC, &hdmi->unknown);
843 /* Init various registers, select pll3 as clock source */
844 writel(SUNXI_HDMI_VIDEO_POL_TX_CLK, &hdmi->video_polarity);
845 writel(SUNXI_HDMI_PAD_CTRL0_RUN, &hdmi->pad_ctrl0);
846 writel(SUNXI_HDMI_PAD_CTRL1, &hdmi->pad_ctrl1);
847 writel(SUNXI_HDMI_PLL_CTRL, &hdmi->pll_ctrl);
848 writel(SUNXI_HDMI_PLL_DBG0_PLL3, &hdmi->pll_dbg0);
850 /* Setup clk div and doubler */
851 clrsetbits_le32(&hdmi->pll_ctrl, SUNXI_HDMI_PLL_CTRL_DIV_MASK,
852 SUNXI_HDMI_PLL_CTRL_DIV(clk_div));
854 setbits_le32(&hdmi->pad_ctrl1, SUNXI_HDMI_PAD_CTRL1_HALVE);
856 /* Setup timing registers */
857 writel(SUNXI_HDMI_Y(mode->yres) | SUNXI_HDMI_X(mode->xres),
860 x = mode->hsync_len + mode->left_margin;
861 y = mode->vsync_len + mode->upper_margin;
862 writel(SUNXI_HDMI_Y(y) | SUNXI_HDMI_X(x), &hdmi->video_bp);
864 x = mode->right_margin;
865 y = mode->lower_margin;
866 writel(SUNXI_HDMI_Y(y) | SUNXI_HDMI_X(x), &hdmi->video_fp);
870 writel(SUNXI_HDMI_Y(y) | SUNXI_HDMI_X(x), &hdmi->video_spw);
872 if (mode->sync & FB_SYNC_HOR_HIGH_ACT)
873 setbits_le32(&hdmi->video_polarity, SUNXI_HDMI_VIDEO_POL_HOR);
875 if (mode->sync & FB_SYNC_VERT_HIGH_ACT)
876 setbits_le32(&hdmi->video_polarity, SUNXI_HDMI_VIDEO_POL_VER);
879 static void sunxi_hdmi_enable(void)
881 struct sunxi_hdmi_reg * const hdmi =
882 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
885 setbits_le32(&hdmi->video_ctrl, SUNXI_HDMI_VIDEO_CTRL_ENABLE);
888 #endif /* CONFIG_VIDEO_HDMI */
890 #ifdef CONFIG_VIDEO_VGA
892 static void sunxi_vga_mode_set(void)
894 struct sunxi_ccm_reg * const ccm =
895 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
896 struct sunxi_tve_reg * const tve =
897 (struct sunxi_tve_reg *)SUNXI_TVE0_BASE;
900 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_TVE0);
902 /* Set TVE in VGA mode */
903 writel(SUNXI_TVE_GCTRL_DAC_INPUT(0, 1) |
904 SUNXI_TVE_GCTRL_DAC_INPUT(1, 2) |
905 SUNXI_TVE_GCTRL_DAC_INPUT(2, 3), &tve->gctrl);
906 writel(SUNXI_TVE_GCTRL_CFG0_VGA, &tve->cfg0);
907 writel(SUNXI_TVE_GCTRL_DAC_CFG0_VGA, &tve->dac_cfg0);
908 writel(SUNXI_TVE_GCTRL_UNKNOWN1_VGA, &tve->unknown1);
911 static void sunxi_vga_enable(void)
913 struct sunxi_tve_reg * const tve =
914 (struct sunxi_tve_reg *)SUNXI_TVE0_BASE;
916 setbits_le32(&tve->gctrl, SUNXI_TVE_GCTRL_ENABLE);
919 #endif /* CONFIG_VIDEO_VGA */
921 static void sunxi_drc_init(void)
923 #if defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I
924 struct sunxi_ccm_reg * const ccm =
925 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
927 /* On sun6i the drc must be clocked even when in pass-through mode */
928 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_DRC0);
929 clock_set_de_mod_clock(&ccm->iep_drc0_clk_cfg, 300000000);
933 #ifdef CONFIG_VIDEO_VGA_VIA_LCD
934 static void sunxi_vga_external_dac_enable(void)
938 pin = sunxi_name_to_gpio(CONFIG_VIDEO_VGA_EXTERNAL_DAC_EN);
940 gpio_request(pin, "vga_enable");
941 gpio_direction_output(pin, 1);
944 #endif /* CONFIG_VIDEO_VGA_VIA_LCD */
946 #ifdef CONFIG_VIDEO_LCD_SSD2828
947 static int sunxi_ssd2828_init(const struct ctfb_res_modes *mode)
949 struct ssd2828_config cfg = {
950 .csx_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_CS),
951 .sck_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_SCLK),
952 .sdi_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_MOSI),
953 .sdo_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_MISO),
954 .reset_pin = name_to_gpio(CONFIG_VIDEO_LCD_SSD2828_RESET),
955 .ssd2828_tx_clk_khz = CONFIG_VIDEO_LCD_SSD2828_TX_CLK * 1000,
956 .ssd2828_color_depth = 24,
957 #ifdef CONFIG_VIDEO_LCD_PANEL_MIPI_4_LANE_513_MBPS_VIA_SSD2828
958 .mipi_dsi_number_of_data_lanes = 4,
959 .mipi_dsi_bitrate_per_data_lane_mbps = 513,
960 .mipi_dsi_delay_after_exit_sleep_mode_ms = 100,
961 .mipi_dsi_delay_after_set_display_on_ms = 200
963 #error MIPI LCD panel needs configuration parameters
967 if (cfg.csx_pin == -1 || cfg.sck_pin == -1 || cfg.sdi_pin == -1) {
968 printf("SSD2828: SPI pins are not properly configured\n");
971 if (cfg.reset_pin == -1) {
972 printf("SSD2828: Reset pin is not properly configured\n");
976 return ssd2828_init(&cfg, mode);
978 #endif /* CONFIG_VIDEO_LCD_SSD2828 */
980 static void sunxi_engines_init(void)
982 sunxi_composer_init();
987 static void sunxi_mode_set(const struct ctfb_res_modes *mode,
988 unsigned int address)
990 int __maybe_unused clk_div, clk_double;
992 switch (sunxi_display.monitor) {
993 case sunxi_monitor_none:
995 case sunxi_monitor_dvi:
996 case sunxi_monitor_hdmi:
997 #ifdef CONFIG_VIDEO_HDMI
998 sunxi_composer_mode_set(mode, address);
999 sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0);
1000 sunxi_hdmi_mode_set(mode, clk_div, clk_double);
1001 sunxi_composer_enable();
1002 sunxi_lcdc_enable();
1003 sunxi_hdmi_enable();
1006 case sunxi_monitor_lcd:
1007 sunxi_lcdc_panel_enable();
1008 if (IS_ENABLED(CONFIG_VIDEO_LCD_HITACHI_TX18D42VM)) {
1009 mdelay(50); /* Wait for lcd controller power on */
1010 hitachi_tx18d42vm_init();
1012 sunxi_composer_mode_set(mode, address);
1013 sunxi_lcdc_tcon0_mode_set(mode);
1014 sunxi_composer_enable();
1015 sunxi_lcdc_enable();
1016 #ifdef CONFIG_VIDEO_LCD_SSD2828
1017 sunxi_ssd2828_init(mode);
1019 sunxi_lcdc_backlight_enable();
1021 case sunxi_monitor_vga:
1022 #ifdef CONFIG_VIDEO_VGA
1023 sunxi_composer_mode_set(mode, address);
1024 sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 1);
1025 sunxi_vga_mode_set();
1026 sunxi_composer_enable();
1027 sunxi_lcdc_enable();
1029 #elif defined CONFIG_VIDEO_VGA_VIA_LCD
1030 sunxi_composer_mode_set(mode, address);
1031 sunxi_lcdc_tcon0_mode_set(mode);
1032 sunxi_composer_enable();
1033 sunxi_lcdc_enable();
1034 sunxi_vga_external_dac_enable();
1040 static const char *sunxi_get_mon_desc(enum sunxi_monitor monitor)
1043 case sunxi_monitor_none: return "none";
1044 case sunxi_monitor_dvi: return "dvi";
1045 case sunxi_monitor_hdmi: return "hdmi";
1046 case sunxi_monitor_lcd: return "lcd";
1047 case sunxi_monitor_vga: return "vga";
1049 return NULL; /* never reached */
1052 void *video_hw_init(void)
1054 static GraphicDevice *graphic_device = &sunxi_display.graphic_device;
1055 const struct ctfb_res_modes *mode;
1056 struct ctfb_res_modes custom;
1057 const char *options;
1058 #ifdef CONFIG_VIDEO_HDMI
1059 int ret, hpd, hpd_delay, edid;
1062 char *lcd_mode = CONFIG_VIDEO_LCD_MODE;
1065 memset(&sunxi_display, 0, sizeof(struct sunxi_display));
1067 printf("Reserved %dkB of RAM for Framebuffer.\n",
1068 CONFIG_SUNXI_FB_SIZE >> 10);
1069 gd->fb_base = gd->ram_top;
1071 video_get_ctfb_res_modes(RES_MODE_1024x768, 24, &mode,
1072 &sunxi_display.depth, &options);
1073 #ifdef CONFIG_VIDEO_HDMI
1074 hpd = video_get_option_int(options, "hpd", 1);
1075 hpd_delay = video_get_option_int(options, "hpd_delay", 500);
1076 edid = video_get_option_int(options, "edid", 1);
1077 sunxi_display.monitor = sunxi_monitor_dvi;
1078 #elif defined CONFIG_VIDEO_VGA_VIA_LCD
1079 sunxi_display.monitor = sunxi_monitor_vga;
1081 sunxi_display.monitor = sunxi_monitor_lcd;
1083 video_get_option_string(options, "monitor", mon, sizeof(mon),
1084 sunxi_get_mon_desc(sunxi_display.monitor));
1085 for (i = 0; i <= SUNXI_MONITOR_LAST; i++) {
1086 if (strcmp(mon, sunxi_get_mon_desc(i)) == 0) {
1087 sunxi_display.monitor = i;
1091 if (i > SUNXI_MONITOR_LAST)
1092 printf("Unknown monitor: '%s', falling back to '%s'\n",
1093 mon, sunxi_get_mon_desc(sunxi_display.monitor));
1095 #ifdef CONFIG_VIDEO_HDMI
1096 /* If HDMI/DVI is selected do HPD & EDID, and handle fallback */
1097 if (sunxi_display.monitor == sunxi_monitor_dvi ||
1098 sunxi_display.monitor == sunxi_monitor_hdmi) {
1099 /* Always call hdp_detect, as it also enables clocks, etc. */
1100 ret = sunxi_hdmi_hpd_detect(hpd_delay);
1102 printf("HDMI connected: ");
1103 if (edid && sunxi_hdmi_edid_get_mode(&custom) == 0)
1106 sunxi_hdmi_shutdown();
1107 /* Fallback to lcd / vga / none */
1109 sunxi_display.monitor = sunxi_monitor_lcd;
1111 #if defined CONFIG_VIDEO_VGA_VIA_LCD || defined CONFIG_VIDEO_VGA
1112 sunxi_display.monitor = sunxi_monitor_vga;
1114 sunxi_display.monitor = sunxi_monitor_none;
1117 } /* else continue with hdmi/dvi without a cable connected */
1121 switch (sunxi_display.monitor) {
1122 case sunxi_monitor_none:
1124 case sunxi_monitor_dvi:
1125 case sunxi_monitor_hdmi:
1126 #ifdef CONFIG_VIDEO_HDMI
1129 printf("HDMI/DVI not supported on this board\n");
1130 sunxi_display.monitor = sunxi_monitor_none;
1133 case sunxi_monitor_lcd:
1135 sunxi_display.depth = video_get_params(&custom, lcd_mode);
1139 printf("LCD not supported on this board\n");
1140 sunxi_display.monitor = sunxi_monitor_none;
1142 case sunxi_monitor_vga:
1143 #if defined CONFIG_VIDEO_VGA_VIA_LCD || defined CONFIG_VIDEO_VGA
1144 sunxi_display.depth = 18;
1147 printf("VGA not supported on this board\n");
1148 sunxi_display.monitor = sunxi_monitor_none;
1153 if (mode->vmode != FB_VMODE_NONINTERLACED) {
1154 printf("Only non-interlaced modes supported, falling back to 1024x768\n");
1155 mode = &res_mode_init[RES_MODE_1024x768];
1157 printf("Setting up a %dx%d %s console\n", mode->xres,
1158 mode->yres, sunxi_get_mon_desc(sunxi_display.monitor));
1161 sunxi_engines_init();
1162 sunxi_mode_set(mode, gd->fb_base - CONFIG_SYS_SDRAM_BASE);
1165 * These are the only members of this structure that are used. All the
1166 * others are driver specific. There is nothing to decribe pitch or
1167 * stride, but we are lucky with our hw.
1169 graphic_device->frameAdrs = gd->fb_base;
1170 graphic_device->gdfIndex = GDF_32BIT_X888RGB;
1171 graphic_device->gdfBytesPP = 4;
1172 graphic_device->winSizeX = mode->xres;
1173 graphic_device->winSizeY = mode->yres;
1175 return graphic_device;
1181 #if defined(CONFIG_OF_BOARD_SETUP) && defined(CONFIG_VIDEO_DT_SIMPLEFB)
1182 int sunxi_simplefb_setup(void *blob)
1184 static GraphicDevice *graphic_device = &sunxi_display.graphic_device;
1186 const char *pipeline = NULL;
1188 #ifdef CONFIG_MACH_SUN4I
1189 #define PIPELINE_PREFIX "de_fe0-"
1191 #define PIPELINE_PREFIX
1194 switch (sunxi_display.monitor) {
1195 case sunxi_monitor_none:
1197 case sunxi_monitor_dvi:
1198 case sunxi_monitor_hdmi:
1199 pipeline = PIPELINE_PREFIX "de_be0-lcd0-hdmi";
1201 case sunxi_monitor_lcd:
1202 pipeline = PIPELINE_PREFIX "de_be0-lcd0";
1204 case sunxi_monitor_vga:
1205 #ifdef CONFIG_VIDEO_VGA
1206 pipeline = PIPELINE_PREFIX "de_be0-lcd0-tve0";
1207 #elif defined CONFIG_VIDEO_VGA_VIA_LCD
1208 pipeline = PIPELINE_PREFIX "de_be0-lcd0";
1213 /* Find a prefilled simpefb node, matching out pipeline config */
1214 offset = fdt_node_offset_by_compatible(blob, -1,
1215 "allwinner,simple-framebuffer");
1216 while (offset >= 0) {
1217 ret = fdt_find_string(blob, offset, "allwinner,pipeline",
1221 offset = fdt_node_offset_by_compatible(blob, offset,
1222 "allwinner,simple-framebuffer");
1225 eprintf("Cannot setup simplefb: node not found\n");
1226 return 0; /* Keep older kernels working */
1229 ret = fdt_setup_simplefb_node(blob, offset, gd->fb_base,
1230 graphic_device->winSizeX, graphic_device->winSizeY,
1231 graphic_device->winSizeX * graphic_device->gdfBytesPP,
1234 eprintf("Cannot setup simplefb: Error setting properties\n");
1238 #endif /* CONFIG_OF_BOARD_SETUP && CONFIG_VIDEO_DT_SIMPLEFB */