2024-02-01 22:29:01

by Mario Limonciello

[permalink] [raw]
Subject: [PATCH v3 0/5] Add support for fetching EDID from ACPI _DDC

Some laptops ship an EDID in the BIOS encoded in the _DDC method that
differs than the EDID directly on the laptop panel for $REASONS.

This is the EDID that is used by the AMD Windows driver, and so sometimes
different results are found in different operating systems.

This series adds a new DRM helper that will use acpi_video to fetch the
EDID.

On amdgpu when an eDP panel is found the BIOS
is checked first for an EDID and that used as a preference if found.

On nouveau it replaces the previous local function doing a similar role.

This does *not* use struct drm_edid as this will require more involved
amdgpu display driver work that will come separately as part of follow-ups
to: https://lore.kernel.org/amd-gfx/[email protected]/

v2-v3:
* Clean up some of the 'select ACPI_VIDEO' kconfig spaghetti reported by LKP
* Drop the 'select ACPI_VIDEO' from the DRM drivers

Mario Limonciello (5):
ACPI: video: Handle fetching EDID that is longer than 256 bytes
drm: Add drm_get_acpi_edid() helper
drm/amd: Fetch the EDID from _DDC if available for eDP
drm/nouveau: Use drm_get_acpi_edid() helper
drm: Drop unneeded selects in DRM drivers

drivers/acpi/acpi_video.c | 25 +++----
drivers/gpu/drm/Kconfig | 5 ++
drivers/gpu/drm/amd/amdgpu/Kconfig | 7 --
drivers/gpu/drm/amd/amdgpu/amdgpu.h | 1 +
.../gpu/drm/amd/amdgpu/amdgpu_connectors.c | 4 +
drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 8 ++
.../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 10 ++-
.../amd/display/amdgpu_dm/amdgpu_dm_helpers.c | 9 ++-
drivers/gpu/drm/drm_edid.c | 73 +++++++++++++++++++
drivers/gpu/drm/gma500/Kconfig | 6 --
drivers/gpu/drm/i915/Kconfig | 7 --
drivers/gpu/drm/nouveau/nouveau_acpi.c | 27 -------
drivers/gpu/drm/nouveau/nouveau_acpi.h | 2 -
drivers/gpu/drm/nouveau/nouveau_connector.c | 2 +-
drivers/gpu/drm/radeon/Kconfig | 7 --
drivers/gpu/drm/xe/Kconfig | 6 --
include/drm/drm_edid.h | 1 +
17 files changed, 116 insertions(+), 84 deletions(-)

--
2.34.1



2024-02-01 22:29:39

by Mario Limonciello

[permalink] [raw]
Subject: [PATCH v3 2/5] drm: Add drm_get_acpi_edid() helper

Some manufacturers have intentionally put an EDID that differs from
the EDID on the internal panel on laptops. Drivers can call this
helper to attempt to fetch the EDID from the BIOS's ACPI _DDC method.

Signed-off-by: Mario Limonciello <[email protected]>
---
v1->v2:
* Split code from previous amdgpu specific helper to generic drm helper.
v2->v3:
* Add an extra select to fix a variety of randconfig errors found from
LKP robot.
---
drivers/gpu/drm/Kconfig | 5 +++
drivers/gpu/drm/drm_edid.c | 73 ++++++++++++++++++++++++++++++++++++++
include/drm/drm_edid.h | 1 +
3 files changed, 79 insertions(+)

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 2520db0b776e..14df907c96c8 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -21,6 +21,11 @@ menuconfig DRM
select KCMP
select VIDEO_CMDLINE
select VIDEO_NOMODESET
+ select ACPI_VIDEO if ACPI
+ select BACKLIGHT_CLASS_DEVICE if ACPI
+ select INPUT if ACPI
+ select X86_PLATFORM_DEVICES if ACPI && X86
+ select ACPI_WMI if ACPI && X86
help
Kernel-level support for the Direct Rendering Infrastructure (DRI)
introduced in XFree86 4.0. If you say Y here, you need to select
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 69c68804023f..1fbbeaa664b2 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,47 @@ 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_device
+ * @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_device *ddev = data;
+ struct acpi_device *acpidev = ACPI_COMPANION(ddev->dev);
+ unsigned char start = block * EDID_LENGTH;
+ void *edid;
+ int r;
+
+ if (!acpidev)
+ return -ENODEV;
+
+ /* 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 -EINVAL;
+ }
+ 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)
{
@@ -2643,6 +2685,37 @@ struct edid *drm_get_edid(struct drm_connector *connector,
}
EXPORT_SYMBOL(drm_get_edid);

+/**
+ * drm_get_acpi_edid - get EDID data, if available
+ * @connector: connector we're probing
+ *
+ * Use the BIOS to attempt 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.
+ */
+struct edid *drm_get_acpi_edid(struct drm_connector *connector)
+{
+ struct edid *edid = NULL;
+
+ switch (connector->connector_type) {
+ case DRM_MODE_CONNECTOR_LVDS:
+ case DRM_MODE_CONNECTOR_eDP:
+ break;
+ default:
+ return NULL;
+ }
+
+ if (connector->force == DRM_FORCE_OFF)
+ return NULL;
+
+ edid = _drm_do_get_edid(connector, drm_do_probe_acpi_edid, connector->dev, NULL);
+
+ drm_connector_update_edid_property(connector, edid);
+ return edid;
+}
+EXPORT_SYMBOL(drm_get_acpi_edid);
+
/**
* drm_edid_read_custom - Read EDID data using given EDID block read function
* @connector: Connector to use
diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h
index 518d1b8106c7..60fbdc06badc 100644
--- a/include/drm/drm_edid.h
+++ b/include/drm/drm_edid.h
@@ -412,6 +412,7 @@ struct edid *drm_do_get_edid(struct drm_connector *connector,
void *data);
struct edid *drm_get_edid(struct drm_connector *connector,
struct i2c_adapter *adapter);
+struct edid *drm_get_acpi_edid(struct drm_connector *connector);
u32 drm_edid_get_panel_id(struct i2c_adapter *adapter);
struct edid *drm_get_edid_switcheroo(struct drm_connector *connector,
struct i2c_adapter *adapter);
--
2.34.1


2024-02-01 22:30:02

by Mario Limonciello

[permalink] [raw]
Subject: [PATCH v3 5/5] drm: Drop unneeded selects in DRM drivers

All of the selects on ACPI_VIDEO are unnecessary when DRM does the
select for ACPI_VIDEO as it provides a helper for acpi based EDID.

Signed-off-by: Mario Limonciello <[email protected]>
---
v2->v3:
* new patch
---
drivers/gpu/drm/amd/amdgpu/Kconfig | 7 -------
drivers/gpu/drm/gma500/Kconfig | 6 ------
drivers/gpu/drm/i915/Kconfig | 7 -------
drivers/gpu/drm/radeon/Kconfig | 7 -------
drivers/gpu/drm/xe/Kconfig | 6 ------
5 files changed, 33 deletions(-)

diff --git a/drivers/gpu/drm/amd/amdgpu/Kconfig b/drivers/gpu/drm/amd/amdgpu/Kconfig
index 22d88f8ef527..b2178a5a947c 100644
--- a/drivers/gpu/drm/amd/amdgpu/Kconfig
+++ b/drivers/gpu/drm/amd/amdgpu/Kconfig
@@ -22,13 +22,6 @@ config DRM_AMDGPU
select DRM_BUDDY
select DRM_SUBALLOC_HELPER
select DRM_EXEC
- # amdgpu depends on ACPI_VIDEO when ACPI is enabled, for select to work
- # ACPI_VIDEO's dependencies must also be selected.
- select INPUT if ACPI
- select ACPI_VIDEO if ACPI
- # On x86 ACPI_VIDEO also needs ACPI_WMI
- select X86_PLATFORM_DEVICES if ACPI && X86
- select ACPI_WMI if ACPI && X86
help
Choose this option if you have a recent AMD Radeon graphics card.

diff --git a/drivers/gpu/drm/gma500/Kconfig b/drivers/gpu/drm/gma500/Kconfig
index efb4a2dd2f80..6921ef67b256 100644
--- a/drivers/gpu/drm/gma500/Kconfig
+++ b/drivers/gpu/drm/gma500/Kconfig
@@ -6,12 +6,6 @@ config DRM_GMA500
select FB_IOMEM_HELPERS if DRM_FBDEV_EMULATION
select I2C
select I2C_ALGOBIT
- # GMA500 depends on ACPI_VIDEO when ACPI is enabled, just like i915
- select ACPI_VIDEO if ACPI
- select BACKLIGHT_CLASS_DEVICE if ACPI
- select INPUT if ACPI
- select X86_PLATFORM_DEVICES if ACPI
- select ACPI_WMI if ACPI
help
Say yes for an experimental 2D KMS framebuffer driver for the
Intel GMA500 (Poulsbo), Intel GMA600 (Moorestown/Oak Trail) and
diff --git a/drivers/gpu/drm/i915/Kconfig b/drivers/gpu/drm/i915/Kconfig
index b5d6e3352071..476da09433bb 100644
--- a/drivers/gpu/drm/i915/Kconfig
+++ b/drivers/gpu/drm/i915/Kconfig
@@ -22,13 +22,6 @@ config DRM_I915
select I2C
select I2C_ALGOBIT
select IRQ_WORK
- # i915 depends on ACPI_VIDEO when ACPI is enabled
- # but for select to work, need to select ACPI_VIDEO's dependencies, ick
- select BACKLIGHT_CLASS_DEVICE if ACPI
- select INPUT if ACPI
- select X86_PLATFORM_DEVICES if ACPI
- select ACPI_WMI if ACPI
- select ACPI_VIDEO if ACPI
select ACPI_BUTTON if ACPI
select SYNC_FILE
select IOSF_MBI if X86
diff --git a/drivers/gpu/drm/radeon/Kconfig b/drivers/gpu/drm/radeon/Kconfig
index f98356be0af2..12149d594100 100644
--- a/drivers/gpu/drm/radeon/Kconfig
+++ b/drivers/gpu/drm/radeon/Kconfig
@@ -19,13 +19,6 @@ config DRM_RADEON
select INTERVAL_TREE
select I2C
select I2C_ALGOBIT
- # radeon depends on ACPI_VIDEO when ACPI is enabled, for select to work
- # ACPI_VIDEO's dependencies must also be selected.
- select INPUT if ACPI
- select ACPI_VIDEO if ACPI
- # On x86 ACPI_VIDEO also needs ACPI_WMI
- select X86_PLATFORM_DEVICES if ACPI && X86
- select ACPI_WMI if ACPI && X86
help
Choose this option if you have an ATI Radeon graphics card. There
are both PCI and AGP versions. You don't need to choose this to
diff --git a/drivers/gpu/drm/xe/Kconfig b/drivers/gpu/drm/xe/Kconfig
index e36ae1f0d885..cf60bdcafb0c 100644
--- a/drivers/gpu/drm/xe/Kconfig
+++ b/drivers/gpu/drm/xe/Kconfig
@@ -19,13 +19,7 @@ config DRM_XE
select DRM_MIPI_DSI
select RELAY
select IRQ_WORK
- # xe depends on ACPI_VIDEO when ACPI is enabled
- # but for select to work, need to select ACPI_VIDEO's dependencies, ick
- select BACKLIGHT_CLASS_DEVICE if ACPI
- select INPUT if ACPI
- select ACPI_VIDEO if X86 && ACPI
select ACPI_BUTTON if ACPI
- select ACPI_WMI if X86 && ACPI
select SYNC_FILE
select IOSF_MBI
select CRC32
--
2.34.1


Subject: Re: [PATCH v3 5/5] drm: Drop unneeded selects in DRM drivers


On 2/2/2024 3:41 AM, Mario Limonciello wrote:
> All of the selects on ACPI_VIDEO are unnecessary when DRM does the
> select for ACPI_VIDEO as it provides a helper for acpi based EDID.
>
> Signed-off-by: Mario Limonciello <[email protected]>
Reviewed-by: Pranjal Ramajor Asha Kanojiya <[email protected]>

Subject: Re: [PATCH v3 2/5] drm: Add drm_get_acpi_edid() helper


On 2/2/2024 3:41 AM, Mario Limonciello wrote:
> Some manufacturers have intentionally put an EDID that differs from
> the EDID on the internal panel on laptops. Drivers can call this
> helper to attempt to fetch the EDID from the BIOS's ACPI _DDC method.
>
> Signed-off-by: Mario Limonciello <[email protected]>
> ---
> v1->v2:
> * Split code from previous amdgpu specific helper to generic drm helper.
> v2->v3:
> * Add an extra select to fix a variety of randconfig errors found from
> LKP robot.
> ---
> drivers/gpu/drm/Kconfig | 5 +++
> drivers/gpu/drm/drm_edid.c | 73 ++++++++++++++++++++++++++++++++++++++
> include/drm/drm_edid.h | 1 +
> 3 files changed, 79 insertions(+)
>
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index 2520db0b776e..14df907c96c8 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -21,6 +21,11 @@ menuconfig DRM
> select KCMP
> select VIDEO_CMDLINE
> select VIDEO_NOMODESET
> + select ACPI_VIDEO if ACPI
> + select BACKLIGHT_CLASS_DEVICE if ACPI
> + select INPUT if ACPI
> + select X86_PLATFORM_DEVICES if ACPI && X86
> + select ACPI_WMI if ACPI && X86
> help
> Kernel-level support for the Direct Rendering Infrastructure (DRI)
> introduced in XFree86 4.0. If you say Y here, you need to select
> diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
> index 69c68804023f..1fbbeaa664b2 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,47 @@ 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_device
> + * @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_device *ddev = data;
> + struct acpi_device *acpidev = ACPI_COMPANION(ddev->dev);
> + unsigned char start = block * EDID_LENGTH;
> + void *edid;
> + int r;
> +
> + if (!acpidev)
> + return -ENODEV;
> +
> + /* 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 -EINVAL;
> + }
> + if (len > r || start > r || start + len > r) {
> + r = EINVAL;
-EINVAL ?
> + goto cleanup;
> + }
> +
> + memcpy(buf, edid + start, len);
> + r = 0;
New line before goto label?
> +cleanup:
> + kfree(edid);
> + return r;
> +}
> +
> static void connector_bad_edid(struct drm_connector *connector,
> const struct edid *edid, int num_blocks)
> {
> @@ -2643,6 +2685,37 @@ struct edid *drm_get_edid(struct drm_connector *connector,
> }
> EXPORT_SYMBOL(drm_get_edid);
>
> +/**
> + * drm_get_acpi_edid - get EDID data, if available
> + * @connector: connector we're probing
> + *
> + * Use the BIOS to attempt 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.
> + */
> +struct edid *drm_get_acpi_edid(struct drm_connector *connector)
> +{
> + struct edid *edid = NULL;
> +
> + switch (connector->connector_type) {
> + case DRM_MODE_CONNECTOR_LVDS:
> + case DRM_MODE_CONNECTOR_eDP:
> + break;
> + default:
> + return NULL;
> + }
> +
> + if (connector->force == DRM_FORCE_OFF)
> + return NULL;
> +
> + edid = _drm_do_get_edid(connector, drm_do_probe_acpi_edid, connector->dev, NULL);
> +
> + drm_connector_update_edid_property(connector, edid);
> + return edid;
> +}
> +EXPORT_SYMBOL(drm_get_acpi_edid);
> +
> /**
> * drm_edid_read_custom - Read EDID data using given EDID block read function
> * @connector: Connector to use
> diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h
> index 518d1b8106c7..60fbdc06badc 100644
> --- a/include/drm/drm_edid.h
> +++ b/include/drm/drm_edid.h
> @@ -412,6 +412,7 @@ struct edid *drm_do_get_edid(struct drm_connector *connector,
> void *data);
> struct edid *drm_get_edid(struct drm_connector *connector,
> struct i2c_adapter *adapter);
> +struct edid *drm_get_acpi_edid(struct drm_connector *connector);
> u32 drm_edid_get_panel_id(struct i2c_adapter *adapter);
> struct edid *drm_get_edid_switcheroo(struct drm_connector *connector,
> struct i2c_adapter *adapter);

New line before return ?


2024-02-02 10:30:03

by Jani Nikula

[permalink] [raw]
Subject: Re: [PATCH v3 2/5] drm: Add drm_get_acpi_edid() helper

On Thu, 01 Feb 2024, Mario Limonciello <[email protected]> wrote:
> Some manufacturers have intentionally put an EDID that differs from
> the EDID on the internal panel on laptops. Drivers can call this
> helper to attempt to fetch the EDID from the BIOS's ACPI _DDC method.

I'm really not happy about adding new struct edid based APIs to
drm_edid.[ch]. Everything new should be struct drm_edid based. All
drivers should be converting towards struct drm_edid, instead of adding
more legacy to rip out later.

BR,
Jani.

>
> Signed-off-by: Mario Limonciello <[email protected]>
> ---
> v1->v2:
> * Split code from previous amdgpu specific helper to generic drm helper.
> v2->v3:
> * Add an extra select to fix a variety of randconfig errors found from
> LKP robot.
> ---
> drivers/gpu/drm/Kconfig | 5 +++
> drivers/gpu/drm/drm_edid.c | 73 ++++++++++++++++++++++++++++++++++++++
> include/drm/drm_edid.h | 1 +
> 3 files changed, 79 insertions(+)
>
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index 2520db0b776e..14df907c96c8 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -21,6 +21,11 @@ menuconfig DRM
> select KCMP
> select VIDEO_CMDLINE
> select VIDEO_NOMODESET
> + select ACPI_VIDEO if ACPI
> + select BACKLIGHT_CLASS_DEVICE if ACPI
> + select INPUT if ACPI
> + select X86_PLATFORM_DEVICES if ACPI && X86
> + select ACPI_WMI if ACPI && X86
> help
> Kernel-level support for the Direct Rendering Infrastructure (DRI)
> introduced in XFree86 4.0. If you say Y here, you need to select
> diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
> index 69c68804023f..1fbbeaa664b2 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,47 @@ 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_device
> + * @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_device *ddev = data;
> + struct acpi_device *acpidev = ACPI_COMPANION(ddev->dev);
> + unsigned char start = block * EDID_LENGTH;
> + void *edid;
> + int r;
> +
> + if (!acpidev)
> + return -ENODEV;
> +
> + /* 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 -EINVAL;
> + }
> + 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)
> {
> @@ -2643,6 +2685,37 @@ struct edid *drm_get_edid(struct drm_connector *connector,
> }
> EXPORT_SYMBOL(drm_get_edid);
>
> +/**
> + * drm_get_acpi_edid - get EDID data, if available
> + * @connector: connector we're probing
> + *
> + * Use the BIOS to attempt 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.
> + */
> +struct edid *drm_get_acpi_edid(struct drm_connector *connector)
> +{
> + struct edid *edid = NULL;
> +
> + switch (connector->connector_type) {
> + case DRM_MODE_CONNECTOR_LVDS:
> + case DRM_MODE_CONNECTOR_eDP:
> + break;
> + default:
> + return NULL;
> + }
> +
> + if (connector->force == DRM_FORCE_OFF)
> + return NULL;
> +
> + edid = _drm_do_get_edid(connector, drm_do_probe_acpi_edid, connector->dev, NULL);
> +
> + drm_connector_update_edid_property(connector, edid);
> + return edid;
> +}
> +EXPORT_SYMBOL(drm_get_acpi_edid);
> +
> /**
> * drm_edid_read_custom - Read EDID data using given EDID block read function
> * @connector: Connector to use
> diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h
> index 518d1b8106c7..60fbdc06badc 100644
> --- a/include/drm/drm_edid.h
> +++ b/include/drm/drm_edid.h
> @@ -412,6 +412,7 @@ struct edid *drm_do_get_edid(struct drm_connector *connector,
> void *data);
> struct edid *drm_get_edid(struct drm_connector *connector,
> struct i2c_adapter *adapter);
> +struct edid *drm_get_acpi_edid(struct drm_connector *connector);
> u32 drm_edid_get_panel_id(struct i2c_adapter *adapter);
> struct edid *drm_get_edid_switcheroo(struct drm_connector *connector,
> struct i2c_adapter *adapter);

--
Jani Nikula, Intel

2024-02-02 15:20:09

by Mario Limonciello

[permalink] [raw]
Subject: Re: [PATCH v3 2/5] drm: Add drm_get_acpi_edid() helper

On 2/2/2024 04:29, Jani Nikula wrote:
> On Thu, 01 Feb 2024, Mario Limonciello <[email protected]> wrote:
>> Some manufacturers have intentionally put an EDID that differs from
>> the EDID on the internal panel on laptops. Drivers can call this
>> helper to attempt to fetch the EDID from the BIOS's ACPI _DDC method.
>
> I'm really not happy about adding new struct edid based APIs to
> drm_edid.[ch]. Everything new should be struct drm_edid based. All
> drivers should be converting towards struct drm_edid, instead of adding
> more legacy to rip out later.

OK; I'll redo it with struct drm_edid.

The changeover for amdgpu to use drm_edid is going to be a pretty
involved effort so I'm going to use a get_raw in amdgpu for now so we
can unblock the issue this is fixing and let that part get removed when
the rest of the overhaul gets done there.

>
> BR,
> Jani.
>
>>
>> Signed-off-by: Mario Limonciello <[email protected]>
>> ---
>> v1->v2:
>> * Split code from previous amdgpu specific helper to generic drm helper.
>> v2->v3:
>> * Add an extra select to fix a variety of randconfig errors found from
>> LKP robot.
>> ---
>> drivers/gpu/drm/Kconfig | 5 +++
>> drivers/gpu/drm/drm_edid.c | 73 ++++++++++++++++++++++++++++++++++++++
>> include/drm/drm_edid.h | 1 +
>> 3 files changed, 79 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
>> index 2520db0b776e..14df907c96c8 100644
>> --- a/drivers/gpu/drm/Kconfig
>> +++ b/drivers/gpu/drm/Kconfig
>> @@ -21,6 +21,11 @@ menuconfig DRM
>> select KCMP
>> select VIDEO_CMDLINE
>> select VIDEO_NOMODESET
>> + select ACPI_VIDEO if ACPI
>> + select BACKLIGHT_CLASS_DEVICE if ACPI
>> + select INPUT if ACPI
>> + select X86_PLATFORM_DEVICES if ACPI && X86
>> + select ACPI_WMI if ACPI && X86
>> help
>> Kernel-level support for the Direct Rendering Infrastructure (DRI)
>> introduced in XFree86 4.0. If you say Y here, you need to select
>> diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
>> index 69c68804023f..1fbbeaa664b2 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,47 @@ 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_device
>> + * @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_device *ddev = data;
>> + struct acpi_device *acpidev = ACPI_COMPANION(ddev->dev);
>> + unsigned char start = block * EDID_LENGTH;
>> + void *edid;
>> + int r;
>> +
>> + if (!acpidev)
>> + return -ENODEV;
>> +
>> + /* 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 -EINVAL;
>> + }
>> + 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)
>> {
>> @@ -2643,6 +2685,37 @@ struct edid *drm_get_edid(struct drm_connector *connector,
>> }
>> EXPORT_SYMBOL(drm_get_edid);
>>
>> +/**
>> + * drm_get_acpi_edid - get EDID data, if available
>> + * @connector: connector we're probing
>> + *
>> + * Use the BIOS to attempt 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.
>> + */
>> +struct edid *drm_get_acpi_edid(struct drm_connector *connector)
>> +{
>> + struct edid *edid = NULL;
>> +
>> + switch (connector->connector_type) {
>> + case DRM_MODE_CONNECTOR_LVDS:
>> + case DRM_MODE_CONNECTOR_eDP:
>> + break;
>> + default:
>> + return NULL;
>> + }
>> +
>> + if (connector->force == DRM_FORCE_OFF)
>> + return NULL;
>> +
>> + edid = _drm_do_get_edid(connector, drm_do_probe_acpi_edid, connector->dev, NULL);
>> +
>> + drm_connector_update_edid_property(connector, edid);
>> + return edid;
>> +}
>> +EXPORT_SYMBOL(drm_get_acpi_edid);
>> +
>> /**
>> * drm_edid_read_custom - Read EDID data using given EDID block read function
>> * @connector: Connector to use
>> diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h
>> index 518d1b8106c7..60fbdc06badc 100644
>> --- a/include/drm/drm_edid.h
>> +++ b/include/drm/drm_edid.h
>> @@ -412,6 +412,7 @@ struct edid *drm_do_get_edid(struct drm_connector *connector,
>> void *data);
>> struct edid *drm_get_edid(struct drm_connector *connector,
>> struct i2c_adapter *adapter);
>> +struct edid *drm_get_acpi_edid(struct drm_connector *connector);
>> u32 drm_edid_get_panel_id(struct i2c_adapter *adapter);
>> struct edid *drm_get_edid_switcheroo(struct drm_connector *connector,
>> struct i2c_adapter *adapter);
>