
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.bhat@timesys.com Cc: Stefano Babic sbabic@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)
writel(reg, &mxc_ccm->cs2cdr);| (source << MXC_CCM_CS2CDR_LDB_DI0_CLK_SEL_OFFSET));
- 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