diff mbox series

drm/i915/bios: Workaround broken video BIOS in LG Gram 2021

Message ID 20220119160445.3609367-1-hjl.tools@gmail.com (mailing list archive)
State New, archived
Headers show
Series drm/i915/bios: Workaround broken video BIOS in LG Gram 2021 | expand

Commit Message

H.J. Lu Jan. 19, 2022, 4:04 p.m. UTC
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     | 14 ++++---
 drivers/gpu/drm/i915/display/intel_bios.h     |  3 +-
 drivers/gpu/drm/i915/display/intel_opregion.c |  9 +++--
 drivers/gpu/drm/i915/display/intel_quirks.c   | 40 +++++++++++++++++++
 drivers/gpu/drm/i915/display/intel_quirks.h   |  1 +
 drivers/gpu/drm/i915/i915_drv.h               |  1 +
 6 files changed, 58 insertions(+), 10 deletions(-)
diff mbox series

Patch

diff --git a/drivers/gpu/drm/i915/display/intel_bios.c b/drivers/gpu/drm/i915/display/intel_bios.c
index 7d04572dd18b..4e960eb45a5a 100644
--- a/drivers/gpu/drm/i915/display/intel_bios.c
+++ b/drivers/gpu/drm/i915/display/intel_bios.c
@@ -2273,7 +2273,8 @@  static const struct bdb_header *get_bdb_header(const struct vbt_header *vbt)
  *
  * Returns true on valid VBT.
  */
-bool intel_bios_is_valid_vbt(const void *buf, size_t size)
+bool intel_bios_is_valid_vbt(struct drm_i915_private *dev_priv,
+			     const void *buf, size_t size)
 {
 	const struct vbt_header *vbt = buf;
 	const struct bdb_header *bdb;
@@ -2296,16 +2297,17 @@  bool intel_bios_is_valid_vbt(const void *buf, size_t size)
 		return false;
 	}
 
-	size = vbt->vbt_size;
-
 	if (range_overflows_t(size_t,
 			      vbt->bdb_offset,
 			      sizeof(struct bdb_header),
-			      size)) {
+			      vbt->vbt_size)) {
 		DRM_DEBUG_DRIVER("BDB header incomplete\n");
 		return false;
 	}
 
+	if (!(dev_priv->quirks & 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_DEBUG_DRIVER("BDB incomplete\n");
@@ -2359,7 +2361,7 @@  static struct vbt_header *spi_oprom_get_vbt(struct drm_i915_private *i915)
 		*(vbt + store++) = data;
 	}
 
-	if (!intel_bios_is_valid_vbt(vbt, vbt_size))
+	if (!intel_bios_is_valid_vbt(i915, vbt, vbt_size))
 		goto err_free_vbt;
 
 	drm_dbg_kms(&i915->drm, "Found valid VBT in SPI flash\n");
@@ -2416,7 +2418,7 @@  static struct vbt_header *oprom_get_vbt(struct drm_i915_private *i915)
 
 	memcpy_fromio(vbt, p, vbt_size);
 
-	if (!intel_bios_is_valid_vbt(vbt, vbt_size))
+	if (!intel_bios_is_valid_vbt(i915, vbt, vbt_size))
 		goto err_free_vbt;
 
 	pci_unmap_rom(pdev, oprom);
diff --git a/drivers/gpu/drm/i915/display/intel_bios.h b/drivers/gpu/drm/i915/display/intel_bios.h
index 4709c4d29805..368ee87390e7 100644
--- a/drivers/gpu/drm/i915/display/intel_bios.h
+++ b/drivers/gpu/drm/i915/display/intel_bios.h
@@ -231,7 +231,8 @@  struct mipi_pps_data {
 
 void intel_bios_init(struct drm_i915_private *dev_priv);
 void intel_bios_driver_remove(struct drm_i915_private *dev_priv);
-bool intel_bios_is_valid_vbt(const void *buf, size_t size);
+bool intel_bios_is_valid_vbt(struct drm_i915_private *dev_priv,
+			     const void *buf, size_t size);
 bool intel_bios_is_tv_present(struct drm_i915_private *dev_priv);
 bool intel_bios_is_lvds_present(struct drm_i915_private *dev_priv, u8 *i2c_pin);
 bool intel_bios_is_port_present(struct drm_i915_private *dev_priv, enum port port);
diff --git a/drivers/gpu/drm/i915/display/intel_opregion.c b/drivers/gpu/drm/i915/display/intel_opregion.c
index af9d30f56cc1..7a9b4d72d18c 100644
--- a/drivers/gpu/drm/i915/display/intel_opregion.c
+++ b/drivers/gpu/drm/i915/display/intel_opregion.c
@@ -36,6 +36,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
@@ -817,7 +818,7 @@  static int intel_load_vbt_firmware(struct drm_i915_private *dev_priv)
 		return ret;
 	}
 
-	if (intel_bios_is_valid_vbt(fw->data, fw->size)) {
+	if (intel_bios_is_valid_vbt(dev_priv, fw->data, fw->size)) {
 		opregion->vbt_firmware = kmemdup(fw->data, fw->size, GFP_KERNEL);
 		if (opregion->vbt_firmware) {
 			drm_dbg_kms(&dev_priv->drm,
@@ -922,6 +923,8 @@  int intel_opregion_setup(struct drm_i915_private *dev_priv)
 	if (dmi_check_system(intel_no_opregion_vbt))
 		goto out;
 
+	intel_init_opregion_quirks(dev_priv);
+
 	if (opregion->header->over.major >= 2 && opregion->asle &&
 	    opregion->asle->rvda && opregion->asle->rvds) {
 		resource_size_t rvda = opregion->asle->rvda;
@@ -944,7 +947,7 @@  int intel_opregion_setup(struct drm_i915_private *dev_priv)
 
 		vbt = opregion->rvda;
 		vbt_size = opregion->asle->rvds;
-		if (intel_bios_is_valid_vbt(vbt, vbt_size)) {
+		if (intel_bios_is_valid_vbt(dev_priv, vbt, vbt_size)) {
 			drm_dbg_kms(&dev_priv->drm,
 				    "Found valid VBT in ACPI OpRegion (RVDA)\n");
 			opregion->vbt = vbt;
@@ -969,7 +972,7 @@  int intel_opregion_setup(struct drm_i915_private *dev_priv)
 	vbt_size = (mboxes & MBOX_ASLE_EXT) ?
 		OPREGION_ASLE_EXT_OFFSET : OPREGION_SIZE;
 	vbt_size -= OPREGION_VBT_OFFSET;
-	if (intel_bios_is_valid_vbt(vbt, vbt_size)) {
+	if (intel_bios_is_valid_vbt(dev_priv, vbt, vbt_size)) {
 		drm_dbg_kms(&dev_priv->drm,
 			    "Found valid VBT in ACPI OpRegion (Mailbox #4)\n");
 		opregion->vbt = vbt;
diff --git a/drivers/gpu/drm/i915/display/intel_quirks.c b/drivers/gpu/drm/i915/display/intel_quirks.c
index c8488f5ebd04..c2604e8b5353 100644
--- a/drivers/gpu/drm/i915/display/intel_quirks.c
+++ b/drivers/gpu/drm/i915/display/intel_quirks.c
@@ -133,6 +133,36 @@  static const struct intel_dmi_quirk intel_dmi_quirks[] = {
 	},
 };
 
+static void quirk_opregion_use_fw_size_as_vbt_size(struct drm_i915_private *i915)
+{
+	i915->quirks |= QUIRK_USE_FW_SIZE_AS_VBT_SIZE;
+	drm_info(&i915->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 },
@@ -213,3 +243,13 @@  void intel_init_quirks(struct drm_i915_private *i915)
 			intel_dmi_quirks[i].hook(i915);
 	}
 }
+
+void intel_init_opregion_quirks(struct drm_i915_private *i915)
+{
+	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(i915);
+	}
+}
diff --git a/drivers/gpu/drm/i915/display/intel_quirks.h b/drivers/gpu/drm/i915/display/intel_quirks.h
index b0fcff142a56..3a6a84e0d502 100644
--- a/drivers/gpu/drm/i915/display/intel_quirks.h
+++ b/drivers/gpu/drm/i915/display/intel_quirks.h
@@ -9,5 +9,6 @@ 
 struct drm_i915_private;
 
 void intel_init_quirks(struct drm_i915_private *dev_priv);
+void intel_init_opregion_quirks(struct drm_i915_private *dev_priv);
 
 #endif /* __INTEL_QUIRKS_H__ */
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 890f1f6fbc49..08572fae2d97 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -403,6 +403,7 @@  struct i915_drrs {
 #define QUIRK_INCREASE_T12_DELAY (1<<6)
 #define QUIRK_INCREASE_DDI_DISABLED_TIME (1<<7)
 #define QUIRK_NO_PPS_BACKLIGHT_POWER_HOOK (1<<8)
+#define QUIRK_USE_FW_SIZE_AS_VBT_SIZE (1<<9)
 
 struct intel_fbdev;