From 7dfc8a5ebdf6b272de4373da945d756eadcf2786 Mon Sep 17 00:00:00 2001 From: Arthur Heymans Date: Fri, 2 Sep 2016 22:35:32 +0200 Subject: i945/gma.c: use 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. The divisors must be such "refclk * (5 * (m1 + 2) + (m2 + 2))/ (n + 2) / (p1 * p2)" is as close as possible to the target frequency (which is defined by the resolution and refresh rate). This patch also fixes the reference frequency. This patch reuses linux (4.1) code from drivers/gpu/drm/i915/intel_display.c to correctly compute divisors. The result is that some previously not working displays, like many displays found on the Lenovo T60 might work now. Some examples of T60 displays that were known to not work (in payload): Samsung LTN141XA-L01 (14.1" 1024x768) LG-Philips LP150X09 (15.1" 1024x768) IDtech N150U3-L01 (15.1" 1600x1200) IDtech IAQX10N (15.1" 2048x1536) Samsung LTN154X3-L0A (15.4" 1280x800) LG-Philips LP150E06-A5K4 (15.1" 1400x1050) Tested on T60 with 1024x786. Change-Id: I2c7f3bb0024ac005029eaebe3ecdc70c38ac777e Signed-off-by: Arthur Heymans Reviewed-on: https://review.coreboot.org/16504 Tested-by: build bot (Jenkins) Reviewed-by: Martin Roth --- src/northbridge/intel/i945/gma.c | 83 +++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 44 deletions(-) diff --git a/src/northbridge/intel/i945/gma.c b/src/northbridge/intel/i945/gma.c index b1cb3b922f..c92bfbff1e 100644 --- a/src/northbridge/intel/i945/gma.c +++ b/src/northbridge/intel/i945/gma.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "i945.h" #include "chip.h" @@ -43,7 +44,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 +86,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 +159,39 @@ 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; + /* p2 divisor must 7 for dual channel LVDS */ + /* and 14 for single channel LVDS */ + 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 +213,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 +239,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 +249,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 - 1))); mdelay(1); write32(pmmio + DPLL(1), DPLL_VGA_MODE_DIS | @@ -261,8 +257,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 - 1))); mdelay(1); write32(pmmio + HTOTAL(1), ((hactive + right_border + hblank - 1) << 16) -- cgit v1.2.3