Skip to contentSkip to conversation

am335x-fb: support display PLL for lcd-clock / pixel-clock

PATCH New Hannes Schmelzer 2 participants 14 messages Jan 9, 2018 – Jan 11, 2018

Subsystems (4)
lore
Share & tools
lore
#0 lore raw

v3 cover am335x-fb: support display PLL for lcd-clock / pixel-clock

In most cases days ago the LCDC IP-core clock on AM335x was sourced from the peripheral PLL with 192 MHz and the pixel clock for the connected lcd-screen was made more or less accurate with a divider inside LCDC IP- core.

For certain displays, example a standard VGA with 25.17 MHz pixelclock, it is almost impossible to generate a reasonably clock with the 192 MHz base and a simple divider. For those cases we have to use the dedicated DPLL_DISP generating a dividable clock-source for the LCDC.

This series does this with following steps:
- adding register definition of the display PLL to clock_am33xx
- calculate mult, dividers with lowest error and setup the display PLL, selecting as clock-source for LCDC the display PLL.
- drop the hardcoded dependency in BuR/common to the 192 MHz and provide a real clock-frequency instead a divider
- drop the clock selection for the LCDC within board code
- do minor cosmetic cleanups (updating copyright, coding style)

Changes in v3:
- modify copyright year to 2013-2018

Changes in v2:
- am335x-fb: avoid compiler warning
- board/BuR/common: fix the !CONFIG_USE_FDT case

Hannes Schmelzer (6):
mach-omap2: add AM335x Display PLL register definition
am335x-fb: cosmetic: update-copyright
am335x-fb: cosmetic: fix coding style
am335x-fb: setup display PLL
board/BuR: provide real clock-frequency instead a divider board/BuR: drop LCDC clock manipulation from board code

 arch/arm/include/asm/arch-am33xx/clock.h  |  1 +
 arch/arm/mach-omap2/am33xx/clock_am33xx.c |  7 ++++
 board/BuR/brppt1/board.c                  |  3 --
 board/BuR/brxre1/board.c                  |  2 -
 board/BuR/common/common.c                 | 14 ++-----
 drivers/video/am335x-fb.c                 | 64 +++++++++++++++++++++++++++----
 drivers/video/am335x-fb.h                 |  6 +--
 7 files changed, 71 insertions(+), 26 deletions(-)
Signature · 1 line
2.7.4
#1 lore raw

PATCH v3 1/6 mach-omap2: add AM335x Display PLL register definition

Adds the register definition of the Display DPLL

---

Changes in v3: None
Changes in v2: None

 arch/arm/include/asm/arch-am33xx/clock.h  | 1 +
 arch/arm/mach-omap2/am33xx/clock_am33xx.c | 7 +++++++
 2 files changed, 8 insertions(+)
arch/arm/include/asm/arch-am33xx/clock.h +1 -0
index 5399bb8..9dbcd3a 100644
@@ -104,6 +104,7 @@ extern const struct dpll_regs dpll_mpu_regs;
 extern const struct dpll_regs dpll_core_regs;
 extern const struct dpll_regs dpll_per_regs;
 extern const struct dpll_regs dpll_ddr_regs;
+extern const struct dpll_regs dpll_disp_regs;
 extern const struct dpll_params dpll_mpu_opp[NUM_CRYSTAL_FREQ][NUM_OPPS];
 extern const struct dpll_params dpll_core_1000MHz[NUM_CRYSTAL_FREQ];
 extern const struct dpll_params dpll_per_192MHz[NUM_CRYSTAL_FREQ];
arch/arm/mach-omap2/am33xx/clock_am33xx.c +7 -0
index 1780bbd..9ab4d25 100644
@@ -52,6 +52,13 @@ const struct dpll_regs dpll_ddr_regs = {
 	.cm_div_m2_dpll		= CM_WKUP + 0xA0,
 };
 
+const struct dpll_regs dpll_disp_regs = {
+	.cm_clkmode_dpll	= CM_WKUP + 0x98,
+	.cm_idlest_dpll		= CM_WKUP + 0x48,
+	.cm_clksel_dpll		= CM_WKUP + 0x54,
+	.cm_div_m2_dpll		= CM_WKUP + 0xA4,
+};
+
 struct dpll_params dpll_mpu_opp100 = {
 		CONFIG_SYS_MPUCLK, OSC-1, 1, -1, -1, -1, -1};
 const struct dpll_params dpll_core_opp100 = {
Signature · 1 line
2.7.4
#3 lore raw

PATCH v3 2/6 am335x-fb: cosmetic: update-copyright

In reply to Hannes Schmelzer

---

Changes in v3:
- modify copyright year to 2013-2018

Changes in v2: None

 drivers/video/am335x-fb.c | 4 ++--
 drivers/video/am335x-fb.h | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)
drivers/video/am335x-fb.c +2 -2
index a8b3e74..c8100d8 100644
@@ -1,6 +1,6 @@
 /*
- * Copyright (C) 2013 Hannes Schmelzer <[email protected]>
- * Bernecker & Rainer Industrieelektronik GmbH - http://www.br-automation.com
+ * Copyright (C) 2013-2018 Hannes Schmelzer <[email protected]>
+ * B&R Industrial Automation GmbH - http://www.br-automation.com
  *
  * minimal framebuffer driver for TI's AM335x SoC to be compatible with
  * Wolfgang Denk's LCD-Framework (CONFIG_LCD, common/lcd.c)
drivers/video/am335x-fb.h +2 -2
index 3f4b567..903a8f1 100644
@@ -1,6 +1,6 @@
 /*
- * Copyright (C) 2013 Hannes Schmelzer <[email protected]> -
- * Bernecker & Rainer Industrieelektronik GmbH - http://www.br-automation.com
+ * Copyright (C) 2013-2018 Hannes Schmelzer <[email protected]> -
+ * B&R Industrial Automation GmbH - http://www.br-automation.com
  *
  * SPDX-License-Identifier:	GPL-2.0+
  */
Signature · 1 line
2.7.4
#5 lore raw

PATCH v3 3/6 am335x-fb: cosmetic: fix coding style

In reply to Hannes Schmelzer

---

Changes in v3: None
Changes in v2:
- am335x-fb: avoid compiler warning

 drivers/video/am335x-fb.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/video/am335x-fb.c +3 -3
index c8100d8..697b701 100644
@@ -108,11 +108,11 @@ int am335xfb_init(struct am335x_lcdpanel *panel)
 {
 	u32 raster_ctrl = 0;
 
-	if (0 == gd->fb_base) {
+	if (gd->fb_base == 0) {
 		printf("ERROR: no valid fb_base stored in GLOBAL_DATA_PTR!\n");
 		return -1;
 	}
-	if (0 == panel) {
+	if (panel == NULL) {
 		printf("ERROR: missing ptr to am335x_lcdpanel!\n");
 		return -1;
 	}
@@ -147,7 +147,7 @@ int am335xfb_init(struct am335x_lcdpanel *panel)
 	gd->fb_base += 0x20;
 
 	/* turn ON display through powercontrol function if accessible */
-	if (0 != panel->panel_power_ctrl)
+	if (panel->panel_power_ctrl != NULL)
 		panel->panel_power_ctrl(1);
 
 	debug("am335x-fb: wait for stable power ...\n");
Signature · 1 line
2.7.4
#7 lore raw

PATCH v3 4/6 am335x-fb: setup display PLL

In reply to Hannes Schmelzer

The LCDC IP-core an be feed from several clock sources, one of those is a dedicated DPLL for generating a dividable base-clock for this IP-core.

The TRM specifies the maximum input frequency for the LCCD with 200 MHz, so we must not exceed this value with the PLL frequency (which can lock much higher).

This patch tries every combination of multipliers and divisors of the PLL and the IP-core itself for getting as near as possible the the requested panel->pxl_clk.

---

Changes in v3: None
Changes in v2: None

 drivers/video/am335x-fb.c | 54 ++++++++++++++++++++++++++++++++++++++++++++---
 drivers/video/am335x-fb.h |  2 +-
 2 files changed, 52 insertions(+), 4 deletions(-)
drivers/video/am335x-fb.c +51 -3
index 697b701..a7892f7 100644
@@ -12,7 +12,11 @@
  * SPDX-License-Identifier:	GPL-2.0+
  */
 #include <common.h>
+#include <asm/io.h>
 #include <asm/arch/hardware.h>
+#include <asm/arch/omap.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/sys_proto.h>
 #include <lcd.h>
 #include "am335x-fb.h"
 
@@ -20,6 +24,7 @@
 #error "hw-base address of LCD-Controller (LCD_CNTL_BASE) not defined!"
 #endif
 
+#define LCDC_FMAX				200000000
 
 /* LCD Control Register */
 #define LCD_CLK_DIVISOR(x)			((x) << 8)
@@ -96,6 +101,7 @@ struct am335x_lcdhw {
 };
 
 static struct am335x_lcdhw *lcdhw = (void *)LCD_CNTL_BASE;
+
 DECLARE_GLOBAL_DATA_PTR;
 
 int lcd_get_size(int *line_length)
@@ -108,6 +114,11 @@ int am335xfb_init(struct am335x_lcdpanel *panel)
 {
 	u32 raster_ctrl = 0;
 
+	struct cm_dpll *const cmdpll = (struct cm_dpll *)CM_DPLL;
+	struct dpll_params dpll_disp = { 1, 0, 1, -1, -1, -1, -1 };
+	unsigned int m, n, d, best_d = 2;
+	int err = 0, err_r = 0;
+
 	if (gd->fb_base == 0) {
 		printf("ERROR: no valid fb_base stored in GLOBAL_DATA_PTR!\n");
 		return -1;
@@ -132,14 +143,51 @@ int am335xfb_init(struct am335x_lcdpanel *panel)
 		return -1;
 	}
 
+	/* check given clock-frequency */
+	if (panel->pxl_clk > (LCDC_FMAX / 2)) {
+		pr_err("am335x-fb: requested pxl-clk: %d not supported!\n",
+		       panel->pxl_clk);
+		return -1;
+	}
+
 	debug("setting up LCD-Controller for %dx%dx%d (hfp=%d,hbp=%d,hsw=%d / ",
 	      panel->hactive, panel->vactive, panel->bpp,
 	      panel->hfp, panel->hbp, panel->hsw);
-	debug("vfp=%d,vbp=%d,vsw=%d / clk-div=%d)\n",
-	      panel->vfp, panel->vfp, panel->vsw, panel->pxl_clk_div);
+	debug("vfp=%d,vbp=%d,vsw=%d / clk=%d)\n",
+	      panel->vfp, panel->vfp, panel->vsw, panel->pxl_clk);
 	debug("using frambuffer at 0x%08x with size %d.\n",
 	      (unsigned int)gd->fb_base, FBSIZE(panel));
 
+	/* setup display pll for requested clock frequency */
+	err = panel->pxl_clk;
+	err_r = err;
+
+	for (d = 2; d < 255; d++) {
+		for (m = 2; m < 2047; m++) {
+			if ((V_OSCK * m) < (panel->pxl_clk * d))
+				continue;
+			n = (V_OSCK * m) / (panel->pxl_clk * d);
+			if (n > 127)
+				break;
+			if (((V_OSCK * m) / n) > LCDC_FMAX)
+				break;
+
+			err = abs((V_OSCK * m) / n / d - panel->pxl_clk);
+			if (err < err_r) {
+				err_r = err;
+				dpll_disp.m = m;
+				dpll_disp.n = n;
+				best_d = d;
+			}
+		}
+	}
+	debug("%s: PLL: best error %d Hz (M %d, N %d, DISP %d)\n",
+	      __func__, err_r, dpll_disp.m, dpll_disp.n, best_d);
+	do_setup_dpll(&dpll_disp_regs, &dpll_disp);
+
+	/* clock source for LCDC from dispPLL M2 */
+	writel(0x0, &cmdpll->clklcdcpixelclk);
+
 	/* palette default entry */
 	memset((void *)gd->fb_base, 0, 0x20);
 	*(unsigned int *)gd->fb_base = 0x4000;
@@ -154,7 +202,7 @@ int am335xfb_init(struct am335x_lcdpanel *panel)
 	mdelay(panel->pup_delay);
 	lcdhw->clkc_enable = LCD_CORECLKEN | LCD_LIDDCLKEN | LCD_DMACLKEN;
 	lcdhw->raster_ctrl = 0;
-	lcdhw->ctrl = LCD_CLK_DIVISOR(panel->pxl_clk_div) | LCD_RASTER_MODE;
+	lcdhw->ctrl = LCD_CLK_DIVISOR(best_d) | LCD_RASTER_MODE;
 	lcdhw->lcddma_fb0_base = gd->fb_base;
 	lcdhw->lcddma_fb0_ceiling = gd->fb_base + FBSIZE(panel);
 	lcdhw->lcddma_fb1_base = gd->fb_base;
drivers/video/am335x-fb.h +1 -1
index 903a8f1..f99b341 100644
@@ -53,7 +53,7 @@ struct am335x_lcdpanel {
 	unsigned int	vfp;		/* Vertical front porch */
 	unsigned int	vbp;		/* Vertical back porch */
 	unsigned int	vsw;		/* Vertical Sync Pulse Width */
-	unsigned int	pxl_clk_div;	/* Pixel clock divider*/
+	unsigned int	pxl_clk;	/* Pixel clock */
 	unsigned int	pol;		/* polarity of sync, clock signals */
 	unsigned int	pup_delay;	/*
 					 * time in ms after power on to
Signature · 1 line
2.7.4
#8 lore raw

PATCH v3 4/6 am335x-fb: setup display PLL

Quoted 7 lines · On Tue, 9 Jan 2018 19:01:34 +0100 Hannes Schmelzer oe5hpm at oevsv.at wrote:

The LCDC IP-core an be feed from several clock sources, one of those is a dedicated DPLL for generating a dividable base-clock for this IP-core.

The TRM specifies the maximum input frequency for the LCCD with 200 MHz, so we must not exceed this value with the PLL frequency (which can lock much higher).

Quoted 5 lines

This patch tries every combination of multipliers and divisors of the PLL and the IP-core itself for getting as near as possible the the requested panel->pxl_clk.

#9 lore raw

PATCH v3 5/6 board/BuR: provide real clock-frequency instead a divider

In reply to Hannes Schmelzer

Actual am335x-fb implementation takes now a real clock frequency instead a divider. So this component doesn't need to know anymore some base frequency of the LCDC, we simply provide the pixel-clock frequency.

---

Changes in v3: None
Changes in v2:
- board/BuR/common: fix the !CONFIG_USE_FDT case

 board/BuR/common/common.c | 14 ++++----------
 1 file changed, 4 insertions(+), 10 deletions(-)
board/BuR/common/common.c +4 -10
index 7e49cdf..f2d8a65 100644
@@ -146,13 +146,7 @@ int load_lcdtiming(struct am335x_lcdpanel *panel)
 	pnltmp.vsw = FDTPROP(PATHTIM, "vsync-len");
 	pnltmp.pup_delay = FDTPROP(PATHTIM, "pupdelay");
 	pnltmp.pon_delay = FDTPROP(PATHTIM, "pondelay");
-
-	/* calc. proper clk-divisor */
-	dtbprop = FDTPROP(PATHTIM, "clock-frequency");
-	if (dtbprop != ~0UL)
-		pnltmp.pxl_clk_div = 192000000 / dtbprop;
-	else
-		pnltmp.pxl_clk_div = ~0UL;
+	pnltmp.pxl_clk = FDTPROP(PATHTIM, "clock-frequency");
 
 	/* check polarity of control-signals */
 	dtbprop = FDTPROP(PATHTIM, "hsync-active");
@@ -202,7 +196,7 @@ int load_lcdtiming(struct am335x_lcdpanel *panel)
 	pnltmp.vfp = env_get_ulong("ds1_vfp", 10, ~0UL);
 	pnltmp.vbp = env_get_ulong("ds1_vbp", 10, ~0UL);
 	pnltmp.vsw = env_get_ulong("ds1_vsw", 10, ~0UL);
-	pnltmp.pxl_clk_div = env_get_ulong("ds1_pxlclkdiv", 10, ~0UL);
+	pnltmp.pxl_clk = env_get_ulong("ds1_pxlclk", 10, ~0UL);
 	pnltmp.pol = env_get_ulong("ds1_pol", 16, ~0UL);
 	pnltmp.pup_delay = env_get_ulong("ds1_pupdelay", 10, ~0UL);
 	pnltmp.pon_delay = env_get_ulong("ds1_tondelay", 10, ~0UL);
@@ -218,7 +212,7 @@ int load_lcdtiming(struct am335x_lcdpanel *panel)
 	   ~0UL == (pnltmp.vfp) ||
 	   ~0UL == (pnltmp.vbp) ||
 	   ~0UL == (pnltmp.vsw) ||
-	   ~0UL == (pnltmp.pxl_clk_div) ||
+	   ~0UL == (pnltmp.pxl_clk) ||
 	   ~0UL == (pnltmp.pol) ||
 	   ~0UL == (pnltmp.pup_delay) ||
 	   ~0UL == (pnltmp.pon_delay)
@@ -241,7 +235,7 @@ int load_lcdtiming(struct am335x_lcdpanel *panel)
 			pnltmp.hactive, pnltmp.vactive, pnltmp.bpp,
 			pnltmp.hfp, pnltmp.hbp, pnltmp.hsw,
 			pnltmp.vfp, pnltmp.vbp, pnltmp.vsw,
-			pnltmp.pxl_clk_div, pnltmp.pol, pnltmp.pon_delay);
+			pnltmp.pxl_clk, pnltmp.pol, pnltmp.pon_delay);
 
 		return -1;
 	}
Signature · 1 line
2.7.4
#10 lore raw

PATCH v3 5/6 board/BuR: provide real clock-frequency instead a divider

Quoted 5 lines · On Tue, 9 Jan 2018 19:01:35 +0100 Hannes Schmelzer oe5hpm at oevsv.at wrote:

Actual am335x-fb implementation takes now a real clock frequency instead a divider. So this component doesn't need to know anymore some base frequency of the LCDC, we simply provide the pixel-clock frequency.

#11 lore raw

PATCH v3 6/6 board/BuR: drop LCDC clock manipulation from board code

In reply to Hannes Schmelzer

The clock selection is done now from the am335x-fb code, so there is no more need doing this in the board code.

---

Changes in v3: None
Changes in v2: None

 board/BuR/brppt1/board.c | 3 ---
 board/BuR/brxre1/board.c | 2 --
 2 files changed, 5 deletions(-)
board/BuR/brppt1/board.c +0 -3
index 6083479..9f7b2d9 100644
@@ -120,9 +120,6 @@ void am33xx_spl_board_init(void)
 	};
 	do_enable_clocks(clk_domains, clk_modules_tsspecific, 1);
 
-	/* setup LCD-Pixel Clock */
-	writel(0x2, &cmdpll->clklcdcpixelclk);	/* clock comes from perPLL M2 */
-
 	/* setup I2C */
 	enable_i2c_pin_mux();
 	i2c_set_bus_num(0);
board/BuR/brxre1/board.c +0 -2
index 856ac3d..e176b60 100644
@@ -121,8 +121,6 @@ void am33xx_spl_board_init(void)
 		0
 	};
 	do_enable_clocks(clk_domains, clk_modules_xre1specific, 1);
-	/* setup LCD-Pixel Clock */
-	writel(0x2, CM_DPLL + 0x34);
 	/* power-OFF LCD-Display */
 	gpio_direction_output(LCD_PWR, 0);
 
Signature · 1 line
2.7.4
#13 lore raw

v3 cover am335x-fb: support display PLL for lcd-clock / pixel-clock

In reply to Hannes Schmelzer

On Tue, 9 Jan 2018 19:01:30 +0100
Hannes Schmelzer oe5hpm at oevsv.at wrote:
...

Quoted 8 lines

Hannes Schmelzer (6):
mach-omap2: add AM335x Display PLL register definition
am335x-fb: cosmetic: update-copyright
am335x-fb: cosmetic: fix coding style
am335x-fb: setup display PLL
board/BuR: provide real clock-frequency instead a divider board/BuR: drop LCDC clock manipulation from board code

Quoted 8 lines
 arch/arm/include/asm/arch-am33xx/clock.h  |  1 +
 arch/arm/mach-omap2/am33xx/clock_am33xx.c |  7 ++++
 board/BuR/brppt1/board.c                  |  3 --
 board/BuR/brxre1/board.c                  |  2 -
 board/BuR/common/common.c                 | 14 ++-----
 drivers/video/am335x-fb.c                 | 64 +++++++++++++++++++++++++++----
 drivers/video/am335x-fb.h                 |  6 +--
 7 files changed, 71 insertions(+), 26 deletions(-)

Applied v3 series to u-boot-video/master, thanks!

--
Anatolij