[U-Boot] [PATCH v2 1/2] sunxi: video: Add lcd output support
Siarhei Siamashka
siarhei.siamashka at gmail.com
Thu Jan 1 21:21:51 CET 2015
On Wed, 31 Dec 2014 13:07:20 +0100
Hans de Goede <hdegoede at redhat.com> wrote:
> 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 at redhat.com>
> ---
> Changes in v2:
> -Do not request backlight gpio twices
> -Fix some spelling errors in comments
> -Fix some no longer accurate comments
> ---
> arch/arm/include/asm/arch-sunxi/display.h | 25 ++++-
> arch/arm/include/asm/arch-sunxi/gpio.h | 2 +
> board/sunxi/Kconfig | 43 ++++++-
> doc/README.video | 50 +++++++--
> drivers/video/sunxi_display.c | 179 ++++++++++++++++++++++++++++--
> 5 files changed, 272 insertions(+), 27 deletions(-)
>
> diff --git a/arch/arm/include/asm/arch-sunxi/display.h b/arch/arm/include/asm/arch-sunxi/display.h
> index 00e3466..dcb2fe4 100644
> --- a/arch/arm/include/asm/arch-sunxi/display.h
> +++ b/arch/arm/include/asm/arch-sunxi/display.h
> @@ -57,14 +57,13 @@ struct sunxi_lcdc_reg {
> u32 int0; /* 0x04 */
> u32 int1; /* 0x08 */
> u8 res0[0x04]; /* 0x0c */
> - u32 frame_ctrl; /* 0x10 */
> - u8 res1[0x2c]; /* 0x14 */
> + u32 frame_ctrl[12]; /* 0x10 */
> u32 tcon0_ctrl; /* 0x40 */
> u32 tcon0_dclk; /* 0x44 */
> - u32 tcon0_basic_timing0; /* 0x48 */
> - u32 tcon0_basic_timing1; /* 0x4c */
> - u32 tcon0_basic_timing2; /* 0x50 */
> - u32 tcon0_basic_timing3; /* 0x54 */
> + u32 tcon0_timing_active; /* 0x48 */
> + u32 tcon0_timing_h; /* 0x4c */
> + u32 tcon0_timing_v; /* 0x50 */
> + u32 tcon0_timing_sync; /* 0x54 */
> u32 tcon0_hv_intf; /* 0x58 */
> u8 res2[0x04]; /* 0x5c */
> u32 tcon0_cpu_intf; /* 0x60 */
> @@ -179,7 +178,21 @@ struct sunxi_hdmi_reg {
> #define SUNXI_LCDC_CTRL_IO_MAP_TCON0 (0 << 0)
> #define SUNXI_LCDC_CTRL_IO_MAP_TCON1 (1 << 0)
> #define SUNXI_LCDC_CTRL_TCON_ENABLE (1 << 31)
> +#define SUNXI_LCDC_FRAME_CTRL0_RGB666 ((1 << 31) | (0 << 4))
> +#define SUNXI_LCDC_FRAME_CTRL0_RGB656 ((1 << 31) | (5 << 4))
This would be probably SUNXI_LCDC_FRAME_CTRL0_RGB565 according to
the Allwinner documentation of the TCON0_FRM_CTL_REG register:
0: 6bit frm output
1: 5bit frm output
Since we have 5 there (101 bit pattern), it simply means RGB565.
> +#define SUNXI_LCDC_FRAME_CTRL_DITHER0 0x11111111
> +#define SUNXI_LCDC_FRAME_CTRL_DITHER1 0x01010000
> +#define SUNXI_LCDC_FRAME_CTRL_DITHER2 0x15151111
> +#define SUNXI_LCDC_FRAME_CTRL_DITHER3 0x57575555
> +#define SUNXI_LCDC_FRAME_CTRL_DITHER4 0x7f7f7777
> +#define SUNXI_LCDC_TCON0_CTRL_CLK_DELAY(n) (((n) & 0x1f) << 4)
> +#define SUNXI_LCDC_TCON0_CTRL_ENABLE (1 << 31)
> +#define SUNXI_LCDC_TCON0_DCLK_DIV(n) ((n) << 0)
> #define SUNXI_LCDC_TCON0_DCLK_ENABLE (0xf << 28)
> +#define SUNXI_LCDC_TCON0_TIMING_H_BP(n) (((n) - 1) << 0)
> +#define SUNXI_LCDC_TCON0_TIMING_H_TOTAL(n) (((n) - 1) << 16)
> +#define SUNXI_LCDC_TCON0_TIMING_V_BP(n) (((n) - 1) << 0)
> +#define SUNXI_LCDC_TCON0_TIMING_V_TOTAL(n) (((n) * 2) << 16)
> #define SUNXI_LCDC_TCON1_CTRL_CLK_DELAY(n) (((n) & 0x1f) << 4)
> #define SUNXI_LCDC_TCON1_CTRL_ENABLE (1 << 31)
> #define SUNXI_LCDC_TCON1_TIMING_H_BP(n) (((n) - 1) << 0)
> diff --git a/arch/arm/include/asm/arch-sunxi/gpio.h b/arch/arm/include/asm/arch-sunxi/gpio.h
> index 32941cb..9438f5a 100644
> --- a/arch/arm/include/asm/arch-sunxi/gpio.h
> +++ b/arch/arm/include/asm/arch-sunxi/gpio.h
> @@ -150,6 +150,8 @@ enum sunxi_gpio_number {
>
> #define SUNXI_GPC6_SDC2 3
>
> +#define SUNXI_GPD0_LCD0 2
> +
> #define SUNXI_GPF0_SDC0 2
>
> #define SUNXI_GPF2_SDC0 2
> diff --git a/board/sunxi/Kconfig b/board/sunxi/Kconfig
> index 72c0165..6f2377d 100644
> --- a/board/sunxi/Kconfig
> +++ b/board/sunxi/Kconfig
> @@ -281,17 +281,52 @@ config USB2_VBUS_PIN
> See USB1_VBUS_PIN help text.
>
> config VIDEO
> - boolean "Enable graphical uboot console on HDMI"
> + boolean "Enable graphical uboot console on HDMI, LCD or VGA"
> default y
> ---help---
> - Say Y here to add support for using a cfb console on the HDMI output
> - found on most sunxi devices.
> + Say Y here to add support for using a cfb console on the HDMI, LCD
> + or VGA output found on most sunxi devices. See doc/README.video for
> + info on how to select the video output and mode.
> +
> +config VIDEO_LCD_MODE
> + string "LCD panel timing details"
> + depends on VIDEO
> + default ""
> + ---help---
> + LCD panel timing details string, leave empty if there is no LCD panel.
> + This is in drivers/video/videomodes.c: video_get_params() format, e.g.
> + 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
> +
> +config VIDEO_LCD_POWER
> + string "LCD panel power enable pin"
> + depends on VIDEO
> + default ""
> + ---help---
> + Set the power enable pin for the LCD panel. This takes a string in the
> + format understood by sunxi_name_to_gpio, e.g. PH1 for pin 1 of port H.
> +
> +config VIDEO_LCD_BL_EN
> + string "LCD panel backlight enable pin"
> + depends on VIDEO
> + default ""
> + ---help---
> + Set the backlight enable pin for the LCD panel. This takes a string in the
> + the format understood by sunxi_name_to_gpio, e.g. PH1 for pin 1 of
> + port H.
> +
> +config VIDEO_LCD_BL_PWM
> + string "LCD panel backlight pwm pin"
> + depends on VIDEO
> + default ""
> + ---help---
> + Set the backlight pwm pin for the LCD panel. This takes a string in the
> + format understood by sunxi_name_to_gpio, e.g. PH1 for pin 1 of port H.
>
> config USB_KEYBOARD
> boolean "Enable USB keyboard support"
> default y
> ---help---
> Say Y here to add support for using a USB keyboard (typically used
> - in combination with a graphical console on HDMI).
> + in combination with a graphical console).
>
> endif
> diff --git a/doc/README.video b/doc/README.video
> index dadbfcd..cfe6318 100644
> --- a/doc/README.video
> +++ b/doc/README.video
> @@ -5,15 +5,8 @@
> * SPDX-License-Identifier: GPL-2.0+
> */
>
> -U-Boot MPC8xx video controller driver
> -======================================
> -
> -The driver has been tested with the following configurations:
> -
> -- MPC823FADS with AD7176 on a PAL TV (YCbYCr) - arsenio at tin.it
> -
> "video-mode" environment variable
> -===============================
> +=================================
>
> The 'video-mode' environment variable can be used to enable and configure
> some video drivers. The format matches the video= command-line option used
> @@ -28,4 +21,45 @@ for Linux:
> <freq> The frequency (in Hz) to use.
> <options> A comma-separated list of device-specific options
>
> +
> +U-Boot MPC8xx video controller driver
> +=====================================
> +
> +The driver has been tested with the following configurations:
> +
> +- MPC823FADS with AD7176 on a PAL TV (YCbYCr) - arsenio at tin.it
> +
> Example: video-mode=fslfb:1280x1024-32 at 60,monitor=dvi
> +
> +
> +U-boot sunxi video controller driver
> +====================================
> +
> +U-boot supports hdmi and lcd output on Allwinner sunxi SoCs, lcd output
> +requires the CONFIG_VIDEO_LCD_MODE Kconfig value to be set.
> +
> +The sunxi u-boot driver supports the following video-mode options:
> +
> +- monitor=[none|dvi|hdmi|lcd] - Select the video output to use
> + none: Disable video output.
> + dvi/hdmi: Selects output over the hdmi connector with dvi resp. hdmi output
> + format, if edid is used the format is automatically selected.
> + lcd: Selects video output to a LCD screen.
> + vga: Selects bideo output over the VGA connector.
> + Defaults to monitor=dvi.
> +
> +- hpd=[0|1] - Enable use of the hdmi HotPlug Detect feature
> + 0: Disabled. Configure dvi/hdmi output even if no cable is detected
> + 1: Enabled. If a LCD has been configured fallback to the LCD when no cable is
> + detected, if no LCD is configured, disable video ouput.
> + Defaults to hpd=1.
> +
> +- edid=[0|1] - Enable use of DDC + EDID to get monitor info
> + 0: Disabled.
> + 1: Enabled. If valid EDID info was read from the monitor the EDID info will
> + overrides the xres, yres and refresh from the video-mode env. variable.
> + Defaults to edid=1.
> +
> +For example to always use the hdmi connector, even if no cable is inserted,
> +using edid info when available and otherwise initalizing it at 1024x768 at 60Hz,
> +use: video-mode=sunxi:1024x768-24 at 60,monitor=dvi,hpd=0,edid=1 .
> diff --git a/drivers/video/sunxi_display.c b/drivers/video/sunxi_display.c
> index ad38f16..fee1474 100644
> --- a/drivers/video/sunxi_display.c
> +++ b/drivers/video/sunxi_display.c
> @@ -11,7 +11,9 @@
>
> #include <asm/arch/clock.h>
> #include <asm/arch/display.h>
> +#include <asm/arch/gpio.h>
> #include <asm/global_data.h>
> +#include <asm/gpio.h>
> #include <asm/io.h>
> #include <errno.h>
> #include <fdtdec.h>
> @@ -34,6 +36,7 @@ struct sunxi_display {
> GraphicDevice graphic_device;
> bool enabled;
> enum sunxi_monitor monitor;
> + unsigned int depth;
> } sunxi_display;
>
> /*
> @@ -435,6 +438,133 @@ static void sunxi_lcdc_enable(void)
> setbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_TCON_ENABLE);
> }
>
> +static void sunxi_lcdc_panel_enable(void)
> +{
> + int pin;
> +
> + /*
> + * Start with backlight disabled to avoid the screen flashing to
> + * white while the lcd inits.
> + */
> + pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_EN);
> + if (pin != -1) {
> + gpio_request(pin, "lcd_backlight_enable");
> + gpio_direction_output(pin, 0);
> + }
> +
> + pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_PWM);
> + if (pin != -1) {
> + gpio_request(pin, "lcd_backlight_pwm");
> + /* backlight pwm is inverted, set to 1 to disable backlight */
> + gpio_direction_output(pin, 1);
> + }
> +
> + /* Give the backlight some time to turn off and power up the panel. */
> + mdelay(40);
> + pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_POWER);
> + if (pin != -1) {
> + gpio_request(pin, "lcd_power");
> + gpio_direction_output(pin, 1);
> + }
> +}
> +
> +static void sunxi_lcdc_backlight_enable(void)
> +{
> + int pin;
> +
> + /*
> + * We want to have scanned out at least one frame before enabling the
> + * backlight to avoid the screen flashing to white when we enable it.
> + */
> + mdelay(40);
> +
> + pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_EN);
> + if (pin != -1)
> + gpio_direction_output(pin, 1);
> +
> + pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_PWM);
> + if (pin != -1) {
> + /* backlight pwm is inverted, set to 0 to enable backlight */
> + gpio_direction_output(pin, 0);
> + }
> +}
> +
> +static int sunxi_lcdc_get_clk_delay(const struct ctfb_res_modes *mode)
> +{
> + int delay;
> +
> + delay = mode->lower_margin + mode->vsync_len + mode->upper_margin - 2;
> + return (delay > 30) ? 30 : delay;
> +}
> +
> +static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode)
> +{
> + struct sunxi_lcdc_reg * const lcdc =
> + (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
> + int bp, clk_delay, clk_div, clk_double, pin, total;
> +
> + for (pin = SUNXI_GPD(0); pin <= SUNXI_GPD(27); pin++)
> + sunxi_gpio_set_cfgpin(pin, SUNXI_GPD0_LCD0);
> +
> + sunxi_lcdc_pll_set(0, mode->pixclock_khz, &clk_div, &clk_double);
> +
> + /* Use tcon0 */
> + clrsetbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_IO_MAP_MASK,
> + SUNXI_LCDC_CTRL_IO_MAP_TCON0);
> +
> + clk_delay = sunxi_lcdc_get_clk_delay(mode);
> + writel(SUNXI_LCDC_TCON0_CTRL_ENABLE |
> + SUNXI_LCDC_TCON0_CTRL_CLK_DELAY(clk_delay), &lcdc->tcon0_ctrl);
> +
> + writel(SUNXI_LCDC_TCON0_DCLK_ENABLE |
> + SUNXI_LCDC_TCON0_DCLK_DIV(clk_div), &lcdc->tcon0_dclk);
> +
> + writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(mode->yres),
> + &lcdc->tcon0_timing_active);
> +
> + bp = mode->hsync_len + mode->left_margin;
> + total = mode->xres + mode->right_margin + bp;
> + writel(SUNXI_LCDC_TCON0_TIMING_H_TOTAL(total) |
> + SUNXI_LCDC_TCON0_TIMING_H_BP(bp), &lcdc->tcon0_timing_h);
> +
> + bp = mode->vsync_len + mode->upper_margin;
> + total = mode->yres + mode->lower_margin + bp;
> + writel(SUNXI_LCDC_TCON0_TIMING_V_TOTAL(total) |
> + SUNXI_LCDC_TCON0_TIMING_V_BP(bp), &lcdc->tcon0_timing_v);
> +
> + writel(SUNXI_LCDC_X(mode->hsync_len) | SUNXI_LCDC_Y(mode->vsync_len),
> + &lcdc->tcon0_timing_sync);
> +
> + /* We only support hv-sync parallel lcd-s for now */
> + writel(0, &lcdc->tcon0_hv_intf);
> + writel(0, &lcdc->tcon0_cpu_intf);
> +
> + if (sunxi_display.depth == 18 || sunxi_display.depth == 17) {
Here 17 is not quite correct for RGB565.
Also these are dithering settings, and this code just unconditionally
enables the right dithering for 16-bit LCD displays (this seems to
be never used in any real device from the sunxi-boards repository) or
18-bit LCD displays, which are very common.
Because 32-bit framebuffers support more colors than the 18-bit LCD
hardware can show, dithering is needed and used:
http://en.wikipedia.org/wiki/Frame_rate_control
Do we want to have a separate option to enable/disable dithering? Or
just keep it always enabled until somebody complains?
A simple program for testing dithering effects/usefulness can be found
here:
http://lists.denx.de/pipermail/u-boot/2015-January/200031.html
> + writel(SUNXI_LCDC_FRAME_CTRL_DITHER0, &lcdc->frame_ctrl[1]);
> + writel(SUNXI_LCDC_FRAME_CTRL_DITHER0, &lcdc->frame_ctrl[2]);
> + writel(SUNXI_LCDC_FRAME_CTRL_DITHER0, &lcdc->frame_ctrl[3]);
> + writel(SUNXI_LCDC_FRAME_CTRL_DITHER0, &lcdc->frame_ctrl[4]);
> + writel(SUNXI_LCDC_FRAME_CTRL_DITHER0, &lcdc->frame_ctrl[5]);
> + writel(SUNXI_LCDC_FRAME_CTRL_DITHER0, &lcdc->frame_ctrl[6]);
> + writel(SUNXI_LCDC_FRAME_CTRL_DITHER1, &lcdc->frame_ctrl[7]);
> + writel(SUNXI_LCDC_FRAME_CTRL_DITHER2, &lcdc->frame_ctrl[8]);
> + writel(SUNXI_LCDC_FRAME_CTRL_DITHER3, &lcdc->frame_ctrl[9]);
> + writel(SUNXI_LCDC_FRAME_CTRL_DITHER4, &lcdc->frame_ctrl[10]);
> + writel(((sunxi_display.depth == 18) ?
> + SUNXI_LCDC_FRAME_CTRL0_RGB666 :
> + SUNXI_LCDC_FRAME_CTRL0_RGB656),
> + &lcdc->frame_ctrl[0]);
> + }
> +
> + /*
> + * Bit 24 and 25 of tcon0_io_polarity can be used to invert hsync /
> + * vsync polarity, but this leads to noise problems, so we always
> + * keep the polarity positive.
> + */
> + writel(0, &lcdc->tcon0_io_polarity);
> + writel(0, &lcdc->tcon0_io_tristate);
> +}
> +
> static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode,
> int *clk_div, int *clk_double)
> {
> @@ -618,7 +748,12 @@ static void sunxi_mode_set(const struct ctfb_res_modes *mode,
> }
> break;
> case sunxi_monitor_lcd:
> - /* TODO */
> + sunxi_lcdc_panel_enable();
> + sunxi_composer_mode_set(mode, address);
> + sunxi_lcdc_tcon0_mode_set(mode);
> + sunxi_composer_enable();
> + sunxi_lcdc_enable();
> + sunxi_lcdc_backlight_enable();
> break;
> case sunxi_monitor_vga:
> break;
> @@ -641,11 +776,11 @@ void *video_hw_init(void)
> {
> static GraphicDevice *graphic_device = &sunxi_display.graphic_device;
> const struct ctfb_res_modes *mode;
> - struct ctfb_res_modes edid_mode;
> + struct ctfb_res_modes custom;
> const char *options;
> - unsigned int depth;
> int i, ret, hpd, edid;
> char mon[16];
> + char *lcd_mode = CONFIG_VIDEO_LCD_MODE;
>
> memset(&sunxi_display, 0, sizeof(struct sunxi_display));
>
> @@ -653,7 +788,8 @@ void *video_hw_init(void)
> CONFIG_SUNXI_FB_SIZE >> 10);
> gd->fb_base = gd->ram_top;
>
> - video_get_ctfb_res_modes(RES_MODE_1024x768, 24, &mode, &depth, &options);
> + video_get_ctfb_res_modes(RES_MODE_1024x768, 24, &mode,
> + &sunxi_display.depth, &options);
> hpd = video_get_option_int(options, "hpd", 1);
> edid = video_get_option_int(options, "edid", 1);
> sunxi_display.monitor = sunxi_monitor_dvi;
> @@ -678,16 +814,26 @@ void *video_hw_init(void)
> ret = sunxi_hdmi_hpd_detect();
> if (ret) {
> printf("HDMI connected: ");
> - if (edid && sunxi_hdmi_edid_get_mode(&edid_mode) == 0)
> - mode = &edid_mode;
> + if (edid && sunxi_hdmi_edid_get_mode(&custom) == 0)
> + mode = &custom;
> break;
> }
> if (!hpd)
> break; /* User has requested to ignore hpd */
>
> sunxi_hdmi_shutdown();
> - return NULL;
> +
> + if (lcd_mode[0] == 0)
> + return NULL; /* No LCD, bail */
> +
> + /* Fall back / through to LCD */
> + sunxi_display.monitor = sunxi_monitor_lcd;
> case sunxi_monitor_lcd:
> + if (lcd_mode[0]) {
> + sunxi_display.depth = video_get_params(&custom, lcd_mode);
> + mode = &custom;
> + break;
> + }
> printf("LCD not supported on this board\n");
> return NULL;
> case sunxi_monitor_vga:
> @@ -729,16 +875,31 @@ int sunxi_simplefb_setup(void *blob)
> {
> static GraphicDevice *graphic_device = &sunxi_display.graphic_device;
> int offset, ret;
> + const char *pipeline = NULL;
>
> if (!sunxi_display.enabled)
> return 0;
>
> - /* Find a framebuffer node, with pipeline == "de_be0-lcd0-hdmi" */
> + switch (sunxi_display.monitor) {
> + case sunxi_monitor_none:
> + return 0;
> + case sunxi_monitor_dvi:
> + case sunxi_monitor_hdmi:
> + pipeline = "de_be0-lcd0-hdmi";
> + break;
> + case sunxi_monitor_lcd:
> + pipeline = "de_be0-lcd0";
> + break;
> + case sunxi_monitor_vga:
> + break;
> + }
> +
> + /* Find a prefilled simpefb node, matching out pipeline config */
> offset = fdt_node_offset_by_compatible(blob, -1,
> "allwinner,simple-framebuffer");
> while (offset >= 0) {
> ret = fdt_find_string(blob, offset, "allwinner,pipeline",
> - "de_be0-lcd0-hdmi");
> + pipeline);
> if (ret == 0)
> break;
> offset = fdt_node_offset_by_compatible(blob, offset,
--
Best regards,
Siarhei Siamashka
More information about the U-Boot
mailing list