i.MX6: mx6qsabrelite: Add splash screen support

Adds support for HDMI, two LVDS panels and one RGB panel to
the SABRE-Lite board.

Displays supported:
         HDMI		- 1024 x 768 for maximum compatibility
         Hannstar-XGA   - 1024 x 768 LVDS (Freescale part number MCIMX-LVDS1)
         wsvga-lvds     - 1024 x 600 LVDS (Boundary p/n Nit6X_1024x600)
         wvga-rgb       - 800 x 480 RGB (Boundary p/n Nit6X_800x480)

Since the ipuv3_fb display driver currently supports only a single display,
this code auto-detects panel by probing the HDMI Phy for Hot Plug Detect
or the I2C touch controller of the LVDS and RGB displays in the priority
listed above.

Setting 'panel' environment variable to one of the names above will
override auto-detection.

Signed-off-by: Eric Nelson <eric.nelson@boundarydevices.com>
This commit is contained in:
Eric Nelson 2012-10-03 07:28:43 +00:00 committed by Stefano Babic
parent f95c960bcc
commit e58010b554
2 changed files with 343 additions and 1 deletions

View file

@ -36,6 +36,12 @@
#include <micrel.h>
#include <miiphy.h>
#include <netdev.h>
#include <linux/fb.h>
#include <ipu_pixfmt.h>
#include <asm/arch/crm_regs.h>
#include <asm/arch/mxc_hdmi.h>
#include <i2c.h>
DECLARE_GLOBAL_DATA_PTR;
#define UART_PAD_CTRL (PAD_CTL_PKE | PAD_CTL_PUE | \
@ -375,14 +381,337 @@ int setup_sata(void)
}
#endif
#if defined(CONFIG_VIDEO_IPUV3)
static iomux_v3_cfg_t const backlight_pads[] = {
/* Backlight on RGB connector: J15 */
MX6Q_PAD_SD1_DAT3__GPIO_1_21 | MUX_PAD_CTRL(NO_PAD_CTRL),
#define RGB_BACKLIGHT_GP IMX_GPIO_NR(1, 21)
/* Backlight on LVDS connector: J6 */
MX6Q_PAD_SD1_CMD__GPIO_1_18 | MUX_PAD_CTRL(NO_PAD_CTRL),
#define LVDS_BACKLIGHT_GP IMX_GPIO_NR(1, 18)
};
static iomux_v3_cfg_t const rgb_pads[] = {
MX6Q_PAD_DI0_DISP_CLK__IPU1_DI0_DISP_CLK,
MX6Q_PAD_DI0_PIN15__IPU1_DI0_PIN15,
MX6Q_PAD_DI0_PIN2__IPU1_DI0_PIN2,
MX6Q_PAD_DI0_PIN3__IPU1_DI0_PIN3,
MX6Q_PAD_DI0_PIN4__GPIO_4_20,
MX6Q_PAD_DISP0_DAT0__IPU1_DISP0_DAT_0,
MX6Q_PAD_DISP0_DAT1__IPU1_DISP0_DAT_1,
MX6Q_PAD_DISP0_DAT2__IPU1_DISP0_DAT_2,
MX6Q_PAD_DISP0_DAT3__IPU1_DISP0_DAT_3,
MX6Q_PAD_DISP0_DAT4__IPU1_DISP0_DAT_4,
MX6Q_PAD_DISP0_DAT5__IPU1_DISP0_DAT_5,
MX6Q_PAD_DISP0_DAT6__IPU1_DISP0_DAT_6,
MX6Q_PAD_DISP0_DAT7__IPU1_DISP0_DAT_7,
MX6Q_PAD_DISP0_DAT8__IPU1_DISP0_DAT_8,
MX6Q_PAD_DISP0_DAT9__IPU1_DISP0_DAT_9,
MX6Q_PAD_DISP0_DAT10__IPU1_DISP0_DAT_10,
MX6Q_PAD_DISP0_DAT11__IPU1_DISP0_DAT_11,
MX6Q_PAD_DISP0_DAT12__IPU1_DISP0_DAT_12,
MX6Q_PAD_DISP0_DAT13__IPU1_DISP0_DAT_13,
MX6Q_PAD_DISP0_DAT14__IPU1_DISP0_DAT_14,
MX6Q_PAD_DISP0_DAT15__IPU1_DISP0_DAT_15,
MX6Q_PAD_DISP0_DAT16__IPU1_DISP0_DAT_16,
MX6Q_PAD_DISP0_DAT17__IPU1_DISP0_DAT_17,
MX6Q_PAD_DISP0_DAT18__IPU1_DISP0_DAT_18,
MX6Q_PAD_DISP0_DAT19__IPU1_DISP0_DAT_19,
MX6Q_PAD_DISP0_DAT20__IPU1_DISP0_DAT_20,
MX6Q_PAD_DISP0_DAT21__IPU1_DISP0_DAT_21,
MX6Q_PAD_DISP0_DAT22__IPU1_DISP0_DAT_22,
MX6Q_PAD_DISP0_DAT23__IPU1_DISP0_DAT_23,
};
struct display_info_t {
int bus;
int addr;
int pixfmt;
int (*detect)(struct display_info_t const *dev);
void (*enable)(struct display_info_t const *dev);
struct fb_videomode mode;
};
static int detect_hdmi(struct display_info_t const *dev)
{
return __raw_readb(HDMI_ARB_BASE_ADDR+HDMI_PHY_STAT0) & HDMI_PHY_HPD;
}
static void enable_hdmi(struct display_info_t const *dev)
{
u8 reg;
printf("%s: setup HDMI monitor\n", __func__);
reg = __raw_readb(
HDMI_ARB_BASE_ADDR
+HDMI_PHY_CONF0);
reg |= HDMI_PHY_CONF0_PDZ_MASK;
__raw_writeb(reg,
HDMI_ARB_BASE_ADDR
+HDMI_PHY_CONF0);
udelay(3000);
reg |= HDMI_PHY_CONF0_ENTMDS_MASK;
__raw_writeb(reg,
HDMI_ARB_BASE_ADDR
+HDMI_PHY_CONF0);
udelay(3000);
reg |= HDMI_PHY_CONF0_GEN2_TXPWRON_MASK;
__raw_writeb(reg,
HDMI_ARB_BASE_ADDR
+HDMI_PHY_CONF0);
__raw_writeb(HDMI_MC_PHYRSTZ_ASSERT,
HDMI_ARB_BASE_ADDR+HDMI_MC_PHYRSTZ);
}
static int detect_i2c(struct display_info_t const *dev)
{
return ((0 == i2c_set_bus_num(dev->bus))
&&
(0 == i2c_probe(dev->addr)));
}
static void enable_lvds(struct display_info_t const *dev)
{
struct iomuxc *iomux = (struct iomuxc *)
IOMUXC_BASE_ADDR;
u32 reg = readl(&iomux->gpr[2]);
reg |= IOMUXC_GPR2_DATA_WIDTH_CH0_24BIT;
writel(reg, &iomux->gpr[2]);
gpio_direction_output(LVDS_BACKLIGHT_GP, 1);
}
static void enable_rgb(struct display_info_t const *dev)
{
imx_iomux_v3_setup_multiple_pads(
rgb_pads,
ARRAY_SIZE(rgb_pads));
gpio_direction_output(RGB_BACKLIGHT_GP, 1);
}
static struct display_info_t const displays[] = {{
.bus = -1,
.addr = 0,
.pixfmt = IPU_PIX_FMT_RGB24,
.detect = detect_hdmi,
.enable = enable_hdmi,
.mode = {
.name = "HDMI",
.refresh = 60,
.xres = 1024,
.yres = 768,
.pixclock = 15385,
.left_margin = 220,
.right_margin = 40,
.upper_margin = 21,
.lower_margin = 7,
.hsync_len = 60,
.vsync_len = 10,
.sync = FB_SYNC_EXT,
.vmode = FB_VMODE_NONINTERLACED
} }, {
.bus = 2,
.addr = 0x4,
.pixfmt = IPU_PIX_FMT_LVDS666,
.detect = detect_i2c,
.enable = enable_lvds,
.mode = {
.name = "Hannstar-XGA",
.refresh = 60,
.xres = 1024,
.yres = 768,
.pixclock = 15385,
.left_margin = 220,
.right_margin = 40,
.upper_margin = 21,
.lower_margin = 7,
.hsync_len = 60,
.vsync_len = 10,
.sync = FB_SYNC_EXT,
.vmode = FB_VMODE_NONINTERLACED
} }, {
.bus = 2,
.addr = 0x38,
.pixfmt = IPU_PIX_FMT_LVDS666,
.detect = detect_i2c,
.enable = enable_lvds,
.mode = {
.name = "wsvga-lvds",
.refresh = 60,
.xres = 1024,
.yres = 600,
.pixclock = 15385,
.left_margin = 220,
.right_margin = 40,
.upper_margin = 21,
.lower_margin = 7,
.hsync_len = 60,
.vsync_len = 10,
.sync = FB_SYNC_EXT,
.vmode = FB_VMODE_NONINTERLACED
} }, {
.bus = 2,
.addr = 0x48,
.pixfmt = IPU_PIX_FMT_RGB666,
.detect = detect_i2c,
.enable = enable_rgb,
.mode = {
.name = "wvga-rgb",
.refresh = 57,
.xres = 800,
.yres = 480,
.pixclock = 37037,
.left_margin = 40,
.right_margin = 60,
.upper_margin = 10,
.lower_margin = 10,
.hsync_len = 20,
.vsync_len = 10,
.sync = 0,
.vmode = FB_VMODE_NONINTERLACED
} } };
int board_video_skip(void)
{
int i;
int ret;
char const *panel = getenv("panel");
if (!panel) {
for (i = 0; i < ARRAY_SIZE(displays); i++) {
struct display_info_t const *dev = displays+i;
if (dev->detect(dev)) {
panel = dev->mode.name;
printf("auto-detected panel %s\n", panel);
break;
}
}
if (!panel) {
panel = displays[0].mode.name;
printf("No panel detected: default to %s\n", panel);
}
} else {
for (i = 0; i < ARRAY_SIZE(displays); i++) {
if (!strcmp(panel, displays[i].mode.name))
break;
}
}
if (i < ARRAY_SIZE(displays)) {
ret = ipuv3_fb_init(&displays[i].mode, 0,
displays[i].pixfmt);
if (!ret) {
displays[i].enable(displays+i);
printf("Display: %s (%ux%u)\n",
displays[i].mode.name,
displays[i].mode.xres,
displays[i].mode.yres);
} else
printf("LCD %s cannot be configured: %d\n",
displays[i].mode.name, ret);
} else {
printf("unsupported panel %s\n", panel);
ret = -EINVAL;
}
return (0 != ret);
}
static void setup_display(void)
{
struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR;
struct anatop_regs *anatop = (struct anatop_regs *)ANATOP_BASE_ADDR;
struct iomuxc *iomux = (struct iomuxc *)IOMUXC_BASE_ADDR;
int reg;
/* Turn on LDB0,IPU,IPU DI0 clocks */
reg = __raw_readl(&mxc_ccm->CCGR3);
reg |= MXC_CCM_CCGR3_IPU1_IPU_DI0_OFFSET
|MXC_CCM_CCGR3_LDB_DI0_MASK;
writel(reg, &mxc_ccm->CCGR3);
/* Turn on HDMI PHY clock */
reg = __raw_readl(&mxc_ccm->CCGR2);
reg |= MXC_CCM_CCGR2_HDMI_TX_IAHBCLK_MASK
|MXC_CCM_CCGR2_HDMI_TX_ISFRCLK_MASK;
writel(reg, &mxc_ccm->CCGR2);
/* clear HDMI PHY reset */
__raw_writeb(HDMI_MC_PHYRSTZ_DEASSERT,
HDMI_ARB_BASE_ADDR+HDMI_MC_PHYRSTZ);
/* set PFD1_FRAC to 0x13 == 455 MHz (480*18)/0x13 */
writel(ANATOP_PFD_480_PFD1_FRAC_MASK, &anatop->pfd_480_clr);
writel(0x13<<ANATOP_PFD_480_PFD1_FRAC_SHIFT, &anatop->pfd_480_set);
/* set LDB0, LDB1 clk select to 011/011 */
reg = readl(&mxc_ccm->cs2cdr);
reg &= ~(MXC_CCM_CS2CDR_LDB_DI0_CLK_SEL_MASK
|MXC_CCM_CS2CDR_LDB_DI1_CLK_SEL_MASK);
reg |= (3<<MXC_CCM_CS2CDR_LDB_DI0_CLK_SEL_OFFSET)
|(3<<MXC_CCM_CS2CDR_LDB_DI1_CLK_SEL_OFFSET);
writel(reg, &mxc_ccm->cs2cdr);
reg = readl(&mxc_ccm->cscmr2);
reg |= MXC_CCM_CSCMR2_LDB_DI0_IPU_DIV;
writel(reg, &mxc_ccm->cscmr2);
reg = readl(&mxc_ccm->chsccdr);
reg &= ~(MXC_CCM_CHSCCDR_IPU1_DI0_PRE_CLK_SEL_MASK
|MXC_CCM_CHSCCDR_IPU1_DI0_PODF_MASK
|MXC_CCM_CHSCCDR_IPU1_DI0_CLK_SEL_MASK);
reg |= (CHSCCDR_CLK_SEL_LDB_DI0
<<MXC_CCM_CHSCCDR_IPU1_DI0_CLK_SEL_OFFSET)
|(CHSCCDR_PODF_DIVIDE_BY_3
<<MXC_CCM_CHSCCDR_IPU1_DI0_PODF_OFFSET)
|(CHSCCDR_IPU_PRE_CLK_540M_PFD
<<MXC_CCM_CHSCCDR_IPU1_DI0_PRE_CLK_SEL_OFFSET);
writel(reg, &mxc_ccm->chsccdr);
reg = IOMUXC_GPR2_BGREF_RRMODE_EXTERNAL_RES
|IOMUXC_GPR2_DI1_VS_POLARITY_ACTIVE_HIGH
|IOMUXC_GPR2_DI0_VS_POLARITY_ACTIVE_LOW
|IOMUXC_GPR2_BIT_MAPPING_CH1_SPWG
|IOMUXC_GPR2_DATA_WIDTH_CH1_18BIT
|IOMUXC_GPR2_BIT_MAPPING_CH0_SPWG
|IOMUXC_GPR2_DATA_WIDTH_CH0_18BIT
|IOMUXC_GPR2_LVDS_CH1_MODE_DISABLED
|IOMUXC_GPR2_LVDS_CH0_MODE_ENABLED_DI0;
writel(reg, &iomux->gpr[2]);
reg = readl(&iomux->gpr[3]);
reg = (reg & ~IOMUXC_GPR3_LVDS0_MUX_CTL_MASK)
| (IOMUXC_GPR3_MUX_SRC_IPU1_DI0
<<IOMUXC_GPR3_LVDS0_MUX_CTL_OFFSET);
writel(reg, &iomux->gpr[3]);
/* backlights off until needed */
imx_iomux_v3_setup_multiple_pads(backlight_pads,
ARRAY_SIZE(backlight_pads));
gpio_direction_input(LVDS_BACKLIGHT_GP);
gpio_direction_input(RGB_BACKLIGHT_GP);
}
#endif
int board_early_init_f(void)
{
setup_iomux_uart();
setup_buttons();
#if defined(CONFIG_VIDEO_IPUV3)
setup_display();
#endif
return 0;
}
/*
* Do not overwrite the console
* Use always serial for U-Boot console
*/
int overwrite_console(void)
{
return 1;
}
int board_init(void)
{
/* address of boot parameters */

View file

@ -37,7 +37,7 @@
#define CONFIG_REVISION_TAG
/* Size of malloc() pool */
#define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + 2 * 1024 * 1024)
#define CONFIG_SYS_MALLOC_LEN (10 * 1024 * 1024)
#define CONFIG_BOARD_EARLY_INIT_F
#define CONFIG_MISC_INIT_R
@ -119,6 +119,19 @@
/* Miscellaneous commands */
#define CONFIG_CMD_BMODE
/* Framebuffer and LCD */
#define CONFIG_VIDEO
#define CONFIG_VIDEO_IPUV3
#define CONFIG_CFB_CONSOLE
#define CONFIG_VGA_AS_SINGLE_DEVICE
#define CONFIG_SYS_CONSOLE_IS_IN_ENV
#define CONFIG_SYS_CONSOLE_OVERWRITE_ROUTINE
#define CONFIG_VIDEO_BMP_RLE8
#define CONFIG_SPLASH_SCREEN
#define CONFIG_BMP_16BPP
#define CONFIG_VIDEO_LOGO
#define CONFIG_IPUV3_CLK 260000000
/* allow to overwrite serial and ethaddr */
#define CONFIG_ENV_OVERWRITE
#define CONFIG_CONS_INDEX 1