[U-Boot] [PATCH 2/5] rockchip: video: Add a display driver for rockchip LVDS

Simon Glass sjg at chromium.org
Sun Mar 13 03:12:12 CET 2016


Hi Jacob,

On 12 March 2016 at 06:13, Jacob Chen <jacob-chen at 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-chen at iotwrt.com>
> Signed-off-by: jacob <jacob-chen at 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


More information about the U-Boot mailing list