broadwell: Turn off panel backlight in S5 SMI handler

In order for some panels to meet spec when the system is put
into S5 by way of power button during firmware (i.e. not by
the OS) then it needs to turn off the backlight and give it
time to turn off before going into S5.

If the OS properly sequences the panel down then the backlight
enable bit will not be set in this step and nothing will happen.

BUG=chrome-os-partner:33994
BRANCH=broadwell
TEST=build and boot on samus

Change-Id: Ic86f388218f889b1fe690cc1bfc5c3e233e95115
Signed-off-by: Stefan Reinauer <reinauer@chromium.org>
Original-Commit-Id: e3c9c131a87bae380e1fd3f96c9ad780441add56
Original-Change-Id: I43c5aee8e32768fc9e82790c9f7ceda0ed17ed13
Original-Signed-off-by: Duncan Laurie <dlaurie@chromium.org>
Original-Reviewed-on: https://chromium-review.googlesource.com/240852
Original-Reviewed-by: Shawn N <shawnn@chromium.org>
Reviewed-on: http://review.coreboot.org/9490
Tested-by: build bot (Jenkins)
Reviewed-by: Patrick Georgi <pgeorgi@google.com>
diff --git a/src/soc/intel/broadwell/smihandler.c b/src/soc/intel/broadwell/smihandler.c
index 5004a6a..63c9373 100644
--- a/src/soc/intel/broadwell/smihandler.c
+++ b/src/soc/intel/broadwell/smihandler.c
@@ -36,6 +36,7 @@
 #include <soc/rcba.h>
 #include <soc/smm.h>
 #include <soc/xhci.h>
+#include <drivers/intel/gma/i915_reg.h>
 
 static u8 smm_initialized = 0;
 
@@ -109,6 +110,45 @@
         }
 }
 
+/*
+ * Turn off the backlight if it is on, and wait for the specified
+ * backlight off delay.  This will allow panel power timings to meet
+ * spec and prevent brief garbage on the screen when turned off
+ * during firmware with power button triggered SMI.
+ */
+static void backlight_off(void)
+{
+	void *reg_base;
+	uint32_t pp_ctrl;
+	uint32_t bl_off_delay;
+
+	reg_base = (void *)((uintptr_t)pci_read_config32(SA_DEV_IGD, PCI_BASE_ADDRESS_0) & ~0xf);
+
+	/* Check if backlight is enabled */
+	pp_ctrl = read32(reg_base + PCH_PP_CONTROL);
+	if (!(pp_ctrl & EDP_BLC_ENABLE))
+		return;
+
+	/* Enable writes to this register */
+	pp_ctrl &= ~PANEL_UNLOCK_MASK;
+	pp_ctrl |= PANEL_UNLOCK_REGS;
+
+	/* Turn off backlight */
+	pp_ctrl &= ~EDP_BLC_ENABLE;
+
+	write32(reg_base + PCH_PP_CONTROL, pp_ctrl);
+	read32(reg_base + PCH_PP_CONTROL);
+
+	/* Read backlight off delay in 100us units */
+	bl_off_delay = read32(reg_base + PCH_PP_OFF_DELAYS);
+	bl_off_delay &= PANEL_LIGHT_OFF_DELAY_MASK;
+	bl_off_delay *= 100;
+
+	/* Wait for backlight to turn off */
+	udelay(bl_off_delay);
+
+	printk(BIOS_INFO, "Backlight turned off\n");
+}
 
 static void southbridge_smi_sleep(void)
 {
@@ -170,6 +210,9 @@
 	case SLP_TYP_S5:
 		printk(BIOS_DEBUG, "SMI#: Entering S5 (Soft Power off)\n");
 
+		/* Turn off backlight if needed */
+		backlight_off();
+
 		/* Disable all GPE */
 		disable_all_gpe();