Hi Jacob, On 12 March 2016 at 06:13, Jacob Chen <jacob-c...@iotwrt.com> wrote: > Some Rockchip SoCs support LVDS output. Add a display driver for this so > that these displays can be used on supported boards. > > Signed-off-by: Jacob Chen <jacob-c...@iotwrt.com> > Signed-off-by: jacob <jacob-c...@iotwrt.com> > --- > > arch/arm/include/asm/arch-rockchip/lvds_rk3288.h | 99 +++++++++ > drivers/video/rockchip/Makefile | 2 +- > drivers/video/rockchip/rk_lvds.c | 246 > +++++++++++++++++++++++ > 3 files changed, 346 insertions(+), 1 deletion(-) > create mode 100644 arch/arm/include/asm/arch-rockchip/lvds_rk3288.h > create mode 100644 drivers/video/rockchip/rk_lvds.c >
This looks OK - a few nits below. > diff --git a/arch/arm/include/asm/arch-rockchip/lvds_rk3288.h > b/arch/arm/include/asm/arch-rockchip/lvds_rk3288.h > new file mode 100644 > index 0000000..de16f3d > --- /dev/null > +++ b/arch/arm/include/asm/arch-rockchip/lvds_rk3288.h > @@ -0,0 +1,99 @@ > +/* > + * Copyright 2016 Rockchip Inc. > + * > + * SPDX-License-Identifier: GPL-2.0+ > + */ > + > +#ifndef _ASM_ARCH_LVDS_RK3288_H > +#define _ASM_ARCH_LVDS_RK3288_H > + > +#define RK3288_LVDS_CH0_REG0 0x00 > +#define RK3288_LVDS_CH0_REG0_LVDS_EN BIT(7) > +#define RK3288_LVDS_CH0_REG0_TTL_EN BIT(6) > +#define RK3288_LVDS_CH0_REG0_LANECK_EN BIT(5) > +#define RK3288_LVDS_CH0_REG0_LANE4_EN BIT(4) > +#define RK3288_LVDS_CH0_REG0_LANE3_EN BIT(3) > +#define RK3288_LVDS_CH0_REG0_LANE2_EN BIT(2) > +#define RK3288_LVDS_CH0_REG0_LANE1_EN BIT(1) > +#define RK3288_LVDS_CH0_REG0_LANE0_EN BIT(0) > + > +#define RK3288_LVDS_CH0_REG1 0x04 > +#define RK3288_LVDS_CH0_REG1_LANECK_BIAS BIT(5) > +#define RK3288_LVDS_CH0_REG1_LANE4_BIAS BIT(4) > +#define RK3288_LVDS_CH0_REG1_LANE3_BIAS BIT(3) > +#define RK3288_LVDS_CH0_REG1_LANE2_BIAS BIT(2) > +#define RK3288_LVDS_CH0_REG1_LANE1_BIAS BIT(1) > +#define RK3288_LVDS_CH0_REG1_LANE0_BIAS BIT(0) > + > +#define RK3288_LVDS_CH0_REG2 0x08 > +#define RK3288_LVDS_CH0_REG2_RESERVE_ON BIT(7) > +#define RK3288_LVDS_CH0_REG2_LANECK_LVDS_MODE BIT(6) > +#define RK3288_LVDS_CH0_REG2_LANE4_LVDS_MODE BIT(5) > +#define RK3288_LVDS_CH0_REG2_LANE3_LVDS_MODE BIT(4) > +#define RK3288_LVDS_CH0_REG2_LANE2_LVDS_MODE BIT(3) > +#define RK3288_LVDS_CH0_REG2_LANE1_LVDS_MODE BIT(2) > +#define RK3288_LVDS_CH0_REG2_LANE0_LVDS_MODE BIT(1) > +#define RK3288_LVDS_CH0_REG2_PLL_FBDIV8 BIT(0) > + > +#define RK3288_LVDS_CH0_REG3 0x0c > +#define RK3288_LVDS_CH0_REG3_PLL_FBDIV_MASK 0xff > + > +#define RK3288_LVDS_CH0_REG4 0x10 > +#define RK3288_LVDS_CH0_REG4_LANECK_TTL_MODE BIT(5) > +#define RK3288_LVDS_CH0_REG4_LANE4_TTL_MODE BIT(4) > +#define RK3288_LVDS_CH0_REG4_LANE3_TTL_MODE BIT(3) > +#define RK3288_LVDS_CH0_REG4_LANE2_TTL_MODE BIT(2) > +#define RK3288_LVDS_CH0_REG4_LANE1_TTL_MODE BIT(1) > +#define RK3288_LVDS_CH0_REG4_LANE0_TTL_MODE BIT(0) > + > +#define RK3288_LVDS_CH0_REG5 0x14 > +#define RK3288_LVDS_CH0_REG5_LANECK_TTL_DATA BIT(5) > +#define RK3288_LVDS_CH0_REG5_LANE4_TTL_DATA BIT(4) > +#define RK3288_LVDS_CH0_REG5_LANE3_TTL_DATA BIT(3) > +#define RK3288_LVDS_CH0_REG5_LANE2_TTL_DATA BIT(2) > +#define RK3288_LVDS_CH0_REG5_LANE1_TTL_DATA BIT(1) > +#define RK3288_LVDS_CH0_REG5_LANE0_TTL_DATA BIT(0) > + > +#define RK3288_LVDS_CFG_REGC 0x30 > +#define RK3288_LVDS_CFG_REGC_PLL_ENABLE 0x00 > +#define RK3288_LVDS_CFG_REGC_PLL_DISABLE 0xff > + > +#define RK3288_LVDS_CH0_REGD 0x34 > +#define RK3288_LVDS_CH0_REGD_PLL_PREDIV_MASK 0x1f > + > +#define RK3288_LVDS_CH0_REG20 0x80 > +#define RK3288_LVDS_CH0_REG20_MSB 0x45 > +#define RK3288_LVDS_CH0_REG20_LSB 0x44 > + > +#define RK3288_LVDS_CFG_REG21 0x84 > +#define RK3288_LVDS_CFG_REG21_TX_ENABLE 0x92 > +#define RK3288_LVDS_CFG_REG21_TX_DISABLE 0x00 > + > +/* fbdiv value is split over 2 registers, with bit8 in reg2 */ > +#define RK3288_LVDS_PLL_FBDIV_REG2(_fbd) \ > + (_fbd & BIT(8) ? RK3288_LVDS_CH0_REG2_PLL_FBDIV8 : 0) > +#define RK3288_LVDS_PLL_FBDIV_REG3(_fbd) \ > + (_fbd & RK3288_LVDS_CH0_REG3_PLL_FBDIV_MASK) > +#define RK3288_LVDS_PLL_PREDIV_REGD(_pd) \ > + (_pd & RK3288_LVDS_CH0_REGD_PLL_PREDIV_MASK) > + > +#define RK3288_LVDS_SOC_CON6_SEL_VOP_LIT BIT(3) > + > +#define LVDS_FMT_MASK (0x07 << 16) > +#define LVDS_MSB (0x01 << 3) > +#define LVDS_DUAL (0x01 << 4) > +#define LVDS_FMT_1 (0x01 << 5) > +#define LVDS_TTL_EN (0x01 << 6) > +#define LVDS_START_PHASE_RST_1 (0x01 << 7) > +#define LVDS_DCLK_INV (0x01 << 8) > +#define LVDS_CH0_EN (0x01 << 11) > +#define LVDS_CH1_EN (0x01 << 12) > +#define LVDS_PWRDN (0x01 << 15) Can you drop the 0x0 prefix on these? It doesn't seem useful. > + > +#define LVDS_24BIT (0 << 1) > +#define LVDS_18BIT (1 << 1) > +#define LVDS_FORMAT_VESA (0 << 0) > +#define LVDS_FORMAT_JEIDA (1 << 0) > + > + > +#endif > diff --git a/drivers/video/rockchip/Makefile b/drivers/video/rockchip/Makefile > index 0e9a8ac..7962f86 100644 > --- a/drivers/video/rockchip/Makefile > +++ b/drivers/video/rockchip/Makefile > @@ -5,4 +5,4 @@ > # SPDX-License-Identifier: GPL-2.0+ > # > > -obj-y += rk_edp.o rk_hdmi.o rk_vop.o > +obj-y += rk_edp.o rk_hdmi.o rk_vop.o rk_lvds.o > diff --git a/drivers/video/rockchip/rk_lvds.c > b/drivers/video/rockchip/rk_lvds.c > new file mode 100644 > index 0000000..f44ef98 > --- /dev/null > +++ b/drivers/video/rockchip/rk_lvds.c > @@ -0,0 +1,246 @@ > +/* > + * Copyright 2016 Rockchip Inc. > + * > + * SPDX-License-Identifier: GPL-2.0+ > + */ > + > +#include <common.h> > +#include <clk.h> > +#include <display.h> > +#include <dm.h> > +#include <edid.h> > +#include <panel.h> > +#include <regmap.h> > +#include <syscon.h> > +#include <asm/gpio.h> > +#include <asm/io.h> > +#include <asm/arch/clock.h> > +#include <asm/arch/lvds_rk3288.h> > +#include <asm/arch/grf_rk3288.h> > +#include <dt-bindings/clock/rk3288-cru.h> > + > +DECLARE_GLOBAL_DATA_PTR; > + > +#define DISPLAY_OUTPUT_RGB 0 > +#define DISPLAY_OUTPUT_LVDS 1 > +#define DISPLAY_OUTPUT_DUAL_LVDS 2 > + > +struct rk_lvds_priv { > + void __iomem *regs; > + struct rk3288_grf *grf; > + struct udevice *panel; > + > + int output; > + int format; Please add comments as to what these members are above the struct: /** * struct rk_lvds_priv - Holds private LVDS driver data * @regs: ... ... > +}; > + > +static inline void lvds_writel(struct rk_lvds_priv *lvds, u32 offset, u32 > val) > +{ > + writel(val, lvds->regs + offset); > + > + writel(val, lvds->regs + offset + 0x100); > +} > + > +int rk_lvds_enable(struct udevice *dev, int panel_bpp, > + const struct display_timing *edid) > +{ > + struct rk_lvds_priv *priv = dev_get_priv(dev); > + struct display_plat *uc_plat = dev_get_uclass_platdata(dev); > + int ret = 0; > + unsigned int val = 0; > + > + ret = panel_enable_backlight(priv->panel); > + if (ret) { > + debug("%s: backlight error: %d\n", __func__, ret); > + return ret; > + } > + > + /* Select the video source */ > + if (uc_plat->source_id) > + val = RK3288_LVDS_SOC_CON6_SEL_VOP_LIT | > + (RK3288_LVDS_SOC_CON6_SEL_VOP_LIT << 16); > + else > + val = RK3288_LVDS_SOC_CON6_SEL_VOP_LIT << 16; > + ret = rk_setreg(&priv->grf->soc_con6, val); > + > + /* Select data transfer format */ > + val = priv->format; > + if (priv->output == DISPLAY_OUTPUT_DUAL_LVDS) > + val |= LVDS_DUAL | LVDS_CH0_EN | LVDS_CH1_EN; > + else if (priv->output == DISPLAY_OUTPUT_LVDS) > + val |= LVDS_CH0_EN; > + else if (priv->output == DISPLAY_OUTPUT_RGB) > + val |= LVDS_TTL_EN | LVDS_CH0_EN | LVDS_CH1_EN; > + val |= (0xffff << 16); > + rk_setreg(&priv->grf->soc_con7, val); > + > + /* Enable LVDS PHY */ > + if (priv->output == DISPLAY_OUTPUT_RGB) { > + lvds_writel(priv, RK3288_LVDS_CH0_REG0, > + RK3288_LVDS_CH0_REG0_TTL_EN | > + RK3288_LVDS_CH0_REG0_LANECK_EN | > + RK3288_LVDS_CH0_REG0_LANE4_EN | > + RK3288_LVDS_CH0_REG0_LANE3_EN | > + RK3288_LVDS_CH0_REG0_LANE2_EN | > + RK3288_LVDS_CH0_REG0_LANE1_EN | > + RK3288_LVDS_CH0_REG0_LANE0_EN); > + lvds_writel(priv, RK3288_LVDS_CH0_REG2, > + RK3288_LVDS_PLL_FBDIV_REG2(0x46)); > + > + lvds_writel(priv, RK3288_LVDS_CH0_REG3, > + RK3288_LVDS_PLL_FBDIV_REG3(0x46)); > + lvds_writel(priv, RK3288_LVDS_CH0_REG4, > + RK3288_LVDS_CH0_REG4_LANECK_TTL_MODE | > + RK3288_LVDS_CH0_REG4_LANE4_TTL_MODE | > + RK3288_LVDS_CH0_REG4_LANE3_TTL_MODE | > + RK3288_LVDS_CH0_REG4_LANE2_TTL_MODE | > + RK3288_LVDS_CH0_REG4_LANE1_TTL_MODE | > + RK3288_LVDS_CH0_REG4_LANE0_TTL_MODE); > + lvds_writel(priv, RK3288_LVDS_CH0_REG5, > + RK3288_LVDS_CH0_REG5_LANECK_TTL_DATA | > + RK3288_LVDS_CH0_REG5_LANE4_TTL_DATA | > + RK3288_LVDS_CH0_REG5_LANE3_TTL_DATA | > + RK3288_LVDS_CH0_REG5_LANE2_TTL_DATA | > + RK3288_LVDS_CH0_REG5_LANE1_TTL_DATA | > + RK3288_LVDS_CH0_REG5_LANE0_TTL_DATA); > + lvds_writel(priv, RK3288_LVDS_CH0_REGD, > + RK3288_LVDS_PLL_PREDIV_REGD(0x0a)); > + lvds_writel(priv, RK3288_LVDS_CH0_REG20, > + RK3288_LVDS_CH0_REG20_LSB); > + } else { > + lvds_writel(priv, RK3288_LVDS_CH0_REG0, > + RK3288_LVDS_CH0_REG0_LVDS_EN | > + RK3288_LVDS_CH0_REG0_LANECK_EN | > + RK3288_LVDS_CH0_REG0_LANE4_EN | > + RK3288_LVDS_CH0_REG0_LANE3_EN | > + RK3288_LVDS_CH0_REG0_LANE2_EN | > + RK3288_LVDS_CH0_REG0_LANE1_EN | > + RK3288_LVDS_CH0_REG0_LANE0_EN); > + lvds_writel(priv, RK3288_LVDS_CH0_REG1, > + RK3288_LVDS_CH0_REG1_LANECK_BIAS | > + RK3288_LVDS_CH0_REG1_LANE4_BIAS | > + RK3288_LVDS_CH0_REG1_LANE3_BIAS | > + RK3288_LVDS_CH0_REG1_LANE2_BIAS | > + RK3288_LVDS_CH0_REG1_LANE1_BIAS | > + RK3288_LVDS_CH0_REG1_LANE0_BIAS); > + lvds_writel(priv, RK3288_LVDS_CH0_REG2, > + RK3288_LVDS_CH0_REG2_RESERVE_ON | > + RK3288_LVDS_CH0_REG2_LANECK_LVDS_MODE | > + RK3288_LVDS_CH0_REG2_LANE4_LVDS_MODE | > + RK3288_LVDS_CH0_REG2_LANE3_LVDS_MODE | > + RK3288_LVDS_CH0_REG2_LANE2_LVDS_MODE | > + RK3288_LVDS_CH0_REG2_LANE1_LVDS_MODE | > + RK3288_LVDS_CH0_REG2_LANE0_LVDS_MODE | > + RK3288_LVDS_PLL_FBDIV_REG2(0x46)); > + lvds_writel(priv, RK3288_LVDS_CH0_REG3, > + RK3288_LVDS_PLL_FBDIV_REG3(0x46)); > + lvds_writel(priv, RK3288_LVDS_CH0_REG4, 0x00); > + lvds_writel(priv, RK3288_LVDS_CH0_REG5, 0x00); > + lvds_writel(priv, RK3288_LVDS_CH0_REGD, > + RK3288_LVDS_PLL_PREDIV_REGD(0x0a)); > + lvds_writel(priv, RK3288_LVDS_CH0_REG20, > + RK3288_LVDS_CH0_REG20_LSB); > + } > + > + /* Power on */ > + writel(RK3288_LVDS_CFG_REGC_PLL_ENABLE, > + priv->regs + RK3288_LVDS_CFG_REGC); > + > + writel(RK3288_LVDS_CFG_REG21_TX_ENABLE, > + priv->regs + RK3288_LVDS_CFG_REG21); > + > + return 0; > +} > + > +int rk_lvds_read_timing(struct udevice *dev, struct display_timing *timing) > +{ > + if (fdtdec_decode_display_timing > + (gd->fdt_blob, dev->of_offset, 0, timing)) { > + debug("%s: Failed to decode display timing\n", __func__); > + return -EINVAL; > + } > + > + return 0; > +} > + > +static int rk_lvds_ofdata_to_platdata(struct udevice *dev) > +{ > + struct rk_lvds_priv *priv = dev_get_priv(dev); > + const void *blob = gd->fdt_blob; > + int node = dev->of_offset; > + int ret; > + priv->regs = (void *)dev_get_addr(dev); > + priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); > + > + ret = fdtdec_get_uint(blob, node, "rockchip,output", -1); > + if (ret != -1) { > + priv->output = ret; > + debug("LVDS output : %d\n", ret); > + } else { > + /* default set it as output rgb */ > + priv->output = DISPLAY_OUTPUT_RGB; > + } > + > + ret = fdtdec_get_uint(blob, node, "rockchip,data-mapping", -1); > + if (ret != -1) { > + priv->format = ret; > + debug("LVDS data-mapping : %d\n", ret); > + } else { > + /* default set it as format jeida */ > + priv->format = LVDS_FORMAT_JEIDA; > + } > + > + ret = fdtdec_get_uint(blob, node, "rockchip,data-width", -1); Do you have a binding file for these settings? > + if (ret != -1) { > + debug("LVDS data-width : %d\n", ret); > + if (ret == 24) { > + priv->format |= LVDS_24BIT; > + } else if (ret == 18) { > + priv->format |= LVDS_18BIT; > + } else { > + debug("rockchip-lvds unsupport data-width[%d]\n", > ret); > + ret = -EINVAL; > + return ret; > + } > + } else { > + priv->format |= LVDS_24BIT; > + } > + > + return 0; > +} > + > +int rk_lvds_probe(struct udevice *dev) > +{ > + struct rk_lvds_priv *priv = dev_get_priv(dev); > + int ret; > + > + ret = uclass_get_device_by_phandle(UCLASS_PANEL, dev, > "rockchip,panel", > + &priv->panel); > + if (ret) { > + debug("%s: Cannot find panel for '%s' (ret=%d)\n", __func__, > + dev->name, ret); > + return ret; > + } > + > + return 0; > +} > + > +static const struct dm_display_ops lvds_rockchip_ops = { > + .read_timing = rk_lvds_read_timing, > + .enable = rk_lvds_enable, > +}; > + > +static const struct udevice_id rockchip_lvds_ids[] = { > + {.compatible = "rockchip,rk3288-lvds"}, > + {} > +}; > + > +U_BOOT_DRIVER(lvds_rockchip) = { > + .name = "lvds_rockchip", > + .id = UCLASS_DISPLAY, > + .of_match = rockchip_lvds_ids, > + .ops = &lvds_rockchip_ops, > + .ofdata_to_platdata = rk_lvds_ofdata_to_platdata, > + .probe = rk_lvds_probe, > + .priv_auto_alloc_size = sizeof(struct rk_lvds_priv), > +}; > -- > 2.3.5 > Regards, Simon _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot