tegra124: clock: Add display clocks and functions

Add functions to provide access to the display clocks on Tegra124 including
setting the clock rate for an EDP display.

Signed-off-by: Simon Glass <sjg@chromium.org>
Signed-off-by: Tom Warren <twarren@nvidia.com>
diff --git a/arch/arm/include/asm/arch-tegra/clk_rst.h b/arch/arm/include/asm/arch-tegra/clk_rst.h
index 7d28e16..de50e08 100644
--- a/arch/arm/include/asm/arch-tegra/clk_rst.h
+++ b/arch/arm/include/asm/arch-tegra/clk_rst.h
@@ -202,9 +202,13 @@
 	uint crc_reserved52[1];		/* _reserved_52, 0x554 */
 	uint crc_super_gr3d_clk_div;	/* _SUPER_GR3D_CLK_DIVIDER_0, 0x558 */
 	uint crc_spare_reg0;		/* _SPARE_REG0_0, 0x55C */
-
-	/* Tegra124 - skip to 0x600 here for new CLK_SOURCE_ regs */
-	uint crc_reserved60[40];	/* _reserved_60, 0x560 - 0x5FC */
+	u32 _rsv32[4];                  /*                    0x560-0x56c */
+	u32 crc_plld2_ss_cfg;		/* _PLLD2_SS_CFG            0x570 */
+	u32 _rsv32_1[7];		/*                      0x574-58c */
+	struct clk_pll_simple plldp;	/* _PLLDP_BASE, 0x590 _PLLDP_MISC */
+	u32 crc_plldp_ss_cfg;		/* _PLLDP_SS_CFG, 0x598 */
+	u32 _rsrv32_2[25];
+	/* Tegra124 */
 	uint crc_clk_src_x[TEGRA_CLK_SOURCES_X]; /* XUSB, etc, 0x600-0x678 */
 };
 
@@ -440,4 +444,9 @@
 #define PLLX_IDDQ_SHIFT			3
 #define PLLX_IDDQ_MASK			(1U << PLLX_IDDQ_SHIFT)
 
+/* CLK_RST_PLLDP_SS_CFG */
+#define PLLDP_SS_CFG_CLAMP		(1 << 22)
+#define PLLDP_SS_CFG_UNDOCUMENTED	(1 << 24)
+#define PLLDP_SS_CFG_DITHER		(1 << 28)
+
 #endif	/* _TEGRA_CLK_RST_H_ */
diff --git a/arch/arm/include/asm/arch-tegra124/clock-tables.h b/arch/arm/include/asm/arch-tegra124/clock-tables.h
index daf9a2b..7005855 100644
--- a/arch/arm/include/asm/arch-tegra124/clock-tables.h
+++ b/arch/arm/include/asm/arch-tegra124/clock-tables.h
@@ -25,6 +25,7 @@
 	CLOCK_ID_XCPU = CLOCK_ID_FIRST_SIMPLE,
 	CLOCK_ID_EPCI,
 	CLOCK_ID_SFROM32KHZ,
+	CLOCK_ID_DP,	/* Special for Tegra124 */
 
 	/* These are the base clocks (inputs to the Tegra SoC) */
 	CLOCK_ID_32KHZ,
@@ -424,7 +425,7 @@
 
 	/* 0x58 */
 	PERIPHC_58h,
-	PERIPHC_59h,
+	PERIPHC_SOR,
 	PERIPHC_5ah,
 	PERIPHC_5bh,
 	PERIPHC_SATAOOB,
diff --git a/arch/arm/include/asm/arch-tegra124/clock.h b/arch/arm/include/asm/arch-tegra124/clock.h
index 8e65086..e202cc5 100644
--- a/arch/arm/include/asm/arch-tegra124/clock.h
+++ b/arch/arm/include/asm/arch-tegra124/clock.h
@@ -16,6 +16,27 @@
 #define OSC_FREQ_SHIFT          28
 #define OSC_FREQ_MASK           (0xF << OSC_FREQ_SHIFT)
 
+/* CLK_RST_CONTROLLER_CLK_SOURCE_SOR0_0 */
+#define SOR0_CLK_SEL0			(1 << 14)
+#define SOR0_CLK_SEL1			(1 << 15)
+
 int tegra_plle_enable(void);
 
+void clock_sor_enable_edp_clock(void);
+
+/**
+ * clock_set_display_rate() - Set the display clock rate
+ *
+ * @frequency: the requested PLLD frequency
+ *
+ * Return the PLLD frequenc (which may not quite what was requested), or 0
+ * on failure
+ */
+u32 clock_set_display_rate(u32 frequency);
+
+/**
+ * clock_set_up_plldp() - Set up the EDP clock ready for use
+ */
+void clock_set_up_plldp(void);
+
 #endif	/* _TEGRA124_CLOCK_H_ */
diff --git a/arch/arm/mach-tegra/clock.c b/arch/arm/mach-tegra/clock.c
index 4b58cc1..cdd5438 100644
--- a/arch/arm/mach-tegra/clock.c
+++ b/arch/arm/mach-tegra/clock.c
@@ -593,6 +593,7 @@
 	pll_rate[CLOCK_ID_MEMORY] = clock_get_rate(CLOCK_ID_MEMORY);
 	pll_rate[CLOCK_ID_PERIPH] = clock_get_rate(CLOCK_ID_PERIPH);
 	pll_rate[CLOCK_ID_CGENERAL] = clock_get_rate(CLOCK_ID_CGENERAL);
+	pll_rate[CLOCK_ID_DISPLAY] = clock_get_rate(CLOCK_ID_DISPLAY);
 	pll_rate[CLOCK_ID_OSC] = clock_get_rate(CLOCK_ID_OSC);
 	pll_rate[CLOCK_ID_SFROM32KHZ] = 32768;
 	pll_rate[CLOCK_ID_XCPU] = clock_get_rate(CLOCK_ID_XCPU);
@@ -600,6 +601,7 @@
 	debug("PLLM = %d\n", pll_rate[CLOCK_ID_MEMORY]);
 	debug("PLLP = %d\n", pll_rate[CLOCK_ID_PERIPH]);
 	debug("PLLC = %d\n", pll_rate[CLOCK_ID_CGENERAL]);
+	debug("PLLD = %d\n", pll_rate[CLOCK_ID_DISPLAY]);
 	debug("PLLX = %d\n", pll_rate[CLOCK_ID_XCPU]);
 
 	/* Do any special system timer/TSC setup */
diff --git a/arch/arm/mach-tegra/tegra124/clock.c b/arch/arm/mach-tegra/tegra124/clock.c
index fc8bd19..2d17550 100644
--- a/arch/arm/mach-tegra/tegra124/clock.c
+++ b/arch/arm/mach-tegra/tegra124/clock.c
@@ -42,6 +42,7 @@
 	CLOCK_TYPE_ASPTE,
 	CLOCK_TYPE_PMDACD2T,
 	CLOCK_TYPE_PCST,
+	CLOCK_TYPE_DP,
 
 	CLOCK_TYPE_PC2CC3M,
 	CLOCK_TYPE_PC2CC3S_T,
@@ -101,6 +102,10 @@
 	{ CLK(PERIPH),	CLK(CGENERAL),	CLK(SFROM32KHZ),	CLK(OSC),
 		CLK(NONE),	CLK(NONE),	CLK(NONE),	CLK(NONE),
 		MASK_BITS_31_28},
+	/* CLOCK_TYPE_DP */
+	{ CLK(NONE),	CLK(NONE),	CLK(NONE),	CLK(NONE),
+		CLK(NONE),	CLK(NONE),	CLK(NONE),	CLK(NONE),
+		MASK_BITS_31_28},
 
 	/* Additional clock types on Tegra114+ */
 	/* CLOCK_TYPE_PC2CC3M */
@@ -259,7 +264,7 @@
 
 	/* 0x58 */
 	TYPE(PERIPHC_58h,	CLOCK_TYPE_NONE),
-	TYPE(PERIPHC_59h,	CLOCK_TYPE_NONE),
+	TYPE(PERIPHC_SOR,	CLOCK_TYPE_NONE),
 	TYPE(PERIPHC_5ah,	CLOCK_TYPE_NONE),
 	TYPE(PERIPHC_5bh,	CLOCK_TYPE_NONE),
 	TYPE(PERIPHC_SATAOOB,	CLOCK_TYPE_PCMT),
@@ -546,7 +551,7 @@
 	NONE(X_RESERVED19),
 	NONE(ADX1),
 	NONE(DPAUX),
-	NONE(SOR0),
+	PERIPHC_SOR,
 	NONE(X_RESERVED23),
 
 	/* 184 */
@@ -594,7 +599,10 @@
 	assert(periph_id >= PERIPH_ID_FIRST && periph_id < PERIPH_ID_COUNT);
 	internal_id = periph_id_to_internal_id[periph_id];
 	assert(internal_id != -1);
-	if (internal_id >= PERIPHC_VW_FIRST) {
+	if (internal_id >= PERIPHC_X_FIRST) {
+		internal_id -= PERIPHC_X_FIRST;
+		return &clkrst->crc_clk_src_x[internal_id];
+	} else if (internal_id >= PERIPHC_VW_FIRST) {
 		internal_id -= PERIPHC_VW_FIRST;
 		return &clkrst->crc_clk_src_vw[internal_id];
 	} else {
@@ -657,8 +665,10 @@
 	assert(clock_periph_id_isvalid(periph_id));
 	if ((int)periph_id < (int)PERIPH_ID_VW_FIRST)
 		clk = &clkrst->crc_clk_out_enb[PERIPH_REG(periph_id)];
-	else
+	else if ((int)periph_id < PERIPH_ID_X_FIRST)
 		clk = &clkrst->crc_clk_out_enb_vw[PERIPH_REG(periph_id)];
+	else
+		clk = &clkrst->crc_clk_out_enb_x;
 	reg = readl(clk);
 	if (enable)
 		reg |= PERIPH_MASK(periph_id);
@@ -678,8 +688,10 @@
 	assert(clock_periph_id_isvalid(periph_id));
 	if (periph_id < PERIPH_ID_VW_FIRST)
 		reset = &clkrst->crc_rst_dev[PERIPH_REG(periph_id)];
-	else
+	else if ((int)periph_id < PERIPH_ID_X_FIRST)
 		reset = &clkrst->crc_rst_dev_vw[PERIPH_REG(periph_id)];
+	else
+		reset = &clkrst->crc_rst_devices_x;
 	reg = readl(reset);
 	if (enable)
 		reg |= PERIPH_MASK(periph_id);
@@ -933,3 +945,122 @@
 
 	return 0;
 }
+
+void clock_sor_enable_edp_clock(void)
+{
+	u32 *reg;
+
+	/* uses PLLP, has a non-standard bit layout. */
+	reg = get_periph_source_reg(PERIPH_ID_SOR0);
+	setbits_le32(reg, SOR0_CLK_SEL0);
+}
+
+u32 clock_set_display_rate(u32 frequency)
+{
+	/**
+	 * plld (fo) = vco >> p, where 500MHz < vco < 1000MHz
+	 *           = (cf * n) >> p, where 1MHz < cf < 6MHz
+	 *           = ((ref / m) * n) >> p
+	 *
+	 * Iterate the possible values of p (3 bits, 2^7) to find out a minimum
+	 * safe vco, then find best (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 values are not in "safe" range by panel specification.
+	 */
+	u32 ref = clock_get_rate(CLOCK_ID_OSC);
+	u32 divm, divn, divp, cpcon;
+	u32 cf, vco, rounded_rate = frequency;
+	u32 diff, best_diff, best_m = 0, best_n = 0, best_p;
+	const u32 max_m = 1 << 5, max_n = 1 << 10, max_p = 1 << 3,
+		  mhz = 1000 * 1000, min_vco = 500 * mhz, max_vco = 1000 * mhz,
+		  min_cf = 1 * mhz, max_cf = 6 * mhz;
+	int mux_bits, divider_bits, source;
+
+	for (divp = 0, vco = frequency; vco < min_vco && divp < max_p; divp++)
+		vco <<= 1;
+
+	if (vco < min_vco || vco > max_vco) {
+		printf("%s: Cannot find out a supported VCO for Frequency (%u)\n",
+		       __func__, frequency);
+		return 0;
+	}
+
+	best_p = divp;
+	best_diff = vco;
+
+	for (divm = 1; divm < max_m && best_diff; divm++) {
+		cf = ref / divm;
+		if (cf < min_cf)
+			break;
+		if (cf > max_cf)
+			continue;
+
+		divn = vco / cf;
+		if (divn >= max_n)
+			continue;
+
+		diff = vco - divn * cf;
+		if (divn + 1 < max_n && diff > cf / 2) {
+			divn++;
+			diff = cf - diff;
+		}
+
+		if (diff >= best_diff)
+			continue;
+
+		best_diff = diff;
+		best_m = divm;
+		best_n = divn;
+	}
+
+	if (best_n < 50)
+		cpcon = 2;
+	else if (best_n < 300)
+		cpcon = 3;
+	else if (best_n < 600)
+		cpcon = 8;
+	else
+		cpcon = 12;
+
+	if (best_diff) {
+		printf("%s: Failed to match output frequency %u, best difference is %u\n",
+		       __func__, frequency, best_diff);
+		rounded_rate = (ref / best_m * best_n) >> best_p;
+	}
+
+	debug("%s: PLLD=%u ref=%u, m/n/p/cpcon=%u/%u/%u/%u\n",
+	      __func__, rounded_rate, ref, best_m, best_n, best_p, cpcon);
+
+	source = get_periph_clock_source(PERIPH_ID_DISP1, CLOCK_ID_DISPLAY,
+					 &mux_bits, &divider_bits);
+	clock_ll_set_source_bits(PERIPH_ID_DISP1, mux_bits, source);
+	clock_set_rate(CLOCK_ID_DISPLAY, best_n, best_m, best_p, cpcon);
+
+	return rounded_rate;
+}
+
+void clock_set_up_plldp(void)
+{
+	struct clk_rst_ctlr *clkrst =
+			(struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE;
+	u32 value;
+
+	value = PLLDP_SS_CFG_UNDOCUMENTED | PLLDP_SS_CFG_DITHER;
+	writel(value | PLLDP_SS_CFG_CLAMP, &clkrst->crc_plldp_ss_cfg);
+	clock_start_pll(CLOCK_ID_DP, 1, 90, 3, 0, 0);
+	writel(value, &clkrst->crc_plldp_ss_cfg);
+}
+
+struct clk_pll_simple *clock_get_simple_pll(enum clock_id clkid)
+{
+	struct clk_rst_ctlr *clkrst =
+			(struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE;
+
+	if (clkid == CLOCK_ID_DP)
+		return &clkrst->plldp;
+
+	return NULL;
+}