Re: [PATCH v6 3/5] drm: Add support to get EDID from ACPI

2024-04-17 Thread Geert Uytterhoeven
Hi Mario,

On Thu, Feb 15, 2024 at 8:04 PM Mario Limonciello
 wrote:
> On 2/15/2024 12:47, Ville Syrjälä wrote:
> > On Thu, Feb 15, 2024 at 12:20:56PM -0600, Mario Limonciello wrote:
> >> On 2/14/2024 17:13, Ville Syrjälä wrote:
> >>> On Wed, Feb 14, 2024 at 03:57:54PM -0600, Mario Limonciello wrote:
>  --- a/include/drm/drm_connector.h
>  +++ b/include/drm/drm_connector.h
>  @@ -1886,6 +1886,12 @@ struct drm_connector {
> 
> /** @hdr_sink_metadata: HDR Metadata Information read from 
>  sink */
> struct hdr_sink_metadata hdr_sink_metadata;
>  +
>  +  /**
>  +   * @acpi_edid_allowed: Get the EDID from the BIOS, if available.
>  +   * This is only applicable to eDP and LVDS displays.
>  +   */
>  +  bool acpi_edid_allowed;
> >>>
> >>> Aren't there other bools/small stuff in there for tighter packing?
> >>
> >> Does the compiler automatically do the packing if you put bools nearby
> >> in a struct?  If so; TIL.
> >
> > Yes. Well, depends on the types and their alignment requirements
> > of course, and/or whether you specified __packed or not.
> >
> > You can use 'pahole' to find the holes in structures.
>
> Thanks!  I don't see a __packed attribute on struct drm_connector, but
> I'll put it near by other bools in case that changes in the future.

FTR, don't add __packed unless you have a very good reason to do so.
With __packed, the compiler will emit multiple byte-accesses to
access multi-byte integrals on platforms that do not support unaligned
memory access.

Gr{oetje,eeting}s,

Geert

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- ge...@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds


Re: [PATCH v6 3/5] drm: Add support to get EDID from ACPI

2024-02-15 Thread Mario Limonciello

On 2/15/2024 12:47, Ville Syrjälä wrote:

On Thu, Feb 15, 2024 at 12:20:56PM -0600, Mario Limonciello wrote:

On 2/14/2024 17:13, Ville Syrjälä wrote:

On Wed, Feb 14, 2024 at 03:57:54PM -0600, Mario Limonciello wrote:

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

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

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 872edb47bb53..3db89e6af01d 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -8,6 +8,7 @@
   menuconfig DRM
tristate "Direct Rendering Manager (XFree86 4.1.0 and higher DRI 
support)"
depends on (AGP || AGP=n) && !EMULATED_CMPXCHG && HAS_DMA
+   depends on (ACPI_VIDEO || ACPI_VIDEO=n)
select DRM_PANEL_ORIENTATION_QUIRKS
select DRM_KMS_HELPER if DRM_FBDEV_EMULATION
select FB_CORE if DRM_FBDEV_EMULATION
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 923c4423151c..cdc30c6d05d5 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -28,6 +28,7 @@
* DEALINGS IN THE SOFTWARE.
*/
   
+#include 

   #include 
   #include 
   #include 
@@ -2188,6 +2189,58 @@ drm_do_probe_ddc_edid(void *data, u8 *buf, unsigned int 
block, size_t len)
return ret == xfers ? 0 : -1;
   }
   
+/**

+ * drm_do_probe_acpi_edid() - get EDID information via ACPI _DDC
+ * @data: struct drm_connector
+ * @buf: EDID data buffer to be filled
+ * @block: 128 byte EDID block to start fetching from
+ * @len: EDID data buffer length to fetch
+ *
+ * Try to fetch EDID information by calling acpi_video_get_edid() function.
+ *
+ * Return: 0 on success or error code on failure.
+ */
+static int
+drm_do_probe_acpi_edid(void *data, u8 *buf, unsigned int block, size_t len)
+{
+   struct drm_connector *connector = data;
+   struct drm_device *ddev = connector->dev;
+   struct acpi_device *acpidev = ACPI_COMPANION(ddev->dev);
+   unsigned char start = block * EDID_LENGTH;
+   void *edid;
+   int r;
+
+   if (!acpidev)
+   return -ENODEV;
+
+   switch (connector->connector_type) {
+   case DRM_MODE_CONNECTOR_LVDS:
+   case DRM_MODE_CONNECTOR_eDP:
+   break;
+   default:
+   return -EINVAL;
+   }


We could have other types of connectors that want this too.
I don't see any real benefit in having this check tbh. Drivers
should simply notset the flag on connectors where it won't work,
and only the driver can really know that.


Ack.




+   /* fetch the entire edid from BIOS */
+   r = acpi_video_get_edid(acpidev, ACPI_VIDEO_DISPLAY_LCD, -1, );
+   if (r < 0) {
+   DRM_DEBUG_KMS("Failed to get EDID from ACPI: %d\n", r);
+   return r;
+   }
+   if (len > r || start > r || start + len > r) {
+   r = -EINVAL;
+   goto cleanup;
+   }
+
+   memcpy(buf, edid + start, len);
+   r = 0;
+
+cleanup:
+   kfree(edid);
+
+   return r;
+}
+
   static void connector_bad_edid(struct drm_connector *connector,
   const struct edid *edid, int num_blocks)
   {
@@ -2621,7 +2674,8 @@ EXPORT_SYMBOL(drm_probe_ddc);
* @connector: connector we're probing
* @adapter: I2C adapter to use for DDC
*
- * Poke the given I2C channel to grab EDID data if possible.  If found,
+ * If the connector allows it, try to fetch EDID data using ACPI. If not found
+ * poke the given I2C channel to grab EDID data if possible.  If found,
* attach it to the connector.
*
* Return: Pointer to valid EDID or NULL if we couldn't find any.
@@ -2629,20 +2683,50 @@ EXPORT_SYMBOL(drm_probe_ddc);
   struct edid *drm_get_edid(struct drm_connector *connector,
  struct i2c_adapter *adapter)
   {
-   struct edid *edid;
+   struct edid *edid = NULL;
   
   	if (connector->force == DRM_FORCE_OFF)

return NULL;
   
-	if (connector->force == DRM_FORCE_UNSPECIFIED && !drm_probe_ddc(adapter))

-   return NULL;
+   if (connector->acpi_edid_allowed)
+   edid = _drm_do_get_edid(connector, drm_do_probe_acpi_edid, 
connector, NULL);
+
+   if (!edid) {
+   if (connector->force == DRM_FORCE_UNSPECIFIED && 
!drm_probe_ddc(adapter))
+   return NULL;
+   edid = _drm_do_get_edid(connector, drm_do_probe_ddc_edid, 
adapter, NULL);
+   }
   
-	edid = _drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter, NULL);


Re: [PATCH v6 3/5] drm: Add support to get EDID from ACPI

2024-02-15 Thread Ville Syrjälä
On Thu, Feb 15, 2024 at 12:20:56PM -0600, Mario Limonciello wrote:
> On 2/14/2024 17:13, Ville Syrjälä wrote:
> > On Wed, Feb 14, 2024 at 03:57:54PM -0600, Mario Limonciello wrote:
> >> Some manufacturers have intentionally put an EDID that differs from
> >> the EDID on the internal panel on laptops.  Drivers that prefer to
> >> fetch this EDID can set a bit on the drm_connector to indicate that
> >> the DRM EDID helpers should try to fetch it and it is preferred if
> >> it's present.
> >>
> >> Signed-off-by: Mario Limonciello 
> >> ---
> >>   drivers/gpu/drm/Kconfig |   1 +
> >>   drivers/gpu/drm/drm_edid.c  | 109 +---
> >>   include/drm/drm_connector.h |   6 ++
> >>   include/drm/drm_edid.h  |   1 +
> >>   4 files changed, 109 insertions(+), 8 deletions(-)
> >>
> >> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> >> index 872edb47bb53..3db89e6af01d 100644
> >> --- a/drivers/gpu/drm/Kconfig
> >> +++ b/drivers/gpu/drm/Kconfig
> >> @@ -8,6 +8,7 @@
> >>   menuconfig DRM
> >>tristate "Direct Rendering Manager (XFree86 4.1.0 and higher DRI 
> >> support)"
> >>depends on (AGP || AGP=n) && !EMULATED_CMPXCHG && HAS_DMA
> >> +  depends on (ACPI_VIDEO || ACPI_VIDEO=n)
> >>select DRM_PANEL_ORIENTATION_QUIRKS
> >>select DRM_KMS_HELPER if DRM_FBDEV_EMULATION
> >>select FB_CORE if DRM_FBDEV_EMULATION
> >> diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
> >> index 923c4423151c..cdc30c6d05d5 100644
> >> --- a/drivers/gpu/drm/drm_edid.c
> >> +++ b/drivers/gpu/drm/drm_edid.c
> >> @@ -28,6 +28,7 @@
> >>* DEALINGS IN THE SOFTWARE.
> >>*/
> >>   
> >> +#include 
> >>   #include 
> >>   #include 
> >>   #include 
> >> @@ -2188,6 +2189,58 @@ drm_do_probe_ddc_edid(void *data, u8 *buf, unsigned 
> >> int block, size_t len)
> >>return ret == xfers ? 0 : -1;
> >>   }
> >>   
> >> +/**
> >> + * drm_do_probe_acpi_edid() - get EDID information via ACPI _DDC
> >> + * @data: struct drm_connector
> >> + * @buf: EDID data buffer to be filled
> >> + * @block: 128 byte EDID block to start fetching from
> >> + * @len: EDID data buffer length to fetch
> >> + *
> >> + * Try to fetch EDID information by calling acpi_video_get_edid() 
> >> function.
> >> + *
> >> + * Return: 0 on success or error code on failure.
> >> + */
> >> +static int
> >> +drm_do_probe_acpi_edid(void *data, u8 *buf, unsigned int block, size_t 
> >> len)
> >> +{
> >> +  struct drm_connector *connector = data;
> >> +  struct drm_device *ddev = connector->dev;
> >> +  struct acpi_device *acpidev = ACPI_COMPANION(ddev->dev);
> >> +  unsigned char start = block * EDID_LENGTH;
> >> +  void *edid;
> >> +  int r;
> >> +
> >> +  if (!acpidev)
> >> +  return -ENODEV;
> >> +
> >> +  switch (connector->connector_type) {
> >> +  case DRM_MODE_CONNECTOR_LVDS:
> >> +  case DRM_MODE_CONNECTOR_eDP:
> >> +  break;
> >> +  default:
> >> +  return -EINVAL;
> >> +  }
> > 
> > We could have other types of connectors that want this too.
> > I don't see any real benefit in having this check tbh. Drivers
> > should simply notset the flag on connectors where it won't work,
> > and only the driver can really know that.
> 
> Ack.
> 
> > 
> >> +  /* fetch the entire edid from BIOS */
> >> +  r = acpi_video_get_edid(acpidev, ACPI_VIDEO_DISPLAY_LCD, -1, );
> >> +  if (r < 0) {
> >> +  DRM_DEBUG_KMS("Failed to get EDID from ACPI: %d\n", r);
> >> +  return r;
> >> +  }
> >> +  if (len > r || start > r || start + len > r) {
> >> +  r = -EINVAL;
> >> +  goto cleanup;
> >> +  }
> >> +
> >> +  memcpy(buf, edid + start, len);
> >> +  r = 0;
> >> +
> >> +cleanup:
> >> +  kfree(edid);
> >> +
> >> +  return r;
> >> +}
> >> +
> >>   static void connector_bad_edid(struct drm_connector *connector,
> >>   const struct edid *edid, int num_blocks)
> >>   {
> >> @@ -2621,7 +2674,8 @@ EXPORT_SYMBOL(drm_probe_ddc);
> >>* @connector: connector we're probing
> >>* @adapter: I2C adapter to use for DDC
> >>*
> >> - * Poke the given I2C channel to grab EDID data if possible.  If found,
> >> + * If the connector allows it, try to fetch EDID data using ACPI. If not 
> >> found
> >> + * poke the given I2C channel to grab EDID data if possible.  If found,
> >>* attach it to the connector.
> >>*
> >>* Return: Pointer to valid EDID or NULL if we couldn't find any.
> >> @@ -2629,20 +2683,50 @@ EXPORT_SYMBOL(drm_probe_ddc);
> >>   struct edid *drm_get_edid(struct drm_connector *connector,
> >>  struct i2c_adapter *adapter)
> >>   {
> >> -  struct edid *edid;
> >> +  struct edid *edid = NULL;
> >>   
> >>if (connector->force == DRM_FORCE_OFF)
> >>return NULL;
> >>   
> >> -  if (connector->force == DRM_FORCE_UNSPECIFIED && 
> >> !drm_probe_ddc(adapter))
> >> -  return NULL;
> >> +  if (connector->acpi_edid_allowed)
> >> +  edid = _drm_do_get_edid(connector, 

Re: [PATCH v6 3/5] drm: Add support to get EDID from ACPI

2024-02-15 Thread Mario Limonciello

On 2/14/2024 17:13, Ville Syrjälä wrote:

On Wed, Feb 14, 2024 at 03:57:54PM -0600, Mario Limonciello wrote:

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

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

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 872edb47bb53..3db89e6af01d 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -8,6 +8,7 @@
  menuconfig DRM
tristate "Direct Rendering Manager (XFree86 4.1.0 and higher DRI 
support)"
depends on (AGP || AGP=n) && !EMULATED_CMPXCHG && HAS_DMA
+   depends on (ACPI_VIDEO || ACPI_VIDEO=n)
select DRM_PANEL_ORIENTATION_QUIRKS
select DRM_KMS_HELPER if DRM_FBDEV_EMULATION
select FB_CORE if DRM_FBDEV_EMULATION
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 923c4423151c..cdc30c6d05d5 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -28,6 +28,7 @@
   * DEALINGS IN THE SOFTWARE.
   */
  
+#include 

  #include 
  #include 
  #include 
@@ -2188,6 +2189,58 @@ drm_do_probe_ddc_edid(void *data, u8 *buf, unsigned int 
block, size_t len)
return ret == xfers ? 0 : -1;
  }
  
+/**

+ * drm_do_probe_acpi_edid() - get EDID information via ACPI _DDC
+ * @data: struct drm_connector
+ * @buf: EDID data buffer to be filled
+ * @block: 128 byte EDID block to start fetching from
+ * @len: EDID data buffer length to fetch
+ *
+ * Try to fetch EDID information by calling acpi_video_get_edid() function.
+ *
+ * Return: 0 on success or error code on failure.
+ */
+static int
+drm_do_probe_acpi_edid(void *data, u8 *buf, unsigned int block, size_t len)
+{
+   struct drm_connector *connector = data;
+   struct drm_device *ddev = connector->dev;
+   struct acpi_device *acpidev = ACPI_COMPANION(ddev->dev);
+   unsigned char start = block * EDID_LENGTH;
+   void *edid;
+   int r;
+
+   if (!acpidev)
+   return -ENODEV;
+
+   switch (connector->connector_type) {
+   case DRM_MODE_CONNECTOR_LVDS:
+   case DRM_MODE_CONNECTOR_eDP:
+   break;
+   default:
+   return -EINVAL;
+   }


We could have other types of connectors that want this too.
I don't see any real benefit in having this check tbh. Drivers
should simply notset the flag on connectors where it won't work,
and only the driver can really know that.


Ack.




+   /* fetch the entire edid from BIOS */
+   r = acpi_video_get_edid(acpidev, ACPI_VIDEO_DISPLAY_LCD, -1, );
+   if (r < 0) {
+   DRM_DEBUG_KMS("Failed to get EDID from ACPI: %d\n", r);
+   return r;
+   }
+   if (len > r || start > r || start + len > r) {
+   r = -EINVAL;
+   goto cleanup;
+   }
+
+   memcpy(buf, edid + start, len);
+   r = 0;
+
+cleanup:
+   kfree(edid);
+
+   return r;
+}
+
  static void connector_bad_edid(struct drm_connector *connector,
   const struct edid *edid, int num_blocks)
  {
@@ -2621,7 +2674,8 @@ EXPORT_SYMBOL(drm_probe_ddc);
   * @connector: connector we're probing
   * @adapter: I2C adapter to use for DDC
   *
- * Poke the given I2C channel to grab EDID data if possible.  If found,
+ * If the connector allows it, try to fetch EDID data using ACPI. If not found
+ * poke the given I2C channel to grab EDID data if possible.  If found,
   * attach it to the connector.
   *
   * Return: Pointer to valid EDID or NULL if we couldn't find any.
@@ -2629,20 +2683,50 @@ EXPORT_SYMBOL(drm_probe_ddc);
  struct edid *drm_get_edid(struct drm_connector *connector,
  struct i2c_adapter *adapter)
  {
-   struct edid *edid;
+   struct edid *edid = NULL;
  
  	if (connector->force == DRM_FORCE_OFF)

return NULL;
  
-	if (connector->force == DRM_FORCE_UNSPECIFIED && !drm_probe_ddc(adapter))

-   return NULL;
+   if (connector->acpi_edid_allowed)
+   edid = _drm_do_get_edid(connector, drm_do_probe_acpi_edid, 
connector, NULL);
+
+   if (!edid) {
+   if (connector->force == DRM_FORCE_UNSPECIFIED && 
!drm_probe_ddc(adapter))
+   return NULL;
+   edid = _drm_do_get_edid(connector, drm_do_probe_ddc_edid, 
adapter, NULL);
+   }
  
-	edid = _drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter, NULL);

drm_connector_update_edid_property(connector, edid);
return edid;
  }
  EXPORT_SYMBOL(drm_get_edid);
  
+/**

+ * 

Re: [PATCH v6 3/5] drm: Add support to get EDID from ACPI

2024-02-15 Thread Jani Nikula
On Thu, 15 Feb 2024, Ville Syrjälä  wrote:
> On Wed, Feb 14, 2024 at 03:57:54PM -0600, Mario Limonciello wrote:
>> +static int
>> +drm_do_probe_acpi_edid(void *data, u8 *buf, unsigned int block, size_t len)
>> +{
>> +struct drm_connector *connector = data;
>> +struct drm_device *ddev = connector->dev;
>> +struct acpi_device *acpidev = ACPI_COMPANION(ddev->dev);
>> +unsigned char start = block * EDID_LENGTH;
>> +void *edid;
>> +int r;
>> +
>> +if (!acpidev)
>> +return -ENODEV;
>> +
>> +switch (connector->connector_type) {
>> +case DRM_MODE_CONNECTOR_LVDS:
>> +case DRM_MODE_CONNECTOR_eDP:
>> +break;
>> +default:
>> +return -EINVAL;
>> +}
>
> We could have other types of connectors that want this too.
> I don't see any real benefit in having this check tbh. Drivers
> should simply notset the flag on connectors where it won't work,
> and only the driver can really know that.

Agreed.

>>  const struct drm_edid *drm_edid_read(struct drm_connector *connector)
>>  {
>> +const struct drm_edid *drm_edid = NULL;
>> +
>>  if (drm_WARN_ON(connector->dev, !connector->ddc))
>>  return NULL;
>>  
>> -return drm_edid_read_ddc(connector, connector->ddc);
>> +if (connector->acpi_edid_allowed)
>
> That should probably be called 'prefer_acpi_edid' or something
> since it's the first choice when the flag is set.
>
> But I'm not so sure there's any real benefit in having this
> flag at all. You anyway have to modify the driver to use this,
> so why not just have the driver do the call directly instead of
> adding this extra detour via the flag?

Heh, round and round we go [1].


BR,
Jani.

[1] https://lore.kernel.org/r/87sf23auxv@intel.com


-- 
Jani Nikula, Intel


Re: [PATCH v6 3/5] drm: Add support to get EDID from ACPI

2024-02-15 Thread Jani Nikula
On Wed, 14 Feb 2024, Mario Limonciello  wrote:
> Some manufacturers have intentionally put an EDID that differs from
> the EDID on the internal panel on laptops.  Drivers that prefer to
> fetch this EDID can set a bit on the drm_connector to indicate that
> the DRM EDID helpers should try to fetch it and it is preferred if
> it's present.

I just replied to a previous version of the patch [1]. Looks like all
the comments there still hold.

BR,
Jani.


[1] https://lore.kernel.org/r/87eddd6d41@intel.com


>
> Signed-off-by: Mario Limonciello 
> ---
>  drivers/gpu/drm/Kconfig |   1 +
>  drivers/gpu/drm/drm_edid.c  | 109 +---
>  include/drm/drm_connector.h |   6 ++
>  include/drm/drm_edid.h  |   1 +
>  4 files changed, 109 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index 872edb47bb53..3db89e6af01d 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -8,6 +8,7 @@
>  menuconfig DRM
>   tristate "Direct Rendering Manager (XFree86 4.1.0 and higher DRI 
> support)"
>   depends on (AGP || AGP=n) && !EMULATED_CMPXCHG && HAS_DMA
> + depends on (ACPI_VIDEO || ACPI_VIDEO=n)
>   select DRM_PANEL_ORIENTATION_QUIRKS
>   select DRM_KMS_HELPER if DRM_FBDEV_EMULATION
>   select FB_CORE if DRM_FBDEV_EMULATION
> diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
> index 923c4423151c..cdc30c6d05d5 100644
> --- a/drivers/gpu/drm/drm_edid.c
> +++ b/drivers/gpu/drm/drm_edid.c
> @@ -28,6 +28,7 @@
>   * DEALINGS IN THE SOFTWARE.
>   */
>  
> +#include 
>  #include 
>  #include 
>  #include 
> @@ -2188,6 +2189,58 @@ drm_do_probe_ddc_edid(void *data, u8 *buf, unsigned 
> int block, size_t len)
>   return ret == xfers ? 0 : -1;
>  }
>  
> +/**
> + * drm_do_probe_acpi_edid() - get EDID information via ACPI _DDC
> + * @data: struct drm_connector
> + * @buf: EDID data buffer to be filled
> + * @block: 128 byte EDID block to start fetching from
> + * @len: EDID data buffer length to fetch
> + *
> + * Try to fetch EDID information by calling acpi_video_get_edid() function.
> + *
> + * Return: 0 on success or error code on failure.
> + */
> +static int
> +drm_do_probe_acpi_edid(void *data, u8 *buf, unsigned int block, size_t len)
> +{
> + struct drm_connector *connector = data;
> + struct drm_device *ddev = connector->dev;
> + struct acpi_device *acpidev = ACPI_COMPANION(ddev->dev);
> + unsigned char start = block * EDID_LENGTH;
> + void *edid;
> + int r;
> +
> + if (!acpidev)
> + return -ENODEV;
> +
> + switch (connector->connector_type) {
> + case DRM_MODE_CONNECTOR_LVDS:
> + case DRM_MODE_CONNECTOR_eDP:
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + /* fetch the entire edid from BIOS */
> + r = acpi_video_get_edid(acpidev, ACPI_VIDEO_DISPLAY_LCD, -1, );
> + if (r < 0) {
> + DRM_DEBUG_KMS("Failed to get EDID from ACPI: %d\n", r);
> + return r;
> + }
> + if (len > r || start > r || start + len > r) {
> + r = -EINVAL;
> + goto cleanup;
> + }
> +
> + memcpy(buf, edid + start, len);
> + r = 0;
> +
> +cleanup:
> + kfree(edid);
> +
> + return r;
> +}
> +
>  static void connector_bad_edid(struct drm_connector *connector,
>  const struct edid *edid, int num_blocks)
>  {
> @@ -2621,7 +2674,8 @@ EXPORT_SYMBOL(drm_probe_ddc);
>   * @connector: connector we're probing
>   * @adapter: I2C adapter to use for DDC
>   *
> - * Poke the given I2C channel to grab EDID data if possible.  If found,
> + * If the connector allows it, try to fetch EDID data using ACPI. If not 
> found
> + * poke the given I2C channel to grab EDID data if possible.  If found,
>   * attach it to the connector.
>   *
>   * Return: Pointer to valid EDID or NULL if we couldn't find any.
> @@ -2629,20 +2683,50 @@ EXPORT_SYMBOL(drm_probe_ddc);
>  struct edid *drm_get_edid(struct drm_connector *connector,
> struct i2c_adapter *adapter)
>  {
> - struct edid *edid;
> + struct edid *edid = NULL;
>  
>   if (connector->force == DRM_FORCE_OFF)
>   return NULL;
>  
> - if (connector->force == DRM_FORCE_UNSPECIFIED && 
> !drm_probe_ddc(adapter))
> - return NULL;
> + if (connector->acpi_edid_allowed)
> + edid = _drm_do_get_edid(connector, drm_do_probe_acpi_edid, 
> connector, NULL);
> +
> + if (!edid) {
> + if (connector->force == DRM_FORCE_UNSPECIFIED && 
> !drm_probe_ddc(adapter))
> + return NULL;
> + edid = _drm_do_get_edid(connector, drm_do_probe_ddc_edid, 
> adapter, NULL);
> + }
>  
> - edid = _drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter, 
> NULL);
>   drm_connector_update_edid_property(connector, edid);
>   return edid;
>  }
>  EXPORT_SYMBOL(drm_get_edid);
>  

Re: [PATCH v6 3/5] drm: Add support to get EDID from ACPI

2024-02-14 Thread Ville Syrjälä
On Wed, Feb 14, 2024 at 03:57:54PM -0600, Mario Limonciello wrote:
> Some manufacturers have intentionally put an EDID that differs from
> the EDID on the internal panel on laptops.  Drivers that prefer to
> fetch this EDID can set a bit on the drm_connector to indicate that
> the DRM EDID helpers should try to fetch it and it is preferred if
> it's present.
> 
> Signed-off-by: Mario Limonciello 
> ---
>  drivers/gpu/drm/Kconfig |   1 +
>  drivers/gpu/drm/drm_edid.c  | 109 +---
>  include/drm/drm_connector.h |   6 ++
>  include/drm/drm_edid.h  |   1 +
>  4 files changed, 109 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index 872edb47bb53..3db89e6af01d 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -8,6 +8,7 @@
>  menuconfig DRM
>   tristate "Direct Rendering Manager (XFree86 4.1.0 and higher DRI 
> support)"
>   depends on (AGP || AGP=n) && !EMULATED_CMPXCHG && HAS_DMA
> + depends on (ACPI_VIDEO || ACPI_VIDEO=n)
>   select DRM_PANEL_ORIENTATION_QUIRKS
>   select DRM_KMS_HELPER if DRM_FBDEV_EMULATION
>   select FB_CORE if DRM_FBDEV_EMULATION
> diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
> index 923c4423151c..cdc30c6d05d5 100644
> --- a/drivers/gpu/drm/drm_edid.c
> +++ b/drivers/gpu/drm/drm_edid.c
> @@ -28,6 +28,7 @@
>   * DEALINGS IN THE SOFTWARE.
>   */
>  
> +#include 
>  #include 
>  #include 
>  #include 
> @@ -2188,6 +2189,58 @@ drm_do_probe_ddc_edid(void *data, u8 *buf, unsigned 
> int block, size_t len)
>   return ret == xfers ? 0 : -1;
>  }
>  
> +/**
> + * drm_do_probe_acpi_edid() - get EDID information via ACPI _DDC
> + * @data: struct drm_connector
> + * @buf: EDID data buffer to be filled
> + * @block: 128 byte EDID block to start fetching from
> + * @len: EDID data buffer length to fetch
> + *
> + * Try to fetch EDID information by calling acpi_video_get_edid() function.
> + *
> + * Return: 0 on success or error code on failure.
> + */
> +static int
> +drm_do_probe_acpi_edid(void *data, u8 *buf, unsigned int block, size_t len)
> +{
> + struct drm_connector *connector = data;
> + struct drm_device *ddev = connector->dev;
> + struct acpi_device *acpidev = ACPI_COMPANION(ddev->dev);
> + unsigned char start = block * EDID_LENGTH;
> + void *edid;
> + int r;
> +
> + if (!acpidev)
> + return -ENODEV;
> +
> + switch (connector->connector_type) {
> + case DRM_MODE_CONNECTOR_LVDS:
> + case DRM_MODE_CONNECTOR_eDP:
> + break;
> + default:
> + return -EINVAL;
> + }

We could have other types of connectors that want this too.
I don't see any real benefit in having this check tbh. Drivers
should simply notset the flag on connectors where it won't work,
and only the driver can really know that.

> + /* fetch the entire edid from BIOS */
> + r = acpi_video_get_edid(acpidev, ACPI_VIDEO_DISPLAY_LCD, -1, );
> + if (r < 0) {
> + DRM_DEBUG_KMS("Failed to get EDID from ACPI: %d\n", r);
> + return r;
> + }
> + if (len > r || start > r || start + len > r) {
> + r = -EINVAL;
> + goto cleanup;
> + }
> +
> + memcpy(buf, edid + start, len);
> + r = 0;
> +
> +cleanup:
> + kfree(edid);
> +
> + return r;
> +}
> +
>  static void connector_bad_edid(struct drm_connector *connector,
>  const struct edid *edid, int num_blocks)
>  {
> @@ -2621,7 +2674,8 @@ EXPORT_SYMBOL(drm_probe_ddc);
>   * @connector: connector we're probing
>   * @adapter: I2C adapter to use for DDC
>   *
> - * Poke the given I2C channel to grab EDID data if possible.  If found,
> + * If the connector allows it, try to fetch EDID data using ACPI. If not 
> found
> + * poke the given I2C channel to grab EDID data if possible.  If found,
>   * attach it to the connector.
>   *
>   * Return: Pointer to valid EDID or NULL if we couldn't find any.
> @@ -2629,20 +2683,50 @@ EXPORT_SYMBOL(drm_probe_ddc);
>  struct edid *drm_get_edid(struct drm_connector *connector,
> struct i2c_adapter *adapter)
>  {
> - struct edid *edid;
> + struct edid *edid = NULL;
>  
>   if (connector->force == DRM_FORCE_OFF)
>   return NULL;
>  
> - if (connector->force == DRM_FORCE_UNSPECIFIED && 
> !drm_probe_ddc(adapter))
> - return NULL;
> + if (connector->acpi_edid_allowed)
> + edid = _drm_do_get_edid(connector, drm_do_probe_acpi_edid, 
> connector, NULL);
> +
> + if (!edid) {
> + if (connector->force == DRM_FORCE_UNSPECIFIED && 
> !drm_probe_ddc(adapter))
> + return NULL;
> + edid = _drm_do_get_edid(connector, drm_do_probe_ddc_edid, 
> adapter, NULL);
> + }
>  
> - edid = _drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter, 
> NULL);
>   

[PATCH v6 3/5] drm: Add support to get EDID from ACPI

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

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

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 872edb47bb53..3db89e6af01d 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -8,6 +8,7 @@
 menuconfig DRM
tristate "Direct Rendering Manager (XFree86 4.1.0 and higher DRI 
support)"
depends on (AGP || AGP=n) && !EMULATED_CMPXCHG && HAS_DMA
+   depends on (ACPI_VIDEO || ACPI_VIDEO=n)
select DRM_PANEL_ORIENTATION_QUIRKS
select DRM_KMS_HELPER if DRM_FBDEV_EMULATION
select FB_CORE if DRM_FBDEV_EMULATION
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 923c4423151c..cdc30c6d05d5 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -28,6 +28,7 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
+#include 
 #include 
 #include 
 #include 
@@ -2188,6 +2189,58 @@ drm_do_probe_ddc_edid(void *data, u8 *buf, unsigned int 
block, size_t len)
return ret == xfers ? 0 : -1;
 }
 
+/**
+ * drm_do_probe_acpi_edid() - get EDID information via ACPI _DDC
+ * @data: struct drm_connector
+ * @buf: EDID data buffer to be filled
+ * @block: 128 byte EDID block to start fetching from
+ * @len: EDID data buffer length to fetch
+ *
+ * Try to fetch EDID information by calling acpi_video_get_edid() function.
+ *
+ * Return: 0 on success or error code on failure.
+ */
+static int
+drm_do_probe_acpi_edid(void *data, u8 *buf, unsigned int block, size_t len)
+{
+   struct drm_connector *connector = data;
+   struct drm_device *ddev = connector->dev;
+   struct acpi_device *acpidev = ACPI_COMPANION(ddev->dev);
+   unsigned char start = block * EDID_LENGTH;
+   void *edid;
+   int r;
+
+   if (!acpidev)
+   return -ENODEV;
+
+   switch (connector->connector_type) {
+   case DRM_MODE_CONNECTOR_LVDS:
+   case DRM_MODE_CONNECTOR_eDP:
+   break;
+   default:
+   return -EINVAL;
+   }
+
+   /* fetch the entire edid from BIOS */
+   r = acpi_video_get_edid(acpidev, ACPI_VIDEO_DISPLAY_LCD, -1, );
+   if (r < 0) {
+   DRM_DEBUG_KMS("Failed to get EDID from ACPI: %d\n", r);
+   return r;
+   }
+   if (len > r || start > r || start + len > r) {
+   r = -EINVAL;
+   goto cleanup;
+   }
+
+   memcpy(buf, edid + start, len);
+   r = 0;
+
+cleanup:
+   kfree(edid);
+
+   return r;
+}
+
 static void connector_bad_edid(struct drm_connector *connector,
   const struct edid *edid, int num_blocks)
 {
@@ -2621,7 +2674,8 @@ EXPORT_SYMBOL(drm_probe_ddc);
  * @connector: connector we're probing
  * @adapter: I2C adapter to use for DDC
  *
- * Poke the given I2C channel to grab EDID data if possible.  If found,
+ * If the connector allows it, try to fetch EDID data using ACPI. If not found
+ * poke the given I2C channel to grab EDID data if possible.  If found,
  * attach it to the connector.
  *
  * Return: Pointer to valid EDID or NULL if we couldn't find any.
@@ -2629,20 +2683,50 @@ EXPORT_SYMBOL(drm_probe_ddc);
 struct edid *drm_get_edid(struct drm_connector *connector,
  struct i2c_adapter *adapter)
 {
-   struct edid *edid;
+   struct edid *edid = NULL;
 
if (connector->force == DRM_FORCE_OFF)
return NULL;
 
-   if (connector->force == DRM_FORCE_UNSPECIFIED && 
!drm_probe_ddc(adapter))
-   return NULL;
+   if (connector->acpi_edid_allowed)
+   edid = _drm_do_get_edid(connector, drm_do_probe_acpi_edid, 
connector, NULL);
+
+   if (!edid) {
+   if (connector->force == DRM_FORCE_UNSPECIFIED && 
!drm_probe_ddc(adapter))
+   return NULL;
+   edid = _drm_do_get_edid(connector, drm_do_probe_ddc_edid, 
adapter, NULL);
+   }
 
-   edid = _drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter, 
NULL);
drm_connector_update_edid_property(connector, edid);
return edid;
 }
 EXPORT_SYMBOL(drm_get_edid);
 
+/**
+ * drm_edid_read_acpi - get EDID data, if available
+ * @connector: connector we're probing
+ *
+ * Use the BIOS to attempt to grab EDID data if possible.
+ *
+ * The returned pointer must be freed using drm_edid_free().
+ *
+ * Return: Pointer to valid EDID or NULL if we couldn't find any.
+ */
+const struct drm_edid *drm_edid_read_acpi(struct drm_connector *connector)
+{
+