nb/intel/*/gma: Port ACPI opregion to older platforms

Port the ACPI opregion implementation that resides in
drivers/intel/gma to older platforms.

It allows to include a vbt.bin and allows GNU/Linux to load the
opregion as ASLS is being set.

Windows' Intel will likely ignore it as it relies on legacy VBIOS
to be loaded at 0xc0000.

Tested successfully on DG43GT (x4x) with vbt.bin,
with X200 (gm45) with vendor option rom and
D945GCLF (i945) with fake vbt.

Change-Id: I1896411155592b343e48cbd116e2f70fb0dbfafa
Signed-off-by: Patrick Rudolph <siro@das-labor.org>
Reviewed-on: https://review.coreboot.org/21766
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Arthur Heymans <arthur@aheymans.xyz>
diff --git a/src/northbridge/intel/i945/gma.c b/src/northbridge/intel/i945/gma.c
index 37f73ed..8d60bc9 100644
--- a/src/northbridge/intel/i945/gma.c
+++ b/src/northbridge/intel/i945/gma.c
@@ -23,10 +23,13 @@
 #include <edid.h>
 #include <drivers/intel/gma/edid.h>
 #include <drivers/intel/gma/i915.h>
+#include <drivers/intel/gma/opregion.h>
 #include <string.h>
 #include <pc80/vga.h>
 #include <pc80/vga_io.h>
 #include <commonlib/helpers.h>
+#include <cbmem.h>
+#include <southbridge/intel/i82801gx/nvs.h>
 
 #include "i945.h"
 #include "chip.h"
@@ -49,6 +52,19 @@
 
 #define DEFAULT_BLC_PWM 180
 
+uintptr_t gma_get_gnvs_aslb(const void *gnvs)
+{
+	const global_nvs_t *gnvs_ptr = gnvs;
+	return (uintptr_t)(gnvs_ptr ? gnvs_ptr->aslb : 0);
+}
+
+void gma_set_gnvs_aslb(void *gnvs, uintptr_t aslb)
+{
+	global_nvs_t *gnvs_ptr = gnvs;
+	if (gnvs_ptr)
+		gnvs_ptr->aslb = aslb;
+}
+
 static int gtt_setup(u8 *mmiobase)
 {
 	unsigned long PGETBL_save;
@@ -687,6 +703,8 @@
 		/* PCI Init, will run VBIOS */
 		pci_dev_init(dev);
 	}
+
+	intel_gma_restore_opregion();
 }
 
 /* This doesn't reclaim stolen UMA memory, but IGD could still
@@ -767,6 +785,37 @@
 	pci_dev_read_resources(dev);
 }
 
+static unsigned long
+gma_write_acpi_tables(struct device *const dev,
+		      unsigned long current,
+		      struct acpi_rsdp *const rsdp)
+{
+	igd_opregion_t *opregion = (igd_opregion_t *)current;
+	global_nvs_t *gnvs;
+
+	if (intel_gma_init_igd_opregion(opregion) != CB_SUCCESS)
+		return current;
+
+	current += sizeof(igd_opregion_t);
+
+	/* GNVS has been already set up */
+	gnvs = cbmem_find(CBMEM_ID_ACPI_GNVS);
+	if (gnvs) {
+		/* IGD OpRegion Base Address */
+		gma_set_gnvs_aslb(gnvs, (uintptr_t)opregion);
+	} else {
+		printk(BIOS_ERR, "Error: GNVS table not found.\n");
+	}
+
+	current = acpi_align_current(current);
+	return current;
+}
+
+static const char *gma_acpi_name(const struct device *dev)
+{
+	return "GFX0";
+}
+
 static struct pci_operations gma_pci_ops = {
 	.set_subsystem    = gma_set_subsystem,
 };
@@ -781,6 +830,8 @@
 	.enable			= 0,
 	.disable		= gma_func0_disable,
 	.ops_pci		= &gma_pci_ops,
+	.acpi_name		= gma_acpi_name,
+	.write_acpi_tables	= gma_write_acpi_tables,
 };