Hi Akshay, On 15/03/2016 19:10, Akshay Bhat wrote: > 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.
I propose you split your patch exactly as you describe in the commit message into 3 patches. Generally, a patch/patchset should address a single issue. This lets to better understand the workarounf with MMDC handshake, and let use it on other boards too. > > 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); > +} I frankly ask you if you think that this function can be factorized and moved from board code to common code. What do you think ? Is there something that let the implementation bound to the board (I have not seen it) ? > + > +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; > Best regards, Stefano Babic -- ===================================================================== DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: +49-8142-66989-53 Fax: +49-8142-66989-80 Email: sba...@denx.de ===================================================================== _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot