[coreboot-gerrit] New patch to review for coreboot: d397126 tegra124: Setup clock PLLD by approximating display panel pixel clock.

Marc Jones (marc.jones@se-eng.com) gerrit at coreboot.org
Wed Dec 10 04:20:19 CET 2014


Marc Jones (marc.jones at se-eng.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/7762

-gerrit

commit d397126e56f05fb49dedc3f9c1fdb4cf6a8926d3
Author: Hung-Te Lin <hungte at chromium.org>
Date:   Tue Apr 8 20:03:40 2014 +0800

    tegra124: Setup clock PLLD by approximating display panel pixel clock.
    
    PLLD, the clock for display, was previously hard-coded to 306MHz. To support
    more different panels, we should calcualte PLLD by panel pixel clock
    configuration.
    
    Note existing pixel clock configurations for nyan* boards won't work (they used
    to rely on hard-coded approximated values) so the device trees are also
    modified.
    
    BRANCH=none
    BUG=chrome-os-partner:25933
    TEST=emerge-nyan_big coreboot chromeos-bootimage
         See panel correctly initialized and got DEV screen.
    
    Original-Change-Id: I8d592f0cc044e7c4e4803c45955642e791210ad3
    Original-Signed-off-by: Hung-Te Lin <hungte at chromium.org>
    Original-Reviewed-on: https://chromium-review.googlesource.com/193565
    (cherry picked from commit 4f9b793633ebb2d104b0544e3b72fa0d105951c4)
    Signed-off-by: Marc Jones <marc.jones at se-eng.com>
    
    Change-Id: Ib2cabbad60af010e872505e888eab485ba8c2916
---
 src/mainboard/google/nyan/devicetree.cb       | 10 +--
 src/mainboard/google/nyan_big/devicetree.cb   | 10 +--
 src/mainboard/google/nyan_blaze/devicetree.cb | 10 +--
 src/soc/nvidia/tegra124/chip.h                |  1 -
 src/soc/nvidia/tegra124/clock.c               | 92 +++++++++++++++++++++++----
 src/soc/nvidia/tegra124/display.c             | 28 ++++++--
 src/soc/nvidia/tegra124/include/soc/clock.h   |  1 +
 7 files changed, 106 insertions(+), 46 deletions(-)

diff --git a/src/mainboard/google/nyan/devicetree.cb b/src/mainboard/google/nyan/devicetree.cb
index 600bdbe..8854450 100644
--- a/src/mainboard/google/nyan/devicetree.cb
+++ b/src/mainboard/google/nyan/devicetree.cb
@@ -79,15 +79,7 @@ chip soc/nvidia/tegra124
 	register "vsync_width" = "12"
 	register "vback_porch" = "12"
 
-	# we *know* the pixel clock for this system.
-	# 1592 x 800 x 60Hz = 76416000
-	register "pixel_clock" = "76416000"
-	register "pll_div" = "2"
-
-	# use plld_out0 (ie, plld/2) as clock source
-	#  plld -> plld_out0 -> pclk
-	#  plld = plld_out0 * 2 = (pclk * pll_div) * 2
-	#       = 305664000Hz
+	register "pixel_clock" = "76400000"
 
 	# link configurations
 	register "lane_count" = "1"
diff --git a/src/mainboard/google/nyan_big/devicetree.cb b/src/mainboard/google/nyan_big/devicetree.cb
index 600bdbe..8854450 100644
--- a/src/mainboard/google/nyan_big/devicetree.cb
+++ b/src/mainboard/google/nyan_big/devicetree.cb
@@ -79,15 +79,7 @@ chip soc/nvidia/tegra124
 	register "vsync_width" = "12"
 	register "vback_porch" = "12"
 
-	# we *know* the pixel clock for this system.
-	# 1592 x 800 x 60Hz = 76416000
-	register "pixel_clock" = "76416000"
-	register "pll_div" = "2"
-
-	# use plld_out0 (ie, plld/2) as clock source
-	#  plld -> plld_out0 -> pclk
-	#  plld = plld_out0 * 2 = (pclk * pll_div) * 2
-	#       = 305664000Hz
+	register "pixel_clock" = "76400000"
 
 	# link configurations
 	register "lane_count" = "1"
diff --git a/src/mainboard/google/nyan_blaze/devicetree.cb b/src/mainboard/google/nyan_blaze/devicetree.cb
index 600bdbe..8854450 100644
--- a/src/mainboard/google/nyan_blaze/devicetree.cb
+++ b/src/mainboard/google/nyan_blaze/devicetree.cb
@@ -79,15 +79,7 @@ chip soc/nvidia/tegra124
 	register "vsync_width" = "12"
 	register "vback_porch" = "12"
 
-	# we *know* the pixel clock for this system.
-	# 1592 x 800 x 60Hz = 76416000
-	register "pixel_clock" = "76416000"
-	register "pll_div" = "2"
-
-	# use plld_out0 (ie, plld/2) as clock source
-	#  plld -> plld_out0 -> pclk
-	#  plld = plld_out0 * 2 = (pclk * pll_div) * 2
-	#       = 305664000Hz
+	register "pixel_clock" = "76400000"
 
 	# link configurations
 	register "lane_count" = "1"
diff --git a/src/soc/nvidia/tegra124/chip.h b/src/soc/nvidia/tegra124/chip.h
index 87d043a..89a8d97 100644
--- a/src/soc/nvidia/tegra124/chip.h
+++ b/src/soc/nvidia/tegra124/chip.h
@@ -89,7 +89,6 @@ struct soc_nvidia_tegra124_config {
 	int vfront_porch;
 
 	int pixel_clock;
-	int pll_div;;
 
 	/* The minimum link configuraton settings */
 	u32 lane_count;
diff --git a/src/soc/nvidia/tegra124/clock.c b/src/soc/nvidia/tegra124/clock.c
index 4675b7b..d12c4b2 100644
--- a/src/soc/nvidia/tegra124/clock.c
+++ b/src/soc/nvidia/tegra124/clock.c
@@ -88,7 +88,6 @@ struct {
 	int khz;
 	struct pllcx_dividers	pllx;	/* target:  CONFIG_PLLX_KHZ */
 	struct pllcx_dividers	pllc;	/* target:  600 MHz */
-	struct pllpad_dividers	plld;	/* target:  306 MHz */
 	struct pllu_dividers	pllu;	/* target;  960 MHz */
 	struct pllcx_dividers	plldp;	/* target;  270 MHz */
 	/* Based on T124 TRM (to be updatd), PLLP is set to 408MHz in HW.
@@ -99,7 +98,6 @@ struct {
 		.khz = 12000,
 		.pllx = {.n = TEGRA_PLLX_KHZ / 12000, .m =  1, .p = 0},
 		.pllc = {.n =  50, .m =  1, .p = 0},
-		.plld = {.n = 306, .m = 12, .p = 0, .cpcon = 8},
 		.pllu = {.n = 960, .m = 12, .p = 0, .cpcon = 12, .lfcon = 2},
 		.plldp = {.n = 90, .m =  1, .p = 3},
 	},
@@ -107,7 +105,6 @@ struct {
 		.khz = 13000,
 		.pllx = {.n = TEGRA_PLLX_KHZ / 13000, .m =  1, .p = 0},
 		.pllc = {.n = 231, .m =  5, .p = 0},		/* 600.6 MHz */
-		.plld = {.n = 306, .m = 13, .p = 0, .cpcon = 8},
 		.pllu = {.n = 960, .m = 13, .p = 0, .cpcon = 12, .lfcon = 2},
 		.plldp = {.n = 83, .m =  1, .p = 3},		/* 269.75 MHz */
 	},
@@ -115,7 +112,6 @@ struct {
 		.khz = 16800,
 		.pllx = {.n = TEGRA_PLLX_KHZ / 16800, .m =  1, .p = 0},
 		.pllc = {.n = 250, .m =  7, .p = 0},
-		.plld = {.n = 309, .m = 17, .p = 0, .cpcon = 8}, /* 305.4 MHz*/
 		.pllu = {.n = 400, .m =  7, .p = 0, .cpcon = 5, .lfcon = 2},
 		.plldp = {.n = 64, .m =  1, .p = 3},		/* 268.8 MHz */
 	},
@@ -123,7 +119,6 @@ struct {
 		.khz = 19200,
 		.pllx = {.n = TEGRA_PLLX_KHZ / 19200, .m =  1, .p = 0},
 		.pllc = {.n = 125, .m =  4, .p = 0},
-		.plld = {.n = 271, .m = 17, .p = 0, .cpcon = 8}, /* 306.1 MHz */
 		.pllu = {.n = 200, .m =  4, .p = 0, .cpcon = 3, .lfcon = 2},
 		.plldp = {.n = 56, .m =  1, .p = 3},		/* 270.75 MHz */
 	},
@@ -131,7 +126,6 @@ struct {
 		.khz = 26000,
 		.pllx = {.n = TEGRA_PLLX_KHZ / 26000, .m =  1, .p = 0},
 		.pllc = {.n =  23, .m =  1, .p = 0},		   /* 598 MHz */
-		.plld = {.n = 306, .m = 26, .p = 0, .cpcon = 8},
 		.pllu = {.n = 960, .m = 26, .p = 0, .cpcon = 12, .lfcon = 2},
 		.plldp = {.n = 83, .m =  2, .p = 3},		/* 266.50 MHz */
 	},
@@ -143,7 +137,6 @@ struct {
 		 */
 		.pllx = {.n = TEGRA_PLLX_KHZ / 19200, .m =  1, .p = 0},
 		.pllc = {.n = 125, .m =  4, .p = 0},
-		.plld = {.n = 271, .m = 17, .p = 0, .cpcon = 8}, /* 306.1 MHz */
 		.pllu = {.n = 200, .m =  4, .p = 0, .cpcon = 3, .lfcon = 2},
 		.plldp = {.n = 56, .m =  2, .p = 3},		/* 268 MHz */
 	},
@@ -155,7 +148,6 @@ struct {
 		 */
 		.pllx = {.n = TEGRA_PLLX_KHZ / 12000, .m =  1, .p = 0},
 		.pllc = {.n =  50, .m =  1, .p = 0},
-		.plld = {.n = 306, .m = 12, .p = 0, .cpcon = 8},
 		.pllu = {.n = 960, .m = 12, .p = 0, .cpcon = 12, .lfcon = 2},
 		.plldp = {.n = 90, .m =  4, .p = 3},		/* 264 MHz */
 	},
@@ -288,10 +280,86 @@ static void graphics_pll(void)
 	scfg = (1<<28) | (1<<24);
 	writel(scfg, cfg);
 
-	/* Init clock source for disp1: plld (actually plld_out0) */
-	init_pll(&clk_rst->plld_base, &clk_rst->plld_misc,
-		osc_table[osc].plld,
-		(PLLUD_MISC_LOCK_ENABLE | PLLD_MISC_CLK_ENABLE));
+	/* disp1 will be set when panel information (pixel clock) is
+	 * retrieved (clock_display).
+	 */
+}
+
+/* Init PLLD clock source. */
+int
+clock_display(u32 frequency)
+{
+	/**
+	 * plld (fo) = vco >> p, where 500MHz < vco < 1000MHz
+	 *           = (cf * n) >> p, where 1MHz < cf < 6MHz
+	 *           = ((ref / m) * n) >> p
+	 *
+	 * Assume p = 0, find (m, n). since m has only 5 bits, we can iterate
+	 * all possible values.  Note Tegra 124 supports 11 bits for n, but our
+	 * pll_fields has only 10 bits for n.
+	 *
+	 * Note values undershoot or overshoot target output frequency may not
+	 * work if the value is not in "safe" range in panel specification, so
+	 * we want exact match.
+	 */
+
+	struct pllpad_dividers plld = { 0 };
+	u32 ref = clock_get_osc_khz() * 1000, m, n;
+	u32 cf, vco = frequency;
+	const u32 max_m = 1 << 5, max_n = 1 << 10, mhz = 1000 * 1000,
+		  min_vco = 500 * mhz, max_vco = 1000 * mhz,
+		  min_cf = 1 * mhz, max_cf = 6 * mhz;
+
+	/* TODO(hungte) Replace this by clock_get_pll_input_khz */
+	switch (clock_get_osc_bits()) {
+	case OSC_FREQ_OSC48:
+		ref /= 4;
+		break;
+	case OSC_FREQ_OSC38P4:
+		ref /= 2;
+		break;
+	}
+
+	if (vco < min_vco || vco > max_vco) {
+		printk(BIOS_ERR, "%s: VCO (%d) out of range. Cannot support.\n",
+		       __func__, vco);
+		return -1;
+	}
+
+	for (m = 1; m < max_m; m++) {
+		cf = ref / m;
+		if (cf < min_cf)
+			break;
+
+		n = vco / cf;
+		if (vco != cf * n || n >= max_n || cf > max_cf)
+			continue;
+
+		plld.m = m;
+		plld.n = n;
+
+		if (n < 50)
+			plld.cpcon = 2;
+		else if (n < 300)
+			plld.cpcon = 3;
+		else if (n < 600)
+			plld.cpcon = 8;
+		else
+			plld.cpcon = 12;
+
+		printk(BIOS_DEBUG, "%s: PLLD=%u ref=%u, m/n/p/cpcon="
+		       "%u/%u/%u/%u\n", __func__,
+		       (ref / plld.m * plld.n) >> plld.p, ref,
+		       plld.m, plld.n, plld.p, plld.cpcon);
+
+		init_pll(&clk_rst->plld_base, &clk_rst->plld_misc, plld,
+			 (PLLUD_MISC_LOCK_ENABLE | PLLD_MISC_CLK_ENABLE));
+		return 0;
+	}
+
+	printk(BIOS_ERR, "%s: Failed to match output frequency %u.\n",
+	       __func__, frequency);
+	return -1;
 }
 
 /* Initialize the UART and put it on CLK_M so we can use it during clock_init().
diff --git a/src/soc/nvidia/tegra124/display.c b/src/soc/nvidia/tegra124/display.c
index 4a0f450..c3117fd 100644
--- a/src/soc/nvidia/tegra124/display.c
+++ b/src/soc/nvidia/tegra124/display.c
@@ -98,8 +98,6 @@ static void print_mode(const struct soc_nvidia_tegra124_config *config)
 static int update_display_mode(struct display_controller *disp_ctrl,
 			       struct soc_nvidia_tegra124_config *config)
 {
-	unsigned long div = config->pll_div;
-
 	print_mode(config);
 
 	WRITEL(0x1, &disp_ctrl->disp.disp_timing_opt);
@@ -119,10 +117,25 @@ static int update_display_mode(struct display_controller *disp_ctrl,
 	WRITEL(config->xres | (config->yres << 16),
 		&disp_ctrl->disp.disp_active);
 
+	/**
+	 * We want to use PLLD_out0, which is PLLD / 2:
+	 *   PixelClock = (PLLD / 2) / ShiftClockDiv / PixelClockDiv.
+	 *
+	 * Currently most panels work inside clock range 50MHz~100MHz, and PLLD
+	 * has some requirements to have VCO in range 500MHz~1000MHz (see
+	 * clock.c for more detail). To simplify calculation, we set
+	 * PixelClockDiv to 1 and ShiftClockDiv to 5. In future these values
+	 * may be calculated by clock_display, to allow wider frequency range.
+	 *
+	 * Note ShiftClockDiv is a 7.1 format value.
+	 */
+	const u32 shift_clock_div = 5;
 	WRITEL((PIXEL_CLK_DIVIDER_PCD1 << PIXEL_CLK_DIVIDER_SHIFT) |
-		SHIFT_CLK_DIVIDER(div),
-		&disp_ctrl->disp.disp_clk_ctrl);
-	return 0;
+	       ((shift_clock_div - 1) * 2) << SHIFT_CLK_DIVIDER_SHIFT,
+	       &disp_ctrl->disp.disp_clk_ctrl);
+	printk(BIOS_DEBUG, "%s: PixelClock=%u, ShiftClockDiv=%u\n",
+	       __func__, config->pixel_clock, shift_clock_div);
+	return clock_display(config->pixel_clock * shift_clock_div * 2);
 }
 
 static void update_window(struct display_controller *disp_ctrl,
@@ -285,7 +298,10 @@ void display_startup(device_t dev)
 	}
 
 	/* Configure dc mode */
-	update_display_mode(disp_ctrl, config);
+	if (update_display_mode(disp_ctrl, config)) {
+		printk(BIOS_ERR, "dc: failed to configure display mode.\n");
+		return;
+	}
 
 	/* Enable dp */
 	dp_enable(dc->out);
diff --git a/src/soc/nvidia/tegra124/include/soc/clock.h b/src/soc/nvidia/tegra124/include/soc/clock.h
index 10cf2d8..aff6abe 100644
--- a/src/soc/nvidia/tegra124/include/soc/clock.h
+++ b/src/soc/nvidia/tegra124/include/soc/clock.h
@@ -278,6 +278,7 @@ enum clock_source {  /* Careful: Not true for all sources, always check TRM! */
 #define TEGRA_PLLU_KHZ   (960000)
 
 int clock_get_osc_khz(void);
+int clock_display(u32 frequency);
 void clock_early_uart(void);
 void clock_external_output(int clk_id);
 void clock_sdram(u32 m, u32 n, u32 p, u32 setup, u32 ph45, u32 ph90,



More information about the coreboot-gerrit mailing list