From eab314f5909dba208d4cb7dc5aac6ee707d5d171 Mon Sep 17 00:00:00 2001 From: Jacob Chen Date: Mon, 14 Mar 2016 11:20:14 +0800 Subject: dm: video: Add a operation to display uclass The current display class only allow to get timing from edid. So add a operation to get timing directly from driver. In driver, I will use fdtdec_decode_display_timing to get timing. Signed-off-by: Jacob Chen Acked-by: Simon Glass diff --git a/drivers/video/display-uclass.c b/drivers/video/display-uclass.c index 31522ea..e4763de 100644 --- a/drivers/video/display-uclass.c +++ b/drivers/video/display-uclass.c @@ -36,6 +36,9 @@ int display_read_timing(struct udevice *dev, struct display_timing *timing) u8 buf[EDID_EXT_SIZE]; int ret; + if (ops && ops->read_timing) + return ops->read_timing(dev, timing); + if (!ops || !ops->read_edid) return -ENOSYS; ret = ops->read_edid(dev, buf, sizeof(buf)); diff --git a/include/display.h b/include/display.h index c180e76..b1c4766 100644 --- a/include/display.h +++ b/include/display.h @@ -23,7 +23,7 @@ struct display_plat { }; /** - * display_read_timing() - Read timing information from EDID + * display_read_timing() - Read timing information * * @dev: Device to read from * @return 0 if OK, -ve on error @@ -43,6 +43,15 @@ int display_enable(struct udevice *dev, int panel_bpp, struct dm_display_ops { /** + * read_timing() - Read information directly + * + * @dev: Device to read from + * @timing: Display timings + * @return 0 if OK, -ve on error + */ + int (*read_timing)(struct udevice *dev, struct display_timing *timing); + + /** * read_edid() - Read information from EDID * * @dev: Device to read from -- cgit v0.10.2 From 450f3c713543be514905468f08dfda312d640802 Mon Sep 17 00:00:00 2001 From: Jacob Chen Date: Mon, 14 Mar 2016 11:20:15 +0800 Subject: rockchip: Add a binding file for rk3288 video Signed-off-by: Jacob Chen Acked-by: Simon Glass diff --git a/include/dt-bindings/video/rk3288.h b/include/dt-bindings/video/rk3288.h new file mode 100644 index 0000000..7bfd247 --- /dev/null +++ b/include/dt-bindings/video/rk3288.h @@ -0,0 +1,11 @@ +#ifndef __DT_BINDINGS_VIDEO_RK3288_H__ +#define __DT_BINDINGS_VIDEO_RK3288_H__ + +#define LVDS_OUTPUT_RGB 0 +#define LVDS_OUTPUT_SINGLE 1 +#define LVDS_OUTPUT_DUAL 2 + +#define LVDS_FORMAT_VESA 0 +#define LVDS_FORMAT_JEIDA 1 + +#endif -- cgit v0.10.2 From 35ac89dd8fe7e9373d8c4336e7fbe93ae3fd59d7 Mon Sep 17 00:00:00 2001 From: Jacob Chen Date: Mon, 14 Mar 2016 11:20:16 +0800 Subject: rockchip: video: Add a display driver for rockchip LVDS 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 Acked-by: Simon Glass 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..121a898 --- /dev/null +++ b/arch/arm/include/asm/arch-rockchip/lvds_rk3288.h @@ -0,0 +1,97 @@ +/* + * 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 (7 << 16) +#define LVDS_MSB (1 << 3) +#define LVDS_DUAL (1 << 4) +#define LVDS_FMT_1 (1 << 5) +#define LVDS_TTL_EN (1 << 6) +#define LVDS_START_PHASE_RST_1 (1 << 7) +#define LVDS_DCLK_INV (1 << 8) +#define LVDS_CH0_EN (1 << 11) +#define LVDS_CH1_EN (1 << 12) +#define LVDS_PWRDN (1 << 15) + +#define LVDS_24BIT (0 << 1) +#define LVDS_18BIT (1 << 1) + + +#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..dc10b86 --- /dev/null +++ b/drivers/video/rockchip/rk_lvds.c @@ -0,0 +1,254 @@ +/* + * Copyright 2016 Rockchip Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/** + * struct rk_lvds_priv - private rockchip lvds display driver info + * + * @reg: LVDS register address + * @grf: GRF register + * @panel: Panel device that is used in driver + * + * @output: Output mode, decided single or double channel, + * LVDS or LVTLL + * @format: Data format that RGB data will packing as + */ +struct rk_lvds_priv { + void __iomem *regs; + struct rk3288_grf *grf; + struct udevice *panel; + + int output; + int format; +}; + +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; + rk_setreg(&priv->grf->soc_con6, val); + + /* Select data transfer format */ + val = priv->format; + if (priv->output == LVDS_OUTPUT_DUAL) + val |= LVDS_DUAL | LVDS_CH0_EN | LVDS_CH1_EN; + else if (priv->output == LVDS_OUTPUT_SINGLE) + val |= LVDS_CH0_EN; + else if (priv->output == LVDS_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 == LVDS_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_int(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 = LVDS_OUTPUT_RGB; + } + + ret = fdtdec_get_int(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_int(blob, node, "rockchip,data-width", -1); + 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), +}; -- cgit v0.10.2 From cfd9794115a46aaf00266f21d8e8d8555d921b70 Mon Sep 17 00:00:00 2001 From: Jacob Chen Date: Mon, 14 Mar 2016 11:20:17 +0800 Subject: rockchip: dts: Add LVDS support Add these node to be used in rockchip LVDS and VOP driver. Signed-off-by: Jacob Chen Acked-by: Simon Glass diff --git a/arch/arm/dts/rk3288.dtsi b/arch/arm/dts/rk3288.dtsi index e51c75c..3dab0fc 100644 --- a/arch/arm/dts/rk3288.dtsi +++ b/arch/arm/dts/rk3288.dtsi @@ -9,6 +9,7 @@ #include #include #include +#include #include "skeleton.dtsi" / { @@ -683,6 +684,10 @@ reg = <1>; remote-endpoint = <&hdmi_in_vopb>; }; + vopb_out_lvds: endpoint@2 { + reg = <2>; + remote-endpoint = <&lvds_in_vopb>; + }; }; }; @@ -719,7 +724,10 @@ reg = <1>; remote-endpoint = <&hdmi_in_vopl>; }; - + vopl_out_lvds: endpoint@2 { + reg = <2>; + remote-endpoint = <&lvds_in_vopl>; + }; }; }; @@ -786,6 +794,34 @@ }; }; + lvds: lvds@ff96c000 { + compatible = "rockchip,rk3288-lvds"; + reg = <0xff96c000 0x4000>; + clocks = <&cru PCLK_LVDS_PHY>; + clock-names = "pclk_lvds"; + pinctrl-names = "default"; + pinctrl-0 = <&lcdc0_ctl>; + rockchip,grf = <&grf>; + status = "disabled"; + ports { + #address-cells = <1>; + #size-cells = <0>; + lvds_in: port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + lvds_in_vopb: endpoint@0 { + reg = <0>; + remote-endpoint = <&vopb_out_lvds>; + }; + lvds_in_vopl: endpoint@1 { + reg = <1>; + remote-endpoint = <&vopl_out_lvds>; + }; + }; + }; + }; + hdmi_audio: hdmi_audio { compatible = "rockchip,rk3288-hdmi-audio"; i2s-controller = <&i2s>; @@ -1109,6 +1145,15 @@ }; }; + lcdc0 { + lcdc0_ctl: lcdc0-ctl { + rockchip,pins = <1 24 RK_FUNC_1 &pcfg_pull_none>, + <1 25 RK_FUNC_1 &pcfg_pull_none>, + <1 26 RK_FUNC_1 &pcfg_pull_none>, + <1 27 RK_FUNC_1 &pcfg_pull_none>; + }; + }; + sdmmc { sdmmc_clk: sdmmc-clk { rockchip,pins = <6 20 RK_FUNC_1 &pcfg_pull_none>; -- cgit v0.10.2 From 8530783594f6de14ec6d4b7bf804437d6e1a4f5f Mon Sep 17 00:00:00 2001 From: Jacob Chen Date: Mon, 14 Mar 2016 11:20:18 +0800 Subject: rockchip: video: Add LVDS support in vop driver LVDS have a different display out mode, add code to get right flag. The vop_ip decide display device and the remote_vop_id decide which vop was being used. So we should use the remote_vop_id to set DCLK_VOP. Signed-off-by: Jacob Chen Acked-by: Simon Glass diff --git a/arch/arm/include/asm/arch-rockchip/vop_rk3288.h b/arch/arm/include/asm/arch-rockchip/vop_rk3288.h index 0104ba3..0ce3d67 100644 --- a/arch/arm/include/asm/arch-rockchip/vop_rk3288.h +++ b/arch/arm/include/asm/arch-rockchip/vop_rk3288.h @@ -89,6 +89,7 @@ enum { enum vop_modes { VOP_MODE_EDP = 0, VOP_MODE_HDMI, + VOP_MODE_LVDS, VOP_MODE_NONE, VOP_MODE_AUTO_DETECT, VOP_MODE_UNKNOWN, diff --git a/drivers/video/rockchip/rk_vop.c b/drivers/video/rockchip/rk_vop.c index adbc68f..a54af17 100644 --- a/drivers/video/rockchip/rk_vop.c +++ b/drivers/video/rockchip/rk_vop.c @@ -102,6 +102,7 @@ void rkvop_mode_set(struct rk3288_vop *regs, u32 hfront_porch = edid->hfront_porch.typ; u32 vfront_porch = edid->vfront_porch.typ; uint flags; + int mode_flags; switch (mode) { case VOP_MODE_HDMI: @@ -113,9 +114,20 @@ void rkvop_mode_set(struct rk3288_vop *regs, 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; } - flags = V_DSP_OUT_MODE(15) | + if (mode == VOP_MODE_HDMI || mode == VOP_MODE_EDP) + /* RGBaaa */ + mode_flags = 15; + else + /* RGB888 */ + mode_flags = 0; + + 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)); @@ -227,7 +239,7 @@ int rk_display_init(struct udevice *dev, ulong fbbase, ret = rkclk_get_clk(CLK_NEW, &clk); if (!ret) { - ret = clk_set_periph_rate(clk, DCLK_VOP0 + vop_id, + ret = clk_set_periph_rate(clk, DCLK_VOP0 + remote_vop_id, timing.pixelclock.typ); } if (ret) { -- cgit v0.10.2 From c87c30e3ba9ab110c031c14d1f349118d32041c1 Mon Sep 17 00:00:00 2001 From: Jacob Chen Date: Mon, 14 Mar 2016 11:20:19 +0800 Subject: doc: dt-bindings: Describe rockchip LVDS interface I didn't have a common board to enable LVDS. So add this dcocument to help others who want to enable LVDS in their board. Signed-off-by: Jacob Chen Acked-by: Simon Glass diff --git a/doc/device-tree-bindings/video/rockchip-lvds.txt b/doc/device-tree-bindings/video/rockchip-lvds.txt new file mode 100644 index 0000000..07e3d3f --- /dev/null +++ b/doc/device-tree-bindings/video/rockchip-lvds.txt @@ -0,0 +1,77 @@ +Rockchip LVDS interface +------------------ + +Required properties: +- compatible: "rockchip,rk3288-lvds"; + +- reg: physical base address of the controller and length + of memory mapped region. +- clocks: must include clock specifiers corresponding to entries in the + clock-names property. +- clock-names: must contain "pclk_lvds" + +- rockchip,grf: phandle to the general register files syscon + +- rockchip,data-mapping: should be or , + This describes how the color bits are laid out in the + serialized LVDS signal. +- rockchip,data-width : should be <18> or <24>; +- rockchip,output: should be , or + , This describes the output face. + +- display-timings : described by + doc/devicetree/device-tree-bindings/video/display-timing.txt. + +Example: + lvds: lvds@ff96c000 { + compatible = "rockchip,rk3288-lvds"; + reg = <0xff96c000 0x4000>; + clocks = <&cru PCLK_LVDS_PHY>; + clock-names = "pclk_lvds"; + pinctrl-names = "default"; + pinctrl-0 = <&lcdc0_ctl>; + rockchip,grf = <&grf>; + status = "disabled"; + ports { + #address-cells = <1>; + #size-cells = <0>; + + lvds_in: port@0 { + reg = <0>; + + #address-cells = <1>; + #size-cells = <0>; + + lvds_in_vopb: endpoint@0 { + reg = <0>; + remote-endpoint = <&vopb_out_lvds>; + }; + lvds_in_vopl: endpoint@1 { + reg = <1>; + remote-endpoint = <&vopl_out_lvds>; + }; + }; + }; + }; + + &lvds { + rockchip,data-mapping = ; + rockchip,data-width = <24>; + rockchip,output = ; + rockchip,panel = <&panel>; + status = "okay"; + + display-timings { + timing@0 { + clock-frequency = <40000000>; + hactive = <1920>; + vactive = <1080>; + hsync-len = <44>; + hfront-porch = <88>; + hback-porch = <148>; + vfront-porch = <4>; + vback-porch = <36>; + vsync-len = <5>; + }; + }; + }; -- cgit v0.10.2 From 3cb4f25cc702db17455583599d0940c81337a17a Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 9 Mar 2016 16:07:21 +0800 Subject: video: ipu: avoid overflow issue Multiplication, as "clk->parent->rate * 16" may overflow. So use do_div to avoid such issue. Signed-off-by: Peng Fan Signed-off-by: Sandor Yu Cc: Anatolij Gustschin Cc: Stefano Babic Cc: Fabio Estevam diff --git a/drivers/video/ipu_common.c b/drivers/video/ipu_common.c index 9f85102..36d4b23 100644 --- a/drivers/video/ipu_common.c +++ b/drivers/video/ipu_common.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "ipu.h" #include "ipu_regs.h" @@ -275,50 +276,84 @@ static inline void ipu_ch_param_set_buffer(uint32_t ch, int bufNum, static void ipu_pixel_clk_recalc(struct clk *clk) { - u32 div = __raw_readl(DI_BS_CLKGEN0(clk->id)); - if (div == 0) - clk->rate = 0; - else - clk->rate = (clk->parent->rate * 16) / div; + u32 div; + u64 final_rate = (unsigned long long)clk->parent->rate * 16; + + div = __raw_readl(DI_BS_CLKGEN0(clk->id)); + debug("read BS_CLKGEN0 div:%d, final_rate:%lld, prate:%ld\n", + div, final_rate, clk->parent->rate); + + clk->rate = 0; + if (div != 0) { + do_div(final_rate, div); + clk->rate = final_rate; + } } static unsigned long ipu_pixel_clk_round_rate(struct clk *clk, unsigned long rate) { - u32 div, div1; - u32 tmp; + u64 div, final_rate; + u32 remainder; + u64 parent_rate = (unsigned long long)clk->parent->rate * 16; + /* * Calculate divider * Fractional part is 4 bits, * so simply multiply by 2^4 to get fractional part. */ - tmp = (clk->parent->rate * 16); - div = tmp / rate; - + div = parent_rate; + remainder = do_div(div, rate); + /* Round the divider value */ + if (remainder > (rate / 2)) + div++; if (div < 0x10) /* Min DI disp clock divider is 1 */ div = 0x10; if (div & ~0xFEF) div &= 0xFF8; else { - div1 = div & 0xFE0; - if ((tmp/div1 - tmp/div) < rate / 4) - div = div1; - else - div &= 0xFF8; + /* Round up divider if it gets us closer to desired pix clk */ + if ((div & 0xC) == 0xC) { + div += 0x10; + div &= ~0xF; + } } - return (clk->parent->rate * 16) / div; + final_rate = parent_rate; + do_div(final_rate, div); + + return final_rate; } static int ipu_pixel_clk_set_rate(struct clk *clk, unsigned long rate) { - u32 div = (clk->parent->rate * 16) / rate; + u64 div, parent_rate; + u32 remainder; + + parent_rate = (unsigned long long)clk->parent->rate * 16; + div = parent_rate; + remainder = do_div(div, rate); + /* Round the divider value */ + if (remainder > (rate / 2)) + div++; + + /* Round up divider if it gets us closer to desired pix clk */ + if ((div & 0xC) == 0xC) { + div += 0x10; + div &= ~0xF; + } + if (div > 0x1000) + debug("Overflow, DI_BS_CLKGEN0 div:0x%x\n", (u32)div); __raw_writel(div, DI_BS_CLKGEN0(clk->id)); - /* Setup pixel clock timing */ + /* + * Setup pixel clock timing + * Down time is half of period + */ __raw_writel((div / 16) << 16, DI_BS_CLKGEN1(clk->id)); - clk->rate = (clk->parent->rate * 16) / div; + clk->rate = (u64)(clk->parent->rate * 16) / div; + return 0; } -- cgit v0.10.2 From 51674985751b947521f3fea2636ccf799ff1f487 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 9 Mar 2016 16:07:22 +0800 Subject: imx: mx6: correct IPU clock The CONFIG_IPUV3_CLK should be 264000000, to i.MX6DL, it should be 198000000. Signed-off-by: Peng Fan Signed-off-by: Sandor Yu Cc: Stefano Babic Cc: Fabio Estevam Cc: Peter Robinson diff --git a/include/configs/mx6sabre_common.h b/include/configs/mx6sabre_common.h index 29d1f91..a6d821b 100644 --- a/include/configs/mx6sabre_common.h +++ b/include/configs/mx6sabre_common.h @@ -225,7 +225,11 @@ #define CONFIG_BMP_16BPP #define CONFIG_VIDEO_LOGO #define CONFIG_VIDEO_BMP_LOGO -#define CONFIG_IPUV3_CLK 260000000 +#ifdef CONFIG_MX6DL +#define CONFIG_IPUV3_CLK 198000000 +#else +#define CONFIG_IPUV3_CLK 264000000 +#endif #define CONFIG_IMX_HDMI #define CONFIG_IMX_VIDEO_SKIP -- cgit v0.10.2 From 00b1d2d3178747c113dd8f939cf3e2d449bfaf54 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 9 Mar 2016 16:07:23 +0800 Subject: imx: mx6: hdmi: handle overflow condition If HDMI_IH_FC_STAT2_OVERFLOW_MASK is set, we need to do TMDS software reset and write to clear fc_invidconf register. We need minimum 3 times to write to clear the fc_invidconf register, so choose 5 loops here. Signed-off-by: Peng Fan Signed-off-by: Sandor Yu Cc: Stefano Babic Cc: Fabio Estevam diff --git a/arch/arm/cpu/armv7/mx6/soc.c b/arch/arm/cpu/armv7/mx6/soc.c index a34675c..91a3deb 100644 --- a/arch/arm/cpu/armv7/mx6/soc.c +++ b/arch/arm/cpu/armv7/mx6/soc.c @@ -548,7 +548,8 @@ void imx_setup_hdmi(void) { struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR; struct hdmi_regs *hdmi = (struct hdmi_regs *)HDMI_ARB_BASE_ADDR; - int reg; + int reg, count; + u8 val; /* Turn on HDMI PHY clock */ reg = readl(&mxc_ccm->CCGR2); @@ -565,6 +566,16 @@ void imx_setup_hdmi(void) |(CHSCCDR_IPU_PRE_CLK_540M_PFD << MXC_CCM_CHSCCDR_IPU1_DI0_PRE_CLK_SEL_OFFSET); writel(reg, &mxc_ccm->chsccdr); + + /* Clear the overflow condition */ + if (readb(&hdmi->ih_fc_stat2) & HDMI_IH_FC_STAT2_OVERFLOW_MASK) { + /* TMDS software reset */ + writeb((u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, &hdmi->mc_swrstz); + val = readb(&hdmi->fc_invidconf); + /* Need minimum 3 times to write to clear the register */ + for (count = 0 ; count < 5 ; count++) + writeb(val, &hdmi->fc_invidconf); + } } #endif -- cgit v0.10.2