lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20240214215756.6530-4-mario.limonciello@amd.com>
Date: Wed, 14 Feb 2024 15:57:54 -0600
From: Mario Limonciello <mario.limonciello@....com>
To: Daniel Vetter <daniel@...ll.ch>, Jani Nikula
	<jani.nikula@...ux.intel.com>, Alex Deucher <alexander.deucher@....com>,
	"Hans de Goede" <hdegoede@...hat.com>, "open list:DRM DRIVERS"
	<dri-devel@...ts.freedesktop.org>
CC: <amd-gfx@...ts.freedesktop.org>, "open list:USB SUBSYSTEM"
	<linux-usb@...r.kernel.org>, <linux-fbdev@...r.kernel.org>,
	<nouveau@...ts.freedesktop.org>, <intel-gfx@...ts.freedesktop.org>,
	<platform-driver-x86@...r.kernel.org>, <intel-xe@...ts.freedesktop.org>,
	<linux-renesas-soc@...r.kernel.org>, "open list:ACPI"
	<linux-acpi@...r.kernel.org>, open list <linux-kernel@...r.kernel.org>,
	Melissa Wen <mwen@...lia.com>, Mark Pearson <mpearson-lenovo@...ebb.ca>,
	Mario Limonciello <mario.limonciello@....com>
Subject: [PATCH v6 3/5] drm: Add support to get EDID from ACPI

Some manufacturers have intentionally put an EDID that differs from
the EDID on the internal panel on laptops.  Drivers that prefer to
fetch this EDID can set a bit on the drm_connector to indicate that
the DRM EDID helpers should try to fetch it and it is preferred if
it's present.

Signed-off-by: Mario Limonciello <mario.limonciello@....com>
---
 drivers/gpu/drm/Kconfig     |   1 +
 drivers/gpu/drm/drm_edid.c  | 109 +++++++++++++++++++++++++++++++++---
 include/drm/drm_connector.h |   6 ++
 include/drm/drm_edid.h      |   1 +
 4 files changed, 109 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 872edb47bb53..3db89e6af01d 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -8,6 +8,7 @@
 menuconfig DRM
 	tristate "Direct Rendering Manager (XFree86 4.1.0 and higher DRI support)"
 	depends on (AGP || AGP=n) && !EMULATED_CMPXCHG && HAS_DMA
+	depends on (ACPI_VIDEO || ACPI_VIDEO=n)
 	select DRM_PANEL_ORIENTATION_QUIRKS
 	select DRM_KMS_HELPER if DRM_FBDEV_EMULATION
 	select FB_CORE if DRM_FBDEV_EMULATION
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 923c4423151c..cdc30c6d05d5 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -28,6 +28,7 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
+#include <acpi/video.h>
 #include <linux/bitfield.h>
 #include <linux/cec.h>
 #include <linux/hdmi.h>
@@ -2188,6 +2189,58 @@ drm_do_probe_ddc_edid(void *data, u8 *buf, unsigned int block, size_t len)
 	return ret == xfers ? 0 : -1;
 }
 
+/**
+ * drm_do_probe_acpi_edid() - get EDID information via ACPI _DDC
+ * @data: struct drm_connector
+ * @buf: EDID data buffer to be filled
+ * @block: 128 byte EDID block to start fetching from
+ * @len: EDID data buffer length to fetch
+ *
+ * Try to fetch EDID information by calling acpi_video_get_edid() function.
+ *
+ * Return: 0 on success or error code on failure.
+ */
+static int
+drm_do_probe_acpi_edid(void *data, u8 *buf, unsigned int block, size_t len)
+{
+	struct drm_connector *connector = data;
+	struct drm_device *ddev = connector->dev;
+	struct acpi_device *acpidev = ACPI_COMPANION(ddev->dev);
+	unsigned char start = block * EDID_LENGTH;
+	void *edid;
+	int r;
+
+	if (!acpidev)
+		return -ENODEV;
+
+	switch (connector->connector_type) {
+	case DRM_MODE_CONNECTOR_LVDS:
+	case DRM_MODE_CONNECTOR_eDP:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* fetch the entire edid from BIOS */
+	r = acpi_video_get_edid(acpidev, ACPI_VIDEO_DISPLAY_LCD, -1, &edid);
+	if (r < 0) {
+		DRM_DEBUG_KMS("Failed to get EDID from ACPI: %d\n", r);
+		return r;
+	}
+	if (len > r || start > r || start + len > r) {
+		r = -EINVAL;
+		goto cleanup;
+	}
+
+	memcpy(buf, edid + start, len);
+	r = 0;
+
+cleanup:
+	kfree(edid);
+
+	return r;
+}
+
 static void connector_bad_edid(struct drm_connector *connector,
 			       const struct edid *edid, int num_blocks)
 {
@@ -2621,7 +2674,8 @@ EXPORT_SYMBOL(drm_probe_ddc);
  * @connector: connector we're probing
  * @adapter: I2C adapter to use for DDC
  *
- * Poke the given I2C channel to grab EDID data if possible.  If found,
+ * If the connector allows it, try to fetch EDID data using ACPI. If not found
+ * poke the given I2C channel to grab EDID data if possible.  If found,
  * attach it to the connector.
  *
  * Return: Pointer to valid EDID or NULL if we couldn't find any.
@@ -2629,20 +2683,50 @@ EXPORT_SYMBOL(drm_probe_ddc);
 struct edid *drm_get_edid(struct drm_connector *connector,
 			  struct i2c_adapter *adapter)
 {
-	struct edid *edid;
+	struct edid *edid = NULL;
 
 	if (connector->force == DRM_FORCE_OFF)
 		return NULL;
 
-	if (connector->force == DRM_FORCE_UNSPECIFIED && !drm_probe_ddc(adapter))
-		return NULL;
+	if (connector->acpi_edid_allowed)
+		edid = _drm_do_get_edid(connector, drm_do_probe_acpi_edid, connector, NULL);
+
+	if (!edid) {
+		if (connector->force == DRM_FORCE_UNSPECIFIED && !drm_probe_ddc(adapter))
+			return NULL;
+		edid = _drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter, NULL);
+	}
 
-	edid = _drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter, NULL);
 	drm_connector_update_edid_property(connector, edid);
 	return edid;
 }
 EXPORT_SYMBOL(drm_get_edid);
 
+/**
+ * drm_edid_read_acpi - get EDID data, if available
+ * @connector: connector we're probing
+ *
+ * Use the BIOS to attempt to grab EDID data if possible.
+ *
+ * The returned pointer must be freed using drm_edid_free().
+ *
+ * Return: Pointer to valid EDID or NULL if we couldn't find any.
+ */
+const struct drm_edid *drm_edid_read_acpi(struct drm_connector *connector)
+{
+	const struct drm_edid *drm_edid;
+
+	if (connector->force == DRM_FORCE_OFF)
+		return NULL;
+
+	drm_edid = drm_edid_read_custom(connector, drm_do_probe_acpi_edid, connector);
+
+	/* Note: Do *not* call connector updates here. */
+
+	return drm_edid;
+}
+EXPORT_SYMBOL(drm_edid_read_acpi);
+
 /**
  * drm_edid_read_custom - Read EDID data using given EDID block read function
  * @connector: Connector to use
@@ -2727,10 +2811,11 @@ const struct drm_edid *drm_edid_read_ddc(struct drm_connector *connector,
 EXPORT_SYMBOL(drm_edid_read_ddc);
 
 /**
- * drm_edid_read - Read EDID data using connector's I2C adapter
+ * drm_edid_read - Read EDID data using BIOS or connector's I2C adapter
  * @connector: Connector to use
  *
- * Read EDID using the connector's I2C adapter.
+ * Read EDID from BIOS if allowed by connector or by using the connector's
+ * I2C adapter.
  *
  * The EDID may be overridden using debugfs override_edid or firmware EDID
  * (drm_edid_load_firmware() and drm.edid_firmware parameter), in this priority
@@ -2742,10 +2827,18 @@ EXPORT_SYMBOL(drm_edid_read_ddc);
  */
 const struct drm_edid *drm_edid_read(struct drm_connector *connector)
 {
+	const struct drm_edid *drm_edid = NULL;
+
 	if (drm_WARN_ON(connector->dev, !connector->ddc))
 		return NULL;
 
-	return drm_edid_read_ddc(connector, connector->ddc);
+	if (connector->acpi_edid_allowed)
+		drm_edid = drm_edid_read_acpi(connector);
+
+	if (!drm_edid)
+		drm_edid = drm_edid_read_ddc(connector, connector->ddc);
+
+	return drm_edid;
 }
 EXPORT_SYMBOL(drm_edid_read);
 
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index fe88d7fc6b8f..74ed47f37a69 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -1886,6 +1886,12 @@ struct drm_connector {
 
 	/** @hdr_sink_metadata: HDR Metadata Information read from sink */
 	struct hdr_sink_metadata hdr_sink_metadata;
+
+	/**
+	 * @acpi_edid_allowed: Get the EDID from the BIOS, if available.
+	 * This is only applicable to eDP and LVDS displays.
+	 */
+	bool acpi_edid_allowed;
 };
 
 #define obj_to_connector(x) container_of(x, struct drm_connector, base)
diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h
index 7923bc00dc7a..1c1ee927de9c 100644
--- a/include/drm/drm_edid.h
+++ b/include/drm/drm_edid.h
@@ -459,5 +459,6 @@ bool drm_edid_is_digital(const struct drm_edid *drm_edid);
 
 const u8 *drm_find_edid_extension(const struct drm_edid *drm_edid,
 				  int ext_id, int *ext_index);
+const struct drm_edid *drm_edid_read_acpi(struct drm_connector *connector);
 
 #endif /* __DRM_EDID_H__ */
-- 
2.34.1


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ