2024-02-07 22:44:54

by Mario Limonciello

[permalink] [raw]
Subject: [PATCH v4 0/3] Add drm_get_acpi_edid() helper

The drm_get_acpi_edid() helper is for drivers that would prefer
to get the EDID from ACPI instead of from the panel.

Earlier versions of this series were aimed at using this in amdgpu
and nouveau.

This version does NOT update amdgpu as the change will require a
larger overhaul to use struct drm_edid. There will be a follow up
patch to amdgpu after Melissa Wen finishes that effort [2].

https://lore.kernel.org/dri-devel/[email protected]/ [1]
https://lore.kernel.org/amd-gfx/[email protected]/ [2]
Mario Limonciello (3):
drm: Add drm_get_acpi_edid() helper
drm/nouveau: Use drm_get_acpi_edid() helper
drm: Drop unneeded selects in DRM drivers

drivers/gpu/drm/Kconfig | 5 ++
drivers/gpu/drm/amd/amdgpu/Kconfig | 7 --
drivers/gpu/drm/drm_edid.c | 77 +++++++++++++++++++++
drivers/gpu/drm/gma500/Kconfig | 6 --
drivers/gpu/drm/i915/Kconfig | 7 --
drivers/gpu/drm/nouveau/Kconfig | 4 --
drivers/gpu/drm/nouveau/nouveau_acpi.c | 27 --------
drivers/gpu/drm/nouveau/nouveau_acpi.h | 2 -
drivers/gpu/drm/nouveau/nouveau_connector.c | 20 +++---
drivers/gpu/drm/radeon/Kconfig | 7 --
drivers/gpu/drm/xe/Kconfig | 6 --
include/drm/drm_edid.h | 1 +
12 files changed, 92 insertions(+), 77 deletions(-)

--
2.34.1



2024-02-07 22:45:02

by Mario Limonciello

[permalink] [raw]
Subject: [PATCH v4 1/3] 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]>
---
drivers/gpu/drm/Kconfig | 5 +++
drivers/gpu/drm/drm_edid.c | 77 ++++++++++++++++++++++++++++++++++++++
include/drm/drm_edid.h | 1 +
3 files changed, 83 insertions(+)

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 6ec33d36f3a4..ec2bb71e8b36 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 923c4423151c..c649b4f9fd8e 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,49 @@ 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 +2687,39 @@ 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.
+ *
+ * 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_get_acpi_edid(struct drm_connector *connector)
+{
+ const struct drm_edid *drm_edid;
+
+ 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;
+
+ drm_edid = drm_edid_read_custom(connector, drm_do_probe_acpi_edid, connector->dev);
+
+ /* Note: Do *not* call connector updates here. */
+
+ return drm_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 7923bc00dc7a..ca41be289fc6 100644
--- a/include/drm/drm_edid.h
+++ b/include/drm/drm_edid.h
@@ -410,6 +410,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);
+const struct drm_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-07 22:45:24

by Mario Limonciello

[permalink] [raw]
Subject: [PATCH v4 2/3] drm/nouveau: Use drm_get_acpi_edid() helper

Rather than inventing a wrapper to acpi_video_get_edid() use the
one provided by drm. This fixes two problems:
1. A memory leak that the memory provided by the ACPI call was
never freed.
2. Validation of the BIOS provided blob.

Convert the usage in nouveau_connector_detect_lvds() to use
struct drm_edid at the same time.

Signed-off-by: Mario Limonciello <[email protected]>
---
drivers/gpu/drm/nouveau/nouveau_acpi.c | 27 ---------------------
drivers/gpu/drm/nouveau/nouveau_acpi.h | 2 --
drivers/gpu/drm/nouveau/nouveau_connector.c | 20 +++++++--------
3 files changed, 9 insertions(+), 40 deletions(-)

diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c
index 8f0c69aad248..de9daafb3fbb 100644
--- a/drivers/gpu/drm/nouveau/nouveau_acpi.c
+++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c
@@ -360,33 +360,6 @@ void nouveau_unregister_dsm_handler(void) {}
void nouveau_switcheroo_optimus_dsm(void) {}
#endif

-void *
-nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector)
-{
- struct acpi_device *acpidev;
- int type, ret;
- void *edid;
-
- switch (connector->connector_type) {
- case DRM_MODE_CONNECTOR_LVDS:
- case DRM_MODE_CONNECTOR_eDP:
- type = ACPI_VIDEO_DISPLAY_LCD;
- break;
- default:
- return NULL;
- }
-
- acpidev = ACPI_COMPANION(dev->dev);
- if (!acpidev)
- return NULL;
-
- ret = acpi_video_get_edid(acpidev, type, -1, &edid);
- if (ret < 0)
- return NULL;
-
- return kmemdup(edid, EDID_LENGTH, GFP_KERNEL);
-}
-
bool nouveau_acpi_video_backlight_use_native(void)
{
return acpi_video_backlight_use_native();
diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.h b/drivers/gpu/drm/nouveau/nouveau_acpi.h
index e39dd8b94b8b..6a3def8e6cca 100644
--- a/drivers/gpu/drm/nouveau/nouveau_acpi.h
+++ b/drivers/gpu/drm/nouveau/nouveau_acpi.h
@@ -10,7 +10,6 @@ bool nouveau_is_v1_dsm(void);
void nouveau_register_dsm_handler(void);
void nouveau_unregister_dsm_handler(void);
void nouveau_switcheroo_optimus_dsm(void);
-void *nouveau_acpi_edid(struct drm_device *, struct drm_connector *);
bool nouveau_acpi_video_backlight_use_native(void);
void nouveau_acpi_video_register_backlight(void);
#else
@@ -19,7 +18,6 @@ static inline bool nouveau_is_v1_dsm(void) { return false; };
static inline void nouveau_register_dsm_handler(void) {}
static inline void nouveau_unregister_dsm_handler(void) {}
static inline void nouveau_switcheroo_optimus_dsm(void) {}
-static inline void *nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector) { return NULL; }
static inline bool nouveau_acpi_video_backlight_use_native(void) { return true; }
static inline void nouveau_acpi_video_register_backlight(void) {}
#endif
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
index 856b3ef5edb8..4c47d231c65e 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -687,7 +687,7 @@ nouveau_connector_detect_lvds(struct drm_connector *connector, bool force)
struct nouveau_drm *drm = nouveau_drm(dev);
struct nouveau_connector *nv_connector = nouveau_connector(connector);
struct nouveau_encoder *nv_encoder = NULL;
- struct edid *edid = NULL;
+ const struct drm_edid *drm_edid = NULL;
enum drm_connector_status status = connector_status_disconnected;

nv_encoder = find_encoder(connector, DCB_OUTPUT_LVDS);
@@ -698,7 +698,7 @@ nouveau_connector_detect_lvds(struct drm_connector *connector, bool force)
if (!drm->vbios.fp_no_ddc) {
status = nouveau_connector_detect(connector, force);
if (status == connector_status_connected) {
- edid = nv_connector->edid;
+ drm_edid = drm_edid_alloc(nv_connector->edid, EDID_LENGTH);
goto out;
}
}
@@ -713,8 +713,8 @@ nouveau_connector_detect_lvds(struct drm_connector *connector, bool force)
* valid - it's not (rh#613284)
*/
if (nv_encoder->dcb->lvdsconf.use_acpi_for_edid) {
- edid = nouveau_acpi_edid(dev, connector);
- if (edid) {
+ drm_edid = drm_get_acpi_edid(connector);
+ if (drm_edid) {
status = connector_status_connected;
goto out;
}
@@ -734,12 +734,9 @@ nouveau_connector_detect_lvds(struct drm_connector *connector, bool force)
* stored for the panel stored in them.
*/
if (!drm->vbios.fp_no_ddc) {
- edid = (struct edid *)nouveau_bios_embedded_edid(dev);
- if (edid) {
- edid = kmemdup(edid, EDID_LENGTH, GFP_KERNEL);
- if (edid)
- status = connector_status_connected;
- }
+ drm_edid = drm_edid_alloc(nouveau_bios_embedded_edid(dev), EDID_LENGTH);
+ if (drm_edid)
+ status = connector_status_connected;
}

out:
@@ -750,7 +747,8 @@ nouveau_connector_detect_lvds(struct drm_connector *connector, bool force)
status = connector_status_unknown;
#endif

- nouveau_connector_set_edid(nv_connector, edid);
+ drm_edid_connector_update(connector, drm_edid);
+ drm_edid_free(drm_edid);
if (nv_encoder)
nouveau_connector_set_encoder(connector, nv_encoder);
return status;
--
2.34.1


2024-02-07 22:45:35

by Mario Limonciello

[permalink] [raw]
Subject: [PATCH v4 3/3] 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.

Reviewed-by: Pranjal Ramajor Asha Kanojiya <[email protected]>
Signed-off-by: Mario Limonciello <[email protected]>
---
drivers/gpu/drm/amd/amdgpu/Kconfig | 7 -------
drivers/gpu/drm/gma500/Kconfig | 6 ------
drivers/gpu/drm/i915/Kconfig | 7 -------
drivers/gpu/drm/nouveau/Kconfig | 4 ----
drivers/gpu/drm/radeon/Kconfig | 7 -------
drivers/gpu/drm/xe/Kconfig | 6 ------
6 files changed, 37 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/nouveau/Kconfig b/drivers/gpu/drm/nouveau/Kconfig
index 1e6aaf95ff7c..61f531abd3e3 100644
--- a/drivers/gpu/drm/nouveau/Kconfig
+++ b/drivers/gpu/drm/nouveau/Kconfig
@@ -20,11 +20,7 @@ config DRM_NOUVEAU
select ACPI_WMI if ACPI && X86
select MXM_WMI if ACPI && X86
select POWER_SUPPLY
- # Similar to i915, we need to select ACPI_VIDEO and it's dependencies
- select BACKLIGHT_CLASS_DEVICE if ACPI && X86
- select INPUT if ACPI && X86
select THERMAL if ACPI && X86
- select ACPI_VIDEO if ACPI && X86
select SND_HDA_COMPONENT if SND_HDA_CORE
help
Choose this option for open-source NVIDIA support.
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


2024-02-08 09:57:45

by Jani Nikula

[permalink] [raw]
Subject: Re: [PATCH v4 1/3] drm: Add drm_get_acpi_edid() helper

On Wed, 07 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.
>
> Signed-off-by: Mario Limonciello <[email protected]>
> ---
> drivers/gpu/drm/Kconfig | 5 +++
> drivers/gpu/drm/drm_edid.c | 77 ++++++++++++++++++++++++++++++++++++++
> include/drm/drm_edid.h | 1 +
> 3 files changed, 83 insertions(+)
>
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index 6ec33d36f3a4..ec2bb71e8b36 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

I think I'll defer to drm maintainers on whether this is okay or
something to be avoided.


> 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 923c4423151c..c649b4f9fd8e 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,49 @@ 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 +2687,39 @@ struct edid *drm_get_edid(struct drm_connector *connector,
> }
> EXPORT_SYMBOL(drm_get_edid);
>
> +/**
> + * drm_get_acpi_edid - get EDID data, if available

I'd prefer all the new EDID API to be named drm_edid_*. Makes a clean
break from the old API, and is more consistent.

So perhaps drm_edid_read_acpi() to be in line with all the other struct
drm_edid based EDID reading functions.

> + * @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_get_acpi_edid(struct drm_connector *connector)
> +{
> + const struct drm_edid *drm_edid;
> +
> + 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;
> +
> + drm_edid = drm_edid_read_custom(connector, drm_do_probe_acpi_edid, connector->dev);
> +
> + /* Note: Do *not* call connector updates here. */
> +
> + return drm_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 7923bc00dc7a..ca41be289fc6 100644
> --- a/include/drm/drm_edid.h
> +++ b/include/drm/drm_edid.h
> @@ -410,6 +410,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);
> +const struct drm_edid *drm_get_acpi_edid(struct drm_connector *connector);

There's a comment

/* Interface based on struct drm_edid */

towards the end of the file, gathering all the new API under it.

Other than that, LGTM,

BR,
Jani.

> 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-08 13:20:48

by Maxime Ripard

[permalink] [raw]
Subject: Re: Re: [PATCH v4 1/3] drm: Add drm_get_acpi_edid() helper

On Thu, Feb 08, 2024 at 11:57:11AM +0200, Jani Nikula wrote:
> On Wed, 07 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.
> >
> > Signed-off-by: Mario Limonciello <[email protected]>
> > ---
> > drivers/gpu/drm/Kconfig | 5 +++
> > drivers/gpu/drm/drm_edid.c | 77 ++++++++++++++++++++++++++++++++++++++
> > include/drm/drm_edid.h | 1 +
> > 3 files changed, 83 insertions(+)
> >
> > diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> > index 6ec33d36f3a4..ec2bb71e8b36 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
>
> I think I'll defer to drm maintainers on whether this is okay or
> something to be avoided.
>
>
> > 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 923c4423151c..c649b4f9fd8e 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,49 @@ 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 +2687,39 @@ struct edid *drm_get_edid(struct drm_connector *connector,
> > }
> > EXPORT_SYMBOL(drm_get_edid);
> >
> > +/**
> > + * drm_get_acpi_edid - get EDID data, if available
>
> I'd prefer all the new EDID API to be named drm_edid_*. Makes a clean
> break from the old API, and is more consistent.
>
> So perhaps drm_edid_read_acpi() to be in line with all the other struct
> drm_edid based EDID reading functions.
>
> > + * @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_get_acpi_edid(struct drm_connector *connector)
> > +{
> > + const struct drm_edid *drm_edid;
> > +
> > + 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;
> > +
> > + drm_edid = drm_edid_read_custom(connector, drm_do_probe_acpi_edid, connector->dev);
> > +
> > + /* Note: Do *not* call connector updates here. */
> > +
> > + return drm_edid;
> > +}
> > +EXPORT_SYMBOL(drm_get_acpi_edid);

Why shouldn't we use the BIOS/UEFI to retrieve them if it's available?

I guess what I'm asking is why should we make this an exported function
that drivers would have to call explicitly, instead of just making it
part of the usual EDID retrieval interface.

Maxime


Attachments:
(No filename) (4.89 kB)
signature.asc (235.00 B)
Download all attachments

2024-02-08 14:32:28

by Jani Nikula

[permalink] [raw]
Subject: Re: Re: [PATCH v4 1/3] drm: Add drm_get_acpi_edid() helper

On Thu, 08 Feb 2024, Maxime Ripard <[email protected]> wrote:
> On Thu, Feb 08, 2024 at 11:57:11AM +0200, Jani Nikula wrote:
>> On Wed, 07 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.
>> >
>> > Signed-off-by: Mario Limonciello <[email protected]>
>> > ---
>> > drivers/gpu/drm/Kconfig | 5 +++
>> > drivers/gpu/drm/drm_edid.c | 77 ++++++++++++++++++++++++++++++++++++++
>> > include/drm/drm_edid.h | 1 +
>> > 3 files changed, 83 insertions(+)
>> >
>> > diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
>> > index 6ec33d36f3a4..ec2bb71e8b36 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
>>
>> I think I'll defer to drm maintainers on whether this is okay or
>> something to be avoided.
>>
>>
>> > 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 923c4423151c..c649b4f9fd8e 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,49 @@ 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 +2687,39 @@ struct edid *drm_get_edid(struct drm_connector *connector,
>> > }
>> > EXPORT_SYMBOL(drm_get_edid);
>> >
>> > +/**
>> > + * drm_get_acpi_edid - get EDID data, if available
>>
>> I'd prefer all the new EDID API to be named drm_edid_*. Makes a clean
>> break from the old API, and is more consistent.
>>
>> So perhaps drm_edid_read_acpi() to be in line with all the other struct
>> drm_edid based EDID reading functions.
>>
>> > + * @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_get_acpi_edid(struct drm_connector *connector)
>> > +{
>> > + const struct drm_edid *drm_edid;
>> > +
>> > + 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;
>> > +
>> > + drm_edid = drm_edid_read_custom(connector, drm_do_probe_acpi_edid, connector->dev);
>> > +
>> > + /* Note: Do *not* call connector updates here. */
>> > +
>> > + return drm_edid;
>> > +}
>> > +EXPORT_SYMBOL(drm_get_acpi_edid);
>
> Why shouldn't we use the BIOS/UEFI to retrieve them if it's available?
>
> I guess what I'm asking is why should we make this an exported function
> that drivers would have to call explicitly, instead of just making it
> part of the usual EDID retrieval interface.

Two main questions:

What if the EDID from ACPI is bogus? Needs to be configurable in the
connector somehow?

What if you have multiple local panels? This seems to only support one,
and would return the same EDID for both.

BR,
Jani.


--
Jani Nikula, Intel

2024-02-08 18:48:54

by Mario Limonciello

[permalink] [raw]
Subject: Re: [PATCH v4 1/3] drm: Add drm_get_acpi_edid() helper

On 2/8/2024 08:31, Jani Nikula wrote:
> On Thu, 08 Feb 2024, Maxime Ripard <[email protected]> wrote:
>> On Thu, Feb 08, 2024 at 11:57:11AM +0200, Jani Nikula wrote:
>>> On Wed, 07 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.
>>>>
>>>> Signed-off-by: Mario Limonciello <[email protected]>
>>>> ---
>>>> drivers/gpu/drm/Kconfig | 5 +++
>>>> drivers/gpu/drm/drm_edid.c | 77 ++++++++++++++++++++++++++++++++++++++
>>>> include/drm/drm_edid.h | 1 +
>>>> 3 files changed, 83 insertions(+)
>>>>
>>>> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
>>>> index 6ec33d36f3a4..ec2bb71e8b36 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
>>>
>>> I think I'll defer to drm maintainers on whether this is okay or
>>> something to be avoided.

It's pretty much the same thing that all the other drivers do right now.
Because the symbol is now used by DRM it needs to do this.

Patch 3 in this version of the series tears it out from all the drivers.

>>>
>>>
>>>> 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 923c4423151c..c649b4f9fd8e 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,49 @@ 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 +2687,39 @@ struct edid *drm_get_edid(struct drm_connector *connector,
>>>> }
>>>> EXPORT_SYMBOL(drm_get_edid);
>>>>
>>>> +/**
>>>> + * drm_get_acpi_edid - get EDID data, if available
>>>
>>> I'd prefer all the new EDID API to be named drm_edid_*. Makes a clean
>>> break from the old API, and is more consistent.
>>>
>>> So perhaps drm_edid_read_acpi() to be in line with all the other struct
>>> drm_edid based EDID reading functions.
>>>

Roger that. Even if it ends up not being exported out will rename as such.

>>>> + * @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_get_acpi_edid(struct drm_connector *connector)
>>>> +{
>>>> + const struct drm_edid *drm_edid;
>>>> +
>>>> + 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;
>>>> +
>>>> + drm_edid = drm_edid_read_custom(connector, drm_do_probe_acpi_edid, connector->dev);
>>>> +
>>>> + /* Note: Do *not* call connector updates here. */
>>>> +
>>>> + return drm_edid;
>>>> +}
>>>> +EXPORT_SYMBOL(drm_get_acpi_edid);
>>
>> Why shouldn't we use the BIOS/UEFI to retrieve them if it's available?
>>
>> I guess what I'm asking is why should we make this an exported function
>> that drivers would have to call explicitly, instead of just making it
>> part of the usual EDID retrieval interface.
>
> Two main questions:
>
> What if the EDID from ACPI is bogus? Needs to be configurable in the
> connector somehow?

In the earlier versions of the patch that touched amdgpu I left a knob
in the amdgpu kernel module to let users turn this off. Whenever a
variation of this hits amdgpu I'm planning to keep that there unless
Alex or Christian have opinions against it.

>
> What if you have multiple local panels? This seems to only support one,
> and would return the same EDID for both.
>

The GPU driver should know best how many panels it's dealing with.

How about if we make it "opt-out" by the connector? The connector can
set a flag before it tries to get the EDID that it doesn't want one from
the BIOS for any reason (module parameter, too many eDP etc).

2024-02-09 11:14:01

by Daniel Vetter

[permalink] [raw]
Subject: Re: [PATCH v4 1/3] drm: Add drm_get_acpi_edid() helper

On Thu, Feb 08, 2024 at 11:57:11AM +0200, Jani Nikula wrote:
> On Wed, 07 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.
> >
> > Signed-off-by: Mario Limonciello <[email protected]>
> > ---
> > drivers/gpu/drm/Kconfig | 5 +++
> > drivers/gpu/drm/drm_edid.c | 77 ++++++++++++++++++++++++++++++++++++++
> > include/drm/drm_edid.h | 1 +
> > 3 files changed, 83 insertions(+)
> >
> > diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> > index 6ec33d36f3a4..ec2bb71e8b36 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
>
> I think I'll defer to drm maintainers on whether this is okay or
> something to be avoided.

Uh yeah this is a bit much, and select just messes with everything. Just
#ifdef this in the code with a dummy alternative, if users configure their
kernel without acpi but need it, they get to keep all the pieces.

Alternatively make a DRM_ACPI_HELPERS symbol, but imo a Kconfig for every
function is also not great. And just using #ifdef in the code also works
for CONFIG_OF, which is exactly the same thing for platforms using dt to
describe hw.

Also I'd expect ACPI code to already provide dummy functions if ACPI is
provided, so you probably dont even need all that much #ifdef in the code.

What we defo cant do is select platform/hw stuff just because you enable
CONFIG_DRM.
-Sima

>
>
> > 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 923c4423151c..c649b4f9fd8e 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,49 @@ 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 +2687,39 @@ struct edid *drm_get_edid(struct drm_connector *connector,
> > }
> > EXPORT_SYMBOL(drm_get_edid);
> >
> > +/**
> > + * drm_get_acpi_edid - get EDID data, if available
>
> I'd prefer all the new EDID API to be named drm_edid_*. Makes a clean
> break from the old API, and is more consistent.
>
> So perhaps drm_edid_read_acpi() to be in line with all the other struct
> drm_edid based EDID reading functions.
>
> > + * @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_get_acpi_edid(struct drm_connector *connector)
> > +{
> > + const struct drm_edid *drm_edid;
> > +
> > + 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;
> > +
> > + drm_edid = drm_edid_read_custom(connector, drm_do_probe_acpi_edid, connector->dev);
> > +
> > + /* Note: Do *not* call connector updates here. */
> > +
> > + return drm_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 7923bc00dc7a..ca41be289fc6 100644
> > --- a/include/drm/drm_edid.h
> > +++ b/include/drm/drm_edid.h
> > @@ -410,6 +410,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);
> > +const struct drm_edid *drm_get_acpi_edid(struct drm_connector *connector);
>
> There's a comment
>
> /* Interface based on struct drm_edid */
>
> towards the end of the file, gathering all the new API under it.
>
> Other than that, LGTM,
>
> BR,
> Jani.
>
> > 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

--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

2024-02-09 15:36:34

by Mario Limonciello

[permalink] [raw]
Subject: Re: [PATCH v4 1/3] drm: Add drm_get_acpi_edid() helper

On 2/9/2024 05:07, Daniel Vetter wrote:
> On Thu, Feb 08, 2024 at 11:57:11AM +0200, Jani Nikula wrote:
>> On Wed, 07 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.
>>>
>>> Signed-off-by: Mario Limonciello <[email protected]>
>>> ---
>>> drivers/gpu/drm/Kconfig | 5 +++
>>> drivers/gpu/drm/drm_edid.c | 77 ++++++++++++++++++++++++++++++++++++++
>>> include/drm/drm_edid.h | 1 +
>>> 3 files changed, 83 insertions(+)
>>>
>>> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
>>> index 6ec33d36f3a4..ec2bb71e8b36 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
>>
>> I think I'll defer to drm maintainers on whether this is okay or
>> something to be avoided.
>
> Uh yeah this is a bit much, and select just messes with everything. Just
> #ifdef this in the code with a dummy alternative, if users configure their
> kernel without acpi but need it, they get to keep all the pieces.
>
> Alternatively make a DRM_ACPI_HELPERS symbol, but imo a Kconfig for every
> function is also not great. And just using #ifdef in the code also works
> for CONFIG_OF, which is exactly the same thing for platforms using dt to
> describe hw.
>
> Also I'd expect ACPI code to already provide dummy functions if ACPI is
> provided, so you probably dont even need all that much #ifdef in the code.
>
> What we defo cant do is select platform/hw stuff just because you enable
> CONFIG_DRM.
> -Sima

The problem was with linking. I'll experiment with #ifdef for the next
version.

>
>>
>>
>>> 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 923c4423151c..c649b4f9fd8e 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,49 @@ 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 +2687,39 @@ struct edid *drm_get_edid(struct drm_connector *connector,
>>> }
>>> EXPORT_SYMBOL(drm_get_edid);
>>>
>>> +/**
>>> + * drm_get_acpi_edid - get EDID data, if available
>>
>> I'd prefer all the new EDID API to be named drm_edid_*. Makes a clean
>> break from the old API, and is more consistent.
>>
>> So perhaps drm_edid_read_acpi() to be in line with all the other struct
>> drm_edid based EDID reading functions.
>>
>>> + * @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_get_acpi_edid(struct drm_connector *connector)
>>> +{
>>> + const struct drm_edid *drm_edid;
>>> +
>>> + 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;
>>> +
>>> + drm_edid = drm_edid_read_custom(connector, drm_do_probe_acpi_edid, connector->dev);
>>> +
>>> + /* Note: Do *not* call connector updates here. */
>>> +
>>> + return drm_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 7923bc00dc7a..ca41be289fc6 100644
>>> --- a/include/drm/drm_edid.h
>>> +++ b/include/drm/drm_edid.h
>>> @@ -410,6 +410,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);
>>> +const struct drm_edid *drm_get_acpi_edid(struct drm_connector *connector);
>>
>> There's a comment
>>
>> /* Interface based on struct drm_edid */
>>
>> towards the end of the file, gathering all the new API under it.
>>
>> Other than that, LGTM,
>>
>> BR,
>> Jani.
>>
>>> 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-09 18:58:29

by Daniel Vetter

[permalink] [raw]
Subject: Re: [PATCH v4 1/3] drm: Add drm_get_acpi_edid() helper

On Fri, Feb 09, 2024 at 09:34:13AM -0600, Mario Limonciello wrote:
> On 2/9/2024 05:07, Daniel Vetter wrote:
> > On Thu, Feb 08, 2024 at 11:57:11AM +0200, Jani Nikula wrote:
> > > On Wed, 07 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.
> > > >
> > > > Signed-off-by: Mario Limonciello <[email protected]>
> > > > ---
> > > > drivers/gpu/drm/Kconfig | 5 +++
> > > > drivers/gpu/drm/drm_edid.c | 77 ++++++++++++++++++++++++++++++++++++++
> > > > include/drm/drm_edid.h | 1 +
> > > > 3 files changed, 83 insertions(+)
> > > >
> > > > diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> > > > index 6ec33d36f3a4..ec2bb71e8b36 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
> > >
> > > I think I'll defer to drm maintainers on whether this is okay or
> > > something to be avoided.
> >
> > Uh yeah this is a bit much, and select just messes with everything. Just
> > #ifdef this in the code with a dummy alternative, if users configure their
> > kernel without acpi but need it, they get to keep all the pieces.
> >
> > Alternatively make a DRM_ACPI_HELPERS symbol, but imo a Kconfig for every
> > function is also not great. And just using #ifdef in the code also works
> > for CONFIG_OF, which is exactly the same thing for platforms using dt to
> > describe hw.
> >
> > Also I'd expect ACPI code to already provide dummy functions if ACPI is
> > provided, so you probably dont even need all that much #ifdef in the code.
> >
> > What we defo cant do is select platform/hw stuff just because you enable
> > CONFIG_DRM.
> > -Sima
>
> The problem was with linking. I'll experiment with #ifdef for the next
> version.

Ah yes, if e.g. acpi is a module but drm is built-in then it will compile,
but not link.

You need

depends on (ACPI || ACPI=n)

for this. Looks a bit funny but works for all combinations.

Since this gets mess it might be useful to have a DRM_ACPI_HELPERS Kconfig
that controls all this.
-Sima

>
> >
> > >
> > >
> > > > 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 923c4423151c..c649b4f9fd8e 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,49 @@ 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 +2687,39 @@ struct edid *drm_get_edid(struct drm_connector *connector,
> > > > }
> > > > EXPORT_SYMBOL(drm_get_edid);
> > > > +/**
> > > > + * drm_get_acpi_edid - get EDID data, if available
> > >
> > > I'd prefer all the new EDID API to be named drm_edid_*. Makes a clean
> > > break from the old API, and is more consistent.
> > >
> > > So perhaps drm_edid_read_acpi() to be in line with all the other struct
> > > drm_edid based EDID reading functions.
> > >
> > > > + * @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_get_acpi_edid(struct drm_connector *connector)
> > > > +{
> > > > + const struct drm_edid *drm_edid;
> > > > +
> > > > + 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;
> > > > +
> > > > + drm_edid = drm_edid_read_custom(connector, drm_do_probe_acpi_edid, connector->dev);
> > > > +
> > > > + /* Note: Do *not* call connector updates here. */
> > > > +
> > > > + return drm_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 7923bc00dc7a..ca41be289fc6 100644
> > > > --- a/include/drm/drm_edid.h
> > > > +++ b/include/drm/drm_edid.h
> > > > @@ -410,6 +410,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);
> > > > +const struct drm_edid *drm_get_acpi_edid(struct drm_connector *connector);
> > >
> > > There's a comment
> > >
> > > /* Interface based on struct drm_edid */
> > >
> > > towards the end of the file, gathering all the new API under it.
> > >
> > > Other than that, LGTM,
> > >
> > > BR,
> > > Jani.
> > >
> > > > 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
> >
>

--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

2024-02-11 05:19:42

by Mario Limonciello

[permalink] [raw]
Subject: Re: [PATCH v4 1/3] drm: Add drm_get_acpi_edid() helper

On 2/9/2024 12:57, Daniel Vetter wrote:
> On Fri, Feb 09, 2024 at 09:34:13AM -0600, Mario Limonciello wrote:
>> On 2/9/2024 05:07, Daniel Vetter wrote:
>>> On Thu, Feb 08, 2024 at 11:57:11AM +0200, Jani Nikula wrote:
>>>> On Wed, 07 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.
>>>>>
>>>>> Signed-off-by: Mario Limonciello <[email protected]>
>>>>> ---
>>>>> drivers/gpu/drm/Kconfig | 5 +++
>>>>> drivers/gpu/drm/drm_edid.c | 77 ++++++++++++++++++++++++++++++++++++++
>>>>> include/drm/drm_edid.h | 1 +
>>>>> 3 files changed, 83 insertions(+)
>>>>>
>>>>> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
>>>>> index 6ec33d36f3a4..ec2bb71e8b36 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
>>>>
>>>> I think I'll defer to drm maintainers on whether this is okay or
>>>> something to be avoided.
>>>
>>> Uh yeah this is a bit much, and select just messes with everything. Just
>>> #ifdef this in the code with a dummy alternative, if users configure their
>>> kernel without acpi but need it, they get to keep all the pieces.
>>>
>>> Alternatively make a DRM_ACPI_HELPERS symbol, but imo a Kconfig for every
>>> function is also not great. And just using #ifdef in the code also works
>>> for CONFIG_OF, which is exactly the same thing for platforms using dt to
>>> describe hw.
>>>
>>> Also I'd expect ACPI code to already provide dummy functions if ACPI is
>>> provided, so you probably dont even need all that much #ifdef in the code.
>>>
>>> What we defo cant do is select platform/hw stuff just because you enable
>>> CONFIG_DRM.
>>> -Sima
>>
>> The problem was with linking. I'll experiment with #ifdef for the next
>> version.
>
> Ah yes, if e.g. acpi is a module but drm is built-in then it will compile,
> but not link.
>
> You need
>
> depends on (ACPI || ACPI=n)
>
> for this. Looks a bit funny but works for all combinations.

Nope; this fails at link time with this combination:

CONFIG_ACPI=y
CONFIG_ACPI_VIDEO=m
CONFIG_DRM=y

ld: drivers/gpu/drm/drm_edid.o: in function `drm_do_probe_acpi_edid':
drm_edid.c:(.text+0xd34): undefined reference to `acpi_video_get_edid'
make[5]: *** [scripts/Makefile.vmlinux:37: vmlinux] Error 1

So the logical solution is to try
depends on (ACPI_VIDEO || ACPI_VIDEO=n)

But that leads me back to the rabbit hole of why I had the selects moved
to drm instead of drivers in the first place:

drivers/gpu/drm/Kconfig:8:error: recursive dependency detected!
drivers/gpu/drm/Kconfig:8: symbol DRM depends on ACPI_VIDEO
drivers/acpi/Kconfig:213: symbol ACPI_VIDEO depends on
BACKLIGHT_CLASS_DEVICE
drivers/video/backlight/Kconfig:136: symbol BACKLIGHT_CLASS_DEVICE is
selected by DRM_RADEON
drivers/gpu/drm/radeon/Kconfig:3: symbol DRM_RADEON depends on DRM


>
> Since this gets mess it might be useful to have a DRM_ACPI_HELPERS Kconfig
> that controls all this.

How about all those selects that I had in this patch moved to
DRM_ACPI_HELPERS and keep the patch that drops from all the drivers then?

> -Sima
>
>>
>>>
>>>>
>>>>
>>>>> 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 923c4423151c..c649b4f9fd8e 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,49 @@ 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 +2687,39 @@ struct edid *drm_get_edid(struct drm_connector *connector,
>>>>> }
>>>>> EXPORT_SYMBOL(drm_get_edid);
>>>>> +/**
>>>>> + * drm_get_acpi_edid - get EDID data, if available
>>>>
>>>> I'd prefer all the new EDID API to be named drm_edid_*. Makes a clean
>>>> break from the old API, and is more consistent.
>>>>
>>>> So perhaps drm_edid_read_acpi() to be in line with all the other struct
>>>> drm_edid based EDID reading functions.
>>>>
>>>>> + * @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_get_acpi_edid(struct drm_connector *connector)
>>>>> +{
>>>>> + const struct drm_edid *drm_edid;
>>>>> +
>>>>> + 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;
>>>>> +
>>>>> + drm_edid = drm_edid_read_custom(connector, drm_do_probe_acpi_edid, connector->dev);
>>>>> +
>>>>> + /* Note: Do *not* call connector updates here. */
>>>>> +
>>>>> + return drm_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 7923bc00dc7a..ca41be289fc6 100644
>>>>> --- a/include/drm/drm_edid.h
>>>>> +++ b/include/drm/drm_edid.h
>>>>> @@ -410,6 +410,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);
>>>>> +const struct drm_edid *drm_get_acpi_edid(struct drm_connector *connector);
>>>>
>>>> There's a comment
>>>>
>>>> /* Interface based on struct drm_edid */
>>>>
>>>> towards the end of the file, gathering all the new API under it.
>>>>
>>>> Other than that, LGTM,
>>>>
>>>> BR,
>>>> Jani.
>>>>
>>>>> 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-12 11:28:24

by Jani Nikula

[permalink] [raw]
Subject: Re: [PATCH v4 1/3] drm: Add drm_get_acpi_edid() helper

On Sat, 10 Feb 2024, Mario Limonciello <[email protected]> wrote:
> On 2/9/2024 12:57, Daniel Vetter wrote:
>> On Fri, Feb 09, 2024 at 09:34:13AM -0600, Mario Limonciello wrote:
>>> On 2/9/2024 05:07, Daniel Vetter wrote:
>>>> On Thu, Feb 08, 2024 at 11:57:11AM +0200, Jani Nikula wrote:
>>>>> On Wed, 07 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.
>>>>>>
>>>>>> Signed-off-by: Mario Limonciello <[email protected]>
>>>>>> ---
>>>>>> drivers/gpu/drm/Kconfig | 5 +++
>>>>>> drivers/gpu/drm/drm_edid.c | 77 ++++++++++++++++++++++++++++++++++++++
>>>>>> include/drm/drm_edid.h | 1 +
>>>>>> 3 files changed, 83 insertions(+)
>>>>>>
>>>>>> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
>>>>>> index 6ec33d36f3a4..ec2bb71e8b36 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
>>>>>
>>>>> I think I'll defer to drm maintainers on whether this is okay or
>>>>> something to be avoided.
>>>>
>>>> Uh yeah this is a bit much, and select just messes with everything. Just
>>>> #ifdef this in the code with a dummy alternative, if users configure their
>>>> kernel without acpi but need it, they get to keep all the pieces.
>>>>
>>>> Alternatively make a DRM_ACPI_HELPERS symbol, but imo a Kconfig for every
>>>> function is also not great. And just using #ifdef in the code also works
>>>> for CONFIG_OF, which is exactly the same thing for platforms using dt to
>>>> describe hw.
>>>>
>>>> Also I'd expect ACPI code to already provide dummy functions if ACPI is
>>>> provided, so you probably dont even need all that much #ifdef in the code.
>>>>
>>>> What we defo cant do is select platform/hw stuff just because you enable
>>>> CONFIG_DRM.
>>>> -Sima
>>>
>>> The problem was with linking. I'll experiment with #ifdef for the next
>>> version.
>>
>> Ah yes, if e.g. acpi is a module but drm is built-in then it will compile,
>> but not link.
>>
>> You need
>>
>> depends on (ACPI || ACPI=n)
>>
>> for this. Looks a bit funny but works for all combinations.
>
> Nope; this fails at link time with this combination:
>
> CONFIG_ACPI=y
> CONFIG_ACPI_VIDEO=m
> CONFIG_DRM=y
>
> ld: drivers/gpu/drm/drm_edid.o: in function `drm_do_probe_acpi_edid':
> drm_edid.c:(.text+0xd34): undefined reference to `acpi_video_get_edid'
> make[5]: *** [scripts/Makefile.vmlinux:37: vmlinux] Error 1
>
> So the logical solution is to try
> depends on (ACPI_VIDEO || ACPI_VIDEO=n)
>
> But that leads me back to the rabbit hole of why I had the selects moved
> to drm instead of drivers in the first place:
>
> drivers/gpu/drm/Kconfig:8:error: recursive dependency detected!
> drivers/gpu/drm/Kconfig:8: symbol DRM depends on ACPI_VIDEO
> drivers/acpi/Kconfig:213: symbol ACPI_VIDEO depends on
> BACKLIGHT_CLASS_DEVICE
> drivers/video/backlight/Kconfig:136: symbol BACKLIGHT_CLASS_DEVICE is
> selected by DRM_RADEON
> drivers/gpu/drm/radeon/Kconfig:3: symbol DRM_RADEON depends on DRM

Generally speaking the root cause is using "select" instead of "depends
on" in the first place. The excessive selects are just band-aid over
that root cause. And if you try to convert some but not all the selects
to depends ons, you'll get recursive dependencies.

Quoting Documentation/kbuild/kconfig-language.rst:

Note:
select should be used with care. select will force
a symbol to a value without visiting the dependencies.
By abusing select you are able to select a symbol FOO even
if FOO depends on BAR that is not set.
In general use select only for non-visible symbols
(no prompts anywhere) and for symbols with no dependencies.
That will limit the usefulness but on the other hand avoid
the illegal configurations all over.

Yeah, we ignore that, and get to keep all the pieces.


BR,
Jani.


>
>
>>
>> Since this gets mess it might be useful to have a DRM_ACPI_HELPERS Kconfig
>> that controls all this.
>
> How about all those selects that I had in this patch moved to
> DRM_ACPI_HELPERS and keep the patch that drops from all the drivers then?
>
>> -Sima
>>
>>>
>>>>
>>>>>
>>>>>
>>>>>> 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 923c4423151c..c649b4f9fd8e 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,49 @@ 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 +2687,39 @@ struct edid *drm_get_edid(struct drm_connector *connector,
>>>>>> }
>>>>>> EXPORT_SYMBOL(drm_get_edid);
>>>>>> +/**
>>>>>> + * drm_get_acpi_edid - get EDID data, if available
>>>>>
>>>>> I'd prefer all the new EDID API to be named drm_edid_*. Makes a clean
>>>>> break from the old API, and is more consistent.
>>>>>
>>>>> So perhaps drm_edid_read_acpi() to be in line with all the other struct
>>>>> drm_edid based EDID reading functions.
>>>>>
>>>>>> + * @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_get_acpi_edid(struct drm_connector *connector)
>>>>>> +{
>>>>>> + const struct drm_edid *drm_edid;
>>>>>> +
>>>>>> + 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;
>>>>>> +
>>>>>> + drm_edid = drm_edid_read_custom(connector, drm_do_probe_acpi_edid, connector->dev);
>>>>>> +
>>>>>> + /* Note: Do *not* call connector updates here. */
>>>>>> +
>>>>>> + return drm_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 7923bc00dc7a..ca41be289fc6 100644
>>>>>> --- a/include/drm/drm_edid.h
>>>>>> +++ b/include/drm/drm_edid.h
>>>>>> @@ -410,6 +410,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);
>>>>>> +const struct drm_edid *drm_get_acpi_edid(struct drm_connector *connector);
>>>>>
>>>>> There's a comment
>>>>>
>>>>> /* Interface based on struct drm_edid */
>>>>>
>>>>> towards the end of the file, gathering all the new API under it.
>>>>>
>>>>> Other than that, LGTM,
>>>>>
>>>>> BR,
>>>>> Jani.
>>>>>
>>>>>> 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
>>>>
>>>
>>
>

--
Jani Nikula, Intel

2024-02-16 16:44:48

by Daniel Vetter

[permalink] [raw]
Subject: Re: [PATCH v4 1/3] drm: Add drm_get_acpi_edid() helper

On Mon, Feb 12, 2024 at 01:27:57PM +0200, Jani Nikula wrote:
> On Sat, 10 Feb 2024, Mario Limonciello <[email protected]> wrote:
> > On 2/9/2024 12:57, Daniel Vetter wrote:
> >> On Fri, Feb 09, 2024 at 09:34:13AM -0600, Mario Limonciello wrote:
> >>> On 2/9/2024 05:07, Daniel Vetter wrote:
> >>>> On Thu, Feb 08, 2024 at 11:57:11AM +0200, Jani Nikula wrote:
> >>>>> On Wed, 07 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.
> >>>>>>
> >>>>>> Signed-off-by: Mario Limonciello <[email protected]>
> >>>>>> ---
> >>>>>> drivers/gpu/drm/Kconfig | 5 +++
> >>>>>> drivers/gpu/drm/drm_edid.c | 77 ++++++++++++++++++++++++++++++++++++++
> >>>>>> include/drm/drm_edid.h | 1 +
> >>>>>> 3 files changed, 83 insertions(+)
> >>>>>>
> >>>>>> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> >>>>>> index 6ec33d36f3a4..ec2bb71e8b36 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
> >>>>>
> >>>>> I think I'll defer to drm maintainers on whether this is okay or
> >>>>> something to be avoided.
> >>>>
> >>>> Uh yeah this is a bit much, and select just messes with everything. Just
> >>>> #ifdef this in the code with a dummy alternative, if users configure their
> >>>> kernel without acpi but need it, they get to keep all the pieces.
> >>>>
> >>>> Alternatively make a DRM_ACPI_HELPERS symbol, but imo a Kconfig for every
> >>>> function is also not great. And just using #ifdef in the code also works
> >>>> for CONFIG_OF, which is exactly the same thing for platforms using dt to
> >>>> describe hw.
> >>>>
> >>>> Also I'd expect ACPI code to already provide dummy functions if ACPI is
> >>>> provided, so you probably dont even need all that much #ifdef in the code.
> >>>>
> >>>> What we defo cant do is select platform/hw stuff just because you enable
> >>>> CONFIG_DRM.
> >>>> -Sima
> >>>
> >>> The problem was with linking. I'll experiment with #ifdef for the next
> >>> version.
> >>
> >> Ah yes, if e.g. acpi is a module but drm is built-in then it will compile,
> >> but not link.
> >>
> >> You need
> >>
> >> depends on (ACPI || ACPI=n)
> >>
> >> for this. Looks a bit funny but works for all combinations.
> >
> > Nope; this fails at link time with this combination:
> >
> > CONFIG_ACPI=y
> > CONFIG_ACPI_VIDEO=m
> > CONFIG_DRM=y
> >
> > ld: drivers/gpu/drm/drm_edid.o: in function `drm_do_probe_acpi_edid':
> > drm_edid.c:(.text+0xd34): undefined reference to `acpi_video_get_edid'
> > make[5]: *** [scripts/Makefile.vmlinux:37: vmlinux] Error 1
> >
> > So the logical solution is to try
> > depends on (ACPI_VIDEO || ACPI_VIDEO=n)
> >
> > But that leads me back to the rabbit hole of why I had the selects moved
> > to drm instead of drivers in the first place:
> >
> > drivers/gpu/drm/Kconfig:8:error: recursive dependency detected!
> > drivers/gpu/drm/Kconfig:8: symbol DRM depends on ACPI_VIDEO
> > drivers/acpi/Kconfig:213: symbol ACPI_VIDEO depends on
> > BACKLIGHT_CLASS_DEVICE
> > drivers/video/backlight/Kconfig:136: symbol BACKLIGHT_CLASS_DEVICE is
> > selected by DRM_RADEON
> > drivers/gpu/drm/radeon/Kconfig:3: symbol DRM_RADEON depends on DRM
>
> Generally speaking the root cause is using "select" instead of "depends
> on" in the first place. The excessive selects are just band-aid over
> that root cause. And if you try to convert some but not all the selects
> to depends ons, you'll get recursive dependencies.
>
> Quoting Documentation/kbuild/kconfig-language.rst:
>
> Note:
> select should be used with care. select will force
> a symbol to a value without visiting the dependencies.
> By abusing select you are able to select a symbol FOO even
> if FOO depends on BAR that is not set.
> In general use select only for non-visible symbols
> (no prompts anywhere) and for symbols with no dependencies.
> That will limit the usefulness but on the other hand avoid
> the illegal configurations all over.
>
> Yeah, we ignore that, and get to keep all the pieces.

Yeah we need radically fewer select and replace them with depends. The
idea is that people just magically get the correct kernel config because
even menuconfig sucks at showing you why you cannot enable a driver.

But it's really not a good solution to that issue, and we need to stop
suffering. Reality is that enabling a correct config for complex drivers
like we have in drm is a bit a black belt art :-/
-Sima

>
>
> BR,
> Jani.
>
>
> >
> >
> >>
> >> Since this gets mess it might be useful to have a DRM_ACPI_HELPERS Kconfig
> >> that controls all this.
> >
> > How about all those selects that I had in this patch moved to
> > DRM_ACPI_HELPERS and keep the patch that drops from all the drivers then?
> >
> >> -Sima
> >>
> >>>
> >>>>
> >>>>>
> >>>>>
> >>>>>> 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 923c4423151c..c649b4f9fd8e 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,49 @@ 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 +2687,39 @@ struct edid *drm_get_edid(struct drm_connector *connector,
> >>>>>> }
> >>>>>> EXPORT_SYMBOL(drm_get_edid);
> >>>>>> +/**
> >>>>>> + * drm_get_acpi_edid - get EDID data, if available
> >>>>>
> >>>>> I'd prefer all the new EDID API to be named drm_edid_*. Makes a clean
> >>>>> break from the old API, and is more consistent.
> >>>>>
> >>>>> So perhaps drm_edid_read_acpi() to be in line with all the other struct
> >>>>> drm_edid based EDID reading functions.
> >>>>>
> >>>>>> + * @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_get_acpi_edid(struct drm_connector *connector)
> >>>>>> +{
> >>>>>> + const struct drm_edid *drm_edid;
> >>>>>> +
> >>>>>> + 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;
> >>>>>> +
> >>>>>> + drm_edid = drm_edid_read_custom(connector, drm_do_probe_acpi_edid, connector->dev);
> >>>>>> +
> >>>>>> + /* Note: Do *not* call connector updates here. */
> >>>>>> +
> >>>>>> + return drm_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 7923bc00dc7a..ca41be289fc6 100644
> >>>>>> --- a/include/drm/drm_edid.h
> >>>>>> +++ b/include/drm/drm_edid.h
> >>>>>> @@ -410,6 +410,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);
> >>>>>> +const struct drm_edid *drm_get_acpi_edid(struct drm_connector *connector);
> >>>>>
> >>>>> There's a comment
> >>>>>
> >>>>> /* Interface based on struct drm_edid */
> >>>>>
> >>>>> towards the end of the file, gathering all the new API under it.
> >>>>>
> >>>>> Other than that, LGTM,
> >>>>>
> >>>>> BR,
> >>>>> Jani.
> >>>>>
> >>>>>> 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
> >>>>
> >>>
> >>
> >
>
> --
> Jani Nikula, Intel

--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch