]> git.sur5r.net Git - u-boot/commitdiff
Merge branch 'master' of git://git.denx.de/u-boot-video
authorTom Rini <trini@konsulko.com>
Tue, 15 Mar 2016 12:01:17 +0000 (08:01 -0400)
committerTom Rini <trini@konsulko.com>
Tue, 15 Mar 2016 12:01:17 +0000 (08:01 -0400)
13 files changed:
arch/arm/cpu/armv7/mx6/soc.c
arch/arm/dts/rk3288.dtsi
arch/arm/include/asm/arch-rockchip/lvds_rk3288.h [new file with mode: 0644]
arch/arm/include/asm/arch-rockchip/vop_rk3288.h
doc/device-tree-bindings/video/rockchip-lvds.txt [new file with mode: 0644]
drivers/video/display-uclass.c
drivers/video/ipu_common.c
drivers/video/rockchip/Makefile
drivers/video/rockchip/rk_lvds.c [new file with mode: 0644]
drivers/video/rockchip/rk_vop.c
include/configs/mx6sabre_common.h
include/display.h
include/dt-bindings/video/rk3288.h [new file with mode: 0644]

index a34675c7957522546e8169cb5269748e4e5804c8..91a3debe910e327592e128a6d81d493206ec0925 100644 (file)
@@ -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
 
index e51c75c150c6b8c7e940f108123f7ffea9d2e8f1..3dab0fc83ead0f70f0b2e99e4e3157eccbb46a27 100644 (file)
@@ -9,6 +9,7 @@
 #include <dt-bindings/clock/rk3288-cru.h>
 #include <dt-bindings/power-domain/rk3288.h>
 #include <dt-bindings/thermal/thermal.h>
+#include <dt-bindings/video/rk3288.h>
 #include "skeleton.dtsi"
 
 / {
                                reg = <1>;
                                remote-endpoint = <&hdmi_in_vopb>;
                        };
+                       vopb_out_lvds: endpoint@2 {
+                               reg = <2>;
+                               remote-endpoint = <&lvds_in_vopb>;
+                       };
                };
        };
 
                                reg = <1>;
                                remote-endpoint = <&hdmi_in_vopl>;
                        };
-
+                       vopl_out_lvds: endpoint@2 {
+                               reg = <2>;
+                               remote-endpoint = <&lvds_in_vopl>;
+                       };
                };
        };
 
                };
        };
 
+       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>;
                        };
                };
 
+               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>;
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 (file)
index 0000000..121a898
--- /dev/null
@@ -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
index 0104ba3f2a391f04e145ae53518c18aec8aab79f..0ce3d6746f32aafd92b78bca14ef2a0390ee6a2d 100644 (file)
@@ -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/doc/device-tree-bindings/video/rockchip-lvds.txt b/doc/device-tree-bindings/video/rockchip-lvds.txt
new file mode 100644 (file)
index 0000000..07e3d3f
--- /dev/null
@@ -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 <LVDS_FORMAT_VESA> or  <LVDS_FORMAT_JEIDA>,
+       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 <LVDS_OUTPUT_RGB>, <LVDS_OUTPUT_SINGLE> or
+       <LVDS_OUTPUT_DUAL>, 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 = <LVDS_FORMAT_VESA>;
+               rockchip,data-width = <24>;
+               rockchip,output = <LVDS_OUTPUT_DUAL>;
+               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>;
+                       };
+               };
+       };
index 31522eac65a54aa490632d7bb501891cf49cf545..e4763de73df5e312ee810eeda9edb8d066176bd9 100644 (file)
@@ -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));
index 9f851029157624605e8ae4450432accf283f3bdc..36d4b23bfeb1a8d5c21713ff4837913fa8d3937b 100644 (file)
@@ -19,6 +19,7 @@
 #include <asm/errno.h>
 #include <asm/arch/imx-regs.h>
 #include <asm/arch/crm_regs.h>
+#include <div64.h>
 #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;
 }
 
index 0e9a8acf69e71826a2fe8fc7295b0e3660a81300..7962f8611eed54bda743c76bcce0742b53a5b1a5 100644 (file)
@@ -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 (file)
index 0000000..dc10b86
--- /dev/null
@@ -0,0 +1,254 @@
+/*
+ * 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>
+#include <dt-bindings/video/rk3288.h>
+
+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),
+};
index adbc68f93694a71c9bb1f5c37f64ed870d8671ce..a54af172ec066dbdd9262ec50b97a8a525216ca5 100644 (file)
@@ -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(&regs->sys_ctrl, M_ALL_OUT_EN,
                                V_EDP_OUT_EN(1));
                break;
+       case VOP_MODE_LVDS:
+               clrsetbits_le32(&regs->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) {
index 29d1f913602709750533f98d8270daf334ea29a1..a6d821bb4dcafd6c9f6ce18808ede31d784a0d12 100644 (file)
 #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
 
index c180e76e534baa908d416914bed96e43e17188ec..b1c476628b4f9aa0cb882e435f0535a681a92df3 100644 (file)
@@ -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
@@ -42,6 +42,15 @@ int display_enable(struct udevice *dev, int panel_bpp,
                   const struct display_timing *timing);
 
 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
         *
diff --git a/include/dt-bindings/video/rk3288.h b/include/dt-bindings/video/rk3288.h
new file mode 100644 (file)
index 0000000..7bfd247
--- /dev/null
@@ -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