diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 5e3357ccee..23d2e04140 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -968,4 +968,12 @@ config PCIEX_LENGTH_128MB
 config PCIEX_LENGTH_64MB
 	bool
 
+config INTEL_GMA_ACPI
+	bool "Generate ACPI table for Intel GMA graphics"
+	help
+	  The Intel GMA graphics driver in Linux expects an ACPI table
+	  which describes the layout of the registers and the display
+	  connected to the device. Enable this option to create this
+	  table so that graphics works correctly.
+
 endmenu
diff --git a/arch/x86/cpu/apollolake/Kconfig b/arch/x86/cpu/apollolake/Kconfig
index 7d7b5c3a0e..6c62da04f0 100644
--- a/arch/x86/cpu/apollolake/Kconfig
+++ b/arch/x86/cpu/apollolake/Kconfig
@@ -45,6 +45,7 @@ config INTEL_APOLLOLAKE
 	imply CMD_CLK
 	imply CLK_INTEL
 	imply ACPI_GPE
+	imply INTEL_GMA_ACPI
 
 if INTEL_APOLLOLAKE
 
diff --git a/arch/x86/cpu/intel_common/Makefile b/arch/x86/cpu/intel_common/Makefile
index cc4e1c962b..adaf53c35e 100644
--- a/arch/x86/cpu/intel_common/Makefile
+++ b/arch/x86/cpu/intel_common/Makefile
@@ -9,6 +9,8 @@ obj-$(CONFIG_$(SPL_TPL_)X86_32BIT_INIT) += report_platform.o
 obj-$(CONFIG_$(SPL_TPL_)X86_32BIT_INIT) += mrc.o
 endif
 
+obj-$(CONFIG_INTEL_GMA_ACPI) += intel_opregion.o
+
 ifdef CONFIG_INTEL_CAR_CQOS
 obj-$(CONFIG_TPL_BUILD) += car2.o
 ifndef CONFIG_SPL_BUILD
diff --git a/arch/x86/cpu/intel_common/intel_opregion.c b/arch/x86/cpu/intel_common/intel_opregion.c
new file mode 100644
index 0000000000..67c381d56d
--- /dev/null
+++ b/arch/x86/cpu/intel_common/intel_opregion.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Writing IntelGraphicsMem table for ACPI
+ *
+ * Copyright 2019 Google LLC
+ * Modified from coreboot src/soc/intel/gma/opregion.c
+ */
+
+#include <common.h>
+#include <binman.h>
+#include <bloblist.h>
+#include <dm.h>
+#include <spi_flash.h>
+#include <asm/intel_opregion.h>
+
+static char vbt_data[8 << 10];
+
+static int locate_vbt(char **vbtp, int *sizep)
+{
+	struct binman_entry vbt;
+	struct udevice *dev;
+	u32 vbtsig = 0;
+	int size;
+	int ret;
+
+	ret = binman_entry_find("intel-vbt", &vbt);
+	if (ret)
+		return log_msg_ret("find VBT", ret);
+	ret = uclass_first_device_err(UCLASS_SPI_FLASH, &dev);
+	if (ret)
+		return log_msg_ret("find flash", ret);
+	size = vbt.size;
+	if (size > sizeof(vbt_data))
+		return log_msg_ret("vbt", -E2BIG);
+	ret = spi_flash_read_dm(dev, vbt.image_pos, size, vbt_data);
+	if (ret)
+		return log_msg_ret("read", ret);
+
+	memcpy(&vbtsig, vbt_data, sizeof(vbtsig));
+	if (vbtsig != VBT_SIGNATURE) {
+		log_err("Missing/invalid signature in VBT data file!\n");
+		return -EINVAL;
+	}
+
+	log_info("Found a VBT of %u bytes\n", size);
+	*sizep = size;
+	*vbtp = vbt_data;
+
+	return 0;
+}
+
+/* Write ASLS PCI register and prepare SWSCI register. */
+static int intel_gma_opregion_register(struct udevice *dev, ulong opregion)
+{
+	int sci_reg;
+
+	if (!device_active(dev))
+		return -ENOENT;
+
+	/*
+	 * Intel BIOS Specification
+	 * Chapter 5.3.7 "Initialise Hardware State"
+	 */
+	dm_pci_write_config32(dev, ASLS, opregion);
+
+	/*
+	 * Atom-based platforms use a combined SMI/SCI register,
+	 * whereas non-Atom platforms use a separate SCI register
+	 */
+	if (IS_ENABLED(CONFIG_INTEL_GMA_SWSMISCI))
+		sci_reg = SWSMISCI;
+	else
+		sci_reg = SWSCI;
+
+	/*
+	 * Intel's Windows driver relies on this:
+	 * Intel BIOS Specification
+	 * Chapter 5.4 "ASL Software SCI Handler"
+	 */
+	dm_pci_clrset_config16(dev, sci_reg, GSSCIE, SMISCISEL);
+
+	return 0;
+}
+
+int intel_gma_init_igd_opregion(struct udevice *dev,
+				struct igd_opregion *opregion)
+{
+	struct optionrom_vbt *vbt = NULL;
+	char *vbt_buf;
+	int vbt_size;
+	int ret;
+
+	ret = locate_vbt(&vbt_buf, &vbt_size);
+	if (ret) {
+		log_err("GMA: VBT couldn't be found\n");
+		return log_msg_ret("find vbt", ret);
+	}
+	vbt = (struct optionrom_vbt *)vbt_buf;
+
+	memset(opregion, '\0', sizeof(struct igd_opregion));
+
+	memcpy(&opregion->header.signature, IGD_OPREGION_SIGNATURE,
+	       sizeof(opregion->header.signature));
+	memcpy(opregion->header.vbios_version, vbt->coreblock_biosbuild,
+	       ARRAY_SIZE(vbt->coreblock_biosbuild));
+	/* Extended VBT support */
+	if (vbt->hdr_vbt_size > sizeof(opregion->vbt.gvd1)) {
+		struct optionrom_vbt *ext_vbt;
+
+		ret = bloblist_ensure_size(BLOBLISTT_INTEL_VBT,
+					   vbt->hdr_vbt_size,
+					   (void **)&ext_vbt);
+		if (ret) {
+			log_err("GMA: Unable to add Ext VBT to bloblist\n");
+			return log_msg_ret("blob", ret);
+		}
+
+		memcpy(ext_vbt, vbt, vbt->hdr_vbt_size);
+		opregion->mailbox3.rvda = (uintptr_t)ext_vbt;
+		opregion->mailbox3.rvds = vbt->hdr_vbt_size;
+	} else {
+		/* Raw VBT size which can fit in gvd1 */
+		printf("copy to %p\n", opregion->vbt.gvd1);
+		memcpy(opregion->vbt.gvd1, vbt, vbt->hdr_vbt_size);
+	}
+
+	/* 8kb */
+	opregion->header.size = sizeof(struct igd_opregion) / 1024;
+
+	/*
+	 * Left-shift version field to accommodate Intel Windows driver quirk
+	 * when not using a VBIOS.
+	 * Required for Legacy boot + NGI, UEFI + NGI, and UEFI + GOP driver.
+	 *
+	 * No adverse effects when using VBIOS or booting Linux.
+	 */
+	opregion->header.version = IGD_OPREGION_VERSION << 24;
+
+	/* FIXME We just assume we're mobile for now */
+	opregion->header.mailboxes = MAILBOXES_MOBILE;
+
+	/* TODO Initialise Mailbox 1 */
+	opregion->mailbox1.clid = 1;
+
+	/* TODO Initialise Mailbox 3 */
+	opregion->mailbox3.bclp = IGD_BACKLIGHT_BRIGHTNESS;
+	opregion->mailbox3.pfit = IGD_FIELD_VALID | IGD_PFIT_STRETCH;
+	opregion->mailbox3.pcft = 0; /* should be (IMON << 1) & 0x3e */
+	opregion->mailbox3.cblv = IGD_FIELD_VALID | IGD_INITIAL_BRIGHTNESS;
+	opregion->mailbox3.bclm[0] = IGD_WORD_FIELD_VALID + 0x0000;
+	opregion->mailbox3.bclm[1] = IGD_WORD_FIELD_VALID + 0x0a19;
+	opregion->mailbox3.bclm[2] = IGD_WORD_FIELD_VALID + 0x1433;
+	opregion->mailbox3.bclm[3] = IGD_WORD_FIELD_VALID + 0x1e4c;
+	opregion->mailbox3.bclm[4] = IGD_WORD_FIELD_VALID + 0x2866;
+	opregion->mailbox3.bclm[5] = IGD_WORD_FIELD_VALID + 0x327f;
+	opregion->mailbox3.bclm[6] = IGD_WORD_FIELD_VALID + 0x3c99;
+	opregion->mailbox3.bclm[7] = IGD_WORD_FIELD_VALID + 0x46b2;
+	opregion->mailbox3.bclm[8] = IGD_WORD_FIELD_VALID + 0x50cc;
+	opregion->mailbox3.bclm[9] = IGD_WORD_FIELD_VALID + 0x5ae5;
+	opregion->mailbox3.bclm[10] = IGD_WORD_FIELD_VALID + 0x64ff;
+
+	/* Write ASLS PCI register and prepare SWSCI register. */
+	ret = intel_gma_opregion_register(dev, (ulong)opregion);
+	if (ret)
+		return log_msg_ret("write asls", ret);
+
+	return 0;
+}
diff --git a/arch/x86/include/asm/intel_opregion.h b/arch/x86/include/asm/intel_opregion.h
new file mode 100644
index 0000000000..fb3e38617e
--- /dev/null
+++ b/arch/x86/include/asm/intel_opregion.h
@@ -0,0 +1,247 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Writing IntelGraphicsMem table for ACPI
+ *
+ * Copyright 2019 Google LLC
+ * Modified from coreboot src/soc/intel/gma/opregion.h
+ */
+
+#ifndef _ASM_INTEL_OPREGION_H_
+#define _ASM_INTEL_OPREGION_H_
+
+#define VBT_SIGNATURE		0x54425624
+
+/* IGD PCI Configuration register */
+#define ASLS		0xfc		/* OpRegion Base */
+#define SWSCI		0xe8		/* SWSCI Register */
+#define SWSMISCI	0xe0		/* SWSMISCI Register */
+#define GSSCIE		BIT(0)		/* SCI Event trigger */
+#define SMISCISEL	BIT(15)		/* Select SMI or SCI event source */
+
+/* mailbox 0: header */
+struct __packed opregion_header {
+	u8	signature[16];	    /* Offset 0    OpRegion signature */
+	u32	size;		    /* Offset 16   OpRegion size */
+	u32	version;	    /* Offset 20   OpRegion structure version */
+	u8	sbios_version[32];  /* Offset 24   System BIOS build version */
+	u8	vbios_version[16];  /* Offset 56   Video BIOS build version */
+	u8	driver_version[16]; /* Offset 72   Graphic drvr build version */
+	u32	mailboxes;	    /* Offset 88   Mailboxes supported */
+	u32	dmod;		    /* Offset 92   Driver Model */
+	u32	pcon;		    /* Offset 96   Platform Capabilities */
+	u16	dver[16];	    /* Offset 100  GOP Version */
+	u8	reserved[124];	    /* Offset 132  Reserved */
+};
+
+#define IGD_OPREGION_SIGNATURE "IntelGraphicsMem"
+#define IGD_OPREGION_VERSION  2
+
+#define IGD_MBOX1	BIT(0)
+#define IGD_MBOX2	BIT(1)
+#define IGD_MBOX3	BIT(2)
+#define IGD_MBOX4	BIT(3)
+#define IGD_MBOX5	BIT(4)
+
+#define MAILBOXES_MOBILE  (IGD_MBOX1 | IGD_MBOX2 | IGD_MBOX3 | \
+			   IGD_MBOX4 | IGD_MBOX5)
+#define MAILBOXES_DESKTOP (IGD_MBOX2 | IGD_MBOX4)
+
+#define SBIOS_VERSION_SIZE 32
+
+/* mailbox 1: public ACPI methods */
+struct __packed opregion_mailbox1 {
+	u32	drdy;	/* Offset 0    Driver readiness */
+	u32	csts;	/* Offset 4    Status */
+	u32	cevt;	/* Offset 8    Current event */
+	u8	reserved[20];	/* Offset 12   Reserved */
+	u32	didl;	/* Offset 32   Supported display device 1 */
+	u32	ddl2;	/* Offset 36   Supported display device 2 */
+	u32	ddl3;	/* Offset 40   Supported display device 3 */
+	u32	ddl4;	/* Offset 44   Supported display device 4 */
+	u32	ddl5;	/* Offset 48   Supported display device 5 */
+	u32	ddl6;	/* Offset 52   Supported display device 6 */
+	u32	ddl7;	/* Offset 56   Supported display device 7 */
+	u32	ddl8;	/* Offset 60   Supported display device 8 */
+	u32	cpdl;	/* Offset 64   Currently present display device 1 */
+	u32	cpl2;	/* Offset 68   Currently present display device 2 */
+	u32	cpl3;	/* Offset 72   Currently present display device 3 */
+	u32	cpl4;	/* Offset 76   Currently present display device 4 */
+	u32	cpl5;	/* Offset 80   Currently present display device 5 */
+	u32	cpl6;	/* Offset 84   Currently present display device 6 */
+	u32	cpl7;	/* Offset 88   Currently present display device 7 */
+	u32	cpl8;	/* Offset 92   Currently present display device 8 */
+	u32	cadl;	/* Offset 96   Currently active display device 1 */
+	u32	cal2;	/* Offset 100  Currently active display device 2 */
+	u32	cal3;	/* Offset 104  Currently active display device 3 */
+	u32	cal4;	/* Offset 108  Currently active display device 4 */
+	u32	cal5;	/* Offset 112  Currently active display device 5 */
+	u32	cal6;	/* Offset 116  Currently active display device 6 */
+	u32	cal7;	/* Offset 120  Currently active display device 7 */
+	u32	cal8;	/* Offset 124  Currently active display device 8 */
+	u32	nadl;	/* Offset 128  Next active device 1 */
+	u32	ndl2;	/* Offset 132  Next active device 2 */
+	u32	ndl3;	/* Offset 136  Next active device 3 */
+	u32	ndl4;	/* Offset 140  Next active device 4 */
+	u32	ndl5;	/* Offset 144  Next active device 5 */
+	u32	ndl6;	/* Offset 148  Next active device 6 */
+	u32	ndl7;	/* Offset 152  Next active device 7 */
+	u32	ndl8;	/* Offset 156  Next active device 8 */
+	u32	aslp;	/* Offset 160  ASL sleep timeout */
+	u32	tidx;	/* Offset 164  Toggle table index */
+	u32	chpd;	/* Offset 168  Current hot plug enable indicator */
+	u32	clid;	/* Offset 172  Current lid state indicator */
+	u32	cdck;	/* Offset 176  Current docking state indicator */
+	u32	sxsw;	/* Offset 180  Display Switch notification on Sx State
+			 * resume
+			 */
+	u32	evts;	/* Offset 184  Events supported by ASL */
+	u32	cnot;	/* Offset 188  Current OS Notification */
+	u32	nrdy;	/* Offset 192  Reasons for DRDY = 0 */
+	u32	ddl9;	/* Offset 196  Extended Supported display device 1 */
+	u32	dd10;	/* Offset 200  Extended Supported display device 2 */
+	u32	dd11;	/* Offset 204  Extended Supported display device 3 */
+	u32	dd12;	/* Offset 208  Extended Supported display device 4 */
+	u32	dd13;	/* Offset 212  Extended Supported display device 5 */
+	u32	dd14;	/* Offset 216  Extended Supported display device 6 */
+	u32	dd15;	/* Offset 220  Extended Supported display device 7 */
+	u32	cpl9;	/* Offset 224  Extended Currently present device 1 */
+	u32	cp10;	/* Offset 228  Extended Currently present device 2 */
+	u32	cp11;	/* Offset 232  Extended Currently present device 3 */
+	u32	cp12;	/* Offset 236  Extended Currently present device 4 */
+	u32	cp13;	/* Offset 240  Extended Currently present device 5 */
+	u32	cp14;	/* Offset 244  Extended Currently present device 6 */
+	u32	cp15;	/* Offset 248  Extended Currently present device 7 */
+	u8	reserved2[4];	/* Offset 252  Reserved 4 bytes */
+};
+
+/* mailbox 2: software sci interface */
+struct __packed opregion_mailbox2 {
+	u32	scic;	/* Offset 0  Software SCI function number parameters */
+	u32	parm;	/* Offset 4  Software SCI function number parameters */
+	u32	dslp;	/* Offset 8  Driver sleep timeout */
+	u8	reserved[244];	/* Offset 12   Reserved */
+};
+
+/* mailbox 3: power conservation */
+struct __packed opregion_mailbox3 {
+	u32	ardy;		/* Offset 0   Driver readiness */
+	u32	aslc;		/* Offset 4   ASLE interrupt command / status */
+	u32	tche;		/* Offset 8   Technology enabled indicator */
+	u32	alsi;		/* Offset 12  Current ALS illuminance reading */
+	u32	bclp;		/* Offset 16  Backlight britness to set */
+	u32	pfit;		/* Offset 20  Panel fitting Request */
+	u32	cblv;		/* Offset 24  Brightness Current State */
+	/* Offset 28  Backlight Brightness Level Duty Cycle Mapping Table */
+	u16	bclm[20];
+	u32	cpfm;		/* Offset 68  Panel Fitting Current Mode */
+	u32	epfm;		/* Offset 72  Enabled Panel Fitting Modes */
+	u8	plut[74];	/* Offset 76  Panel Look Up Table */
+	/* Offset 150 PWM Frequency and Minimum Brightness */
+	u32	pfmb;
+	u32	ccdv;		/* Offset 154 Color Correction Default Values */
+	u32	pcft;		/* Offset 158 Power Conservation Features */
+	u32	srot;		/* Offset 162 Supported Rotation angle */
+	u32	iuer;		/* Offset 166 Intel Ultrabook Event Register */
+	u64	fdsp;		/* Offset 170 FFS Display Physical address */
+	u32	fdss;		/* Offset 178 FFS Display Size */
+	u32	stat;		/* Offset 182 State Indicator */
+	/*
+	 * Offset 186 (Igd opregion offset 0x3BAh)
+	 * Physical address of Raw VBT data
+	 */
+	u64	rvda;
+	/* Offset 194 (Igd opregion offset 0x3C2h) Size of Raw VBT data */
+	u32	rvds;
+	u8	reserved[58];	/* Offset 198 Reserved */
+};
+
+#define IGD_BACKLIGHT_BRIGHTNESS 0xff
+#define IGD_INITIAL_BRIGHTNESS 0x64
+
+#define IGD_FIELD_VALID		BIT(31)
+#define IGD_WORD_FIELD_VALID	BIT(15)
+#define IGD_PFIT_STRETCH 6
+
+/* mailbox 4: vbt */
+struct __packed opregion_vbt {
+	u8 gvd1[6 << 10];
+};
+
+/* Mailbox 5: BIOS to Driver Notification Extension */
+struct __packed opregion_mailbox5 {
+	u32	phed;		/* Offset 7168 Panel Header */
+	u8	bddc[256];	/* Offset 7172 Panel EDID */
+	u8	reserved[764];	/* Offset 7428 764 bytes */
+};
+
+/* IGD OpRegion */
+struct __packed igd_opregion {
+	struct opregion_header header;
+	struct opregion_mailbox1 mailbox1;
+	struct opregion_mailbox2 mailbox2;
+	struct opregion_mailbox3 mailbox3;
+	struct opregion_vbt vbt;
+	struct opregion_mailbox5 mailbox5;
+};
+
+/* Intel Video BIOS (Option ROM) */
+struct __packed optionrom_header {
+	u16	signature;
+	u8	size;
+	u8	reserved[21];
+	u16	pcir_offset;
+	u16	vbt_offset;
+};
+
+#define OPROM_SIGNATURE 0xaa55
+
+struct __packed optionrom_pcir {
+	u32	signature;
+	u16	vendor;
+	u16	device;
+	u16	reserved1;
+	u16	length;
+	u8	revision;
+	u8	classcode[3];
+	u16	imagelength;
+	u16	coderevision;
+	u8	codetype;
+	u8	indicator;
+	u16	reserved2;
+};
+
+struct __packed optionrom_vbt {
+	u8	hdr_signature[20];
+	u16	hdr_version;
+	u16	hdr_size;
+	u16	hdr_vbt_size;
+	u8	hdr_vbt_checksum;
+	u8	hdr_reserved;
+	u32	hdr_vbt_datablock;
+	u32	hdr_aim[4];
+	u8	datahdr_signature[16];
+	u16	datahdr_version;
+	u16	datahdr_size;
+	u16	datahdr_datablocksize;
+	u8	coreblock_id;
+	u16	coreblock_size;
+	u16	coreblock_biossize;
+	u8	coreblock_biostype;
+	u8	coreblock_releasestatus;
+	u8	coreblock_hwsupported;
+	u8	coreblock_integratedhw;
+	u8	coreblock_biosbuild[4];
+	u8	coreblock_biossignon[155];
+};
+
+/**
+ * intel_gma_init_igd_opregion() - Initialise IGD OpRegion
+ *
+ * This is called from ACPI code and OS drivers
+ *
+ * @return 0 if OK, -ve on error
+ */
+int intel_gma_init_igd_opregion(struct udevice *dev,
+				struct igd_opregion *opregion);
+
+#endif /* _ASM_INTEL_OPREGION_H_ */
diff --git a/arch/x86/lib/fsp/fsp_graphics.c b/arch/x86/lib/fsp/fsp_graphics.c
index 98b762209f..3b2f42f166 100644
--- a/arch/x86/lib/fsp/fsp_graphics.c
+++ b/arch/x86/lib/fsp/fsp_graphics.c
@@ -3,12 +3,17 @@
  * Copyright (C) 2017, Bin Meng <bmeng.cn at gmail.com>
  */
 
+#define LOG_CATEGORY UCLASS_VIDEO
+
 #include <common.h>
+#include <acpi_table.h>
 #include <dm.h>
 #include <vbe.h>
 #include <video.h>
 #include <asm/fsp/fsp_support.h>
+#include <asm/intel_opregion.h>
 #include <asm/mtrr.h>
+#include <dm/acpi.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -114,6 +119,28 @@ err:
 	return ret;
 }
 
+static int fsp_video_acpi_write_tables(const struct udevice *dev,
+				       struct acpi_ctx *ctx)
+{
+	struct igd_opregion *opregion;
+	int ret;
+
+	printf("ACPI:    * IGD OpRegion\n");
+	opregion = (struct igd_opregion *)ctx->current;
+
+	ret = intel_gma_init_igd_opregion((struct udevice *)dev, opregion);
+	if (ret)
+		return ret;
+
+	acpi_inc_align(ctx, sizeof(struct igd_opregion));
+
+	return 0;
+}
+
+struct acpi_ops fsp_video_acpi_ops = {
+	.write_tables	= fsp_video_acpi_write_tables,
+};
+
 static const struct udevice_id fsp_video_ids[] = {
 	{ .compatible = "fsp-fb" },
 	{ }
@@ -124,6 +151,7 @@ U_BOOT_DRIVER(fsp_video) = {
 	.id	= UCLASS_VIDEO,
 	.of_match = fsp_video_ids,
 	.probe	= fsp_video_probe,
+	acpi_ops_ptr(&fsp_video_acpi_ops)
 };
 
 static struct pci_device_id fsp_video_supported[] = {
diff --git a/include/bloblist.h b/include/bloblist.h
index 609ac421d6..94b05c5a6f 100644
--- a/include/bloblist.h
+++ b/include/bloblist.h
@@ -27,6 +27,7 @@ enum bloblist_tag_t {
 	BLOBLISTT_SPL_HANDOFF,		/* Hand-off info from SPL */
 	BLOBLISTT_VBOOT_CTX,		/* Chromium OS verified boot context */
 	BLOBLISTT_VBOOT_HANDOFF,	/* Chromium OS internal handoff info */
+	BLOBLISTT_INTEL_VBT,		/* Intel Video-BIOS table */
 };
 
 /**
