Message ID | 1420027641-11613-1-git-send-email-hdegoede@redhat.com |
---|---|
State | Accepted |
Delegated to: | Ian Campbell |
Headers | show |
On Wed, 31 Dec 2014 13:07:20 +0100 Hans de Goede <hdegoede@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@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@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@tin.it > + > Example: video-mode=fslfb:1280x1024-32@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@60Hz, > +use: video-mode=sunxi:1024x768-24@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,
Hi, On 01-01-15 21:21, Siarhei Siamashka wrote: > On Wed, 31 Dec 2014 13:07:20 +0100 > Hans de Goede <hdegoede@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@redhat.com> <snip> >> @@ -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. You're completely right, I took over the weird RGB656 naming from the allwinner kernel code without thinking things through properly. <snip> >> + 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. Right, this should be 16 meaning plain old RGB565, I've fixed both in my personal tree. Thanks for pointing this out! > > 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 <snip> Regards, Hans
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)) +#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@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@tin.it + Example: video-mode=fslfb:1280x1024-32@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@60Hz, +use: video-mode=sunxi:1024x768-24@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) { + 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,
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> --- 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(-)