Implements the below changes:
- Disable LVDS1 on B450v3/B650v3 boards since the final boards no longer
have connectors for the same. Only LVDS0 hardware connectors are present.
- Implement imx6 EB821 or ERR009219 errata for LVDS clock switch.
This patch was ported from Freescale 3.10.17_1.0.0_ga kernel to u-boot.
- Split the display setup into 2 different functions. One for B850v3 that
does a setup of LVDS and HDMI with clock source for LVDS/IPU_DI set to
video PLL. The other for B450v3/B650v3 that does a setup of LVDS only with
clock source for LVDS/IPU_DI set to USB PLL. This helps us generate
accurate pixel clock required for display connected to LVDS.

Signed-off-by: Akshay Bhat <akshay.b...@timesys.com>
Cc: Stefano Babic <sba...@denx.de>
---
 board/ge/bx50v3/bx50v3.c | 240 ++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 198 insertions(+), 42 deletions(-)

diff --git a/board/ge/bx50v3/bx50v3.c b/board/ge/bx50v3/bx50v3.c
index 70c298d..cf2cd1a 100644
--- a/board/ge/bx50v3/bx50v3.c
+++ b/board/ge/bx50v3/bx50v3.c
@@ -390,55 +390,208 @@ struct display_info_t const displays[] = {{
 } } };
 size_t display_count = ARRAY_SIZE(displays);
 
-static void setup_display(void)
+static void enable_videopll(void)
+{
+       struct mxc_ccm_reg *ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR;
+       s32 timeout = 100000;
+
+       setbits_le32(&ccm->analog_pll_video, BM_ANADIG_PLL_VIDEO_POWERDOWN);
+
+       /* set video pll to 910MHz (24MHz * (37+11/12))
+        * video pll post div to 910/4 = 227.5MHz
+        */
+       clrsetbits_le32(&ccm->analog_pll_video,
+                       BM_ANADIG_PLL_VIDEO_DIV_SELECT |
+                       BM_ANADIG_PLL_VIDEO_POST_DIV_SELECT,
+                       BF_ANADIG_PLL_VIDEO_DIV_SELECT(37) |
+                       BF_ANADIG_PLL_VIDEO_POST_DIV_SELECT(0));
+
+       writel(BF_ANADIG_PLL_VIDEO_NUM_A(11), &ccm->analog_pll_video_num);
+       writel(BF_ANADIG_PLL_VIDEO_DENOM_B(12), &ccm->analog_pll_video_denom);
+
+       clrbits_le32(&ccm->analog_pll_video, BM_ANADIG_PLL_VIDEO_POWERDOWN);
+
+       while (timeout--)
+               if (readl(&ccm->analog_pll_video) & BM_ANADIG_PLL_VIDEO_LOCK)
+                       break;
+       if (timeout < 0)
+               printf("Warning: video pll lock timeout!\n");
+
+       clrsetbits_le32(&ccm->analog_pll_video,
+                       BM_ANADIG_PLL_VIDEO_BYPASS,
+                       BM_ANADIG_PLL_VIDEO_ENABLE);
+}
+
+static void set_ldb_clock_source(u8 source)
 {
        struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR;
-       struct iomuxc *iomux = (struct iomuxc *)IOMUXC_BASE_ADDR;
        int reg;
+       /*
+        * Need to follow a strict procedure when changing the LDB
+        * clock, else we can introduce a glitch. Things to keep in
+        * mind:
+        * 1. The current and new parent clocks must be disabled.
+        * 2. The default clock for ldb_dio_clk is mmdc_ch1 which has
+        * no CG bit.
+        * 3. In the RTL implementation of the LDB_DI_CLK_SEL mux
+        * the top four options are in one mux and the PLL3 option along
+        * with another option is in the second mux. There is third mux
+        * used to decide between the first and second mux.
+        * The code below switches the parent to the bottom mux first
+        * and then manipulates the top mux. This ensures that no glitch
+        * will enter the divider.
+        *
+        * Need to disable MMDC_CH1 clock manually as there is no CG bit
+        * for this clock. The only way to disable this clock is to move
+        * it to pll3_sw_clk and then to disable pll3_sw_clk
+        * Make sure periph2_clk2_sel is set to pll3_sw_clk
+        */
+       /* Set MMDC_CH1 mask bit */
+       reg = readl(&mxc_ccm->ccdr);
+       reg |= MXC_CCM_CCDR_MMDC_CH1_HS_MASK;
+       writel(reg, &mxc_ccm->ccdr);
+
+       /* Set periph2_clk2_sel to be sourced from PLL3_sw_clk */
+       reg = readl(&mxc_ccm->cbcmr);
+       reg &= ~MXC_CCM_CBCMR_PERIPH2_CLK2_SEL;
+       writel(reg, &mxc_ccm->cbcmr);
+
+       /*
+        * Set the periph2_clk_sel to the top mux so that
+        * mmdc_ch1 is from pll3_sw_clk.
+        */
+       reg = readl(&mxc_ccm->cbcdr);
+       reg |= MXC_CCM_CBCDR_PERIPH2_CLK_SEL;
+       writel(reg, &mxc_ccm->cbcdr);
+
+       /* Wait for the clock switch */
+       while (readl(&mxc_ccm->cdhipr))
+               ;
+       /* Disable pll3_sw_clk by selecting bypass clock source */
+       reg = readl(&mxc_ccm->ccsr);
+       reg |= MXC_CCM_CCSR_PLL3_SW_CLK_SEL;
+       writel(reg, &mxc_ccm->ccsr);
+
+       /* Set the ldb_di0_clk and ldb_di1_clk to 111b */
+       reg = readl(&mxc_ccm->cs2cdr);
+       reg |= ((7 << MXC_CCM_CS2CDR_LDB_DI1_CLK_SEL_OFFSET)
+             | (7 << MXC_CCM_CS2CDR_LDB_DI0_CLK_SEL_OFFSET));
+       writel(reg, &mxc_ccm->cs2cdr);
 
-       enable_ipu_clock();
-       imx_setup_hdmi();
-
-       reg = readl(&mxc_ccm->CCGR3);
-       reg |=  MXC_CCM_CCGR3_LDB_DI0_MASK;
-       writel(reg, &mxc_ccm->CCGR3);
+       /* Set the ldb_di0_clk and ldb_di1_clk to 100b */
+       reg = readl(&mxc_ccm->cs2cdr);
+       reg &= ~(MXC_CCM_CS2CDR_LDB_DI1_CLK_SEL_MASK
+             | MXC_CCM_CS2CDR_LDB_DI0_CLK_SEL_MASK);
+       reg |= ((4 << MXC_CCM_CS2CDR_LDB_DI1_CLK_SEL_OFFSET)
+             | (4 << MXC_CCM_CS2CDR_LDB_DI0_CLK_SEL_OFFSET));
+       writel(reg, &mxc_ccm->cs2cdr);
 
+       /* Set the ldb_di0_clk and ldb_di1_clk to desired source */
        reg = readl(&mxc_ccm->cs2cdr);
-       reg &= ~(MXC_CCM_CS2CDR_LDB_DI0_CLK_SEL_MASK |
-                MXC_CCM_CS2CDR_LDB_DI1_CLK_SEL_MASK);
-       reg |= (3 << MXC_CCM_CS2CDR_LDB_DI0_CLK_SEL_OFFSET) |
-              (3 << MXC_CCM_CS2CDR_LDB_DI1_CLK_SEL_OFFSET);
+       reg &= ~(MXC_CCM_CS2CDR_LDB_DI1_CLK_SEL_MASK
+             | MXC_CCM_CS2CDR_LDB_DI0_CLK_SEL_MASK);
+       reg |= ((source << MXC_CCM_CS2CDR_LDB_DI1_CLK_SEL_OFFSET)
+             | (source << MXC_CCM_CS2CDR_LDB_DI0_CLK_SEL_OFFSET));
        writel(reg, &mxc_ccm->cs2cdr);
 
-       reg = readl(&mxc_ccm->cscmr2);
-       reg |= (MXC_CCM_CSCMR2_LDB_DI0_IPU_DIV);
-       writel(reg, &mxc_ccm->cscmr2);
-
-       reg = readl(&mxc_ccm->chsccdr);
-       reg |= (CHSCCDR_CLK_SEL_LDB_DI0
-               << MXC_CCM_CHSCCDR_IPU1_DI0_CLK_SEL_OFFSET);
-       writel(reg, &mxc_ccm->chsccdr);
-
-       reg = IOMUXC_GPR2_BGREF_RRMODE_EXTERNAL_RES
-            | IOMUXC_GPR2_DI1_VS_POLARITY_ACTIVE_HIGH
-            | IOMUXC_GPR2_DI0_VS_POLARITY_ACTIVE_LOW
-            | IOMUXC_GPR2_BIT_MAPPING_CH1_SPWG
-            | IOMUXC_GPR2_DATA_WIDTH_CH1_24BIT
-            | IOMUXC_GPR2_BIT_MAPPING_CH0_SPWG
-            | IOMUXC_GPR2_DATA_WIDTH_CH0_24BIT
-            | IOMUXC_GPR2_LVDS_CH1_MODE_ENABLED_DI0
-            | IOMUXC_GPR2_LVDS_CH0_MODE_ENABLED_DI0;
-       writel(reg, &iomux->gpr[2]);
-
-       reg = readl(&iomux->gpr[3]);
-       reg = (reg & ~(IOMUXC_GPR3_LVDS0_MUX_CTL_MASK |
-                      IOMUXC_GPR3_LVDS1_MUX_CTL_MASK |
-                      IOMUXC_GPR3_HDMI_MUX_CTL_MASK))
-           | (IOMUXC_GPR3_MUX_SRC_IPU1_DI0
-              << IOMUXC_GPR3_LVDS0_MUX_CTL_OFFSET)
-           | (IOMUXC_GPR3_MUX_SRC_IPU1_DI0
-              << IOMUXC_GPR3_LVDS1_MUX_CTL_OFFSET);
-       writel(reg, &iomux->gpr[3]);
+       /* Unbypass pll3_sw_clk */
+       reg = readl(&mxc_ccm->ccsr);
+       reg &= ~MXC_CCM_CCSR_PLL3_SW_CLK_SEL;
+       writel(reg, &mxc_ccm->ccsr);
+
+       /*
+        * Set the periph2_clk_sel back to the bottom mux so that
+        * mmdc_ch1 is from its original parent.
+        */
+       reg = readl(&mxc_ccm->cbcdr);
+       reg &= ~MXC_CCM_CBCDR_PERIPH2_CLK_SEL;
+       writel(reg, &mxc_ccm->cbcdr);
+
+       /* Wait for the clock switch */
+       while (readl(&mxc_ccm->cdhipr))
+               ;
+       /* Clear MMDC_CH1 mask bit */
+       reg = readl(&mxc_ccm->ccdr);
+       reg &= ~MXC_CCM_CCDR_MMDC_CH1_HS_MASK;
+       writel(reg, &mxc_ccm->ccdr);
+}
+
+static void setup_display_b850v3(void)
+{
+       struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR;
+       struct iomuxc *iomux = (struct iomuxc *)IOMUXC_BASE_ADDR;
+
+       /* Set LDB clock to Video PLL */
+       set_ldb_clock_source(0);
+       enable_videopll();
+
+       /* IPU1 D0 clock is 227.5 / 3.5 = 65MHz */
+       clrbits_le32(&mxc_ccm->cscmr2, MXC_CCM_CSCMR2_LDB_DI0_IPU_DIV);
+
+       imx_setup_hdmi();
+
+       /* Set LDB_DI0 as clock source for IPU_DI0 */
+       clrsetbits_le32(&mxc_ccm->chsccdr,
+                       MXC_CCM_CHSCCDR_IPU1_DI0_CLK_SEL_MASK,
+                       (CHSCCDR_CLK_SEL_LDB_DI0 <<
+                        MXC_CCM_CHSCCDR_IPU1_DI0_CLK_SEL_OFFSET));
+
+       /* Turn on IPU LDB DI0 clocks */
+       setbits_le32(&mxc_ccm->CCGR3, MXC_CCM_CCGR3_LDB_DI0_MASK);
+
+       enable_ipu_clock();
+
+       writel(IOMUXC_GPR2_BGREF_RRMODE_EXTERNAL_RES |
+              IOMUXC_GPR2_DI1_VS_POLARITY_ACTIVE_LOW |
+              IOMUXC_GPR2_DI0_VS_POLARITY_ACTIVE_LOW |
+              IOMUXC_GPR2_BIT_MAPPING_CH1_SPWG |
+              IOMUXC_GPR2_DATA_WIDTH_CH1_24BIT |
+              IOMUXC_GPR2_BIT_MAPPING_CH0_SPWG |
+              IOMUXC_GPR2_DATA_WIDTH_CH0_24BIT |
+              IOMUXC_GPR2_SPLIT_MODE_EN_MASK |
+              IOMUXC_GPR2_LVDS_CH0_MODE_ENABLED_DI0 |
+              IOMUXC_GPR2_LVDS_CH1_MODE_ENABLED_DI0,
+              &iomux->gpr[2]);
+
+       clrbits_le32(&iomux->gpr[3],
+                    IOMUXC_GPR3_LVDS0_MUX_CTL_MASK |
+                    IOMUXC_GPR3_LVDS1_MUX_CTL_MASK |
+                    IOMUXC_GPR3_HDMI_MUX_CTL_MASK);
+}
+
+static void setup_display_bx50v3(void)
+{
+       struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR;
+       struct iomuxc *iomux = (struct iomuxc *)IOMUXC_BASE_ADDR;
+
+       /* Set LDB clock to USB PLL */
+       set_ldb_clock_source(4);
+
+       /* IPU1 DI0 clock is 480/7 = 68.5 MHz */
+       setbits_le32(&mxc_ccm->cscmr2, MXC_CCM_CSCMR2_LDB_DI0_IPU_DIV);
+
+       /* Set LDB_DI0 as clock source for IPU_DI0 */
+       clrsetbits_le32(&mxc_ccm->chsccdr,
+                       MXC_CCM_CHSCCDR_IPU1_DI0_CLK_SEL_MASK,
+                       (CHSCCDR_CLK_SEL_LDB_DI0 <<
+                       MXC_CCM_CHSCCDR_IPU1_DI0_CLK_SEL_OFFSET));
+
+       /* Turn on IPU LDB DI0 clocks */
+       setbits_le32(&mxc_ccm->CCGR3, MXC_CCM_CCGR3_LDB_DI0_MASK);
+
+       enable_ipu_clock();
+
+       writel(IOMUXC_GPR2_BGREF_RRMODE_EXTERNAL_RES |
+              IOMUXC_GPR2_DI0_VS_POLARITY_ACTIVE_LOW |
+              IOMUXC_GPR2_BIT_MAPPING_CH0_SPWG |
+              IOMUXC_GPR2_DATA_WIDTH_CH0_24BIT |
+              IOMUXC_GPR2_LVDS_CH0_MODE_ENABLED_DI0,
+              &iomux->gpr[2]);
+
+       clrsetbits_le32(&iomux->gpr[3],
+                       IOMUXC_GPR3_LVDS0_MUX_CTL_MASK,
+                      (IOMUXC_GPR3_MUX_SRC_IPU1_DI0 <<
+                       IOMUXC_GPR3_LVDS0_MUX_CTL_OFFSET));
 
        /* backlights off until needed */
        imx_iomux_v3_setup_multiple_pads(backlight_pads,
@@ -487,7 +640,10 @@ int board_init(void)
        gpio_direction_output(SUS_S3_OUT, 1);
        gpio_direction_output(WIFI_EN, 1);
 #if defined(CONFIG_VIDEO_IPUV3)
-       setup_display();
+       if (IS_ENABLED(CONFIG_TARGET_GE_B850V3))
+               setup_display_b850v3();
+       else
+               setup_display_bx50v3();
 #endif
        /* address of boot parameters */
        gd->bd->bi_boot_params = PHYS_SDRAM + 0x100;
-- 
2.7.3

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to