Commit ab3595bc authored by Jani Nikula's avatar Jani Nikula

drm/i915/opregion: let user specify override VBT via firmware load

Sometimes it would be most enlightening to debug systems by replacing
the VBT to be used. For example, in the referenced bug the BIOS provides
different VBT depending on the boot mode (UEFI vs. legacy). It would be
interesting to try the failing boot mode with the VBT from the working
boot, and see if that makes a difference.

Add a module parameter to load the VBT using the firmware loader, not
unlike the EDID firmware mechanism.

As a starting point for experimenting, one can pick up the BIOS provided
VBT from /sys/kernel/debug/dri/0/i915_opregion/i915_vbt.

v2: clarify firmware load return value check (Bob)

v3: kfree the loaded firmware blob

References: https://bugs.freedesktop.org/show_bug.cgi?id=97822#c83Reviewed-by: default avatarBob Paauwe <bob.j.paauwe@intel.com>
Acked-by: default avatarDaniel Vetter <daniel@ffwll.ch>
Signed-off-by: default avatarJani Nikula <jani.nikula@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20170817115209.25912-1-jani.nikula@intel.com
parent a029fa4d
...@@ -646,6 +646,7 @@ struct intel_opregion { ...@@ -646,6 +646,7 @@ struct intel_opregion {
u32 swsci_sbcb_sub_functions; u32 swsci_sbcb_sub_functions;
struct opregion_asle *asle; struct opregion_asle *asle;
void *rvda; void *rvda;
void *vbt_firmware;
const void *vbt; const void *vbt;
u32 vbt_size; u32 vbt_size;
u32 *lid_state; u32 *lid_state;
......
...@@ -118,6 +118,10 @@ MODULE_PARM_DESC(vbt_sdvo_panel_type, ...@@ -118,6 +118,10 @@ MODULE_PARM_DESC(vbt_sdvo_panel_type,
module_param_named_unsafe(reset, i915.reset, int, 0600); module_param_named_unsafe(reset, i915.reset, int, 0600);
MODULE_PARM_DESC(reset, "Attempt GPU resets (0=disabled, 1=full gpu reset, 2=engine reset [default])"); MODULE_PARM_DESC(reset, "Attempt GPU resets (0=disabled, 1=full gpu reset, 2=engine reset [default])");
module_param_named_unsafe(vbt_firmware, i915.vbt_firmware, charp, 0400);
MODULE_PARM_DESC(vbt_firmware,
"Load VBT from specified file under /lib/firmware");
#if IS_ENABLED(CONFIG_DRM_I915_CAPTURE_ERROR) #if IS_ENABLED(CONFIG_DRM_I915_CAPTURE_ERROR)
module_param_named(error_capture, i915.error_capture, bool, 0600); module_param_named(error_capture, i915.error_capture, bool, 0600);
MODULE_PARM_DESC(error_capture, MODULE_PARM_DESC(error_capture,
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include <linux/cache.h> /* for __read_mostly */ #include <linux/cache.h> /* for __read_mostly */
#define I915_PARAMS_FOR_EACH(func) \ #define I915_PARAMS_FOR_EACH(func) \
func(char *, vbt_firmware); \
func(int, modeset); \ func(int, modeset); \
func(int, panel_ignore_lid); \ func(int, panel_ignore_lid); \
func(int, semaphores); \ func(int, semaphores); \
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/dmi.h> #include <linux/dmi.h>
#include <linux/firmware.h>
#include <acpi/video.h> #include <acpi/video.h>
#include <drm/drmP.h> #include <drm/drmP.h>
...@@ -829,6 +830,10 @@ void intel_opregion_unregister(struct drm_i915_private *dev_priv) ...@@ -829,6 +830,10 @@ void intel_opregion_unregister(struct drm_i915_private *dev_priv)
memunmap(opregion->rvda); memunmap(opregion->rvda);
opregion->rvda = NULL; opregion->rvda = NULL;
} }
if (opregion->vbt_firmware) {
kfree(opregion->vbt_firmware);
opregion->vbt_firmware = NULL;
}
opregion->header = NULL; opregion->header = NULL;
opregion->acpi = NULL; opregion->acpi = NULL;
opregion->swsci = NULL; opregion->swsci = NULL;
...@@ -912,6 +917,43 @@ static const struct dmi_system_id intel_no_opregion_vbt[] = { ...@@ -912,6 +917,43 @@ static const struct dmi_system_id intel_no_opregion_vbt[] = {
{ } { }
}; };
static int intel_load_vbt_firmware(struct drm_i915_private *dev_priv)
{
struct intel_opregion *opregion = &dev_priv->opregion;
const struct firmware *fw = NULL;
const char *name = i915.vbt_firmware;
int ret;
if (!name || !*name)
return -ENOENT;
ret = request_firmware(&fw, name, &dev_priv->drm.pdev->dev);
if (ret) {
DRM_ERROR("Requesting VBT firmware \"%s\" failed (%d)\n",
name, ret);
return ret;
}
if (intel_bios_is_valid_vbt(fw->data, fw->size)) {
opregion->vbt_firmware = kmemdup(fw->data, fw->size, GFP_KERNEL);
if (opregion->vbt_firmware) {
DRM_DEBUG_KMS("Found valid VBT firmware \"%s\"\n", name);
opregion->vbt = opregion->vbt_firmware;
opregion->vbt_size = fw->size;
ret = 0;
} else {
ret = -ENOMEM;
}
} else {
DRM_DEBUG_KMS("Invalid VBT firmware \"%s\"\n", name);
ret = -EINVAL;
}
release_firmware(fw);
return ret;
}
int intel_opregion_setup(struct drm_i915_private *dev_priv) int intel_opregion_setup(struct drm_i915_private *dev_priv)
{ {
struct intel_opregion *opregion = &dev_priv->opregion; struct intel_opregion *opregion = &dev_priv->opregion;
...@@ -974,6 +1016,9 @@ int intel_opregion_setup(struct drm_i915_private *dev_priv) ...@@ -974,6 +1016,9 @@ int intel_opregion_setup(struct drm_i915_private *dev_priv)
if (mboxes & MBOX_ASLE_EXT) if (mboxes & MBOX_ASLE_EXT)
DRM_DEBUG_DRIVER("ASLE extension supported\n"); DRM_DEBUG_DRIVER("ASLE extension supported\n");
if (intel_load_vbt_firmware(dev_priv) == 0)
goto out;
if (dmi_check_system(intel_no_opregion_vbt)) if (dmi_check_system(intel_no_opregion_vbt))
goto out; goto out;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment