All of lore.kernel.org
 help / color / mirror / Atom feed
From: Stefano Babic <sbabic@denx.de>
To: u-boot@lists.denx.de
Subject: [U-Boot] [PATCH] board: ge: bx50v3: Update display setup
Date: Wed, 6 Apr 2016 17:18:52 +0200	[thread overview]
Message-ID: <570528DC.1020203@denx.de> (raw)
In-Reply-To: <1458065424-19824-1-git-send-email-akshay.bhat@timesys.com>

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)
> +	      | (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: sbabic at denx.de
=====================================================================

  parent reply	other threads:[~2016-04-06 15:18 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-03-15 18:10 [U-Boot] [PATCH] board: ge: bx50v3: Update display setup Akshay Bhat
2016-03-28 19:27 ` Akshay Bhat
2016-04-06 15:18 ` Stefano Babic [this message]
2016-04-06 15:22   ` Fabio Estevam
2016-04-07  5:10     ` Akshay Bhat
2016-04-07 12:29       ` Fabio Estevam

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=570528DC.1020203@denx.de \
    --to=sbabic@denx.de \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.