drm/nouveau: support fetching LVDS EDID from ACPI
authorBen Skeggs <bskeggs@redhat.com>
Mon, 12 Jul 2010 05:33:07 +0000 (15:33 +1000)
committerBen Skeggs <bskeggs@redhat.com>
Mon, 26 Jul 2010 01:41:45 +0000 (11:41 +1000)
Based on a patch from Matthew Garrett.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Acked-by: Matthew Garrett <mjg@redhat.com>
drivers/gpu/drm/nouveau/nouveau_acpi.c
drivers/gpu/drm/nouveau/nouveau_bios.c
drivers/gpu/drm/nouveau/nouveau_bios.h
drivers/gpu/drm/nouveau/nouveau_connector.c
drivers/gpu/drm/nouveau/nouveau_drv.h

index 381d3851f5c30ea18dbcf0fb49fe84dbc079a28e..c17a055ee3e57c3a356f2fd4d3b85d4432f7d4e5 100644 (file)
@@ -3,6 +3,7 @@
 #include <linux/slab.h>
 #include <acpi/acpi_drivers.h>
 #include <acpi/acpi_bus.h>
+#include <acpi/video.h>
 
 #include "drmP.h"
 #include "drm.h"
@@ -11,6 +12,7 @@
 #include "nouveau_drv.h"
 #include "nouveau_drm.h"
 #include "nv50_display.h"
+#include "nouveau_connector.h"
 
 #include <linux/vga_switcheroo.h>
 
@@ -259,3 +261,37 @@ int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len)
 {
        return nouveau_rom_call(nouveau_dsm_priv.rom_handle, bios, offset, len);
 }
+
+int
+nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector)
+{
+       struct nouveau_connector *nv_connector = nouveau_connector(connector);
+       struct acpi_device *acpidev;
+       acpi_handle handle;
+       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 -EINVAL;
+       }
+
+       handle = DEVICE_ACPI_HANDLE(&dev->pdev->dev);
+       if (!handle)
+               return -ENODEV;
+
+       ret = acpi_bus_get_device(handle, &acpidev);
+       if (ret)
+               return -ENODEV;
+
+       ret = acpi_video_get_edid(acpidev, type, -1, &edid);
+       if (ret < 0)
+               return ret;
+
+       nv_connector->edid = edid;
+       return 0;
+}
index 2297bbc88c6f6c3b0cd0066becaaaf15d19d2323..256c6ed7d9e0f51890c70a077731a09315e572b1 100644 (file)
@@ -5622,7 +5622,9 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb,
                        if (conf & 0x4 || conf & 0x8)
                                entry->lvdsconf.use_power_scripts = true;
                } else {
-                       mask = ~0x5;
+                       mask = ~0x7;
+                       if (conf & 0x2)
+                               entry->lvdsconf.use_acpi_for_edid = true;
                        if (conf & 0x4)
                                entry->lvdsconf.use_power_scripts = true;
                }
index bd33a54f7deb75643e743a02623c687bf820f7cd..cc52aec336912eb177d19eae78463b2aaeb9cc18 100644 (file)
@@ -118,6 +118,7 @@ struct dcb_entry {
                struct {
                        struct sor_conf sor;
                        bool use_straps_for_mode;
+                       bool use_acpi_for_edid;
                        bool use_power_scripts;
                } lvdsconf;
                struct {
index c2fb15311b96b8c900ba81c0c2b478185583184c..50704287a8c20b74f80529b2f1426b8972c7d3fe 100644 (file)
@@ -327,12 +327,29 @@ nouveau_connector_detect_lvds(struct drm_connector *connector)
        if (!nv_encoder)
                return connector_status_disconnected;
 
+       /* Try retrieving EDID via DDC */
        if (!dev_priv->vbios.fp_no_ddc) {
                status = nouveau_connector_detect(connector);
                if (status == connector_status_connected)
                        goto out;
        }
 
+       /* On some laptops (Sony, i'm looking at you) there appears to
+        * be no direct way of accessing the panel's EDID.  The only
+        * option available to us appears to be to ask ACPI for help..
+        *
+        * It's important this check's before trying straps, one of the
+        * said manufacturer's laptops are configured in such a way
+        * the nouveau decides an entry in the VBIOS FP mode table is
+        * valid - it's not (rh#613284)
+        */
+       if (nv_encoder->dcb->lvdsconf.use_acpi_for_edid) {
+               if (!nouveau_acpi_edid(dev, connector)) {
+                       status = connector_status_connected;
+                       goto out;
+               }
+       }
+
        /* If no EDID found above, and the VBIOS indicates a hardcoded
         * modeline is avalilable for the panel, set it as the panel's
         * native mode and exit.
index 16856e0354f8bfc3f4f751d1d2b139f8a8ed73ea..20ca5b82ad6f6a4158245348b07703686362b873 100644 (file)
@@ -827,11 +827,13 @@ void nouveau_register_dsm_handler(void);
 void nouveau_unregister_dsm_handler(void);
 int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len);
 bool nouveau_acpi_rom_supported(struct pci_dev *pdev);
+int nouveau_acpi_edid(struct drm_device *, struct drm_connector *);
 #else
 static inline void nouveau_register_dsm_handler(void) {}
 static inline void nouveau_unregister_dsm_handler(void) {}
 static inline bool nouveau_acpi_rom_supported(struct pci_dev *pdev) { return false; }
 static inline int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len) { return -EINVAL; }
+static inline int nouveau_acpi_edid(struct drm_device *, struct drm_connector *) { return -EINVAL; }
 #endif
 
 /* nouveau_backlight.c */