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@chromium.org> Original-Reviewed-on: https://chromium-review.googlesource.com/193565 (cherry picked from commit 4f9b793633ebb2d104b0544e3b72fa0d105951c4) Signed-off-by: Marc Jones <marc.jones@se-eng.com> Change-Id: Ib2cabbad60af010e872505e888eab485ba8c2916 Reviewed-on: http://review.coreboot.org/7762 Tested-by: build bot (Jenkins) Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
This commit is contained in:
parent
0c9cc5ee3b
commit
1a8e0af78b
|
@ -79,15 +79,7 @@ chip soc/nvidia/tegra124
|
||||||
register "vsync_width" = "12"
|
register "vsync_width" = "12"
|
||||||
register "vback_porch" = "12"
|
register "vback_porch" = "12"
|
||||||
|
|
||||||
# we *know* the pixel clock for this system.
|
register "pixel_clock" = "76400000"
|
||||||
# 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
|
|
||||||
|
|
||||||
# link configurations
|
# link configurations
|
||||||
register "lane_count" = "1"
|
register "lane_count" = "1"
|
||||||
|
|
|
@ -79,15 +79,7 @@ chip soc/nvidia/tegra124
|
||||||
register "vsync_width" = "12"
|
register "vsync_width" = "12"
|
||||||
register "vback_porch" = "12"
|
register "vback_porch" = "12"
|
||||||
|
|
||||||
# we *know* the pixel clock for this system.
|
register "pixel_clock" = "76400000"
|
||||||
# 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
|
|
||||||
|
|
||||||
# link configurations
|
# link configurations
|
||||||
register "lane_count" = "1"
|
register "lane_count" = "1"
|
||||||
|
|
|
@ -79,15 +79,7 @@ chip soc/nvidia/tegra124
|
||||||
register "vsync_width" = "12"
|
register "vsync_width" = "12"
|
||||||
register "vback_porch" = "12"
|
register "vback_porch" = "12"
|
||||||
|
|
||||||
# we *know* the pixel clock for this system.
|
register "pixel_clock" = "76400000"
|
||||||
# 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
|
|
||||||
|
|
||||||
# link configurations
|
# link configurations
|
||||||
register "lane_count" = "1"
|
register "lane_count" = "1"
|
||||||
|
|
|
@ -89,7 +89,6 @@ struct soc_nvidia_tegra124_config {
|
||||||
int vfront_porch;
|
int vfront_porch;
|
||||||
|
|
||||||
int pixel_clock;
|
int pixel_clock;
|
||||||
int pll_div;;
|
|
||||||
|
|
||||||
/* The minimum link configuraton settings */
|
/* The minimum link configuraton settings */
|
||||||
u32 lane_count;
|
u32 lane_count;
|
||||||
|
|
|
@ -88,7 +88,6 @@ struct {
|
||||||
int khz;
|
int khz;
|
||||||
struct pllcx_dividers pllx; /* target: CONFIG_PLLX_KHZ */
|
struct pllcx_dividers pllx; /* target: CONFIG_PLLX_KHZ */
|
||||||
struct pllcx_dividers pllc; /* target: 600 MHz */
|
struct pllcx_dividers pllc; /* target: 600 MHz */
|
||||||
struct pllpad_dividers plld; /* target: 306 MHz */
|
|
||||||
struct pllu_dividers pllu; /* target; 960 MHz */
|
struct pllu_dividers pllu; /* target; 960 MHz */
|
||||||
struct pllcx_dividers plldp; /* target; 270 MHz */
|
struct pllcx_dividers plldp; /* target; 270 MHz */
|
||||||
/* Based on T124 TRM (to be updatd), PLLP is set to 408MHz in HW.
|
/* Based on T124 TRM (to be updatd), PLLP is set to 408MHz in HW.
|
||||||
|
@ -99,7 +98,6 @@ struct {
|
||||||
.khz = 12000,
|
.khz = 12000,
|
||||||
.pllx = {.n = TEGRA_PLLX_KHZ / 12000, .m = 1, .p = 0},
|
.pllx = {.n = TEGRA_PLLX_KHZ / 12000, .m = 1, .p = 0},
|
||||||
.pllc = {.n = 50, .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},
|
.pllu = {.n = 960, .m = 12, .p = 0, .cpcon = 12, .lfcon = 2},
|
||||||
.plldp = {.n = 90, .m = 1, .p = 3},
|
.plldp = {.n = 90, .m = 1, .p = 3},
|
||||||
},
|
},
|
||||||
|
@ -107,7 +105,6 @@ struct {
|
||||||
.khz = 13000,
|
.khz = 13000,
|
||||||
.pllx = {.n = TEGRA_PLLX_KHZ / 13000, .m = 1, .p = 0},
|
.pllx = {.n = TEGRA_PLLX_KHZ / 13000, .m = 1, .p = 0},
|
||||||
.pllc = {.n = 231, .m = 5, .p = 0}, /* 600.6 MHz */
|
.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},
|
.pllu = {.n = 960, .m = 13, .p = 0, .cpcon = 12, .lfcon = 2},
|
||||||
.plldp = {.n = 83, .m = 1, .p = 3}, /* 269.75 MHz */
|
.plldp = {.n = 83, .m = 1, .p = 3}, /* 269.75 MHz */
|
||||||
},
|
},
|
||||||
|
@ -115,7 +112,6 @@ struct {
|
||||||
.khz = 16800,
|
.khz = 16800,
|
||||||
.pllx = {.n = TEGRA_PLLX_KHZ / 16800, .m = 1, .p = 0},
|
.pllx = {.n = TEGRA_PLLX_KHZ / 16800, .m = 1, .p = 0},
|
||||||
.pllc = {.n = 250, .m = 7, .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},
|
.pllu = {.n = 400, .m = 7, .p = 0, .cpcon = 5, .lfcon = 2},
|
||||||
.plldp = {.n = 64, .m = 1, .p = 3}, /* 268.8 MHz */
|
.plldp = {.n = 64, .m = 1, .p = 3}, /* 268.8 MHz */
|
||||||
},
|
},
|
||||||
|
@ -123,7 +119,6 @@ struct {
|
||||||
.khz = 19200,
|
.khz = 19200,
|
||||||
.pllx = {.n = TEGRA_PLLX_KHZ / 19200, .m = 1, .p = 0},
|
.pllx = {.n = TEGRA_PLLX_KHZ / 19200, .m = 1, .p = 0},
|
||||||
.pllc = {.n = 125, .m = 4, .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},
|
.pllu = {.n = 200, .m = 4, .p = 0, .cpcon = 3, .lfcon = 2},
|
||||||
.plldp = {.n = 56, .m = 1, .p = 3}, /* 270.75 MHz */
|
.plldp = {.n = 56, .m = 1, .p = 3}, /* 270.75 MHz */
|
||||||
},
|
},
|
||||||
|
@ -131,7 +126,6 @@ struct {
|
||||||
.khz = 26000,
|
.khz = 26000,
|
||||||
.pllx = {.n = TEGRA_PLLX_KHZ / 26000, .m = 1, .p = 0},
|
.pllx = {.n = TEGRA_PLLX_KHZ / 26000, .m = 1, .p = 0},
|
||||||
.pllc = {.n = 23, .m = 1, .p = 0}, /* 598 MHz */
|
.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},
|
.pllu = {.n = 960, .m = 26, .p = 0, .cpcon = 12, .lfcon = 2},
|
||||||
.plldp = {.n = 83, .m = 2, .p = 3}, /* 266.50 MHz */
|
.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},
|
.pllx = {.n = TEGRA_PLLX_KHZ / 19200, .m = 1, .p = 0},
|
||||||
.pllc = {.n = 125, .m = 4, .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},
|
.pllu = {.n = 200, .m = 4, .p = 0, .cpcon = 3, .lfcon = 2},
|
||||||
.plldp = {.n = 56, .m = 2, .p = 3}, /* 268 MHz */
|
.plldp = {.n = 56, .m = 2, .p = 3}, /* 268 MHz */
|
||||||
},
|
},
|
||||||
|
@ -155,7 +148,6 @@ struct {
|
||||||
*/
|
*/
|
||||||
.pllx = {.n = TEGRA_PLLX_KHZ / 12000, .m = 1, .p = 0},
|
.pllx = {.n = TEGRA_PLLX_KHZ / 12000, .m = 1, .p = 0},
|
||||||
.pllc = {.n = 50, .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},
|
.pllu = {.n = 960, .m = 12, .p = 0, .cpcon = 12, .lfcon = 2},
|
||||||
.plldp = {.n = 90, .m = 4, .p = 3}, /* 264 MHz */
|
.plldp = {.n = 90, .m = 4, .p = 3}, /* 264 MHz */
|
||||||
},
|
},
|
||||||
|
@ -288,10 +280,86 @@ static void graphics_pll(void)
|
||||||
scfg = (1<<28) | (1<<24);
|
scfg = (1<<28) | (1<<24);
|
||||||
writel(scfg, cfg);
|
writel(scfg, cfg);
|
||||||
|
|
||||||
/* Init clock source for disp1: plld (actually plld_out0) */
|
/* disp1 will be set when panel information (pixel clock) is
|
||||||
init_pll(&clk_rst->plld_base, &clk_rst->plld_misc,
|
* retrieved (clock_display).
|
||||||
osc_table[osc].plld,
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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));
|
(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().
|
/* Initialize the UART and put it on CLK_M so we can use it during clock_init().
|
||||||
|
|
|
@ -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,
|
static int update_display_mode(struct display_controller *disp_ctrl,
|
||||||
struct soc_nvidia_tegra124_config *config)
|
struct soc_nvidia_tegra124_config *config)
|
||||||
{
|
{
|
||||||
unsigned long div = config->pll_div;
|
|
||||||
|
|
||||||
print_mode(config);
|
print_mode(config);
|
||||||
|
|
||||||
WRITEL(0x1, &disp_ctrl->disp.disp_timing_opt);
|
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),
|
WRITEL(config->xres | (config->yres << 16),
|
||||||
&disp_ctrl->disp.disp_active);
|
&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) |
|
WRITEL((PIXEL_CLK_DIVIDER_PCD1 << PIXEL_CLK_DIVIDER_SHIFT) |
|
||||||
SHIFT_CLK_DIVIDER(div),
|
((shift_clock_div - 1) * 2) << SHIFT_CLK_DIVIDER_SHIFT,
|
||||||
&disp_ctrl->disp.disp_clk_ctrl);
|
&disp_ctrl->disp.disp_clk_ctrl);
|
||||||
return 0;
|
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,
|
static void update_window(struct display_controller *disp_ctrl,
|
||||||
|
@ -285,7 +298,10 @@ void display_startup(device_t dev)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Configure dc mode */
|
/* 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 */
|
/* Enable dp */
|
||||||
dp_enable(dc->out);
|
dp_enable(dc->out);
|
||||||
|
|
|
@ -278,6 +278,7 @@ enum clock_source { /* Careful: Not true for all sources, always check TRM! */
|
||||||
#define TEGRA_PLLU_KHZ (960000)
|
#define TEGRA_PLLU_KHZ (960000)
|
||||||
|
|
||||||
int clock_get_osc_khz(void);
|
int clock_get_osc_khz(void);
|
||||||
|
int clock_display(u32 frequency);
|
||||||
void clock_early_uart(void);
|
void clock_early_uart(void);
|
||||||
void clock_external_output(int clk_id);
|
void clock_external_output(int clk_id);
|
||||||
void clock_sdram(u32 m, u32 n, u32 p, u32 setup, u32 ph45, u32 ph90,
|
void clock_sdram(u32 m, u32 n, u32 p, u32 setup, u32 ph45, u32 ph90,
|
||||||
|
|
Loading…
Reference in New Issue