#include <fdt_support.h>
#include <video_fb.h>
#include "videomodes.h"
+#include "ssd2828.h"
DECLARE_GLOBAL_DATA_PTR;
struct sunxi_display {
GraphicDevice graphic_device;
- bool enabled;
enum sunxi_monitor monitor;
unsigned int depth;
} sunxi_display;
return 0;
}
-static int sunxi_hdmi_hpd_detect(void)
+static int sunxi_hdmi_hpd_detect(int hpd_delay)
{
struct sunxi_ccm_reg * const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
struct sunxi_hdmi_reg * const hdmi =
(struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
- unsigned long tmo = timer_get_us() + 300000;
+ unsigned long tmo = timer_get_us() + hpd_delay * 1000;
/* Set pll3 to 300MHz */
clock_set_pll3(300000000);
int best_double = 0;
if (tcon == 0) {
+#ifdef CONFIG_VIDEO_LCD_IF_PARALLEL
min_m = 6;
max_m = 127;
+#endif
+#ifdef CONFIG_VIDEO_LCD_IF_LVDS
+ min_m = max_m = 7;
+#endif
} else {
min_m = 1;
max_m = 15;
/* Clock on */
setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_LCD0);
+#ifdef CONFIG_VIDEO_LCD_IF_LVDS
+ setbits_le32(&ccm->lvds_clk_cfg, CCM_LVDS_CTRL_RST);
+#endif
/* Init lcdc */
writel(0, &lcdc->ctrl); /* Disable tcon */
(struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
setbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_TCON_ENABLE);
+#ifdef CONFIG_VIDEO_LCD_IF_LVDS
+ setbits_le32(&lcdc->tcon0_lvds_intf, SUNXI_LCDC_TCON0_LVDS_INTF_ENABLE);
+ setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0);
+ setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_UPDATE);
+ udelay(2); /* delay at least 1200 ns */
+ setbits_le32(&lcdc->lvds_ana1, SUNXI_LCDC_LVDS_ANA1_INIT1);
+ udelay(1); /* delay at least 120 ns */
+ setbits_le32(&lcdc->lvds_ana1, SUNXI_LCDC_LVDS_ANA1_INIT2);
+ setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_UPDATE);
+#endif
}
static void sunxi_lcdc_panel_enable(void)
int bp, clk_delay, clk_div, clk_double, pin, total, val;
for (pin = SUNXI_GPD(0); pin <= SUNXI_GPD(27); pin++)
+#ifdef CONFIG_VIDEO_LCD_IF_PARALLEL
sunxi_gpio_set_cfgpin(pin, SUNXI_GPD0_LCD0);
+#endif
+#ifdef CONFIG_VIDEO_LCD_IF_LVDS
+ sunxi_gpio_set_cfgpin(pin, SUNXI_GPD0_LVDS0);
+#endif
sunxi_lcdc_pll_set(0, mode->pixclock_khz, &clk_div, &clk_double);
writel(SUNXI_LCDC_TCON0_TIMING_V_TOTAL(total) |
SUNXI_LCDC_TCON0_TIMING_V_BP(bp), &lcdc->tcon0_timing_v);
+#ifdef CONFIG_VIDEO_LCD_IF_PARALLEL
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);
+#endif
+#ifdef CONFIG_VIDEO_LCD_IF_LVDS
+ val = (sunxi_display.depth == 18) ? 1 : 0;
+ writel(SUNXI_LCDC_TCON0_LVDS_INTF_BITWIDTH(val), &lcdc->tcon0_lvds_intf);
+#endif
if (sunxi_display.depth == 18 || sunxi_display.depth == 16) {
writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[0]);
&lcdc->tcon0_frm_ctrl);
}
- val = 0;
+ val = SUNXI_LCDC_TCON0_IO_POL_DCLK_PHASE(CONFIG_VIDEO_LCD_DCLK_PHASE);
if (!(mode->sync & FB_SYNC_HOR_HIGH_ACT))
val |= SUNXI_LCDC_TCON_HSYNC_MASK;
if (!(mode->sync & FB_SYNC_VERT_HIGH_ACT))
writel(0, &lcdc->tcon0_io_tristate);
}
-#ifdef CONFIG_VIDEO_HDMI
-
+#if defined CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA
static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode,
- int *clk_div, int *clk_double)
+ int *clk_div, int *clk_double,
+ bool use_portd_hvsync)
{
struct sunxi_lcdc_reg * const lcdc =
(struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
- int bp, total;
+ int bp, clk_delay, total, val;
/* Use tcon1 */
clrsetbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_IO_MAP_MASK,
SUNXI_LCDC_CTRL_IO_MAP_TCON1);
- /* Enabled, 0x1e start delay */
+ clk_delay = sunxi_lcdc_get_clk_delay(mode);
writel(SUNXI_LCDC_TCON1_CTRL_ENABLE |
- SUNXI_LCDC_TCON1_CTRL_CLK_DELAY(0x1e), &lcdc->tcon1_ctrl);
+ SUNXI_LCDC_TCON1_CTRL_CLK_DELAY(clk_delay), &lcdc->tcon1_ctrl);
writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(mode->yres),
&lcdc->tcon1_timing_source);
writel(SUNXI_LCDC_X(mode->hsync_len) | SUNXI_LCDC_Y(mode->vsync_len),
&lcdc->tcon1_timing_sync);
+ if (use_portd_hvsync) {
+ sunxi_gpio_set_cfgpin(SUNXI_GPD(26), SUNXI_GPD0_LCD0);
+ sunxi_gpio_set_cfgpin(SUNXI_GPD(27), SUNXI_GPD0_LCD0);
+
+ val = 0;
+ if (mode->sync & FB_SYNC_HOR_HIGH_ACT)
+ val |= SUNXI_LCDC_TCON_HSYNC_MASK;
+ if (mode->sync & FB_SYNC_VERT_HIGH_ACT)
+ val |= SUNXI_LCDC_TCON_VSYNC_MASK;
+ writel(val, &lcdc->tcon1_io_polarity);
+
+ clrbits_le32(&lcdc->tcon1_io_tristate,
+ SUNXI_LCDC_TCON_VSYNC_MASK |
+ SUNXI_LCDC_TCON_HSYNC_MASK);
+ }
sunxi_lcdc_pll_set(1, mode->pixclock_khz, clk_div, clk_double);
}
+#endif /* CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA */
+
+#ifdef CONFIG_VIDEO_HDMI
static void sunxi_hdmi_setup_info_frames(const struct ctfb_res_modes *mode)
{
#endif /* CONFIG_VIDEO_HDMI */
+#ifdef CONFIG_VIDEO_VGA
+
+static void sunxi_vga_mode_set(void)
+{
+ struct sunxi_ccm_reg * const ccm =
+ (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+ struct sunxi_tve_reg * const tve =
+ (struct sunxi_tve_reg *)SUNXI_TVE0_BASE;
+
+ /* Clock on */
+ setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_TVE0);
+
+ /* Set TVE in VGA mode */
+ writel(SUNXI_TVE_GCTRL_DAC_INPUT(0, 1) |
+ SUNXI_TVE_GCTRL_DAC_INPUT(1, 2) |
+ SUNXI_TVE_GCTRL_DAC_INPUT(2, 3), &tve->gctrl);
+ writel(SUNXI_TVE_GCTRL_CFG0_VGA, &tve->cfg0);
+ writel(SUNXI_TVE_GCTRL_DAC_CFG0_VGA, &tve->dac_cfg0);
+ writel(SUNXI_TVE_GCTRL_UNKNOWN1_VGA, &tve->unknown1);
+}
+
+static void sunxi_vga_enable(void)
+{
+ struct sunxi_tve_reg * const tve =
+ (struct sunxi_tve_reg *)SUNXI_TVE0_BASE;
+
+ setbits_le32(&tve->gctrl, SUNXI_TVE_GCTRL_ENABLE);
+}
+
+#endif /* CONFIG_VIDEO_VGA */
+
static void sunxi_drc_init(void)
{
#if defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I
#endif
}
+#ifdef CONFIG_VIDEO_VGA_VIA_LCD
+static void sunxi_vga_external_dac_enable(void)
+{
+ int pin;
+
+ pin = sunxi_name_to_gpio(CONFIG_VIDEO_VGA_EXTERNAL_DAC_EN);
+ if (pin != -1) {
+ gpio_request(pin, "vga_enable");
+ gpio_direction_output(pin, 1);
+ }
+}
+#endif /* CONFIG_VIDEO_VGA_VIA_LCD */
+
+#ifdef CONFIG_VIDEO_LCD_SSD2828
+static int sunxi_ssd2828_init(const struct ctfb_res_modes *mode)
+{
+ struct ssd2828_config cfg = {
+ .csx_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_CS),
+ .sck_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_SCLK),
+ .sdi_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_MOSI),
+ .sdo_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_MISO),
+ .reset_pin = name_to_gpio(CONFIG_VIDEO_LCD_SSD2828_RESET),
+ .ssd2828_tx_clk_khz = CONFIG_VIDEO_LCD_SSD2828_TX_CLK * 1000,
+ .ssd2828_color_depth = 24,
+#ifdef CONFIG_VIDEO_LCD_PANEL_MIPI_4_LANE_513_MBPS_VIA_SSD2828
+ .mipi_dsi_number_of_data_lanes = 4,
+ .mipi_dsi_bitrate_per_data_lane_mbps = 513,
+ .mipi_dsi_delay_after_exit_sleep_mode_ms = 100,
+ .mipi_dsi_delay_after_set_display_on_ms = 200
+#else
+#error MIPI LCD panel needs configuration parameters
+#endif
+ };
+
+ if (cfg.csx_pin == -1 || cfg.sck_pin == -1 || cfg.sdi_pin == -1) {
+ printf("SSD2828: SPI pins are not properly configured\n");
+ return 1;
+ }
+ if (cfg.reset_pin == -1) {
+ printf("SSD2828: Reset pin is not properly configured\n");
+ return 1;
+ }
+
+ return ssd2828_init(&cfg, mode);
+}
+#endif /* CONFIG_VIDEO_LCD_SSD2828 */
+
static void sunxi_engines_init(void)
{
sunxi_composer_init();
static void sunxi_mode_set(const struct ctfb_res_modes *mode,
unsigned int address)
{
+ int __maybe_unused clk_div, clk_double;
+
switch (sunxi_display.monitor) {
case sunxi_monitor_none:
break;
case sunxi_monitor_dvi:
- case sunxi_monitor_hdmi: {
+ case sunxi_monitor_hdmi:
#ifdef CONFIG_VIDEO_HDMI
- int clk_div, clk_double;
sunxi_composer_mode_set(mode, address);
- sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double);
+ sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0);
sunxi_hdmi_mode_set(mode, clk_div, clk_double);
sunxi_composer_enable();
sunxi_lcdc_enable();
sunxi_hdmi_enable();
#endif
- }
break;
case sunxi_monitor_lcd:
sunxi_lcdc_panel_enable();
sunxi_lcdc_tcon0_mode_set(mode);
sunxi_composer_enable();
sunxi_lcdc_enable();
+#ifdef CONFIG_VIDEO_LCD_SSD2828
+ sunxi_ssd2828_init(mode);
+#endif
sunxi_lcdc_backlight_enable();
break;
case sunxi_monitor_vga:
-#ifdef CONFIG_VIDEO_VGA_VIA_LCD
+#ifdef CONFIG_VIDEO_VGA
+ sunxi_composer_mode_set(mode, address);
+ sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 1);
+ sunxi_vga_mode_set();
+ sunxi_composer_enable();
+ sunxi_lcdc_enable();
+ sunxi_vga_enable();
+#elif defined CONFIG_VIDEO_VGA_VIA_LCD
sunxi_composer_mode_set(mode, address);
sunxi_lcdc_tcon0_mode_set(mode);
sunxi_composer_enable();
sunxi_lcdc_enable();
+ sunxi_vga_external_dac_enable();
#endif
break;
}
struct ctfb_res_modes custom;
const char *options;
#ifdef CONFIG_VIDEO_HDMI
- int ret, hpd, edid;
+ int ret, hpd, hpd_delay, edid;
#endif
char mon[16];
char *lcd_mode = CONFIG_VIDEO_LCD_MODE;
&sunxi_display.depth, &options);
#ifdef CONFIG_VIDEO_HDMI
hpd = video_get_option_int(options, "hpd", 1);
+ hpd_delay = video_get_option_int(options, "hpd_delay", 500);
edid = video_get_option_int(options, "edid", 1);
sunxi_display.monitor = sunxi_monitor_dvi;
#elif defined CONFIG_VIDEO_VGA_VIA_LCD
printf("Unknown monitor: '%s', falling back to '%s'\n",
mon, sunxi_get_mon_desc(sunxi_display.monitor));
+#ifdef CONFIG_VIDEO_HDMI
+ /* If HDMI/DVI is selected do HPD & EDID, and handle fallback */
+ if (sunxi_display.monitor == sunxi_monitor_dvi ||
+ sunxi_display.monitor == sunxi_monitor_hdmi) {
+ /* Always call hdp_detect, as it also enables clocks, etc. */
+ ret = sunxi_hdmi_hpd_detect(hpd_delay);
+ if (ret) {
+ printf("HDMI connected: ");
+ if (edid && sunxi_hdmi_edid_get_mode(&custom) == 0)
+ mode = &custom;
+ } else if (hpd) {
+ sunxi_hdmi_shutdown();
+ /* Fallback to lcd / vga / none */
+ if (lcd_mode[0]) {
+ sunxi_display.monitor = sunxi_monitor_lcd;
+ } else {
+#if defined CONFIG_VIDEO_VGA_VIA_LCD || defined CONFIG_VIDEO_VGA
+ sunxi_display.monitor = sunxi_monitor_vga;
+#else
+ sunxi_display.monitor = sunxi_monitor_none;
+#endif
+ }
+ } /* else continue with hdmi/dvi without a cable connected */
+ }
+#endif
+
switch (sunxi_display.monitor) {
case sunxi_monitor_none:
return NULL;
case sunxi_monitor_dvi:
case sunxi_monitor_hdmi:
-#ifndef CONFIG_VIDEO_HDMI
+#ifdef CONFIG_VIDEO_HDMI
+ break;
+#else
printf("HDMI/DVI not supported on this board\n");
+ sunxi_display.monitor = sunxi_monitor_none;
return NULL;
-#else
- /* Always call hdp_detect, as it also enables clocks, etc. */
- ret = sunxi_hdmi_hpd_detect();
- if (ret) {
- printf("HDMI connected: ");
- if (edid && sunxi_hdmi_edid_get_mode(&custom) == 0)
- mode = &custom;
- break;
- }
- if (!hpd)
- break; /* User has requested to ignore hpd */
-
- sunxi_hdmi_shutdown();
-
- if (lcd_mode[0] == 0)
- return NULL; /* No LCD, bail */
-
- /* Fall back / through to LCD */
- sunxi_display.monitor = sunxi_monitor_lcd;
#endif
case sunxi_monitor_lcd:
if (lcd_mode[0]) {
break;
}
printf("LCD not supported on this board\n");
+ sunxi_display.monitor = sunxi_monitor_none;
return NULL;
case sunxi_monitor_vga:
-#ifdef CONFIG_VIDEO_VGA_VIA_LCD
+#if defined CONFIG_VIDEO_VGA_VIA_LCD || defined CONFIG_VIDEO_VGA
sunxi_display.depth = 18;
break;
#else
printf("VGA not supported on this board\n");
+ sunxi_display.monitor = sunxi_monitor_none;
return NULL;
#endif
}
mode->yres, sunxi_get_mon_desc(sunxi_display.monitor));
}
- sunxi_display.enabled = true;
sunxi_engines_init();
sunxi_mode_set(mode, gd->fb_base - CONFIG_SYS_SDRAM_BASE);
int offset, ret;
const char *pipeline = NULL;
- if (!sunxi_display.enabled)
- return 0;
-
switch (sunxi_display.monitor) {
case sunxi_monitor_none:
return 0;
pipeline = "de_be0-lcd0";
break;
case sunxi_monitor_vga:
+#ifdef CONFIG_VIDEO_VGA
+ pipeline = "de_be0-lcd0-tve0";
+#elif defined CONFIG_VIDEO_VGA_VIA_LCD
pipeline = "de_be0-lcd0";
+#endif
break;
}