From 34f8cdbc30f1fdf4700c73aad26e0fc159af70ab Mon Sep 17 00:00:00 2001
From: Arthur Heymans <arthur@aheymans.xyz>
Date: Fri, 2 Sep 2016 22:35:32 +0200
Subject: [PATCH 1/2] i945/gma.c use latest linux code to calculate divisors.

The code to compute n, m1, m2, p1 divisors is not correct in coreboot and
on some targets hits a working mode at lower refresh rate, which is why
display is working on some targets.
This patch also fixes reference frequency.

This patch reuses linux code to correctly compute divisors.

The result is that some previously not working displays (Lenovo T60 with
1024x786, 1400x1050, 2048x1536)

TESTED on T60 with 1024x786.

Change-Id: I2c7f3bb0024ac005029eaebe3ecdc70c38ac777e
Signed-off-by: Arthur Heymans <arthur@aheymans.xyz>
---
 src/northbridge/intel/i945/gma.c | 82 +++++++++++++++++++---------------------
 1 file changed, 38 insertions(+), 44 deletions(-)

diff --git a/src/northbridge/intel/i945/gma.c b/src/northbridge/intel/i945/gma.c
index 02caa0a..3f0b5b4 100644
--- a/src/northbridge/intel/i945/gma.c
+++ b/src/northbridge/intel/i945/gma.c
@@ -26,6 +26,8 @@
 #include <string.h>
 #include <pc80/vga.h>
 #include <pc80/vga_io.h>
+#include <commonlib/helpers.h>
+
 
 #include "i945.h"
 #include "chip.h"
@@ -43,7 +45,7 @@
 #define PGETBL_CTL	0x2020
 #define PGETBL_ENABLED	0x00000001
 
-#define BASE_FREQUENCY 120000
+#define BASE_FREQUENCY 100000
 
 #if CONFIG_MAINBOARD_DO_NATIVE_VGA_INIT
 
@@ -85,10 +87,10 @@ static int intel_gma_init(struct northbridge_intel_i945_config *conf,
 	u8 edid_data[128];
 	unsigned long temp;
 	int hpolarity, vpolarity;
-	u32 candp1, candn;
-	u32 best_delta = 0xffffffff;
+	u32 smallest_err = 0xffffffff;
 	u32 target_frequency;
 	u32 pixel_p1 = 1;
+	u32 pixel_p2;
 	u32 pixel_n = 1;
 	u32 pixel_m1 = 1;
 	u32 pixel_m2 = 1;
@@ -158,43 +160,37 @@ static int intel_gma_init(struct northbridge_intel_i945_config *conf,
 	write32(pmmio + PORT_HOTPLUG_EN, conf->gpu_hotplug);
 	write32(pmmio + INSTPM, 0x08000000 | INSTPM_AGPBUSY_DIS);
 
-	target_frequency = mode->lvds_dual_channel ? mode->pixel_clock
-		: (2 * mode->pixel_clock);
-
-	/* Find suitable divisors.  */
-	for (candp1 = 1; candp1 <= 8; candp1++) {
-		for (candn = 5; candn <= 10; candn++) {
-			u32 cur_frequency;
-			u32 m; /* 77 - 131.  */
-			u32 denom; /* 35 - 560.  */
-			u32 current_delta;
-
-			denom = candn * candp1 * 7;
-			/* Doesnt overflow for up to
-			   5000000 kHz = 5 GHz.  */
-			m = (target_frequency * denom
-			     + BASE_FREQUENCY / 2) / BASE_FREQUENCY;
-
-			if (m < 77 || m > 131)
-				continue;
-
-			cur_frequency = (BASE_FREQUENCY * m) / denom;
-			if (target_frequency > cur_frequency)
-				current_delta = target_frequency - cur_frequency;
-			else
-				current_delta = cur_frequency - target_frequency;
-
-			if (best_delta > current_delta) {
-				best_delta = current_delta;
-				pixel_n = candn;
-				pixel_p1 = candp1;
-				pixel_m2 = ((m + 3) % 5) + 7;
-				pixel_m1 = (m - pixel_m2) / 5;
+	pixel_p2 = mode->lvds_dual_channel ? 7 : 14;
+	target_frequency = mode->pixel_clock;
+
+	/* Find suitable divisors, m1, m2, p1, n.  */
+	/* refclock * (5 * (m1 + 2) + (m1 + 2)) / (n + 2) / p1 / p2 */
+	/* should be closest to target frequency as possible */
+	u32 candn, candm1, candm2, candp1;
+	for (candm1 = 8; candm1 <= 18; candm1++) {
+		for (candm2 = 3; candm2 <= 7; candm2++) {
+			for (candn = 1; candn <= 6; candn++) {
+				for (candp1 = 1; candp1 <= 8; candp1++) {
+					u32 m = 5 * (candm1 + 2) + (candm2 + 2);
+					u32 p = candp1 * pixel_p2;
+					u32 vco = DIV_ROUND_CLOSEST(BASE_FREQUENCY * m, candn + 2);
+					u32 dot = DIV_ROUND_CLOSEST(vco, p);
+					u32 this_err = ABS(dot - target_frequency);
+					if ((m < 70) || (m > 120))
+						continue;
+					if (this_err < smallest_err) {
+						smallest_err = this_err;
+						pixel_n = candn;
+						pixel_m1 = candm1;
+						pixel_m2 = candm2;
+						pixel_p1 = candp1;
+					}
+				}
 			}
 		}
 	}
 
-	if (best_delta == 0xffffffff) {
+	if (smallest_err == 0xffffffff) {
 		printk (BIOS_ERR, "Couldn't find GFX clock divisors\n");
 		return -1;
 	}
@@ -216,8 +212,8 @@ static int intel_gma_init(struct northbridge_intel_i945_config *conf,
 	printk(BIOS_DEBUG, "Pixel N=%d, M1=%d, M2=%d, P1=%d\n",
 	       pixel_n, pixel_m1, pixel_m2, pixel_p1);
 	printk(BIOS_DEBUG, "Pixel clock %d kHz\n",
-	       BASE_FREQUENCY * (5 * pixel_m1 + pixel_m2) / pixel_n
-	       / (pixel_p1 * 7));
+	       BASE_FREQUENCY * (5 * (pixel_m1 + 2) + (pixel_m2 + 2)) /
+	       (pixel_n + 2) / (pixel_p1 * pixel_p2));
 
 #if !IS_ENABLED(CONFIG_FRAMEBUFFER_KEEP_VESA_MODE)
 	write32(pmmio + PF_WIN_SZ(0), vactive | (hactive << 16));
@@ -242,8 +238,8 @@ static int intel_gma_init(struct northbridge_intel_i945_config *conf,
 	write32(pmmio + PP_CONTROL, PANEL_UNLOCK_REGS
 		| (read32(pmmio + PP_CONTROL) & ~PANEL_UNLOCK_MASK));
 	write32(pmmio + FP0(1),
-		((pixel_n - 2) << 16)
-		| ((pixel_m1 - 2) << 8) | pixel_m2);
+		(pixel_n << 16)
+		| (pixel_m1 << 8) | pixel_m2);
 	write32(pmmio + DPLL(1),
 		DPLL_VGA_MODE_DIS |
 		DPLL_VCO_ENABLE | DPLLB_MODE_LVDS
@@ -252,8 +248,7 @@ static int intel_gma_init(struct northbridge_intel_i945_config *conf,
 		| (conf->gpu_lvds_use_spread_spectrum_clock
 		   ? DPLL_INTEGRATED_CLOCK_VLV | DPLL_INTEGRATED_CRI_CLK_VLV
 		   : 0)
-		| (pixel_p1 << 16)
-		| (pixel_p1));
+		| (0x10000 << pixel_p1));
 	mdelay(1);
 	write32(pmmio + DPLL(1),
 		DPLL_VGA_MODE_DIS |
@@ -261,8 +256,7 @@ static int intel_gma_init(struct northbridge_intel_i945_config *conf,
 		| (mode->lvds_dual_channel ? DPLLB_LVDS_P2_CLOCK_DIV_7
 		   : DPLLB_LVDS_P2_CLOCK_DIV_14)
 		| ((conf->gpu_lvds_use_spread_spectrum_clock ? 3 : 0) << 13)
-		| (pixel_p1 << 16)
-		| (pixel_p1));
+		| (0x10000 << pixel_p1));
 	mdelay(1);
 	write32(pmmio + HTOTAL(1),
 		((hactive + right_border + hblank - 1) << 16)
-- 
2.9.3

