Commit 2dae800f1e6193b64ba587f511c4878c89409cbe

Authored by Hans de Goede
1 parent e84007933a

sunxi: video: Add lcd output support

Add lcd output support, see the new Kconfig entries and doc/README.video for
how to enable / configure this.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Acked-by: Ian Campbell <ijc@hellion.org.uk>

Showing 5 changed files with 278 additions and 27 deletions Side-by-side Diff

arch/arm/include/asm/arch-sunxi/display.h
... ... @@ -57,14 +57,16 @@
57 57 u32 int0; /* 0x04 */
58 58 u32 int1; /* 0x08 */
59 59 u8 res0[0x04]; /* 0x0c */
60   - u32 frame_ctrl; /* 0x10 */
61   - u8 res1[0x2c]; /* 0x14 */
  60 + u32 tcon0_frm_ctrl; /* 0x10 */
  61 + u32 tcon0_frm_seed[6]; /* 0x14 */
  62 + u32 tcon0_frm_table[4]; /* 0x2c */
  63 + u8 res1[4]; /* 0x3c */
62 64 u32 tcon0_ctrl; /* 0x40 */
63 65 u32 tcon0_dclk; /* 0x44 */
64   - u32 tcon0_basic_timing0; /* 0x48 */
65   - u32 tcon0_basic_timing1; /* 0x4c */
66   - u32 tcon0_basic_timing2; /* 0x50 */
67   - u32 tcon0_basic_timing3; /* 0x54 */
  66 + u32 tcon0_timing_active; /* 0x48 */
  67 + u32 tcon0_timing_h; /* 0x4c */
  68 + u32 tcon0_timing_v; /* 0x50 */
  69 + u32 tcon0_timing_sync; /* 0x54 */
68 70 u32 tcon0_hv_intf; /* 0x58 */
69 71 u8 res2[0x04]; /* 0x5c */
70 72 u32 tcon0_cpu_intf; /* 0x60 */
71 73  
72 74  
... ... @@ -175,11 +177,27 @@
175 177 */
176 178 #define SUNXI_LCDC_X(x) (((x) - 1) << 16)
177 179 #define SUNXI_LCDC_Y(y) (((y) - 1) << 0)
  180 +#define SUNXI_LCDC_TCON_VSYNC_MASK (1 << 24)
  181 +#define SUNXI_LCDC_TCON_HSYNC_MASK (1 << 25)
178 182 #define SUNXI_LCDC_CTRL_IO_MAP_MASK (1 << 0)
179 183 #define SUNXI_LCDC_CTRL_IO_MAP_TCON0 (0 << 0)
180 184 #define SUNXI_LCDC_CTRL_IO_MAP_TCON1 (1 << 0)
181 185 #define SUNXI_LCDC_CTRL_TCON_ENABLE (1 << 31)
  186 +#define SUNXI_LCDC_TCON0_FRM_CTRL_RGB666 ((1 << 31) | (0 << 4))
  187 +#define SUNXI_LCDC_TCON0_FRM_CTRL_RGB565 ((1 << 31) | (5 << 4))
  188 +#define SUNXI_LCDC_TCON0_FRM_SEED 0x11111111
  189 +#define SUNXI_LCDC_TCON0_FRM_TAB0 0x01010000
  190 +#define SUNXI_LCDC_TCON0_FRM_TAB1 0x15151111
  191 +#define SUNXI_LCDC_TCON0_FRM_TAB2 0x57575555
  192 +#define SUNXI_LCDC_TCON0_FRM_TAB3 0x7f7f7777
  193 +#define SUNXI_LCDC_TCON0_CTRL_CLK_DELAY(n) (((n) & 0x1f) << 4)
  194 +#define SUNXI_LCDC_TCON0_CTRL_ENABLE (1 << 31)
  195 +#define SUNXI_LCDC_TCON0_DCLK_DIV(n) ((n) << 0)
182 196 #define SUNXI_LCDC_TCON0_DCLK_ENABLE (0xf << 28)
  197 +#define SUNXI_LCDC_TCON0_TIMING_H_BP(n) (((n) - 1) << 0)
  198 +#define SUNXI_LCDC_TCON0_TIMING_H_TOTAL(n) (((n) - 1) << 16)
  199 +#define SUNXI_LCDC_TCON0_TIMING_V_BP(n) (((n) - 1) << 0)
  200 +#define SUNXI_LCDC_TCON0_TIMING_V_TOTAL(n) (((n) * 2) << 16)
183 201 #define SUNXI_LCDC_TCON1_CTRL_CLK_DELAY(n) (((n) & 0x1f) << 4)
184 202 #define SUNXI_LCDC_TCON1_CTRL_ENABLE (1 << 31)
185 203 #define SUNXI_LCDC_TCON1_TIMING_H_BP(n) (((n) - 1) << 0)
arch/arm/include/asm/arch-sunxi/gpio.h
... ... @@ -150,6 +150,8 @@
150 150  
151 151 #define SUNXI_GPC6_SDC2 3
152 152  
  153 +#define SUNXI_GPD0_LCD0 2
  154 +
153 155 #define SUNXI_GPF0_SDC0 2
154 156  
155 157 #define SUNXI_GPF2_SDC0 2
... ... @@ -280,18 +280,53 @@
280 280 See USB1_VBUS_PIN help text.
281 281  
282 282 config VIDEO
283   - boolean "Enable graphical uboot console on HDMI"
  283 + boolean "Enable graphical uboot console on HDMI, LCD or VGA"
284 284 default y
285 285 ---help---
286   - Say Y here to add support for using a cfb console on the HDMI output
287   - found on most sunxi devices.
  286 + Say Y here to add support for using a cfb console on the HDMI, LCD
  287 + or VGA output found on most sunxi devices. See doc/README.video for
  288 + info on how to select the video output and mode.
288 289  
  290 +config VIDEO_LCD_MODE
  291 + string "LCD panel timing details"
  292 + depends on VIDEO
  293 + default ""
  294 + ---help---
  295 + LCD panel timing details string, leave empty if there is no LCD panel.
  296 + This is in drivers/video/videomodes.c: video_get_params() format, e.g.
  297 + x:800,y:480,depth:18,pclk_khz:33000,le:16,ri:209,up:22,lo:22,hs:30,vs:1,sync:0,vmode:0
  298 +
  299 +config VIDEO_LCD_POWER
  300 + string "LCD panel power enable pin"
  301 + depends on VIDEO
  302 + default ""
  303 + ---help---
  304 + Set the power enable pin for the LCD panel. This takes a string in the
  305 + format understood by sunxi_name_to_gpio, e.g. PH1 for pin 1 of port H.
  306 +
  307 +config VIDEO_LCD_BL_EN
  308 + string "LCD panel backlight enable pin"
  309 + depends on VIDEO
  310 + default ""
  311 + ---help---
  312 + Set the backlight enable pin for the LCD panel. This takes a string in the
  313 + the format understood by sunxi_name_to_gpio, e.g. PH1 for pin 1 of
  314 + port H.
  315 +
  316 +config VIDEO_LCD_BL_PWM
  317 + string "LCD panel backlight pwm pin"
  318 + depends on VIDEO
  319 + default ""
  320 + ---help---
  321 + Set the backlight pwm pin for the LCD panel. This takes a string in the
  322 + format understood by sunxi_name_to_gpio, e.g. PH1 for pin 1 of port H.
  323 +
289 324 config USB_KEYBOARD
290 325 boolean "Enable USB keyboard support"
291 326 default y
292 327 ---help---
293 328 Say Y here to add support for using a USB keyboard (typically used
294   - in combination with a graphical console on HDMI).
  329 + in combination with a graphical console).
295 330  
296 331 endif
... ... @@ -5,15 +5,8 @@
5 5 * SPDX-License-Identifier: GPL-2.0+
6 6 */
7 7  
8   -U-Boot MPC8xx video controller driver
9   -======================================
10   -
11   -The driver has been tested with the following configurations:
12   -
13   -- MPC823FADS with AD7176 on a PAL TV (YCbYCr) - arsenio@tin.it
14   -
15 8 "video-mode" environment variable
16   -===============================
  9 +=================================
17 10  
18 11 The 'video-mode' environment variable can be used to enable and configure
19 12 some video drivers. The format matches the video= command-line option used
20 13  
... ... @@ -28,5 +21,46 @@
28 21 <freq> The frequency (in Hz) to use.
29 22 <options> A comma-separated list of device-specific options
30 23  
  24 +
  25 +U-Boot MPC8xx video controller driver
  26 +=====================================
  27 +
  28 +The driver has been tested with the following configurations:
  29 +
  30 +- MPC823FADS with AD7176 on a PAL TV (YCbYCr) - arsenio@tin.it
  31 +
31 32 Example: video-mode=fslfb:1280x1024-32@60,monitor=dvi
  33 +
  34 +
  35 +U-boot sunxi video controller driver
  36 +====================================
  37 +
  38 +U-boot supports hdmi and lcd output on Allwinner sunxi SoCs, lcd output
  39 +requires the CONFIG_VIDEO_LCD_MODE Kconfig value to be set.
  40 +
  41 +The sunxi u-boot driver supports the following video-mode options:
  42 +
  43 +- monitor=[none|dvi|hdmi|lcd] - Select the video output to use
  44 + none: Disable video output.
  45 + dvi/hdmi: Selects output over the hdmi connector with dvi resp. hdmi output
  46 + format, if edid is used the format is automatically selected.
  47 + lcd: Selects video output to a LCD screen.
  48 + vga: Selects bideo output over the VGA connector.
  49 + Defaults to monitor=dvi.
  50 +
  51 +- hpd=[0|1] - Enable use of the hdmi HotPlug Detect feature
  52 + 0: Disabled. Configure dvi/hdmi output even if no cable is detected
  53 + 1: Enabled. If a LCD has been configured fallback to the LCD when no cable is
  54 + detected, if no LCD is configured, disable video ouput.
  55 + Defaults to hpd=1.
  56 +
  57 +- edid=[0|1] - Enable use of DDC + EDID to get monitor info
  58 + 0: Disabled.
  59 + 1: Enabled. If valid EDID info was read from the monitor the EDID info will
  60 + overrides the xres, yres and refresh from the video-mode env. variable.
  61 + Defaults to edid=1.
  62 +
  63 +For example to always use the hdmi connector, even if no cable is inserted,
  64 +using edid info when available and otherwise initalizing it at 1024x768@60Hz,
  65 +use: video-mode=sunxi:1024x768-24@60,monitor=dvi,hpd=0,edid=1 .
drivers/video/sunxi_display.c
... ... @@ -11,7 +11,9 @@
11 11  
12 12 #include <asm/arch/clock.h>
13 13 #include <asm/arch/display.h>
  14 +#include <asm/arch/gpio.h>
14 15 #include <asm/global_data.h>
  16 +#include <asm/gpio.h>
15 17 #include <asm/io.h>
16 18 #include <errno.h>
17 19 #include <fdtdec.h>
... ... @@ -34,6 +36,7 @@
34 36 GraphicDevice graphic_device;
35 37 bool enabled;
36 38 enum sunxi_monitor monitor;
  39 + unsigned int depth;
37 40 } sunxi_display;
38 41  
39 42 /*
... ... @@ -435,6 +438,134 @@
435 438 setbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_TCON_ENABLE);
436 439 }
437 440  
  441 +static void sunxi_lcdc_panel_enable(void)
  442 +{
  443 + int pin;
  444 +
  445 + /*
  446 + * Start with backlight disabled to avoid the screen flashing to
  447 + * white while the lcd inits.
  448 + */
  449 + pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_EN);
  450 + if (pin != -1) {
  451 + gpio_request(pin, "lcd_backlight_enable");
  452 + gpio_direction_output(pin, 0);
  453 + }
  454 +
  455 + pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_PWM);
  456 + if (pin != -1) {
  457 + gpio_request(pin, "lcd_backlight_pwm");
  458 + /* backlight pwm is inverted, set to 1 to disable backlight */
  459 + gpio_direction_output(pin, 1);
  460 + }
  461 +
  462 + /* Give the backlight some time to turn off and power up the panel. */
  463 + mdelay(40);
  464 + pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_POWER);
  465 + if (pin != -1) {
  466 + gpio_request(pin, "lcd_power");
  467 + gpio_direction_output(pin, 1);
  468 + }
  469 +}
  470 +
  471 +static void sunxi_lcdc_backlight_enable(void)
  472 +{
  473 + int pin;
  474 +
  475 + /*
  476 + * We want to have scanned out at least one frame before enabling the
  477 + * backlight to avoid the screen flashing to white when we enable it.
  478 + */
  479 + mdelay(40);
  480 +
  481 + pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_EN);
  482 + if (pin != -1)
  483 + gpio_direction_output(pin, 1);
  484 +
  485 + pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_PWM);
  486 + if (pin != -1) {
  487 + /* backlight pwm is inverted, set to 0 to enable backlight */
  488 + gpio_direction_output(pin, 0);
  489 + }
  490 +}
  491 +
  492 +static int sunxi_lcdc_get_clk_delay(const struct ctfb_res_modes *mode)
  493 +{
  494 + int delay;
  495 +
  496 + delay = mode->lower_margin + mode->vsync_len + mode->upper_margin - 2;
  497 + return (delay > 30) ? 30 : delay;
  498 +}
  499 +
  500 +static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode)
  501 +{
  502 + struct sunxi_lcdc_reg * const lcdc =
  503 + (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
  504 + int bp, clk_delay, clk_div, clk_double, pin, total, val;
  505 +
  506 + for (pin = SUNXI_GPD(0); pin <= SUNXI_GPD(27); pin++)
  507 + sunxi_gpio_set_cfgpin(pin, SUNXI_GPD0_LCD0);
  508 +
  509 + sunxi_lcdc_pll_set(0, mode->pixclock_khz, &clk_div, &clk_double);
  510 +
  511 + /* Use tcon0 */
  512 + clrsetbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_IO_MAP_MASK,
  513 + SUNXI_LCDC_CTRL_IO_MAP_TCON0);
  514 +
  515 + clk_delay = sunxi_lcdc_get_clk_delay(mode);
  516 + writel(SUNXI_LCDC_TCON0_CTRL_ENABLE |
  517 + SUNXI_LCDC_TCON0_CTRL_CLK_DELAY(clk_delay), &lcdc->tcon0_ctrl);
  518 +
  519 + writel(SUNXI_LCDC_TCON0_DCLK_ENABLE |
  520 + SUNXI_LCDC_TCON0_DCLK_DIV(clk_div), &lcdc->tcon0_dclk);
  521 +
  522 + writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(mode->yres),
  523 + &lcdc->tcon0_timing_active);
  524 +
  525 + bp = mode->hsync_len + mode->left_margin;
  526 + total = mode->xres + mode->right_margin + bp;
  527 + writel(SUNXI_LCDC_TCON0_TIMING_H_TOTAL(total) |
  528 + SUNXI_LCDC_TCON0_TIMING_H_BP(bp), &lcdc->tcon0_timing_h);
  529 +
  530 + bp = mode->vsync_len + mode->upper_margin;
  531 + total = mode->yres + mode->lower_margin + bp;
  532 + writel(SUNXI_LCDC_TCON0_TIMING_V_TOTAL(total) |
  533 + SUNXI_LCDC_TCON0_TIMING_V_BP(bp), &lcdc->tcon0_timing_v);
  534 +
  535 + writel(SUNXI_LCDC_X(mode->hsync_len) | SUNXI_LCDC_Y(mode->vsync_len),
  536 + &lcdc->tcon0_timing_sync);
  537 +
  538 + /* We only support hv-sync parallel lcd-s for now */
  539 + writel(0, &lcdc->tcon0_hv_intf);
  540 + writel(0, &lcdc->tcon0_cpu_intf);
  541 +
  542 + if (sunxi_display.depth == 18 || sunxi_display.depth == 16) {
  543 + writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[0]);
  544 + writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[1]);
  545 + writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[2]);
  546 + writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[3]);
  547 + writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[4]);
  548 + writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[5]);
  549 + writel(SUNXI_LCDC_TCON0_FRM_TAB0, &lcdc->tcon0_frm_table[0]);
  550 + writel(SUNXI_LCDC_TCON0_FRM_TAB1, &lcdc->tcon0_frm_table[1]);
  551 + writel(SUNXI_LCDC_TCON0_FRM_TAB2, &lcdc->tcon0_frm_table[2]);
  552 + writel(SUNXI_LCDC_TCON0_FRM_TAB3, &lcdc->tcon0_frm_table[3]);
  553 + writel(((sunxi_display.depth == 18) ?
  554 + SUNXI_LCDC_TCON0_FRM_CTRL_RGB666 :
  555 + SUNXI_LCDC_TCON0_FRM_CTRL_RGB565),
  556 + &lcdc->tcon0_frm_ctrl);
  557 + }
  558 +
  559 + val = 0;
  560 + if (!(mode->sync & FB_SYNC_HOR_HIGH_ACT))
  561 + val |= SUNXI_LCDC_TCON_HSYNC_MASK;
  562 + if (!(mode->sync & FB_SYNC_VERT_HIGH_ACT))
  563 + val |= SUNXI_LCDC_TCON_VSYNC_MASK;
  564 + writel(val, &lcdc->tcon0_io_polarity);
  565 +
  566 + writel(0, &lcdc->tcon0_io_tristate);
  567 +}
  568 +
438 569 static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode,
439 570 int *clk_div, int *clk_double)
440 571 {
... ... @@ -618,7 +749,12 @@
618 749 }
619 750 break;
620 751 case sunxi_monitor_lcd:
621   - /* TODO */
  752 + sunxi_lcdc_panel_enable();
  753 + sunxi_composer_mode_set(mode, address);
  754 + sunxi_lcdc_tcon0_mode_set(mode);
  755 + sunxi_composer_enable();
  756 + sunxi_lcdc_enable();
  757 + sunxi_lcdc_backlight_enable();
622 758 break;
623 759 case sunxi_monitor_vga:
624 760 break;
625 761  
626 762  
... ... @@ -641,11 +777,11 @@
641 777 {
642 778 static GraphicDevice *graphic_device = &sunxi_display.graphic_device;
643 779 const struct ctfb_res_modes *mode;
644   - struct ctfb_res_modes edid_mode;
  780 + struct ctfb_res_modes custom;
645 781 const char *options;
646   - unsigned int depth;
647 782 int i, ret, hpd, edid;
648 783 char mon[16];
  784 + char *lcd_mode = CONFIG_VIDEO_LCD_MODE;
649 785  
650 786 memset(&sunxi_display, 0, sizeof(struct sunxi_display));
651 787  
... ... @@ -653,7 +789,8 @@
653 789 CONFIG_SUNXI_FB_SIZE >> 10);
654 790 gd->fb_base = gd->ram_top;
655 791  
656   - video_get_ctfb_res_modes(RES_MODE_1024x768, 24, &mode, &depth, &options);
  792 + video_get_ctfb_res_modes(RES_MODE_1024x768, 24, &mode,
  793 + &sunxi_display.depth, &options);
657 794 hpd = video_get_option_int(options, "hpd", 1);
658 795 edid = video_get_option_int(options, "edid", 1);
659 796 sunxi_display.monitor = sunxi_monitor_dvi;
660 797  
661 798  
... ... @@ -678,16 +815,26 @@
678 815 ret = sunxi_hdmi_hpd_detect();
679 816 if (ret) {
680 817 printf("HDMI connected: ");
681   - if (edid && sunxi_hdmi_edid_get_mode(&edid_mode) == 0)
682   - mode = &edid_mode;
  818 + if (edid && sunxi_hdmi_edid_get_mode(&custom) == 0)
  819 + mode = &custom;
683 820 break;
684 821 }
685 822 if (!hpd)
686 823 break; /* User has requested to ignore hpd */
687 824  
688 825 sunxi_hdmi_shutdown();
689   - return NULL;
  826 +
  827 + if (lcd_mode[0] == 0)
  828 + return NULL; /* No LCD, bail */
  829 +
  830 + /* Fall back / through to LCD */
  831 + sunxi_display.monitor = sunxi_monitor_lcd;
690 832 case sunxi_monitor_lcd:
  833 + if (lcd_mode[0]) {
  834 + sunxi_display.depth = video_get_params(&custom, lcd_mode);
  835 + mode = &custom;
  836 + break;
  837 + }
691 838 printf("LCD not supported on this board\n");
692 839 return NULL;
693 840 case sunxi_monitor_vga:
694 841  
695 842  
... ... @@ -729,16 +876,31 @@
729 876 {
730 877 static GraphicDevice *graphic_device = &sunxi_display.graphic_device;
731 878 int offset, ret;
  879 + const char *pipeline = NULL;
732 880  
733 881 if (!sunxi_display.enabled)
734 882 return 0;
735 883  
736   - /* Find a framebuffer node, with pipeline == "de_be0-lcd0-hdmi" */
  884 + switch (sunxi_display.monitor) {
  885 + case sunxi_monitor_none:
  886 + return 0;
  887 + case sunxi_monitor_dvi:
  888 + case sunxi_monitor_hdmi:
  889 + pipeline = "de_be0-lcd0-hdmi";
  890 + break;
  891 + case sunxi_monitor_lcd:
  892 + pipeline = "de_be0-lcd0";
  893 + break;
  894 + case sunxi_monitor_vga:
  895 + break;
  896 + }
  897 +
  898 + /* Find a prefilled simpefb node, matching out pipeline config */
737 899 offset = fdt_node_offset_by_compatible(blob, -1,
738 900 "allwinner,simple-framebuffer");
739 901 while (offset >= 0) {
740 902 ret = fdt_find_string(blob, offset, "allwinner,pipeline",
741   - "de_be0-lcd0-hdmi");
  903 + pipeline);
742 904 if (ret == 0)
743 905 break;
744 906 offset = fdt_node_offset_by_compatible(blob, offset,