diff options
Diffstat (limited to 'drivers/video')
25 files changed, 795 insertions, 620 deletions
diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 58f5de5200..ac5371f2ae 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -29,7 +29,6 @@ obj-$(CONFIG_CFB_CONSOLE) += cfb_console.o obj-$(CONFIG_FSL_DIU_FB) += fsl_diu_fb.o videomodes.o obj-$(CONFIG_VIDEO_FSL_DCU_FB) += fsl_dcu_fb.o videomodes.o obj-$(CONFIG_L5F31188) += l5f31188.o -obj-$(CONFIG_MPC8XX_LCD) += mpc8xx_lcd.o obj-$(CONFIG_PXA_LCD) += pxa_lcd.o obj-$(CONFIG_SCF0403_LCD) += scf0403_lcd.o obj-$(CONFIG_S6E8AX0) += s6e8ax0.o diff --git a/drivers/video/atmel_hlcdfb.c b/drivers/video/atmel_hlcdfb.c index 59b9c45616..f77da2ec97 100644 --- a/drivers/video/atmel_hlcdfb.c +++ b/drivers/video/atmel_hlcdfb.c @@ -426,7 +426,9 @@ static void atmel_hlcdc_init(struct udevice *dev) writel(~0UL, ®s->lcdc_baseidr); /* Setup the DMA descriptor, this descriptor will loop to itself */ - desc = (struct lcd_dma_desc *)(uc_plat->base - 16); + desc = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*desc)); + if (!desc) + return; desc->address = (u32)uc_plat->base; @@ -436,7 +438,9 @@ static void atmel_hlcdc_init(struct udevice *dev) desc->next = (u32)desc; /* Flush the DMA descriptor if we enabled dcache */ - flush_dcache_range((u32)desc, (u32)desc + sizeof(*desc)); + flush_dcache_range((u32)desc, + ALIGN(((u32)desc + sizeof(*desc)), + CONFIG_SYS_CACHELINE_SIZE)); writel(desc->address, ®s->lcdc_baseaddr); writel(desc->control, ®s->lcdc_basectrl); @@ -497,15 +501,15 @@ static int atmel_hlcdc_ofdata_to_platdata(struct udevice *dev) { struct atmel_hlcdc_priv *priv = dev_get_priv(dev); const void *blob = gd->fdt_blob; - int node = dev->of_offset; + int node = dev_of_offset(dev); - priv->regs = (struct atmel_hlcd_regs *)dev_get_addr(dev); + priv->regs = (struct atmel_hlcd_regs *)devfdt_get_addr(dev); if (!priv->regs) { debug("%s: No display controller address\n", __func__); return -EINVAL; } - if (fdtdec_decode_display_timing(blob, dev->of_offset, + if (fdtdec_decode_display_timing(blob, dev_of_offset(dev), 0, &priv->timing)) { debug("%s: Failed to decode display timing\n", __func__); return -EINVAL; diff --git a/drivers/video/exynos/exynos_dp.c b/drivers/video/exynos/exynos_dp.c index c5039e7b43..092342e7ad 100644 --- a/drivers/video/exynos/exynos_dp.c +++ b/drivers/video/exynos/exynos_dp.c @@ -6,7 +6,7 @@ * SPDX-License-Identifier: GPL-2.0+ */ -#include <config.h> +#include <common.h> #include <dm.h> #include <common.h> #include <display.h> @@ -883,7 +883,7 @@ static int exynos_dp_ofdata_to_platdata(struct udevice *dev) unsigned int node = dev_of_offset(dev); fdt_addr_t addr; - addr = dev_get_addr(dev); + addr = devfdt_get_addr(dev); if (addr == FDT_ADDR_T_NONE) { debug("Can't get the DP base address\n"); return -EINVAL; diff --git a/drivers/video/exynos/exynos_fb.c b/drivers/video/exynos/exynos_fb.c index 46320e7f02..6ca17f2db2 100644 --- a/drivers/video/exynos/exynos_fb.c +++ b/drivers/video/exynos/exynos_fb.c @@ -486,7 +486,7 @@ int exynos_fb_ofdata_to_platdata(struct udevice *dev) const void *blob = gd->fdt_blob; fdt_addr_t addr; - addr = dev_get_addr(dev); + addr = devfdt_get_addr(dev); if (addr == FDT_ADDR_T_NONE) { debug("Can't get the FIMD base address\n"); return -EINVAL; diff --git a/drivers/video/mpc8xx_lcd.c b/drivers/video/mpc8xx_lcd.c deleted file mode 100644 index b08576eab2..0000000000 --- a/drivers/video/mpc8xx_lcd.c +++ /dev/null @@ -1,400 +0,0 @@ -/* - * (C) Copyright 2001-2002 - * Wolfgang Denk, DENX Software Engineering -- wd@denx.de - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -/************************************************************************/ -/* ** HEADER FILES */ -/************************************************************************/ - -/* #define DEBUG */ - -#include <config.h> -#include <common.h> -#include <command.h> -#include <watchdog.h> -#include <stdarg.h> -#include <lcdvideo.h> -#include <linux/types.h> -#include <stdio_dev.h> -#if defined(CONFIG_POST) -#include <post.h> -#endif -#include <lcd.h> - -#ifdef CONFIG_LCD - -/************************************************************************/ -/* ** CONFIG STUFF -- should be moved to board config file */ -/************************************************************************/ -#ifndef CONFIG_LCD_INFO -#define CONFIG_LCD_INFO /* Display Logo, (C) and system info */ -#endif - -/*----------------------------------------------------------------------*/ -#ifdef CONFIG_KYOCERA_KCS057QV1AJ -/* - * Kyocera KCS057QV1AJ-G23. Passive, color, single scan. - */ -#define LCD_BPP LCD_COLOR4 - -vidinfo_t panel_info = { - 640, 480, 132, 99, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, - LCD_BPP, 1, 0, 1, 0, 5, 0, 0, 0 - /* wbl, vpw, lcdac, wbf */ -}; -#endif /* CONFIG_KYOCERA_KCS057QV1AJ */ -/*----------------------------------------------------------------------*/ - -/*----------------------------------------------------------------------*/ -#ifdef CONFIG_HITACHI_SP19X001_Z1A -/* - * Hitachi SP19X001-. Active, color, single scan. - */ -vidinfo_t panel_info = { - 640, 480, 154, 116, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, - LCD_COLOR8, 1, 0, 1, 0, 0, 0, 0, 0 - /* wbl, vpw, lcdac, wbf */ -}; -#endif /* CONFIG_HITACHI_SP19X001_Z1A */ -/*----------------------------------------------------------------------*/ - -/*----------------------------------------------------------------------*/ -#ifdef CONFIG_NEC_NL6448AC33 -/* - * NEC NL6448AC33-18. Active, color, single scan. - */ -vidinfo_t panel_info = { - 640, 480, 132, 99, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, CONFIG_SYS_LOW, CONFIG_SYS_HIGH, - 3, 0, 0, 1, 1, 144, 2, 0, 33 - /* wbl, vpw, lcdac, wbf */ -}; -#endif /* CONFIG_NEC_NL6448AC33 */ -/*----------------------------------------------------------------------*/ - -#ifdef CONFIG_NEC_NL6448BC20 -/* - * NEC NL6448BC20-08. 6.5", 640x480. Active, color, single scan. - */ -vidinfo_t panel_info = { - 640, 480, 132, 99, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, CONFIG_SYS_LOW, CONFIG_SYS_HIGH, - 3, 0, 0, 1, 1, 144, 2, 0, 33 - /* wbl, vpw, lcdac, wbf */ -}; -#endif /* CONFIG_NEC_NL6448BC20 */ -/*----------------------------------------------------------------------*/ - -#ifdef CONFIG_NEC_NL6448BC33_54 -/* - * NEC NL6448BC33-54. 10.4", 640x480. Active, color, single scan. - */ -vidinfo_t panel_info = { - 640, 480, 212, 158, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, CONFIG_SYS_LOW, CONFIG_SYS_HIGH, - 3, 0, 0, 1, 1, 144, 2, 0, 33 - /* wbl, vpw, lcdac, wbf */ -}; -#endif /* CONFIG_NEC_NL6448BC33_54 */ -/*----------------------------------------------------------------------*/ - -#ifdef CONFIG_SHARP_LQ104V7DS01 -/* - * SHARP LQ104V7DS01. 6.5", 640x480. Active, color, single scan. - */ -vidinfo_t panel_info = { - 640, 480, 132, 99, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, CONFIG_SYS_LOW, CONFIG_SYS_LOW, - 3, 0, 0, 1, 1, 25, 1, 0, 33 - /* wbl, vpw, lcdac, wbf */ -}; -#endif /* CONFIG_SHARP_LQ104V7DS01 */ -/*----------------------------------------------------------------------*/ - -#ifdef CONFIG_SHARP_16x9 -/* - * Sharp 320x240. Active, color, single scan. It isn't 16x9, and I am - * not sure what it is....... - */ -vidinfo_t panel_info = { - 320, 240, 0, 0, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, - 3, 0, 0, 1, 1, 15, 4, 0, 3 -}; -#endif /* CONFIG_SHARP_16x9 */ -/*----------------------------------------------------------------------*/ - -#ifdef CONFIG_SHARP_LQ057Q3DC02 -/* - * Sharp LQ057Q3DC02 display. Active, color, single scan. - */ -#undef LCD_DF -#define LCD_DF 12 - -vidinfo_t panel_info = { - 320, 240, 0, 0, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, CONFIG_SYS_LOW, CONFIG_SYS_HIGH, - 3, 0, 0, 1, 1, 15, 4, 0, 3 - /* wbl, vpw, lcdac, wbf */ -}; -#define CONFIG_LCD_INFO_BELOW_LOGO -#endif /* CONFIG_SHARP_LQ057Q3DC02 */ -/*----------------------------------------------------------------------*/ - -#ifdef CONFIG_SHARP_LQ64D341 -/* - * Sharp LQ64D341 display, 640x480. Active, color, single scan. - */ -vidinfo_t panel_info = { - 640, 480, 0, 0, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, CONFIG_SYS_LOW, CONFIG_SYS_HIGH, - 3, 0, 0, 1, 1, 128, 16, 0, 32 - /* wbl, vpw, lcdac, wbf */ -}; -#endif /* CONFIG_SHARP_LQ64D341 */ - -#ifdef CONFIG_SHARP_LQ065T9DR51U -/* - * Sharp LQ065T9DR51U display, 400x240. Active, color, single scan. - */ -vidinfo_t panel_info = { - 400, 240, 143, 79, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, - 3, 0, 0, 1, 1, 248, 4, 0, 35 - /* wbl, vpw, lcdac, wbf */ -}; -#define CONFIG_LCD_INFO_BELOW_LOGO -#endif /* CONFIG_SHARP_LQ065T9DR51U */ - -#ifdef CONFIG_SHARP_LQ084V1DG21 -/* - * Sharp LQ084V1DG21 display, 640x480. Active, color, single scan. - */ -vidinfo_t panel_info = { - 640, 480, 171, 129, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, CONFIG_SYS_LOW, CONFIG_SYS_LOW, - 3, 0, 0, 1, 1, 160, 3, 0, 48 - /* wbl, vpw, lcdac, wbf */ -}; -#endif /* CONFIG_SHARP_LQ084V1DG21 */ - -/*----------------------------------------------------------------------*/ - -#ifdef CONFIG_HLD1045 -/* - * HLD1045 display, 640x480. Active, color, single scan. - */ -vidinfo_t panel_info = { - 640, 480, 0, 0, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, CONFIG_SYS_LOW, CONFIG_SYS_HIGH, - 3, 0, 0, 1, 1, 160, 3, 0, 48 - /* wbl, vpw, lcdac, wbf */ -}; -#endif /* CONFIG_HLD1045 */ -/*----------------------------------------------------------------------*/ - -#ifdef CONFIG_PRIMEVIEW_V16C6448AC -/* - * Prime View V16C6448AC - */ -vidinfo_t panel_info = { - 640, 480, 130, 98, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, CONFIG_SYS_LOW, CONFIG_SYS_HIGH, - 3, 0, 0, 1, 1, 144, 2, 0, 35 - /* wbl, vpw, lcdac, wbf */ -}; -#endif /* CONFIG_PRIMEVIEW_V16C6448AC */ - -/*----------------------------------------------------------------------*/ - -#ifdef CONFIG_OPTREX_BW -/* - * Optrex CBL50840-2 NF-FW 99 22 M5 - * or - * Hitachi LMG6912RPFC-00T - * or - * Hitachi SP14Q002 - * - * 320x240. Black & white. - */ -#define OPTREX_BPP 0 /* 0 - monochrome, 1 bpp */ - /* 1 - 4 grey levels, 2 bpp */ - /* 2 - 16 grey levels, 4 bpp */ -vidinfo_t panel_info = { - 320, 240, 0, 0, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, - OPTREX_BPP, 0, 0, 0, 0, 0, 0, 0, 0, 4 -}; -#endif /* CONFIG_OPTREX_BW */ - -/************************************************************************/ -/* ----------------- chipset specific functions ----------------------- */ -/************************************************************************/ - -/* - * Calculate fb size for VIDEOLFB_ATAG. - */ -ulong calc_fbsize (void) -{ - ulong size; - int line_length = (panel_info.vl_col * NBITS (panel_info.vl_bpix)) / 8; - - size = line_length * panel_info.vl_row; - - return size; -} - -void lcd_ctrl_init (void *lcdbase) -{ - volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; - volatile lcd823_t *lcdp = &immr->im_lcd; - - uint lccrtmp; - uint lchcr_hpc_tmp; - - /* Initialize the LCD control register according to the LCD - * parameters defined. We do everything here but enable - * the controller. - */ - - lccrtmp = LCDBIT (LCCR_BNUM_BIT, - (((panel_info.vl_row * panel_info.vl_col) * (1 << LCD_BPP)) / 128)); - - lccrtmp |= LCDBIT (LCCR_CLKP_BIT, panel_info.vl_clkp) | - LCDBIT (LCCR_OEP_BIT, panel_info.vl_oep) | - LCDBIT (LCCR_HSP_BIT, panel_info.vl_hsp) | - LCDBIT (LCCR_VSP_BIT, panel_info.vl_vsp) | - LCDBIT (LCCR_DP_BIT, panel_info.vl_dp) | - LCDBIT (LCCR_BPIX_BIT, panel_info.vl_bpix) | - LCDBIT (LCCR_LBW_BIT, panel_info.vl_lbw) | - LCDBIT (LCCR_SPLT_BIT, panel_info.vl_splt) | - LCDBIT (LCCR_CLOR_BIT, panel_info.vl_clor) | - LCDBIT (LCCR_TFT_BIT, panel_info.vl_tft); - -#if 0 - lccrtmp |= ((SIU_LEVEL5 / 2) << 12); - lccrtmp |= LCCR_EIEN; -#endif - - lcdp->lcd_lccr = lccrtmp; - lcdp->lcd_lcsr = 0xFF; /* Clear pending interrupts */ - - /* Initialize LCD controller bus priorities. - */ - immr->im_siu_conf.sc_sdcr &= ~0x0f; /* RAID = LAID = 0 */ - - /* set SHFT/CLOCK division factor 4 - * This needs to be set based upon display type and processor - * speed. The TFT displays run about 20 to 30 MHz. - * I was running 64 MHz processor speed. - * The value for this divider must be chosen so the result is - * an integer of the processor speed (i.e., divide by 3 with - * 64 MHz would be bad). - */ - immr->im_clkrst.car_sccr &= ~0x1F; - immr->im_clkrst.car_sccr |= LCD_DF; /* was 8 */ - - /* Enable LCD on port D. - */ - immr->im_ioport.iop_pdpar |= 0x1FFF; - immr->im_ioport.iop_pddir |= 0x1FFF; - - /* Enable LCD_A/B/C on port B. - */ - immr->im_cpm.cp_pbpar |= 0x00005001; - immr->im_cpm.cp_pbdir |= 0x00005001; - - /* Load the physical address of the linear frame buffer - * into the LCD controller. - * BIG NOTE: This has to be modified to load A and B depending - * upon the split mode of the LCD. - */ - lcdp->lcd_lcfaa = (ulong)lcdbase; - lcdp->lcd_lcfba = (ulong)lcdbase; - - /* MORE HACKS...This must be updated according to 823 manual - * for different panels. - * Udi Finkelstein - done - see below: - * Note: You better not try unsupported combinations such as - * 4-bit wide passive dual scan LCD at 4/8 Bit color. - */ - lchcr_hpc_tmp = - (panel_info.vl_col * - (panel_info.vl_tft ? 8 : - (((2 - panel_info.vl_lbw) << /* 4 bit=2, 8-bit = 1 */ - /* use << to mult by: single scan = 1, dual scan = 2 */ - panel_info.vl_splt) * - (panel_info.vl_bpix | 1)))) >> 3; /* 2/4 BPP = 1, 8/16 BPP = 3 */ - - lcdp->lcd_lchcr = LCHCR_BO | - LCDBIT (LCHCR_AT_BIT, 4) | - LCDBIT (LCHCR_HPC_BIT, lchcr_hpc_tmp) | - panel_info.vl_wbl; - - lcdp->lcd_lcvcr = LCDBIT (LCVCR_VPW_BIT, panel_info.vl_vpw) | - LCDBIT (LCVCR_LCD_AC_BIT, panel_info.vl_lcdac) | - LCDBIT (LCVCR_VPC_BIT, panel_info.vl_row) | - panel_info.vl_wbf; - -} - -/*----------------------------------------------------------------------*/ - -#if LCD_BPP == LCD_COLOR8 -void -lcd_setcolreg (ushort regno, ushort red, ushort green, ushort blue) -{ - volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; - volatile cpm8xx_t *cp = &(immr->im_cpm); - unsigned short colreg, *cmap_ptr; - - cmap_ptr = (unsigned short *)&cp->lcd_cmap[regno * 2]; - - colreg = ((red & 0x0F) << 8) | - ((green & 0x0F) << 4) | - (blue & 0x0F) ; - - *cmap_ptr = colreg; - - debug ("setcolreg: reg %2d @ %p: R=%02X G=%02X B=%02X => %02X%02X\n", - regno, &(cp->lcd_cmap[regno * 2]), - red, green, blue, - cp->lcd_cmap[ regno * 2 ], cp->lcd_cmap[(regno * 2) + 1]); -} -#endif /* LCD_COLOR8 */ - -/*----------------------------------------------------------------------*/ - -ushort *configuration_get_cmap(void) -{ - immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; - cpm8xx_t *cp = &(immr->im_cpm); - return (ushort *)&(cp->lcd_cmap[255 * sizeof(ushort)]); -} - -#if defined(CONFIG_MPC823) -void fb_put_byte(uchar **fb, uchar **from) -{ - *(*fb)++ = (255 - *(*from)++); -} -#endif - -#ifdef CONFIG_LCD_LOGO -#include <bmp_logo.h> -void lcd_logo_set_cmap(void) -{ - int i; - ushort *cmap; - immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; - cpm8xx_t *cp = &(immr->im_cpm); - cmap = (ushort *)&(cp->lcd_cmap[BMP_LOGO_OFFSET * sizeof(ushort)]); - - for (i = 0; i < BMP_LOGO_COLORS; ++i) - *cmap++ = bmp_logo_palette[i]; -} -#endif - -void lcd_enable (void) -{ - volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; - volatile lcd823_t *lcdp = &immr->im_lcd; - - /* Enable the LCD panel */ - immr->im_siu_conf.sc_sdcr |= (1 << (31 - 25)); /* LAM = 1 */ - lcdp->lcd_lccr |= LCCR_PON; -} - -/************************************************************************/ - -#endif /* CONFIG_LCD */ diff --git a/drivers/video/rockchip/Kconfig b/drivers/video/rockchip/Kconfig index 80e399f7d7..b1d7c62fca 100644 --- a/drivers/video/rockchip/Kconfig +++ b/drivers/video/rockchip/Kconfig @@ -12,11 +12,30 @@ menuconfig VIDEO_ROCKCHIP bool "Enable Rockchip Video Support" depends on DM_VIDEO help - Rockchip SoCs provide video output capabilities for High-Definition - Multimedia Interface (HDMI), Low-voltage Differential Signalling - (LVDS), embedded DisplayPort (eDP) and Display Serial Interface - (DSI). This driver supports the on-chip video output device, and - targets the Rockchip RK3288 and RK3399. + Rockchip SoCs provide video output capabilities for High-Definition + Multimedia Interface (HDMI), Low-voltage Differential Signalling + (LVDS), embedded DisplayPort (eDP) and Display Serial Interface (DSI). + + This driver supports the on-chip video output device, and targets the + Rockchip RK3288 and RK3399. + +config VIDEO_ROCKCHIP_MAX_XRES + int "Maximum horizontal resolution (for memory allocation purposes)" + depends on VIDEO_ROCKCHIP + default 1920 + help + The maximum horizontal resolution to support for the framebuffer. + This configuration is used for reserving/allocating memory for the + framebuffer during device-model binding/probing. + +config VIDEO_ROCKCHIP_MAX_YRES + int "Maximum vertical resolution (for memory allocation purposes)" + depends on VIDEO_ROCKCHIP + default 1080 + help + The maximum vertical resolution to support for the framebuffer. + This configuration is used for reserving/allocating memory for the + framebuffer during device-model binding/probing. if VIDEO_ROCKCHIP diff --git a/drivers/video/rockchip/Makefile b/drivers/video/rockchip/Makefile index cd54b12a4e..872dc0f653 100644 --- a/drivers/video/rockchip/Makefile +++ b/drivers/video/rockchip/Makefile @@ -7,8 +7,12 @@ ifdef CONFIG_VIDEO_ROCKCHIP obj-y += rk_vop.o +obj-$(CONFIG_ROCKCHIP_RK3288) += rk3288_vop.o +obj-$(CONFIG_ROCKCHIP_RK3399) += rk3399_vop.o obj-$(CONFIG_DISPLAY_ROCKCHIP_EDP) += rk_edp.o obj-$(CONFIG_DISPLAY_ROCKCHIP_LVDS) += rk_lvds.o -obj-$(CONFIG_DISPLAY_ROCKCHIP_HDMI) += rk_hdmi.o +obj-hdmi-$(CONFIG_ROCKCHIP_RK3288) += rk3288_hdmi.o +obj-hdmi-$(CONFIG_ROCKCHIP_RK3399) += rk3399_hdmi.o +obj-$(CONFIG_DISPLAY_ROCKCHIP_HDMI) += rk_hdmi.o $(obj-hdmi-y) obj-$(CONFIG_DISPLAY_ROCKCHIP_MIPI) += rk_mipi.o endif diff --git a/drivers/video/rockchip/rk3288_hdmi.c b/drivers/video/rockchip/rk3288_hdmi.c new file mode 100644 index 0000000000..eae0dd2abf --- /dev/null +++ b/drivers/video/rockchip/rk3288_hdmi.c @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2017 Theobroma Systems Design und Consulting GmbH + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk.h> +#include <display.h> +#include <dm.h> +#include <dw_hdmi.h> +#include <edid.h> +#include <regmap.h> +#include <syscon.h> +#include <asm/gpio.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/hardware.h> +#include <asm/arch/grf_rk3288.h> +#include <power/regulator.h> +#include "rk_hdmi.h" + +static int rk3288_hdmi_enable(struct udevice *dev, int panel_bpp, + const struct display_timing *edid) +{ + struct rk_hdmi_priv *priv = dev_get_priv(dev); + struct display_plat *uc_plat = dev_get_uclass_platdata(dev); + int vop_id = uc_plat->source_id; + struct rk3288_grf *grf = priv->grf; + + /* hdmi source select hdmi controller */ + rk_setreg(&grf->soc_con6, 1 << 15); + + /* hdmi data from vop id */ + rk_clrsetreg(&grf->soc_con6, 1 << 4, (vop_id == 1) ? (1 << 4) : 0); + + return 0; +} + +static int rk3288_hdmi_ofdata_to_platdata(struct udevice *dev) +{ + struct rk_hdmi_priv *priv = dev_get_priv(dev); + struct dw_hdmi *hdmi = &priv->hdmi; + + hdmi->i2c_clk_high = 0x7a; + hdmi->i2c_clk_low = 0x8d; + + /* + * TODO(sjg@chromium.org): The above values don't work - these + * ones work better, but generate lots of errors in the data. + */ + hdmi->i2c_clk_high = 0x0d; + hdmi->i2c_clk_low = 0x0d; + + return rk_hdmi_ofdata_to_platdata(dev); +} + +static int rk3288_clk_config(struct udevice *dev) +{ + struct display_plat *uc_plat = dev_get_uclass_platdata(dev); + struct clk clk; + int ret; + + /* + * Configure the maximum clock to permit whatever resolution the + * monitor wants + */ + ret = clk_get_by_index(uc_plat->src_dev, 0, &clk); + if (ret >= 0) { + ret = clk_set_rate(&clk, 384000000); + clk_free(&clk); + } + if (ret < 0) { + debug("%s: Failed to set clock in source device '%s': ret=%d\n", + __func__, uc_plat->src_dev->name, ret); + return ret; + } + + return 0; +} + +static const char * const rk3288_regulator_names[] = { + "vcc50_hdmi" +}; + +static int rk3288_hdmi_probe(struct udevice *dev) +{ + /* Enable VOP clock for RK3288 */ + rk3288_clk_config(dev); + + /* Enable regulators required for HDMI */ + rk_hdmi_probe_regulators(dev, rk3288_regulator_names, + ARRAY_SIZE(rk3288_regulator_names)); + + return rk_hdmi_probe(dev); +} + +static const struct dm_display_ops rk3288_hdmi_ops = { + .read_edid = rk_hdmi_read_edid, + .enable = rk3288_hdmi_enable, +}; + +static const struct udevice_id rk3288_hdmi_ids[] = { + { .compatible = "rockchip,rk3288-dw-hdmi" }, + { } +}; + +U_BOOT_DRIVER(rk3288_hdmi_rockchip) = { + .name = "rk3288_hdmi_rockchip", + .id = UCLASS_DISPLAY, + .of_match = rk3288_hdmi_ids, + .ops = &rk3288_hdmi_ops, + .ofdata_to_platdata = rk3288_hdmi_ofdata_to_platdata, + .probe = rk3288_hdmi_probe, + .priv_auto_alloc_size = sizeof(struct rk_hdmi_priv), +}; diff --git a/drivers/video/rockchip/rk3288_vop.c b/drivers/video/rockchip/rk3288_vop.c new file mode 100644 index 0000000000..3a5740a6cb --- /dev/null +++ b/drivers/video/rockchip/rk3288_vop.c @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2017 Theobroma Systems Design und Consulting GmbH + * Copyright (c) 2015 Google, Inc + * Copyright 2014 Rockchip Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <display.h> +#include <dm.h> +#include <regmap.h> +#include <syscon.h> +#include <video.h> +#include <asm/hardware.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/grf_rk3288.h> +#include "rk_vop.h" + +DECLARE_GLOBAL_DATA_PTR; + +static void rk3288_set_pin_polarity(struct udevice *dev, + enum vop_modes mode, u32 polarity) +{ + struct rk_vop_priv *priv = dev_get_priv(dev); + struct rk3288_vop *regs = priv->regs; + + /* The RK3328 VOP (v3.1) has its polarity configuration in ctrl0 */ + clrsetbits_le32(®s->dsp_ctrl0, + M_DSP_DCLK_POL | M_DSP_DEN_POL | + M_DSP_VSYNC_POL | M_DSP_HSYNC_POL, + V_DSP_PIN_POL(polarity)); +} + +static void rk3288_set_io_vsel(struct udevice *dev) +{ + struct rk3288_grf *grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); + + /* lcdc(vop) iodomain select 1.8V */ + rk_setreg(&grf->io_vsel, 1 << 0); +} + +/* + * Try some common regulators. We should really get these from the + * device tree somehow. + */ +static const char * const rk3288_regulator_names[] = { + "vcc18_lcd", + "VCC18_LCD", + "vdd10_lcd_pwren_h", + "vdd10_lcd", + "VDD10_LCD", + "vcc33_lcd" +}; + +static int rk3288_vop_probe(struct udevice *dev) +{ + /* Before relocation we don't need to do anything */ + if (!(gd->flags & GD_FLG_RELOC)) + return 0; + + /* Set the LCDC(vop) iodomain to 1.8V */ + rk3288_set_io_vsel(dev); + + /* Probe regulators required for the RK3288 VOP */ + rk_vop_probe_regulators(dev, rk3288_regulator_names, + ARRAY_SIZE(rk3288_regulator_names)); + + return rk_vop_probe(dev); +} + +static int rk_vop_remove(struct udevice *dev) +{ + struct rk_vop_priv *priv = dev_get_priv(dev); + struct rk3288_vop *regs = priv->regs; + + setbits_le32(®s->sys_ctrl, V_STANDBY_EN(1)); + + /* wait frame complete (60Hz) to enter standby */ + mdelay(17); + + return 0; +} + +struct rkvop_driverdata rk3288_driverdata = { + .features = VOP_FEATURE_OUTPUT_10BIT, + .set_pin_polarity = rk3288_set_pin_polarity, +}; + +static const struct udevice_id rk3288_vop_ids[] = { + { .compatible = "rockchip,rk3288-vop", + .data = (ulong)&rk3288_driverdata }, + { } +}; + +static const struct video_ops rk3288_vop_ops = { +}; + +U_BOOT_DRIVER(rk_vop) = { + .name = "rk3288_vop", + .id = UCLASS_VIDEO, + .of_match = rk3288_vop_ids, + .ops = &rk3288_vop_ops, + .bind = rk_vop_bind, + .probe = rk3288_vop_probe, + .remove = rk_vop_remove, + .priv_auto_alloc_size = sizeof(struct rk_vop_priv), +}; diff --git a/drivers/video/rockchip/rk3399_hdmi.c b/drivers/video/rockchip/rk3399_hdmi.c new file mode 100644 index 0000000000..b1e50974f6 --- /dev/null +++ b/drivers/video/rockchip/rk3399_hdmi.c @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2017 Theobroma Systems Design und Consulting GmbH + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk.h> +#include <display.h> +#include <dm.h> +#include <dw_hdmi.h> +#include <edid.h> +#include <regmap.h> +#include <syscon.h> +#include <asm/gpio.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/hardware.h> +#include <asm/arch/grf_rk3399.h> +#include <power/regulator.h> +#include "rk_hdmi.h" + +static int rk3399_hdmi_enable(struct udevice *dev, int panel_bpp, + const struct display_timing *edid) +{ + struct rk_hdmi_priv *priv = dev_get_priv(dev); + struct display_plat *uc_plat = dev_get_uclass_platdata(dev); + int vop_id = uc_plat->source_id; + struct rk3399_grf_regs *grf = priv->grf; + + /* select the hdmi encoder input data from our source_id */ + rk_clrsetreg(&grf->soc_con20, GRF_RK3399_HDMI_VOP_SEL_MASK, + (vop_id == 1) ? GRF_RK3399_HDMI_VOP_SEL_L : 0); + + return dw_hdmi_enable(&priv->hdmi, edid); +} + +static int rk3399_hdmi_ofdata_to_platdata(struct udevice *dev) +{ + struct rk_hdmi_priv *priv = dev_get_priv(dev); + struct dw_hdmi *hdmi = &priv->hdmi; + + hdmi->i2c_clk_high = 0x7a; + hdmi->i2c_clk_low = 0x8d; + + return rk_hdmi_ofdata_to_platdata(dev); +} + +static const char * const rk3399_regulator_names[] = { + "vcc1v8_hdmi", + "vcc0v9_hdmi" +}; + +static int rk3399_hdmi_probe(struct udevice *dev) +{ + /* Enable regulators required for HDMI */ + rk_hdmi_probe_regulators(dev, rk3399_regulator_names, + ARRAY_SIZE(rk3399_regulator_names)); + + return rk_hdmi_probe(dev); +} + +static const struct dm_display_ops rk3399_hdmi_ops = { + .read_edid = rk_hdmi_read_edid, + .enable = rk3399_hdmi_enable, +}; + +static const struct udevice_id rk3399_hdmi_ids[] = { + { .compatible = "rockchip,rk3399-dw-hdmi" }, + { } +}; + +U_BOOT_DRIVER(rk3399_hdmi_rockchip) = { + .name = "rk3399_hdmi_rockchip", + .id = UCLASS_DISPLAY, + .of_match = rk3399_hdmi_ids, + .ops = &rk3399_hdmi_ops, + .ofdata_to_platdata = rk3399_hdmi_ofdata_to_platdata, + .probe = rk3399_hdmi_probe, + .priv_auto_alloc_size = sizeof(struct rk_hdmi_priv), +}; diff --git a/drivers/video/rockchip/rk3399_vop.c b/drivers/video/rockchip/rk3399_vop.c new file mode 100644 index 0000000000..91a40ab9fe --- /dev/null +++ b/drivers/video/rockchip/rk3399_vop.c @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2017 Theobroma Systems Design und Consulting GmbH + * Copyright (c) 2015 Google, Inc + * Copyright 2014 Rockchip Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <display.h> +#include <dm.h> +#include <regmap.h> +#include <video.h> +#include <asm/hardware.h> +#include <asm/io.h> +#include "rk_vop.h" + +DECLARE_GLOBAL_DATA_PTR; + +static void rk3399_set_pin_polarity(struct udevice *dev, + enum vop_modes mode, u32 polarity) +{ + struct rk_vop_priv *priv = dev_get_priv(dev); + struct rk3288_vop *regs = priv->regs; + + /* + * The RK3399 VOPs (v3.5 and v3.6) require a per-mode setting of + * the polarity configuration (in ctrl1). + */ + switch (mode) { + case VOP_MODE_HDMI: + clrsetbits_le32(®s->dsp_ctrl1, + M_RK3399_DSP_HDMI_POL, + V_RK3399_DSP_HDMI_POL(polarity)); + break; + + case VOP_MODE_EDP: + clrsetbits_le32(®s->dsp_ctrl1, + M_RK3399_DSP_EDP_POL, + V_RK3399_DSP_EDP_POL(polarity)); + break; + + case VOP_MODE_MIPI: + clrsetbits_le32(®s->dsp_ctrl1, + M_RK3399_DSP_MIPI_POL, + V_RK3399_DSP_MIPI_POL(polarity)); + break; + + case VOP_MODE_LVDS: + /* The RK3399 has neither parallel RGB nor LVDS output. */ + default: + debug("%s: unsupported output mode %x\n", __func__, mode); + } +} + +/* + * Try some common regulators. We should really get these from the + * device tree somehow. + */ +static const char * const rk3399_regulator_names[] = { + "vcc33_lcd" +}; + +static int rk3399_vop_probe(struct udevice *dev) +{ + /* Before relocation we don't need to do anything */ + if (!(gd->flags & GD_FLG_RELOC)) + return 0; + + /* Probe regulators required for the RK3399 VOP */ + rk_vop_probe_regulators(dev, rk3399_regulator_names, + ARRAY_SIZE(rk3399_regulator_names)); + + return rk_vop_probe(dev); +} + +struct rkvop_driverdata rk3399_lit_driverdata = { + .set_pin_polarity = rk3399_set_pin_polarity, +}; + +struct rkvop_driverdata rk3399_big_driverdata = { + .features = VOP_FEATURE_OUTPUT_10BIT, + .set_pin_polarity = rk3399_set_pin_polarity, +}; + +static const struct udevice_id rk3399_vop_ids[] = { + { .compatible = "rockchip,rk3399-vop-big", + .data = (ulong)&rk3399_big_driverdata }, + { .compatible = "rockchip,rk3399-vop-lit", + .data = (ulong)&rk3399_lit_driverdata }, + { } +}; + +static const struct video_ops rk3399_vop_ops = { +}; + +U_BOOT_DRIVER(rk3399_vop) = { + .name = "rk3399_vop", + .id = UCLASS_VIDEO, + .of_match = rk3399_vop_ids, + .ops = &rk3399_vop_ops, + .bind = rk_vop_bind, + .probe = rk3399_vop_probe, + .priv_auto_alloc_size = sizeof(struct rk_vop_priv), +}; diff --git a/drivers/video/rockchip/rk_edp.c b/drivers/video/rockchip/rk_edp.c index 7ece038c8f..1527f96eca 100644 --- a/drivers/video/rockchip/rk_edp.c +++ b/drivers/video/rockchip/rk_edp.c @@ -998,13 +998,26 @@ static int rk_edp_ofdata_to_platdata(struct udevice *dev) { struct rk_edp_priv *priv = dev_get_priv(dev); - priv->regs = (struct rk3288_edp *)dev_get_addr(dev); + priv->regs = (struct rk3288_edp *)devfdt_get_addr(dev); priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); return 0; } -int rk_edp_probe(struct udevice *dev) +static int rk_edp_remove(struct udevice *dev) +{ + struct rk_edp_priv *priv = dev_get_priv(dev); + struct rk3288_edp *regs = priv->regs; + + setbits_le32(®s->video_ctl_1, VIDEO_MUTE); + clrbits_le32(®s->video_ctl_1, VIDEO_EN); + clrbits_le32(®s->sys_ctl_3, F_HPD | HPD_CTRL); + setbits_le32(®s->func_en_1, SW_FUNC_EN_N); + + return 0; +} + +static int rk_edp_probe(struct udevice *dev) { struct display_plat *uc_plat = dev_get_uclass_platdata(dev); struct rk_edp_priv *priv = dev_get_priv(dev); @@ -1080,5 +1093,6 @@ U_BOOT_DRIVER(dp_rockchip) = { .ops = &dp_rockchip_ops, .ofdata_to_platdata = rk_edp_ofdata_to_platdata, .probe = rk_edp_probe, + .remove = rk_edp_remove, .priv_auto_alloc_size = sizeof(struct rk_edp_priv), }; diff --git a/drivers/video/rockchip/rk_hdmi.c b/drivers/video/rockchip/rk_hdmi.c index db07588302..a9c8fba7e4 100644 --- a/drivers/video/rockchip/rk_hdmi.c +++ b/drivers/video/rockchip/rk_hdmi.c @@ -1,4 +1,5 @@ /* + * Copyright (c) 2017 Theobroma Systems Design und Consulting GmbH * Copyright (c) 2015 Google, Inc * Copyright 2014 Rockchip Inc. * @@ -14,15 +15,12 @@ #include <regmap.h> #include <syscon.h> #include <asm/gpio.h> +#include <asm/hardware.h> #include <asm/io.h> #include <asm/arch/clock.h> -#include <asm/arch/grf_rk3288.h> -#include <power/regulator.h> - -struct rk_hdmi_priv { - struct dw_hdmi hdmi; - struct rk3288_grf *grf; -}; +#include <asm/arch/hardware.h> +#include "rk_hdmi.h" +#include "rk_vop.h" /* for rk_vop_probe_regulators */ static const struct hdmi_phy_config rockchip_phy_config[] = { { @@ -35,6 +33,9 @@ static const struct hdmi_phy_config rockchip_phy_config[] = { .mpixelclock = 297000000, .sym_ctr = 0x8039, .term = 0x0005, .vlev_ctr = 0x028d, }, { + .mpixelclock = 584000000, + .sym_ctr = 0x8039, .term = 0x0000, .vlev_ctr = 0x019d, + }, { .mpixelclock = ~0ul, .sym_ctr = 0x0000, .term = 0x0000, .vlev_ctr = 0x0000, } @@ -60,43 +61,35 @@ static const struct hdmi_mpll_config rockchip_mpll_cfg[] = { .mpixelclock = 148500000, .cpce = 0x0051, .gmp = 0x0003, .curr = 0x0000, }, { + .mpixelclock = 272000000, + .cpce = 0x0040, .gmp = 0x0003, .curr = 0x0000, + }, { + .mpixelclock = 340000000, + .cpce = 0x0040, .gmp = 0x0003, .curr = 0x0000, + }, { .mpixelclock = ~0ul, .cpce = 0x0051, .gmp = 0x0003, .curr = 0x0000, } }; -static int rk_hdmi_read_edid(struct udevice *dev, u8 *buf, int buf_size) +int rk_hdmi_read_edid(struct udevice *dev, u8 *buf, int buf_size) { struct rk_hdmi_priv *priv = dev_get_priv(dev); return dw_hdmi_read_edid(&priv->hdmi, buf, buf_size); } -static int rk_hdmi_enable(struct udevice *dev, int panel_bpp, - const struct display_timing *edid) -{ - struct rk_hdmi_priv *priv = dev_get_priv(dev); - - return dw_hdmi_enable(&priv->hdmi, edid); -} - -static int rk_hdmi_ofdata_to_platdata(struct udevice *dev) +int rk_hdmi_ofdata_to_platdata(struct udevice *dev) { struct rk_hdmi_priv *priv = dev_get_priv(dev); struct dw_hdmi *hdmi = &priv->hdmi; - hdmi->ioaddr = (ulong)dev_get_addr(dev); + hdmi->ioaddr = (ulong)devfdt_get_addr(dev); hdmi->mpll_cfg = rockchip_mpll_cfg; hdmi->phy_cfg = rockchip_phy_config; - hdmi->i2c_clk_high = 0x7a; - hdmi->i2c_clk_low = 0x8d; - - /* - * TODO(sjg@chromium.org): The above values don't work - these ones - * work better, but generate lots of errors in the data. - */ - hdmi->i2c_clk_high = 0x0d; - hdmi->i2c_clk_low = 0x0d; + + /* hdmi->i2c_clk_{high,low} are set up by the SoC driver */ + hdmi->reg_io_width = 4; hdmi->phy_set = dw_hdmi_phy_cfg; @@ -105,53 +98,17 @@ static int rk_hdmi_ofdata_to_platdata(struct udevice *dev) return 0; } -static int rk_hdmi_probe(struct udevice *dev) +void rk_hdmi_probe_regulators(struct udevice *dev, + const char * const *names, int cnt) +{ + rk_vop_probe_regulators(dev, names, cnt); +} + +int rk_hdmi_probe(struct udevice *dev) { - struct display_plat *uc_plat = dev_get_uclass_platdata(dev); struct rk_hdmi_priv *priv = dev_get_priv(dev); struct dw_hdmi *hdmi = &priv->hdmi; - struct udevice *reg; - struct clk clk; int ret; - int vop_id = uc_plat->source_id; - - ret = clk_get_by_index(dev, 0, &clk); - if (ret >= 0) { - ret = clk_set_rate(&clk, 0); - clk_free(&clk); - } - if (ret) { - debug("%s: Failed to set hdmi clock: ret=%d\n", __func__, ret); - return ret; - } - - /* - * Configure the maximum clock to permit whatever resolution the - * monitor wants - */ - ret = clk_get_by_index(uc_plat->src_dev, 0, &clk); - if (ret >= 0) { - ret = clk_set_rate(&clk, 384000000); - clk_free(&clk); - } - if (ret < 0) { - debug("%s: Failed to set clock in source device '%s': ret=%d\n", - __func__, uc_plat->src_dev->name, ret); - return ret; - } - - ret = regulator_get_by_platname("vcc50_hdmi", ®); - if (!ret) - ret = regulator_set_enable(reg, true); - if (ret) - debug("%s: Cannot set regulator vcc50_hdmi\n", __func__); - - /* hdmi source select hdmi controller */ - rk_setreg(&priv->grf->soc_con6, 1 << 15); - - /* hdmi data from vop id */ - rk_clrsetreg(&priv->grf->soc_con6, 1 << 4, - (vop_id == 1) ? (1 << 4) : 0); ret = dw_hdmi_phy_wait_for_hpd(hdmi); if (ret < 0) { @@ -164,23 +121,3 @@ static int rk_hdmi_probe(struct udevice *dev) return 0; } - -static const struct dm_display_ops rk_hdmi_ops = { - .read_edid = rk_hdmi_read_edid, - .enable = rk_hdmi_enable, -}; - -static const struct udevice_id rk_hdmi_ids[] = { - { .compatible = "rockchip,rk3288-dw-hdmi" }, - { } -}; - -U_BOOT_DRIVER(hdmi_rockchip) = { - .name = "hdmi_rockchip", - .id = UCLASS_DISPLAY, - .of_match = rk_hdmi_ids, - .ops = &rk_hdmi_ops, - .ofdata_to_platdata = rk_hdmi_ofdata_to_platdata, - .probe = rk_hdmi_probe, - .priv_auto_alloc_size = sizeof(struct rk_hdmi_priv), -}; diff --git a/drivers/video/rockchip/rk_hdmi.h b/drivers/video/rockchip/rk_hdmi.h new file mode 100644 index 0000000000..ec396682f1 --- /dev/null +++ b/drivers/video/rockchip/rk_hdmi.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2017 Theobroma Systems Design und Consulting GmbH + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __RK_HDMI_H__ +#define __RK_HDMI_H__ + +struct rkhdmi_driverdata { + /* configuration */ + u8 i2c_clk_high; + u8 i2c_clk_low; + const char * const *regulator_names; + u32 regulator_names_cnt; + /* setters/getters */ + int (*set_input_vop)(struct udevice *dev); + int (*clk_config)(struct udevice *dev); +}; + +struct rk_hdmi_priv { + struct dw_hdmi hdmi; + void *grf; +}; + +/** + * rk_hdmi_read_edid() - read the attached HDMI/DVI monitor's EDID + * + * N.B.: The buffer should be large enough to hold 2 EDID blocks, as + * this function calls dw_hdmi_read_edid, which ignores buf_size + * argument and assumes that there's always enough space for 2 + * EDID blocks. + * + * @dev: device + * @buf: output buffer for the EDID + * @buf_size: number of bytes in the buffer + * @return number of bytes read if OK, -ve if something went wrong + */ +int rk_hdmi_read_edid(struct udevice *dev, u8 *buf, int buf_size); + +/** + * rk_hdmi_probe_regulators() - probe (autoset + enable) regulators + * + * Probes a list of regulators by performing autoset and enable + * operations on them. The list of regulators is an array of string + * pointers and any individual regulator-probe may fail without + * counting as an error. + * + * @dev: device + * @names: array of string-pointers to regulator names to probe + * @cnt: number of elements in the 'names' array + */ +void rk_hdmi_probe_regulators(struct udevice *dev, + const char * const *names, int cnt); +/** + * rk_hdmi_ofdata_to_platdata() - common ofdata_to_platdata implementation + * + * @dev: device + * @return 0 if OK, -ve if something went wrong + */ +int rk_hdmi_ofdata_to_platdata(struct udevice *dev); + +/** + * rk_hdmi_probe() - common probe implementation + * + * Performs the following, common initialisation steps: + * 1. checks for HPD (i.e. a HDMI monitor being attached) + * 2. initialises the Designware HDMI core + * 3. initialises the Designware HDMI PHY + * + * @dev: device + * @return 0 if OK, -ve if something went wrong + */ +int rk_hdmi_probe(struct udevice *dev); + +#endif diff --git a/drivers/video/rockchip/rk_lvds.c b/drivers/video/rockchip/rk_lvds.c index ee43255753..66418ddfad 100644 --- a/drivers/video/rockchip/rk_lvds.c +++ b/drivers/video/rockchip/rk_lvds.c @@ -176,7 +176,7 @@ static int rk_lvds_ofdata_to_platdata(struct udevice *dev) const void *blob = gd->fdt_blob; int node = dev_of_offset(dev); int ret; - priv->regs = (void *)dev_get_addr(dev); + priv->regs = (void *)devfdt_get_addr(dev); priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); ret = fdtdec_get_int(blob, node, "rockchip,output", -1); diff --git a/drivers/video/rockchip/rk_mipi.c b/drivers/video/rockchip/rk_mipi.c index 4d9d12e1d7..ad003970d6 100644 --- a/drivers/video/rockchip/rk_mipi.c +++ b/drivers/video/rockchip/rk_mipi.c @@ -441,7 +441,7 @@ static int rk_mipi_ofdata_to_platdata(struct udevice *dev) __func__, (u64)priv->grf); return -ENXIO; } - priv->regs = (void *)dev_get_addr(dev); + priv->regs = (void *)devfdt_get_addr(dev); if (priv->regs <= 0) { debug("%s: Get MIPI dsi address failed (ret=%llu)\n", __func__, (u64)priv->regs); diff --git a/drivers/video/rockchip/rk_vop.c b/drivers/video/rockchip/rk_vop.c index aa6ca8c859..c979049b5b 100644 --- a/drivers/video/rockchip/rk_vop.c +++ b/drivers/video/rockchip/rk_vop.c @@ -17,24 +17,25 @@ #include <asm/hardware.h> #include <asm/io.h> #include <asm/arch/clock.h> -#include <asm/arch/cru_rk3288.h> -#include <asm/arch/grf_rk3288.h> #include <asm/arch/edp_rk3288.h> #include <asm/arch/vop_rk3288.h> #include <dm/device-internal.h> #include <dm/uclass-internal.h> -#include <dt-bindings/clock/rk3288-cru.h> #include <power/regulator.h> +#include "rk_vop.h" DECLARE_GLOBAL_DATA_PTR; -struct rk_vop_priv { - struct rk3288_vop *regs; - struct rk3288_grf *grf; +enum vop_pol { + HSYNC_POSITIVE = 0, + VSYNC_POSITIVE = 1, + DEN_NEGATIVE = 2, + DCLK_INVERT = 3 }; -void rkvop_enable(struct rk3288_vop *regs, ulong fbbase, - int fb_bits_per_pixel, const struct display_timing *edid) +static void rkvop_enable(struct rk3288_vop *regs, ulong fbbase, + int fb_bits_per_pixel, + const struct display_timing *edid) { u32 lb_mode; u32 rgb_mode; @@ -89,54 +90,86 @@ void rkvop_enable(struct rk3288_vop *regs, ulong fbbase, writel(0x01, ®s->reg_cfg_done); /* enable reg config */ } -void rkvop_mode_set(struct rk3288_vop *regs, - const struct display_timing *edid, enum vop_modes mode) +static void rkvop_set_pin_polarity(struct udevice *dev, + enum vop_modes mode, u32 polarity) { - u32 hactive = edid->hactive.typ; - u32 vactive = edid->vactive.typ; - u32 hsync_len = edid->hsync_len.typ; - u32 hback_porch = edid->hback_porch.typ; - u32 vsync_len = edid->vsync_len.typ; - u32 vback_porch = edid->vback_porch.typ; - u32 hfront_porch = edid->hfront_porch.typ; - u32 vfront_porch = edid->vfront_porch.typ; - uint flags; - int mode_flags; + struct rkvop_driverdata *ops = + (struct rkvop_driverdata *)dev_get_driver_data(dev); + + if (ops->set_pin_polarity) + ops->set_pin_polarity(dev, mode, polarity); +} + +static void rkvop_enable_output(struct udevice *dev, enum vop_modes mode) +{ + struct rk_vop_priv *priv = dev_get_priv(dev); + struct rk3288_vop *regs = priv->regs; + + /* remove from standby */ + clrbits_le32(®s->sys_ctrl, V_STANDBY_EN(1)); switch (mode) { case VOP_MODE_HDMI: clrsetbits_le32(®s->sys_ctrl, M_ALL_OUT_EN, V_HDMI_OUT_EN(1)); break; + case VOP_MODE_EDP: - default: clrsetbits_le32(®s->sys_ctrl, M_ALL_OUT_EN, V_EDP_OUT_EN(1)); break; + case VOP_MODE_LVDS: clrsetbits_le32(®s->sys_ctrl, M_ALL_OUT_EN, V_RGB_OUT_EN(1)); break; + case VOP_MODE_MIPI: clrsetbits_le32(®s->sys_ctrl, M_ALL_OUT_EN, V_MIPI_OUT_EN(1)); - break; + break; + + default: + debug("%s: unsupported output mode %x\n", __func__, mode); } +} - if (mode == VOP_MODE_HDMI || mode == VOP_MODE_EDP) - /* RGBaaa */ - mode_flags = 15; - else - /* RGB888 */ - mode_flags = 0; +static void rkvop_mode_set(struct udevice *dev, + const struct display_timing *edid, + enum vop_modes mode) +{ + struct rk_vop_priv *priv = dev_get_priv(dev); + struct rk3288_vop *regs = priv->regs; + struct rkvop_driverdata *data = + (struct rkvop_driverdata *)dev_get_driver_data(dev); + + u32 hactive = edid->hactive.typ; + u32 vactive = edid->vactive.typ; + u32 hsync_len = edid->hsync_len.typ; + u32 hback_porch = edid->hback_porch.typ; + u32 vsync_len = edid->vsync_len.typ; + u32 vback_porch = edid->vback_porch.typ; + u32 hfront_porch = edid->hfront_porch.typ; + u32 vfront_porch = edid->vfront_porch.typ; + int mode_flags; + u32 pin_polarity; + + pin_polarity = BIT(DCLK_INVERT); + if (edid->flags & DISPLAY_FLAGS_HSYNC_HIGH) + pin_polarity |= BIT(HSYNC_POSITIVE); + if (edid->flags & DISPLAY_FLAGS_VSYNC_HIGH) + pin_polarity |= BIT(VSYNC_POSITIVE); + + rkvop_set_pin_polarity(dev, mode, pin_polarity); + rkvop_enable_output(dev, mode); - flags = V_DSP_OUT_MODE(mode_flags) | - V_DSP_HSYNC_POL(!!(edid->flags & DISPLAY_FLAGS_HSYNC_HIGH)) | - V_DSP_VSYNC_POL(!!(edid->flags & DISPLAY_FLAGS_VSYNC_HIGH)); + mode_flags = 0; /* RGB888 */ + if ((data->features & VOP_FEATURE_OUTPUT_10BIT) && + (mode == VOP_MODE_HDMI || mode == VOP_MODE_EDP)) + mode_flags = 15; /* RGBaaa */ - clrsetbits_le32(®s->dsp_ctrl0, - M_DSP_OUT_MODE | M_DSP_VSYNC_POL | M_DSP_HSYNC_POL, - flags); + clrsetbits_le32(®s->dsp_ctrl0, M_DSP_OUT_MODE, + V_DSP_OUT_MODE(mode_flags)); writel(V_HSYNC(hsync_len) | V_HORPRD(hsync_len + hback_porch + hactive + hfront_porch), @@ -185,7 +218,7 @@ void rkvop_mode_set(struct rk3288_vop *regs, * node within the VOP's 'port' list. * @return 0 if OK, -ve if something went wrong */ -int rk_display_init(struct udevice *dev, ulong fbbase, int ep_node) +static int rk_display_init(struct udevice *dev, ulong fbbase, int ep_node) { struct video_priv *uc_priv = dev_get_uclass_priv(dev); const void *blob = gd->fdt_blob; @@ -255,18 +288,18 @@ int rk_display_init(struct udevice *dev, ulong fbbase, int ep_node) /* Set bitwidth for vop display according to vop mode */ switch (vop_id) { case VOP_MODE_EDP: - case VOP_MODE_HDMI: case VOP_MODE_LVDS: l2bpp = VIDEO_BPP16; break; + case VOP_MODE_HDMI: case VOP_MODE_MIPI: l2bpp = VIDEO_BPP32; break; default: l2bpp = VIDEO_BPP16; } - rkvop_mode_set(regs, &timing, vop_id); + rkvop_mode_set(dev, &timing, vop_id); rkvop_enable(regs, fbbase, 1 << l2bpp, &timing); ret = display_enable(disp, 1 << l2bpp, &timing); @@ -281,52 +314,36 @@ int rk_display_init(struct udevice *dev, ulong fbbase, int ep_node) return 0; } -static int rk_vop_probe(struct udevice *dev) +void rk_vop_probe_regulators(struct udevice *dev, + const char * const *names, int cnt) +{ + int i, ret; + const char *name; + struct udevice *reg; + + for (i = 0; i < cnt; ++i) { + name = names[i]; + debug("%s: probing regulator '%s'\n", dev->name, name); + + ret = regulator_autoset_by_name(name, ®); + if (!ret) + ret = regulator_set_enable(reg, true); + } +} + +int rk_vop_probe(struct udevice *dev) { struct video_uc_platdata *plat = dev_get_uclass_platdata(dev); const void *blob = gd->fdt_blob; struct rk_vop_priv *priv = dev_get_priv(dev); - struct udevice *reg; - int ret, port, node; + int ret = 0; + int port, node; /* Before relocation we don't need to do anything */ if (!(gd->flags & GD_FLG_RELOC)) return 0; - priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); - priv->regs = (struct rk3288_vop *)dev_get_addr(dev); - - /* lcdc(vop) iodomain select 1.8V */ - rk_setreg(&priv->grf->io_vsel, 1 << 0); - - /* - * Try some common regulators. We should really get these from the - * device tree somehow. - */ - ret = regulator_autoset_by_name("vcc18_lcd", ®); - if (ret) - debug("%s: Cannot autoset regulator vcc18_lcd\n", __func__); - ret = regulator_autoset_by_name("VCC18_LCD", ®); - if (ret) - debug("%s: Cannot autoset regulator VCC18_LCD\n", __func__); - ret = regulator_autoset_by_name("vdd10_lcd_pwren_h", ®); - if (ret) { - debug("%s: Cannot autoset regulator vdd10_lcd_pwren_h\n", - __func__); - } - ret = regulator_autoset_by_name("vdd10_lcd", ®); - if (ret) { - debug("%s: Cannot autoset regulator vdd10_lcd\n", - __func__); - } - ret = regulator_autoset_by_name("VDD10_LCD", ®); - if (ret) { - debug("%s: Cannot autoset regulator VDD10_LCD\n", - __func__); - } - ret = regulator_autoset_by_name("vcc33_lcd", ®); - if (ret) - debug("%s: Cannot autoset regulator vcc33_lcd\n", __func__); + priv->regs = (struct rk3288_vop *)devfdt_get_addr(dev); /* * Try all the ports until we find one that works. In practice this @@ -353,31 +370,12 @@ static int rk_vop_probe(struct udevice *dev) return ret; } -static int rk_vop_bind(struct udevice *dev) +int rk_vop_bind(struct udevice *dev) { struct video_uc_platdata *plat = dev_get_uclass_platdata(dev); - plat->size = 1920 * 1200 * 4; + plat->size = 4 * (CONFIG_VIDEO_ROCKCHIP_MAX_XRES * + CONFIG_VIDEO_ROCKCHIP_MAX_YRES); return 0; } - -static const struct video_ops rk_vop_ops = { -}; - -static const struct udevice_id rk_vop_ids[] = { - { .compatible = "rockchip,rk3399-vop-big" }, - { .compatible = "rockchip,rk3399-vop-lit" }, - { .compatible = "rockchip,rk3288-vop" }, - { } -}; - -U_BOOT_DRIVER(rk_vop) = { - .name = "rk_vop", - .id = UCLASS_VIDEO, - .of_match = rk_vop_ids, - .ops = &rk_vop_ops, - .bind = rk_vop_bind, - .probe = rk_vop_probe, - .priv_auto_alloc_size = sizeof(struct rk_vop_priv), -}; diff --git a/drivers/video/rockchip/rk_vop.h b/drivers/video/rockchip/rk_vop.h new file mode 100644 index 0000000000..b2b29c452a --- /dev/null +++ b/drivers/video/rockchip/rk_vop.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2017 Theobroma Systems Design und Consulting GmbH + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __RK_VOP_H__ +#define __RK_VOP_H__ + +#include <asm/arch/vop_rk3288.h> + +struct rk_vop_priv { + void *grf; + void *regs; +}; + +enum vop_features { + VOP_FEATURE_OUTPUT_10BIT = (1 << 0), +}; + +struct rkvop_driverdata { + /* configuration */ + u32 features; + /* block-specific setters/getters */ + void (*set_pin_polarity)(struct udevice *, enum vop_modes, u32); +}; + +/** + * rk_vop_probe() - common probe implementation + * + * Performs the rk_display_init on each port-subnode until finding a + * working port (or returning an error if none of the ports could be + * successfully initialised). + * + * @dev: device + * @return 0 if OK, -ve if something went wrong + */ +int rk_vop_probe(struct udevice *dev); + +/** + * rk_vop_bind() - common bind implementation + * + * Sets the plat->size field to the amount of memory to be reserved for + * the framebuffer: this is always + * (32 BPP) x VIDEO_ROCKCHIP_MAX_XRES x VIDEO_ROCKCHIP_MAX_YRES + * + * @dev: device + * @return 0 (always OK) + */ +int rk_vop_bind(struct udevice *dev); + +/** + * rk_vop_probe_regulators() - probe (autoset + enable) regulators + * + * Probes a list of regulators by performing autoset and enable + * operations on them. The list of regulators is an array of string + * pointers and any individual regulator-probe may fail without + * counting as an error. + * + * @dev: device + * @names: array of string-pointers to regulator names to probe + * @cnt: number of elements in the 'names' array + */ +void rk_vop_probe_regulators(struct udevice *dev, + const char * const *names, int cnt); + +#endif diff --git a/drivers/video/sm501.c b/drivers/video/sm501.c index cafaae5f31..a468bd96ad 100644 --- a/drivers/video/sm501.c +++ b/drivers/video/sm501.c @@ -16,6 +16,7 @@ #include <common.h> #include <asm/io.h> +#include <pci.h> #include <video_fb.h> #include <sm501.h> diff --git a/drivers/video/sunxi/Makefile b/drivers/video/sunxi/Makefile index dbaab61b59..0d64c2021f 100644 --- a/drivers/video/sunxi/Makefile +++ b/drivers/video/sunxi/Makefile @@ -5,5 +5,5 @@ # SPDX-License-Identifier: GPL-2.0+ # -obj-$(CONFIG_VIDEO_SUNXI) += sunxi_display.o lcdc.o tve.o ../videomodes.o +obj-$(CONFIG_VIDEO_SUNXI) += sunxi_display.o lcdc.o tve_common.o ../videomodes.o obj-$(CONFIG_VIDEO_DE2) += sunxi_de2.o sunxi_dw_hdmi.o lcdc.o ../dw_hdmi.o diff --git a/drivers/video/sunxi/sunxi_de2.c b/drivers/video/sunxi/sunxi_de2.c index 9a32c3a020..ee67764ac5 100644 --- a/drivers/video/sunxi/sunxi_de2.c +++ b/drivers/video/sunxi/sunxi_de2.c @@ -56,7 +56,7 @@ static void sunxi_de2_composer_init(void) } static void sunxi_de2_mode_set(int mux, const struct display_timing *mode, - int bpp, ulong address) + int bpp, ulong address, bool is_composite) { ulong de_mux_base = (mux == 0) ? SUNXI_DE2_MUX0_BASE : SUNXI_DE2_MUX1_BASE; @@ -72,6 +72,9 @@ static void sunxi_de2_mode_set(int mux, const struct display_timing *mode, (struct de_ui *)(de_mux_base + SUNXI_DE2_MUX_CHAN_REGS + SUNXI_DE2_MUX_CHAN_SZ * 1); + struct de_csc * const de_csc_regs = + (struct de_csc *)(de_mux_base + + SUNXI_DE2_MUX_DCSC_REGS); u32 size = SUNXI_DE2_WH(mode->hactive.typ, mode->vactive.typ); int channel; u32 format; @@ -128,7 +131,27 @@ static void sunxi_de2_mode_set(int mux, const struct display_timing *mode, writel(0, de_mux_base + SUNXI_DE2_MUX_PEAK_REGS); writel(0, de_mux_base + SUNXI_DE2_MUX_ASE_REGS); writel(0, de_mux_base + SUNXI_DE2_MUX_FCC_REGS); - writel(0, de_mux_base + SUNXI_DE2_MUX_DCSC_REGS); + + if (is_composite) { + /* set CSC coefficients */ + writel(0x107, &de_csc_regs->coef11); + writel(0x204, &de_csc_regs->coef12); + writel(0x64, &de_csc_regs->coef13); + writel(0x4200, &de_csc_regs->coef14); + writel(0x1f68, &de_csc_regs->coef21); + writel(0x1ed6, &de_csc_regs->coef22); + writel(0x1c2, &de_csc_regs->coef23); + writel(0x20200, &de_csc_regs->coef24); + writel(0x1c2, &de_csc_regs->coef31); + writel(0x1e87, &de_csc_regs->coef32); + writel(0x1fb7, &de_csc_regs->coef33); + writel(0x20200, &de_csc_regs->coef34); + + /* enable CSC unit */ + writel(1, &de_csc_regs->csc_ctl); + } else { + writel(0, &de_csc_regs->csc_ctl); + } switch (bpp) { case 16: @@ -153,7 +176,7 @@ static void sunxi_de2_mode_set(int mux, const struct display_timing *mode, static int sunxi_de2_init(struct udevice *dev, ulong fbbase, enum video_log2_bpp l2bpp, - struct udevice *disp, int mux) + struct udevice *disp, int mux, bool is_composite) { struct video_priv *uc_priv = dev_get_uclass_priv(dev); struct display_timing timing; @@ -183,7 +206,7 @@ static int sunxi_de2_init(struct udevice *dev, ulong fbbase, } sunxi_de2_composer_init(); - sunxi_de2_mode_set(mux, &timing, 1 << l2bpp, fbbase); + sunxi_de2_mode_set(mux, &timing, 1 << l2bpp, fbbase, is_composite); ret = display_enable(disp, 1 << l2bpp, &timing); if (ret) { @@ -204,7 +227,6 @@ static int sunxi_de2_probe(struct udevice *dev) struct video_uc_platdata *plat = dev_get_uclass_platdata(dev); struct udevice *disp; int ret; - int mux; /* Before relocation we don't need to do anything */ if (!(gd->flags & GD_FLG_RELOC)) @@ -212,17 +234,31 @@ static int sunxi_de2_probe(struct udevice *dev) ret = uclass_find_device_by_name(UCLASS_DISPLAY, "sunxi_dw_hdmi", &disp); + if (!ret) { + int mux; + if (IS_ENABLED(CONFIG_MACH_SUNXI_H3_H5)) + mux = 0; + else + mux = 1; + + ret = sunxi_de2_init(dev, plat->base, VIDEO_BPP32, disp, mux, + false); + if (!ret) { + video_set_flush_dcache(dev, 1); + return 0; + } + } + + debug("%s: hdmi display not found (ret=%d)\n", __func__, ret); + + ret = uclass_find_device_by_name(UCLASS_DISPLAY, + "sunxi_tve", &disp); if (ret) { - debug("%s: hdmi display not found (ret=%d)\n", __func__, ret); + debug("%s: tv not found (ret=%d)\n", __func__, ret); return ret; } - if (IS_ENABLED(CONFIG_MACH_SUNXI_H3_H5)) - mux = 0; - else - mux = 1; - - ret = sunxi_de2_init(dev, plat->base, VIDEO_BPP32, disp, mux); + ret = sunxi_de2_init(dev, plat->base, VIDEO_BPP32, disp, 1, true); if (ret) return ret; diff --git a/drivers/video/sunxi/tve.c b/drivers/video/sunxi/tve_common.c index adea78a69a..adea78a69a 100644 --- a/drivers/video/sunxi/tve.c +++ b/drivers/video/sunxi/tve_common.c diff --git a/drivers/video/tegra.c b/drivers/video/tegra.c index 0ba3f2c2b4..ec62fc9e51 100644 --- a/drivers/video/tegra.c +++ b/drivers/video/tegra.c @@ -343,7 +343,7 @@ static int tegra_lcd_ofdata_to_platdata(struct udevice *dev) int rgb; int ret; - priv->disp = (struct disp_ctlr *)dev_get_addr(dev); + priv->disp = (struct disp_ctlr *)devfdt_get_addr(dev); if (!priv->disp) { debug("%s: No display controller address\n", __func__); return -EINVAL; diff --git a/drivers/video/tegra124/dp.c b/drivers/video/tegra124/dp.c index 5bf8524a5e..c38b3e5335 100644 --- a/drivers/video/tegra124/dp.c +++ b/drivers/video/tegra124/dp.c @@ -1572,7 +1572,7 @@ static int tegra_dp_ofdata_to_platdata(struct udevice *dev) { struct tegra_dp_plat *plat = dev_get_platdata(dev); - plat->base = dev_get_addr(dev); + plat->base = devfdt_get_addr(dev); return 0; } diff --git a/drivers/video/tegra124/sor.c b/drivers/video/tegra124/sor.c index 5e4140ff53..4324071cdc 100644 --- a/drivers/video/tegra124/sor.c +++ b/drivers/video/tegra124/sor.c @@ -466,11 +466,20 @@ void tegra_dc_sor_set_lane_count(struct udevice *dev, u8 lane_count) static int tegra_dc_sor_power_up(struct udevice *dev, int is_lvds) { struct tegra_dc_sor_data *sor = dev_get_priv(dev); + u32 reg; int ret; if (sor->power_is_up) return 0; + /* + * If for some reason it is already powered up, don't do it again. + * This can happen if U-Boot is the secondary boot loader. + */ + reg = tegra_sor_readl(sor, DP_PADCTL(sor->portnum)); + if (reg & DP_PADCTL_PD_TXD_0_NO) + return 0; + /* Set link bw */ tegra_dc_sor_set_link_bandwidth(dev, is_lvds ? CLK_CNTRL_DP_LINK_SPEED_LVDS : |