From 228e4a56101adf299c8635c7cba900185b9256d0 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Tue, 7 Jun 2022 19:00:18 -0700
Subject: [PATCH] drm/i915/bios: Workaround broken video BIOS in LG Gram 2021

LG Gram 2021 laptop 17Z95P-K.ADE9U1 OpRegion has

FW size: 0x2200
VBT size: 0x2000
BDB offset: 0x30
BDB size: 0x216e

Add intel_init_opregion_quirks to use FW size as VBT size on LG Gram
17Z95P-K.ADE9U1 and update intel_bios_is_valid_vbt to use FW size,
instead of VBT size if the quirk is applied, in range_overflows_t for
BDB size overflow check.  This fixes:

https://gitlab.freedesktop.org/drm/intel/-/issues/4763

Signed-off-by: H.J. Lu <hjl.tools@gmail.com>
---
 drivers/gpu/drm/i915/display/intel_bios.c     |  8 ++--
 drivers/gpu/drm/i915/display/intel_opregion.c |  3 ++
 drivers/gpu/drm/i915/display/intel_quirks.c   | 40 +++++++++++++++++++
 drivers/gpu/drm/i915/display/intel_quirks.h   |  2 +
 4 files changed, 50 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/i915/display/intel_bios.c b/drivers/gpu/drm/i915/display/intel_bios.c
index e0e4e9b62d8d..bf4fb3ca617e 100644
--- a/drivers/gpu/drm/i915/display/intel_bios.c
+++ b/drivers/gpu/drm/i915/display/intel_bios.c
@@ -39,6 +39,7 @@
 #include "intel_display.h"
 #include "intel_display_types.h"
 #include "intel_gmbus.h"
+#include "intel_quirks.h"
 
 #define _INTEL_BIOS_PRIVATE
 #include "intel_vbt_defs.h"
@@ -2999,16 +3000,17 @@ bool intel_bios_is_valid_vbt(struct intel_display *display,
 		return false;
 	}
 
-	size = vbt->vbt_size;
-
 	if (range_overflows_t(size_t,
 			      vbt->bdb_offset,
 			      sizeof(struct bdb_header),
-			      size)) {
+			      vbt->vbt_size)) {
 		drm_dbg_kms(display->drm, "BDB header incomplete\n");
 		return false;
 	}
 
+	if (!intel_has_quirk(display, QUIRK_USE_FW_SIZE_AS_VBT_SIZE))
+		size = vbt->vbt_size;
+
 	bdb = get_bdb_header(vbt);
 	if (range_overflows_t(size_t, vbt->bdb_offset, bdb->bdb_size, size)) {
 		drm_dbg_kms(display->drm, "BDB incomplete\n");
diff --git a/drivers/gpu/drm/i915/display/intel_opregion.c b/drivers/gpu/drm/i915/display/intel_opregion.c
index 0eaa6cd6fe80..b17c91a56ea5 100644
--- a/drivers/gpu/drm/i915/display/intel_opregion.c
+++ b/drivers/gpu/drm/i915/display/intel_opregion.c
@@ -38,6 +38,7 @@
 #include "intel_display_types.h"
 #include "intel_opregion.h"
 #include "intel_pci_config.h"
+#include "intel_quirks.h"
 
 #define OPREGION_HEADER_OFFSET 0
 #define OPREGION_ACPI_OFFSET   0x100
@@ -969,6 +970,8 @@ int intel_opregion_setup(struct intel_display *display)
 	if (dmi_check_system(intel_no_opregion_vbt))
 		goto out;
 
+	intel_init_opregion_quirks(display);
+
 	if (opregion->header->over.major >= 2 && opregion->asle &&
 	    opregion->asle->rvda && opregion->asle->rvds) {
 		resource_size_t rvda = opregion->asle->rvda;
diff --git a/drivers/gpu/drm/i915/display/intel_quirks.c b/drivers/gpu/drm/i915/display/intel_quirks.c
index 8b30e9fd936e..5a9d1d83cc78 100644
--- a/drivers/gpu/drm/i915/display/intel_quirks.c
+++ b/drivers/gpu/drm/i915/display/intel_quirks.c
@@ -166,6 +166,36 @@ static const struct intel_dmi_quirk intel_dmi_quirks[] = {
 	},
 };
 
+static void quirk_opregion_use_fw_size_as_vbt_size(struct intel_display *display)
+{
+	intel_set_quirk(display, QUIRK_USE_FW_SIZE_AS_VBT_SIZE);
+	drm_info(display->drm, "Applying FW size as VBT size quirk in OpRegion\n");
+}
+
+static int intel_dmi_opregion_use_fw_size_as_vbt_size(const struct dmi_system_id *id)
+{
+	DRM_INFO("Use FW size as VBT size on %s in OpRegion\n", id->ident);
+	return 1;
+}
+
+static const struct intel_dmi_quirk intel_dmi_opregion_quirks[] = {
+	{
+		.dmi_id_list = &(const struct dmi_system_id[]) {
+			{
+				.callback = intel_dmi_opregion_use_fw_size_as_vbt_size,
+				.ident = "LG Gram 17Z95P-K.ADE9U1",
+				.matches = {DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LG Electronics"),
+					    DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "17Z95P-K.ADE9U1"),
+					    DMI_EXACT_MATCH(DMI_BIOS_VERSION, "T4ZF0040 X64"),
+					    DMI_EXACT_MATCH(DMI_BIOS_DATE, "10/06/2021"),
+				},
+			},
+			{ }
+		},
+		.hook = quirk_opregion_use_fw_size_as_vbt_size,
+	},
+};
+
 static struct intel_quirk intel_quirks[] = {
 	/* Lenovo U160 cannot use SSC on LVDS */
 	{ 0x0046, 0x17aa, 0x3920, quirk_ssc_force_disable },
@@ -296,3 +326,13 @@ bool intel_has_dpcd_quirk(struct intel_dp *intel_dp, enum intel_quirk_id quirk)
 {
 	return intel_dp->quirks.mask & BIT(quirk);
 }
+
+void intel_init_opregion_quirks(struct intel_display *display)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(intel_dmi_opregion_quirks); i++) {
+		if (dmi_check_system(*intel_dmi_opregion_quirks[i].dmi_id_list) != 0)
+			intel_dmi_opregion_quirks[i].hook(display);
+	}
+}
diff --git a/drivers/gpu/drm/i915/display/intel_quirks.h b/drivers/gpu/drm/i915/display/intel_quirks.h
index cafdebda7535..00a0abd09224 100644
--- a/drivers/gpu/drm/i915/display/intel_quirks.h
+++ b/drivers/gpu/drm/i915/display/intel_quirks.h
@@ -20,6 +20,7 @@ enum intel_quirk_id {
 	QUIRK_LVDS_SSC_DISABLE,
 	QUIRK_NO_PPS_BACKLIGHT_POWER_HOOK,
 	QUIRK_FW_SYNC_LEN,
+	QUIRK_USE_FW_SIZE_AS_VBT_SIZE,
 };
 
 void intel_init_quirks(struct intel_display *display);
@@ -27,5 +28,6 @@ void intel_init_dpcd_quirks(struct intel_dp *intel_dp,
 			    const struct drm_dp_dpcd_ident *ident);
 bool intel_has_quirk(struct intel_display *display, enum intel_quirk_id quirk);
 bool intel_has_dpcd_quirk(struct intel_dp *intel_dp, enum intel_quirk_id quirk);
+void intel_init_opregion_quirks(struct intel_display *display);
 
 #endif /* __INTEL_QUIRKS_H__ */
-- 
2.48.1

