diff options
Diffstat (limited to 'drivers/video/rockchip')
-rw-r--r-- | drivers/video/rockchip/Kconfig | 29 | ||||
-rw-r--r-- | drivers/video/rockchip/Makefile | 6 | ||||
-rw-r--r-- | drivers/video/rockchip/rk3288_hdmi.c | 116 | ||||
-rw-r--r-- | drivers/video/rockchip/rk3288_vop.c | 95 | ||||
-rw-r--r-- | drivers/video/rockchip/rk3399_hdmi.c | 81 | ||||
-rw-r--r-- | drivers/video/rockchip/rk3399_vop.c | 105 | ||||
-rw-r--r-- | drivers/video/rockchip/rk_hdmi.c | 115 | ||||
-rw-r--r-- | drivers/video/rockchip/rk_hdmi.h | 32 | ||||
-rw-r--r-- | drivers/video/rockchip/rk_vop.c | 185 | ||||
-rw-r--r-- | drivers/video/rockchip/rk_vop.h | 33 |
10 files changed, 607 insertions, 190 deletions
diff --git a/drivers/video/rockchip/Kconfig b/drivers/video/rockchip/Kconfig index 80e399f..b1d7c62 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 cd54b12..872dc0f 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 0000000..eae0dd2 --- /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 0000000..e3e1ec7 --- /dev/null +++ b/drivers/video/rockchip/rk3288_vop.c @@ -0,0 +1,95 @@ +/* + * 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); +} + +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, + .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 0000000..b1e5097 --- /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 0000000..91a40ab --- /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_hdmi.c b/drivers/video/rockchip/rk_hdmi.c index cd695ca..a9c8fba 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,27 +61,25 @@ 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; @@ -88,15 +87,9 @@ static int rk_hdmi_ofdata_to_platdata(struct udevice *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 0000000..501ed3a --- /dev/null +++ b/drivers/video/rockchip/rk_hdmi.h @@ -0,0 +1,32 @@ +/* + * 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; +}; + +int rk_hdmi_read_edid(struct udevice *dev, u8 *buf, int buf_size); +void rk_hdmi_probe_regulators(struct udevice *dev, + const char * const *names, int cnt); +int rk_hdmi_ofdata_to_platdata(struct udevice *dev); +int rk_hdmi_probe(struct udevice *dev); + +#endif diff --git a/drivers/video/rockchip/rk_vop.c b/drivers/video/rockchip/rk_vop.c index 48bfcd4..9343796 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,83 @@ 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; 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); - 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)); + 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); - clrsetbits_le32(®s->dsp_ctrl0, - M_DSP_OUT_MODE | M_DSP_VSYNC_POL | M_DSP_HSYNC_POL, - flags); + 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, + V_DSP_OUT_MODE(mode_flags)); writel(V_HSYNC(hsync_len) | V_HORPRD(hsync_len + hback_porch + hactive + hfront_porch), @@ -185,7 +215,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 +285,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,53 +311,37 @@ 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 *)devfdt_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__); - /* * Try all the ports until we find one that works. In practice this * tries EDP first if available, then HDMI. @@ -353,31 +367,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 0000000..9bda514 --- /dev/null +++ b/drivers/video/rockchip/rk_vop.h @@ -0,0 +1,33 @@ +/* + * 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); +}; + +int rk_vop_probe(struct udevice *dev); +int rk_vop_bind(struct udevice *dev); +void rk_vop_probe_regulators(struct udevice *dev, + const char * const *names, int cnt); + +#endif |