Skip to contentSkip to conversation

Provide workarounds to use DRM/KMS with broken graphics hardware

PATCH Superseded Carsten Emde 5 participants 12 messages Mar 10, 2012 – Apr 25, 2012

Subsystems (4)

v1 of 6 Superseded Latest is v6 →

lore
Share & tools
lore
#0 lore raw

cover Provide workarounds to use DRM/KMS with broken graphics hardware

In the good old days when graphics parameters were configured explicitly in a file called xorg.conf, even broken hardware could be managed.

Today, with the advent of Kernel Mode Setting, a graphics board is either correctly working because all components follow the standards - or the computer is unusable, because the screen remains dark after booting or displays the wrong area. Cases when this happens are:
- The BIOS assumes that an LVDS is always connected, even if it isn't.
- The graphics board does not recognize the monitor.
- The graphics board is unable to detect any EDID data.
- The graphics board incorrectly forwards EDID data to the driver.
- The monitor sends bogus EDID data.
- A KVM sends its own EDID data instead of querying the connected monitor.
- The brightness setting of the panel backlight does not work.
- Any combination of the above.
Adding the kernel parameter "nomodeset" helps in most cases, but causes restrictions later on.

The three patches of this series add module parameters to the drm_kms_helper module that
1. allow to load an EDID data set via the firmware interface,
2. provide a module parameter to selectively enable or disable a graphics port,
3. provide a module parameter to select inverted brightness.

EDID data sets along with source files are provided for commonly used screen resolutions (1024x768, 1280x1024, 1680x1050, 1920x1080).

Please note that these patches neither fix a kernel bug nor provide any extra functionality. They simply work around broken hardware that otherwise would be either unusable or usable in a very restricted way. The patches do not modify the current functionality of the related components in any way, unless the kernel is configured accordingly and/or the newly provided module parameters are set.

-Carsten.

#1 lore raw

PATCH 1/3 drivers-gpu-drm-allow-to-load-edid-firmware.patch

Broken monitors and/or broken graphic boards may send erroneous or no EDID data. This also applies to broken KVM devices that are unable to correctly forward the EDID data of the connected monitor but invent their own fantasy data.

This patch allows to load an EDID data set via the firmware interface. It contains data sets of frequently used screen resolutions (1024x768, 1280x1024, 1680x1050 and 1920x1080). The requested EDID data are specified as a module parameter of the drm_kms_helper module, e.g. options drm_kms_helper edid_firmware=edid/1280x1024.bin or as kernel command line parameter.

In addition, an EDID data set can be specified on-the-fly via the /sys/module interface, e.g.
echo edid/myedid.bin >/sys/module/drm_kms_helper/parameters/edid_firmware The new screen mode is considered when the related kernel function is called for the first time after the change. Such calls are made when the X server is started or when the display settings dialog is opened in an already running X server.

EDID data source files are provided for documentation purpose and as a template to create EDID data sets for other screen resolutions. Note that source compilation is not part of the build process, but needs to be done manually, e.g.

#!/bin/bash
cd firmware/edid
for i in [1-9]*.S
do
base=echo $i | sed "s/\.S$//"
cc -c $base.S
objcopy -O ihex $base.o $base.bin.ihex
dos2unix $base.bin.ihex 2>/dev/null
done

The EDID data sets are based on standard timings that may not apply to a particular monitor and even crash it. Ideally, EDID data of the connected monitor should be used. They may be obtained through the drm/cardX/cardX-<connector>/edid entry in the /sys/devices PCI directory of a correctly working graphics adapter.

---
 Documentation/kernel-parameters.txt |    6 
 drivers/gpu/drm/Kconfig             |   10 +
 drivers/gpu/drm/Makefile            |    3 
 drivers/gpu/drm/drm_crtc_helper.c   |    8 -
 drivers/gpu/drm/drm_edid.c          |    3 
 drivers/gpu/drm/drm_edid_load.c     |  147 ++++++++++++++++++++
 firmware/Makefile                   |    7 
 firmware/edid/1024x768.S            |   44 ++++++
 firmware/edid/1024x768.bin.ihex     |    9 +
 firmware/edid/1280x1024.S           |   44 ++++++
 firmware/edid/1280x1024.bin.ihex    |    9 +
 firmware/edid/1680x1050.S           |   44 ++++++
 firmware/edid/1680x1050.bin.ihex    |    9 +
 firmware/edid/1920x1080.S           |   44 ++++++
 firmware/edid/1920x1080.bin.ihex    |    9 +
 firmware/edid/edid.S                |  261 ++++++++++++++++++++++++++++++++++++
 include/drm/drm_crtc.h              |    1 
 include/drm/drm_edid.h              |    1 
 18 files changed, 657 insertions(+), 2 deletions(-)
Documentation/kernel-parameters.txt +6 -0
Index: linux-3.3-rc6/Documentation/kernel-parameters.txt
===================================================================
@@ -713,6 +713,12 @@ bytes respectively. Such letter suffixes
 			The filter can be disabled or changed to another
 			driver later using sysfs.
 
+	drm_kms_helper.edid_firmware=<file>
+			Broken monitors, graphic adapters and KVMs may
+			send no or broken EDID data sets. This parameter
+			allows to specify an EDID data set in the
+			/lib/firmware directory that is used instead.
+
 	dscc4.setup=	[NET]
 
 	earlycon=	[KNL] Output early console device and options.
drivers/gpu/drm/Kconfig +10 -0
Index: linux-3.3-rc6/drivers/gpu/drm/Kconfig
===================================================================
@@ -27,6 +27,16 @@ config DRM_KMS_HELPER
 	help
 	  FB and CRTC helpers for KMS drivers.
 
+config DRM_LOAD_EDID_FIRMWARE
+	bool "Allow to load an EDID data set instead of probing it"
+	depends on DRM_KMS_HELPER
+	help
+	  Say Y here, if you want to specify an EDID data blob to be
+	  loaded from the /lib/firmware directory. This may be necessary,
+	  if the graphics adapter or monitor are unable to provide
+	  appropriate EDID data. Since this feature is provided as a
+	  workaround for broken hardware, the default case is N.
+
 config DRM_TTM
 	tristate
 	depends on DRM
drivers/gpu/drm/Makefile +3 -0
Index: linux-3.3-rc6/drivers/gpu/drm/Makefile
===================================================================
@@ -17,6 +17,9 @@ drm-y       :=	drm_auth.o drm_buffer.o d
 drm-$(CONFIG_COMPAT) += drm_ioc32.o
 
 drm_kms_helper-y := drm_fb_helper.o drm_crtc_helper.o drm_dp_i2c_helper.o
+ifdef CONFIG_DRM_LOAD_EDID_FIRMWARE
+drm_kms_helper-y += drm_edid_load.o
+endif
 
 obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o
 
drivers/gpu/drm/drm_crtc_helper.c +7 -1
Index: linux-3.3-rc6/drivers/gpu/drm/drm_crtc_helper.c
===================================================================
@@ -37,6 +37,7 @@
 #include "drm_fourcc.h"
 #include "drm_crtc_helper.h"
 #include "drm_fb_helper.h"
+#include "drm_edid.h"
 
 static bool drm_kms_helper_poll = true;
 module_param_named(poll, drm_kms_helper_poll, bool, 0600);
@@ -118,7 +119,12 @@ int drm_helper_probe_single_connector_mo
 		goto prune;
 	}
 
-	count = (*connector_funcs->get_modes)(connector);
+#ifdef CONFIG_DRM_LOAD_EDID_FIRMWARE
+	count = load_edid_firmware(connector);
+	if (count == 0)
+#endif
+		count = (*connector_funcs->get_modes)(connector);
+
 	if (count == 0 && connector->status == connector_status_connected)
 		count = drm_add_modes_noedid(connector, 1024, 768);
 	if (count == 0)
drivers/gpu/drm/drm_edid.c +2 -1
Index: linux-3.3-rc6/drivers/gpu/drm/drm_edid.c
===================================================================
@@ -149,7 +149,7 @@ EXPORT_SYMBOL(drm_edid_header_is_valid);
  * Sanity check the EDID block (base or extension).  Return 0 if the block
  * doesn't check out, or 1 if it's valid.
  */
-static bool
+bool
 drm_edid_block_valid(u8 *raw_edid)
 {
 	int i;
@@ -203,6 +203,7 @@ bad:
 	}
 	return 0;
 }
+EXPORT_SYMBOL(drm_edid_block_valid);
 
 /**
  * drm_edid_is_valid - sanity check EDID data
drivers/gpu/drm/drm_edid_load.c new file +147 -0

147 lines not on this page · raw message

firmware/Makefile +7 -0
Index: linux-3.3-rc6/firmware/Makefile
===================================================================
@@ -141,6 +141,13 @@ fw-shipped-$(CONFIG_USB_VICAM) += vicam/
 fw-shipped-$(CONFIG_VIDEO_CPIA2) += cpia2/stv0672_vp4.bin
 fw-shipped-$(CONFIG_YAM) += yam/1200.bin yam/9600.bin
 
+ifdef CONFIG_DRM_LOAD_EDID_FIRMWARE
+fw-shipped-$(CONFIG_DRM_KMS_HELPER) += edid/1024x768.bin
+fw-shipped-$(CONFIG_DRM_KMS_HELPER) += edid/1280x1024.bin
+fw-shipped-$(CONFIG_DRM_KMS_HELPER) += edid/1680x1050.bin
+fw-shipped-$(CONFIG_DRM_KMS_HELPER) += edid/1920x1080.bin
+endif
+
 fw-shipped-all := $(fw-shipped-y) $(fw-shipped-m) $(fw-shipped-)
 
 # Directories which we _might_ need to create, so we have a rule for them.
firmware/edid/1024x768.S new file +44 -0

44 lines not on this page · raw message

firmware/edid/1024x768.bin.ihex new file +9 -0
Index: linux-3.3-rc6/firmware/edid/1024x768.bin.ihex
===================================================================
@@ -0,0 +1,9 @@
+:1000000000FFFFFFFFFFFF0031D8000000000000ED
+:10001000051601036D231A78EA5EC0A4594A982593
+:10002000205054000800614001010101010101015B
+:1000300001010101010164190040410026300890CE
+:100040003600630A11000018000000FF004C696EC2
+:1000500075782023300A20202020000000FD003B7E
+:100060003D2F3107000A202020202020000000FC26
+:10007000004C696E7578205847410A2020200055B1
+:00000001FF
firmware/edid/1280x1024.S new file +44 -0

44 lines not on this page · raw message

firmware/edid/1280x1024.bin.ihex new file +9 -0
Index: linux-3.3-rc6/firmware/edid/1280x1024.bin.ihex
===================================================================
@@ -0,0 +1,9 @@
+:1000000000FFFFFFFFFFFF0031D8000000000000ED
+:10001000051601036D2C2378EA5EC0A4594A982581
+:100020002050540000008180010101010101010103
+:10003000010101010101302A009851002A4030706D
+:100040001300BC631100001E000000FF004C696E2D
+:1000500075782023300A20202020000000FD003B7E
+:100060003D3E400B000A202020202020000000FC04
+:10007000004C696E757820535847410A202000A033
+:00000001FF
firmware/edid/1680x1050.S new file +44 -0

44 lines not on this page · raw message

firmware/edid/1680x1050.bin.ihex new file +9 -0
Index: linux-3.3-rc6/firmware/edid/1680x1050.bin.ihex
===================================================================
@@ -0,0 +1,9 @@
+:1000000000FFFFFFFFFFFF0031D8000000000000ED
+:10001000051601036D2B1B78EA5EC0A4594A98258A
+:10002000205054000000B300010101010101010151
+:1000300001010101010121399030621A274068B0A5
+:100040003600B5111100001E000000FF004C696E63
+:1000500075782023300A20202020000000FD003B7E
+:100060003D40420F000A202020202020000000FCFC
+:10007000004C696E75782057535847410A20002676
+:00000001FF
firmware/edid/1920x1080.S new file +44 -0

44 lines not on this page · raw message

firmware/edid/1920x1080.bin.ihex new file +9 -0
Index: linux-3.3-rc6/firmware/edid/1920x1080.bin.ihex
===================================================================
@@ -0,0 +1,9 @@
+:1000000000FFFFFFFFFFFF0031D8000000000000ED
+:10001000051601036D321C78EA5EC0A4594A982582
+:10002000205054000000D1C0010101010101010173
+:10003000010101010101023A801871382D40582C4C
+:100040004500F4191100001E000000FF004C696E0D
+:1000500075782023300A20202020000000FD003B7E
+:100060003D42440F000A202020202020000000FCF8
+:10007000004C696E7578204648440A20202000050F
+:00000001FF
firmware/edid/edid.S new file +261 -0

261 lines not on this page · raw message

include/drm/drm_crtc.h +1 -0
Index: linux-3.3-rc6/include/drm/drm_crtc.h
===================================================================
@@ -995,6 +995,7 @@ extern int drm_add_modes_noedid(struct d
 				int hdisplay, int vdisplay);
 
 extern int drm_edid_header_is_valid(const u8 *raw_edid);
+extern bool drm_edid_block_valid(u8 *raw_edid);
 extern bool drm_edid_is_valid(struct edid *edid);
 struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev,
 					   int hsize, int vsize, int fresh);
include/drm/drm_edid.h +1 -0
Index: linux-3.3-rc6/include/drm/drm_edid.h
===================================================================
@@ -238,5 +238,6 @@ int drm_av_sync_delay(struct drm_connect
 		      struct drm_display_mode *mode);
 struct drm_connector *drm_select_eld(struct drm_encoder *encoder,
 				     struct drm_display_mode *mode);
+int load_edid_firmware(struct drm_connector *connector);
 
 #endif /* __DRM_EDID_H__ */
#2 lore raw

Re: [PATCH 1/3] drivers-gpu-drm-allow-to-load-edid-firmware.patch

Quoted 6 lines

This patch allows to load an EDID data set via the firmware interface. It contains data sets of frequently used screen resolutions (1024x768, 1280x1024, 1680x1050 and 1920x1080). The requested EDID data are specified as a module parameter of the drm_kms_helper module, e.g. options drm_kms_helper edid_firmware=edid/1280x1024.bin or as kernel command line parameter.

What if the DRM layer and driver are compiled in. They'll come up as console before the file system so the firmware request will hang ?

Given the EDID is tiny and is data not code wouldn't it be simpler (and smaller) if this option compiled in a few generic EDIDs to use ?

Alan

#3 lore raw

Re: [PATCH 1/3] drivers-gpu-drm-allow-to-load-edid-firmware.patch

Quoted 9 lines · Alan Cox
Quoted 6 lines

This patch allows to load an EDID data set via the firmware interface. It contains data sets of frequently used screen resolutions (1024x768, 1280x1024, 1680x1050 and 1920x1080). The requested EDID data are specified as a module parameter of the drm_kms_helper module, e.g. options drm_kms_helper edid_firmware=edid/1280x1024.bin or as kernel command line parameter.

What if the DRM layer and driver are compiled in. They'll come up as console before the file system so the firmware request will hang ?

Admittedly I did not try to compile the DRM layer and driver into the kernel. However, I created an error condition by specifying a non-existing EDID file. In this case, the function returns with error, the mode count remains 0, and the system continues to run as if the edid_firmware= parameter had not been specified.

Now that you were asking, I created a static kernel and did some more tests, but I couldn't find any problem. Everything worked as expected. I believe that the early console still runs in BIOS VGA mode, i.e. either using the BIOS default mode or the one that was specified using the vga= parameter, if any. DRM apparently comes into play at a later stage only, but I will do some more testing.

Quoted 2 lines

Given the EDID is tiny and is data not code wouldn't it be simpler (and smaller) if this option compiled in a few generic EDIDs to use ?

Yes, this certainly would be possible. However, we would loose the flexibility to specify an individual EDID data set without recompiling the kernel. As an alternative, we could compile the four standard EDIDs into the DRM helper instead of providing them as a file, but still leave the option to load an individual EDID via the firmware interface. This would make the patch much smaller and avoid spamming the firmware directory. How about that?

Thanks,
-Carsten.

#4 lore raw

Re: [PATCH 1/3] drivers-gpu-drm-allow-to-load-edid-firmware.patch

Quoted 9 lines · Carsten Emde
Quoted 7 lines · Alan Cox
Quoted 6 lines

This patch allows to load an EDID data set via the firmware interface. It contains data sets of frequently used screen resolutions (1024x768, 1280x1024, 1680x1050 and 1920x1080). The requested EDID data are specified as a module parameter of the drm_kms_helper module, e.g. options drm_kms_helper edid_firmware=edid/1280x1024.bin or as kernel command line parameter.

Quoted 8 lines
Quoted 2 lines

What if the DRM layer and driver are compiled in. They'll come up as console before the file system so the firmware request will hang ?

Admittedly I did not try to compile the DRM layer and driver into the kernel. However, I created an error condition by specifying a non-existing EDID file. In this case, the function returns with error, the mode count remains 0, and the system continues to run as if the edid_firmware= parameter had not been specified.

Unfortunately, as of at least last month, my system hangs when I try to use your feature (just as described by Alan Cox); the log shows that during the boot process, there is a one-minute hang:

  [    0.175207] [drm] radeon: power management initialized
  [   60.896507] [drm:edid_load] *ERROR* Requesting EDID firmware "edid/1920x1200.bin" failed (err=-2)

Is there any way to make your feature smarter about its timing with relation to file system accessibility?

Sincerely,
Michael Witten

#5 lore raw

Re: [PATCH 1/3] drivers-gpu-drm-allow-to-load-edid-firmware.patch

Michael,

Quoted 19 lines · Carsten Emde
Quoted 7 lines · Alan Cox
Quoted 6 lines
Quoted 6 lines

This patch allows to load an EDID data set via the firmware interface. It contains data sets of frequently used screen resolutions (1024x768, 1280x1024, 1680x1050 and 1920x1080). The requested EDID data are specified as a module parameter of the drm_kms_helper module, e.g. options drm_kms_helper edid_firmware=edid/1280x1024.bin or as kernel command line parameter.

Quoted 7 lines
Quoted 2 lines

What if the DRM layer and driver are compiled in. They'll come up as console before the file system so the firmware request will hang ?

Admittedly I did not try to compile the DRM layer and driver into the kernel. However, I created an error condition by specifying a non-existing EDID file. In this case, the function returns with error, the mode count remains 0, and the system continues to run as if the edid_firmware= parameter had not been specified.

Unfortunately, as of at least last month, my system hangs when I try to use your feature (just as described by Alan Cox); the log shows that during the boot process, there is a one-minute hang:

Quoted 5 lines
   [    0.175207] [drm] radeon: power management initialized
   [   60.896507] [drm:edid_load] *ERROR* Requesting EDID firmware "edid/1920x1200.bin" failed (err=-2)

Is there any way to make your feature smarter about its timing with relation to file system accessibility?

Sure.

Please copy your EDID data file to the directory "firmware/edid" of the kernel source tree, configure

CONFIG_EXTRA_FIRMWARE="edid/1920x1200.bin"
CONFIG_EXTRA_FIRMWARE_DIR="firmware"

and rebuild/reboot your kernel.

Does it work then?

-Carsten.

#6 lore raw

Re: [PATCH 1/3] drivers-gpu-drm-allow-to-load-edid-firmware.patch

In reply to Alan Cox

Alan,

Quoted 9 lines
Quoted 6 lines

This patch allows to load an EDID data set via the firmware interface. It contains data sets of frequently used screen resolutions (1024x768, 1280x1024, 1680x1050 and 1920x1080). The requested EDID data are specified as a module parameter of the drm_kms_helper module, e.g. options drm_kms_helper edid_firmware=edid/1280x1024.bin or as kernel command line parameter.

[..]
Given the EDID is tiny and is data not code wouldn't it be simpler (and smaller) if this option compiled in a few generic EDIDs to use ?

I liked the idea so much that I gave it a try. The patch looks much better now, but has the same functionality as before. As the only disadvantage, the patch no longer contains the template to produce a particular EDID. But this code may be made available elsewhere - no need to have it in the kernel.

Hope you like it.

Thanks,
-Carsten.

Broken monitors and/or broken graphic boards may send erroneous or no EDID data. This also applies to broken KVM devices that are unable to correctly forward the EDID data of the connected monitor but invent their own fantasy data.

This patch allows to specify an EDID data set to be used instead of probing the monitor for it. It contains built-in data sets of frequently used screen resolutions. In addition, a particular EDID data set may be provided in the /lib/firmware directory and loaded via the firmware interface. The name is passed to the kernel as module parameter of the drm_kms_helper module either when loaded
options drm_kms_helper edid_firmware=edid/1280x1024.bin
or as kernel commandline parameter
drm_kms_helper.edid_firmware=edid/1280x1024.bin

The built-in data sets are
Resolution Name
--------------------------------

1024x768      edid/1024x768.bin
1280x1024     edid/1280x1024.bin
1680x1050     edid/1680x1050.bin
1920x1080     edid/1920x1080.bin

They are ignored, if a file with the same name is available in the /lib/firmware directory.

The built-in EDID data sets are based on standard timings that may not apply to a particular monitor and even crash it. Ideally, EDID data of the connected monitor should be used. They may be obtained through the drm/cardX/cardX-<connector>/edid entry in the /sys/devices PCI directory of a correctly working graphics adapter.

It is also possible to specify the name of an EDID data set on-the-fly via the /sys/module interface, e.g.
echo edid/myedid.bin >/sys/module/drm_kms_helper/parameters/edid_firmware The new screen mode is considered when the related kernel function is called for the first time after the change. Such calls are made when the X server is started or when the display settings dialog is opened in an already running X server.

---
 Documentation/kernel-parameters.txt |   10 +
 drivers/gpu/drm/Kconfig             |   11 +
 drivers/gpu/drm/Makefile            |    3 
 drivers/gpu/drm/drm_crtc_helper.c   |    8 +
 drivers/gpu/drm/drm_edid.c          |    4 
 drivers/gpu/drm/drm_edid_load.c     |  241 ++++++++++++++++++++++++++++++++++++
 include/drm/drm_crtc.h              |    1 
 include/drm/drm_edid.h              |    1 
 8 files changed, 276 insertions(+), 3 deletions(-)
Documentation/kernel-parameters.txt +10 -0
Index: linux-3.3-rc6/Documentation/kernel-parameters.txt
===================================================================
@@ -713,6 +713,16 @@ bytes respectively. Such letter suffixes
 			The filter can be disabled or changed to another
 			driver later using sysfs.
 
+	drm_kms_helper.edid_firmware=<file>
+			Broken monitors, graphic adapters and KVMs may
+			send no or broken EDID data sets. This parameter
+			allows to specify an EDID data set in the
+			/lib/firmware directory that is used instead.
+			Generic built-in EDID data sets are used, if one of
+			edid/1024x768.bin, edid/1280x1024.bin,
+			edid/1680x1050.bin, or edid/1920x1080.bin is given
+			and no file with the same name exists.
+
 	dscc4.setup=	[NET]
 
 	earlycon=	[KNL] Output early console device and options.
drivers/gpu/drm/Kconfig +11 -0
Index: linux-3.3-rc6/drivers/gpu/drm/Kconfig
===================================================================
@@ -27,6 +27,17 @@ config DRM_KMS_HELPER
 	help
 	  FB and CRTC helpers for KMS drivers.
 
+config DRM_LOAD_EDID_FIRMWARE
+	bool "Allow to specify an EDID data set instead of probing for it"
+	depends on DRM_KMS_HELPER
+	help
+	  Say Y here, if you want to use EDID data to be loaded from the
+	  /lib/firmware directory or one of the provided built-in
+	  data sets. This may be necessary, if the graphics adapter or
+	  monitor are unable to provide appropriate EDID data. Since this
+	  feature is provided as a workaround for broken hardware, the
+	  default case is N.
+
 config DRM_TTM
 	tristate
 	depends on DRM
drivers/gpu/drm/Makefile +3 -0
Index: linux-3.3-rc6/drivers/gpu/drm/Makefile
===================================================================
@@ -17,6 +17,9 @@ drm-y       :=	drm_auth.o drm_buffer.o d
 drm-$(CONFIG_COMPAT) += drm_ioc32.o
 
 drm_kms_helper-y := drm_fb_helper.o drm_crtc_helper.o drm_dp_i2c_helper.o
+ifdef CONFIG_DRM_LOAD_EDID_FIRMWARE
+drm_kms_helper-y += drm_edid_load.o
+endif
 
 obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o
 
drivers/gpu/drm/drm_crtc_helper.c +7 -1
Index: linux-3.3-rc6/drivers/gpu/drm/drm_crtc_helper.c
===================================================================
@@ -37,6 +37,7 @@
 #include "drm_fourcc.h"
 #include "drm_crtc_helper.h"
 #include "drm_fb_helper.h"
+#include "drm_edid.h"
 
 static bool drm_kms_helper_poll = true;
 module_param_named(poll, drm_kms_helper_poll, bool, 0600);
@@ -118,7 +119,12 @@ int drm_helper_probe_single_connector_mo
 		goto prune;
 	}
 
-	count = (*connector_funcs->get_modes)(connector);
+#ifdef CONFIG_DRM_LOAD_EDID_FIRMWARE
+	count = load_edid_firmware(connector);
+	if (count == 0)
+#endif
+		count = (*connector_funcs->get_modes)(connector);
+
 	if (count == 0 && connector->status == connector_status_connected)
 		count = drm_add_modes_noedid(connector, 1024, 768);
 	if (count == 0)
drivers/gpu/drm/drm_edid.c +2 -2
Index: linux-3.3-rc6/drivers/gpu/drm/drm_edid.c
===================================================================
@@ -149,8 +149,7 @@ EXPORT_SYMBOL(drm_edid_header_is_valid);
  * Sanity check the EDID block (base or extension).  Return 0 if the block
  * doesn't check out, or 1 if it's valid.
  */
-static bool
-drm_edid_block_valid(u8 *raw_edid)
+bool drm_edid_block_valid(u8 *raw_edid)
 {
 	int i;
 	u8 csum = 0;
@@ -203,6 +202,7 @@ bad:
 	}
 	return 0;
 }
+EXPORT_SYMBOL(drm_edid_block_valid);
 
 /**
  * drm_edid_is_valid - sanity check EDID data
drivers/gpu/drm/drm_edid_load.c new file +241 -0

241 lines not on this page · raw message

include/drm/drm_crtc.h +1 -0
Index: linux-3.3-rc6/include/drm/drm_crtc.h
===================================================================
@@ -995,6 +995,7 @@ extern int drm_add_modes_noedid(struct d
 				int hdisplay, int vdisplay);
 
 extern int drm_edid_header_is_valid(const u8 *raw_edid);
+extern bool drm_edid_block_valid(u8 *raw_edid);
 extern bool drm_edid_is_valid(struct edid *edid);
 struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev,
 					   int hsize, int vsize, int fresh);
include/drm/drm_edid.h +1 -0
Index: linux-3.3-rc6/include/drm/drm_edid.h
===================================================================
@@ -238,5 +238,6 @@ int drm_av_sync_delay(struct drm_connect
 		      struct drm_display_mode *mode);
 struct drm_connector *drm_select_eld(struct drm_encoder *encoder,
 				     struct drm_display_mode *mode);
+int load_edid_firmware(struct drm_connector *connector);
 
 #endif /* __DRM_EDID_H__ */
#7 lore raw

Re: [PATCH 1/3] drivers-gpu-drm-allow-to-load-edid-firmware.patch

In reply to Carsten Emde
Quoted 9 lines · Carsten Emde

EDID data source files are provided for documentation purpose and as a template to create EDID data sets for other screen resolutions. Note that source compilation is not part of the build process, but needs to be done manually, e.g.

#!/bin/bash
cd firmware/edid
for i in [1-9]*.S
do

Would it be possible to include a version of this patch's Changelog as a file under Documentation/fixing-broken-edid.txt or something, so people don't have to go and find the git commit log to know things like "you need to run dos2unix"? Be a shame to have a changelog that does such a good job of documenting what you need to do, and have it lost in git...

#8 lore raw

PATCH 2/3 drivers-gpu-drm-add-disable-enable-connector.patch

In reply to Carsten Emde

Some recent integrated graphics chipset, notably Intel's "Pineview", also provide on-chip LVDS support. As an extra service, the LVDS interface supplies EDID data - irrespective of whether an LVDS panel is connected or not. The drm_mode_getresources() function, therefore, causes Xorg to always include the LVDS panel into the display and initialize a separate screen for it. e.g. (II) intel(0): Output LVDS1 connected
(II) intel(0): Output VGA1 connected
(II) intel(0): Using spanning desktop for initial modes
(II) intel(0): Output LVDS1 using initial mode 1024x768 +0+0
(II) intel(0): Output VGA1 using initial mode 1280x1024 +1024+0 which is not what you want, if the only connected screen is a VGA monitor. One would assume that the BIOS settings of such systems would allow to separately enable or disable LVDS support; unfortunately, systems have been found in the wild that do not provide this feature.

This patch introduces the module parameter "disable_connector" of the drm_kms_helper module that may specify the name of the connector to be considered disabled, e.g.

  # cat /etc/modprobe.d/drm_kms_helper.conf 
  options drm_kms_helper disable_connector=LVDS-1
which lets Xorg correctly initialize the screen, e.g.
(II) intel(0): Output LVDS1 disconnected 
(II) intel(0): Output VGA1 connected
(II) intel(0): Using exact sizes for initial modes 
(II) intel(0): Output VGA1 using initial mode 1280x1024 +0+0

A second scenario applies to broken graphics adapters that are unable to correctly detect connected monitors and end up in a situation where
the default screen resolution of 1024x768 is selected that may not be appropriate, e.g.
(II) intel(0): Output VGA1 disconnected
(II) intel(0): Output HDMI1 disconnected
(II) intel(0): Output DP1 disconnected
(II) intel(0): Output HDMI2 disconnected
(II) intel(0): Output DP2 disconnected
(WW) intel(0): No outputs definitely connected, trying again...
(II) intel(0): Output VGA1 disconnected
(II) intel(0): Output HDMI1 disconnected
(II) intel(0): Output DP1 disconnected
(II) intel(0): Output HDMI2 disconnected
(II) intel(0): Output DP2 disconnected
(WW) intel(0): Unable to find connected outputs - setting 1024x768 initial framebuffer

This patch introduces a second module parameter "enable_connector" of the drm_kms_helper module that may specify the name of the connector to be taken as connected, e.g.

  # cat /etc/modprobe.d/drm_kms_helper.conf 
  options drm_kms_helper enable_connector=VGA-1
which lets Xorg correctly initialize the screen, e.g.
(II) intel(0): Output VGA1 connected
(II) intel(0): Output HDMI1 disconnected
(II) intel(0): Output DP1 disconnected
(II) intel(0): Output HDMI2 disconnected
(II) intel(0): Output DP2 disconnected
(II) intel(0): Using exact sizes for initial modes
(II) intel(0): Output VGA1 using initial mode 1280x1024 +0+0
---
 Documentation/kernel-parameters.txt |   13 ++++++++++++
 drivers/gpu/drm/drm_crtc.c          |   15 ++++++++++---
 drivers/gpu/drm/drm_crtc_helper.c   |    3 ++
 drivers/gpu/drm/drm_fb_helper.c     |   39 ++++++++++++++++++++++++++++++++++--
 include/drm/drm_crtc.h              |    3 +-
 5 files changed, 66 insertions(+), 7 deletions(-)
Documentation/kernel-parameters.txt +13 -0
Index: linux-3.3-rc6/Documentation/kernel-parameters.txt
===================================================================
@@ -713,12 +713,25 @@ bytes respectively. Such letter suffixes
 			The filter can be disabled or changed to another
 			driver later using sysfs.
 
+	drm_kms_helper.disable_connector=<connector>
+			The BIOS may always report that a panel is connected,
+			irrespective of whether it really is or not which may
+			lead to a bogus screen layout. This parameter is used
+			to explicitly disable a particular connector, e.g.
+			drm_kms_helper disable_connector=LVDS-1
+
 	drm_kms_helper.edid_firmware=<file>
 			Broken monitors, graphic adapters and KVMs may
 			send no or broken EDID data sets. This parameter
 			allows to specify an EDID data set in the
 			/lib/firmware directory that is used instead.
 
+	drm_kms_helper.enable_connector=<connector>
+			Broken hardware may be unable to correctly discover a
+			monitor. This parameter is used to let the drm subsystem
+			assume that a monitor is present at the specified
+			connector, e.g. drm_kms_helper enable_connector=VGA-1
+
 	dscc4.setup=	[NET]
 
 	earlycon=	[KNL] Output early console device and options.
drivers/gpu/drm/drm_crtc.c +11 -4
Index: linux-3.3-rc6/drivers/gpu/drm/drm_crtc.c
===================================================================
@@ -204,6 +204,8 @@ char *drm_get_connector_status_name(enum
 		return "connected";
 	else if (status == connector_status_disconnected)
 		return "disconnected";
+	else if (status == connector_status_disabled)
+		return "disabled";
 	else
 		return "unknown";
 }
@@ -1172,8 +1174,11 @@ int drm_mode_getresources(struct drm_dev
 		list_for_each(lh, &dev->mode_config.crtc_list)
 			crtc_count++;
 
-		list_for_each(lh, &dev->mode_config.connector_list)
-			connector_count++;
+		list_for_each_entry(connector,
+		    &dev->mode_config.connector_list, head)
+			if (connector->status != connector_status_disabled)
+				connector_count++;
+
 
 		list_for_each(lh, &dev->mode_config.encoder_list)
 			encoder_count++;
@@ -1268,8 +1273,10 @@ int drm_mode_getresources(struct drm_dev
 		connector_id = (uint32_t __user *)(unsigned long)card_res->connector_id_ptr;
 		if (file_priv->master->minor->type == DRM_MINOR_CONTROL) {
 			list_for_each_entry(connector,
-					    &dev->mode_config.connector_list,
-					    head) {
+			    &dev->mode_config.connector_list, head) {
+				if (connector->status ==
+				    connector_status_disabled)
+					continue;
 				DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
 					connector->base.id,
 					drm_get_connector_name(connector));
drivers/gpu/drm/drm_crtc_helper.c +3 -0
Index: linux-3.3-rc6/drivers/gpu/drm/drm_crtc_helper.c
===================================================================
@@ -119,6 +119,9 @@ int drm_helper_probe_single_connector_mo
 		goto prune;
 	}
 
+	if (connector->status == connector_status_disabled)
+		return 0;
+
 #ifdef CONFIG_DRM_LOAD_EDID_FIRMWARE
 	count = load_edid_firmware(connector);
 	if (count == 0)
drivers/gpu/drm/drm_fb_helper.c +37 -2
Index: linux-3.3-rc6/drivers/gpu/drm/drm_fb_helper.c
===================================================================
@@ -28,6 +28,7 @@
  *      Jesse Barnes <[email protected]>
  */
 #include <linux/kernel.h>
+#include <linux/module.h>
 #include <linux/sysrq.h>
 #include <linux/slab.h>
 #include <linux/fb.h>
@@ -1056,6 +1057,9 @@ static bool drm_connector_enabled(struct
 {
 	bool enable;
 
+	if (connector->status == connector_status_disabled)
+		return false;
+
 	if (strict) {
 		enable = connector->status == connector_status_connected;
 	} else {
@@ -1064,6 +1068,16 @@ static bool drm_connector_enabled(struct
 	return enable;
 }
 
+static char disable_connector[32];
+module_param_string(disable_connector, disable_connector,
+		    sizeof(disable_connector), 0644);
+MODULE_PARM_DESC(disable_connector,
+	"Do not use specified connector, even if monitor detected");
+static char enable_connector[32];
+module_param_string(enable_connector, enable_connector,
+		    sizeof(enable_connector), 0644);
+MODULE_PARM_DESC(enable_connector,
+	"Use specified connector, even if no monitor connected");
 static void drm_enable_connectors(struct drm_fb_helper *fb_helper,
 				  bool *enabled)
 {
@@ -1072,10 +1086,31 @@ static void drm_enable_connectors(struct
 	int i = 0;
 
 	for (i = 0; i < fb_helper->connector_count; i++) {
+		char *connector_name;
+
 		connector = fb_helper->connector_info[i]->connector;
+		connector_name = drm_get_connector_name(connector);
+
+		if (connector_name == NULL)
+			continue;
+		if (disable_connector[0] != '\0' &&
+		    !strcmp(connector_name, disable_connector)) {
+			connector->status = connector_status_disabled;
+			DRM_DEBUG_KMS("connector %d (\"%s\") disabled "
+			    "by module parameter\n", connector->base.id,
+			    connector_name);
+		}
+		if (enable_connector[0] != '\0' &&
+		    !strcmp(connector_name, enable_connector)) {
+			connector->status = connector_status_connected;
+			DRM_DEBUG_KMS("connector %d (\"%s\") enabled "
+			    "by module parameter\n", connector->base.id,
+			    connector_name);
+		}
 		enabled[i] = drm_connector_enabled(connector, true);
-		DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id,
-			  enabled[i] ? "yes" : "no");
+		DRM_DEBUG_KMS("connector %d (\"%s\") enabled? %s\n",
+		    connector->base.id, connector_name,
+		    enabled[i] ? "yes" : "no");
 		any_enabled |= enabled[i];
 	}
 
include/drm/drm_crtc.h +2 -1
Index: linux-3.3-rc6/include/drm/drm_crtc.h
===================================================================
@@ -172,7 +172,8 @@ struct drm_display_mode {
 enum drm_connector_status {
 	connector_status_connected = 1,
 	connector_status_disconnected = 2,
-	connector_status_unknown = 3,
+	connector_status_disabled = 3,
+	connector_status_unknown = 4,
 };
 
 enum subpixel_order {
maintainer
#9 lore raw

Re: [PATCH 2/3] drivers-gpu-drm-add-disable-enable-connector.patch

Quoted 6 lines · Carsten Emde

Some recent integrated graphics chipset, notably Intel's "Pineview", also provide on-chip LVDS support. As an extra service, the LVDS interface supplies EDID data - irrespective of whether an LVDS panel is connected or not. The drm_mode_getresources() function, therefore, causes Xorg to always include the LVDS panel into the display and initialize a separate screen for it. e.g. (II) intel(0): Output LVDS1 connected

Quoted 8 lines

(II) intel(0): Output VGA1 connected
(II) intel(0): Using spanning desktop for initial modes
(II) intel(0): Output LVDS1 using initial mode 1024x768 +0+0 (II) intel(0): Output VGA1 using initial mode 1280x1024 +1024+0 which is not what you want, if the only connected screen is a VGA monitor. One would assume that the BIOS settings of such systems would allow to separately enable or disable LVDS support; unfortunately, systems have been found in the wild that do not provide this feature.

So video=LVDS-1:d doesn't work for you?

Dave.

#10 lore raw

Re: [PATCH 2/3] drivers-gpu-drm-add-disable-enable-connector.patch

Quoted 16 lines · Dave Airlie
Quoted 6 lines · Carsten Emde

Some recent integrated graphics chipset, notably Intel's "Pineview", also provide on-chip LVDS support. As an extra service, the LVDS interface supplies EDID data - irrespective of whether an LVDS panel is connected or not. The drm_mode_getresources() function, therefore, causes Xorg to always include the LVDS panel into the display and initialize a separate screen for it. e.g. (II) intel(0): Output LVDS1 connected

Quoted 8 lines

(II) intel(0): Output VGA1 connected
(II) intel(0): Using spanning desktop for initial modes
(II) intel(0): Output LVDS1 using initial mode 1024x768 +0+0 (II) intel(0): Output VGA1 using initial mode 1280x1024 +1024+0 which is not what you want, if the only connected screen is a VGA monitor. One would assume that the BIOS settings of such systems would allow to separately enable or disable LVDS support; unfortunately, systems have been found in the wild that do not provide this feature.

Quoted 1 line

So video=LVDS-1:d doesn't work for you?

Oops, yes, you are totally right. By some reason, I overlooked this option. I tried two systems that need forced disabling and enabling with
video=LVDS-1:d video=VGA-1:e
which worked perfectly well.

So please skip this patch, sorry for the noise.

Thanks,
-Carsten.

#11 lore raw

PATCH 3/3 drivers-gpu-drm-i915-invert-backlight-brightness

In reply to Carsten Emde

Following the documentation of the Legacy Backlight Brightness (LBB) Register in the configuration space of some Intel PCI graphics adapters, setting the LBB register with the value 0x0 causes the backlight to be turned off, and 0xFF causes the backlight to be set to 100% intensity
(http://download.intel.com/embedded/processors/Whitepaper/324567.pdf).
At least one of our test systems, however, turns the backlight off at 0xFF and sets it to maximum intensity at 0. In consequence, the screen of this systems becomes dark at an early boot stage which makes it unusable. The same inversion applies to the BLC_PWM_CTL I915 register. This problem was introduced in kernel version 2.6.38 when the PCI device of this system was first supported by the i915 KMS module.

This patch adds a module parameter to invert the backlight brightness value before writing and after reading which makes the affected notebook usable again.

---
 Documentation/kernel-parameters.txt |    9 +++++++++
 drivers/gpu/drm/i915/intel_panel.c  |   14 ++++++++++++++
 2 files changed, 23 insertions(+)
Documentation/kernel-parameters.txt +9 -0
Index: linux-3.3-rc6/Documentation/kernel-parameters.txt
===================================================================
@@ -975,6 +975,15 @@ bytes respectively. Such letter suffixes
 
 	i810=		[HW,DRM]
 
+	i915.invert_brightness
+			[DRM] Invert the sense of the variable that is used to
+			set the brightness of the panel backlight. Normally a
+			value of 0 indicates backlight switched off, and the
+			maximum value sets the backlight to maximum brightness.
+			If this parameter is specified, a value of 0 sets the
+			backlight to maximum brightness, and the maximum value
+			switches the backlight off.
+
 	i8k.ignore_dmi	[HW] Continue probing hardware even if DMI data
 			indicates that the driver is running on unsupported
 			hardware.
drivers/gpu/drm/i915/intel_panel.c +14 -0
Index: linux-3.3-rc6/drivers/gpu/drm/i915/intel_panel.c
===================================================================
@@ -28,6 +28,7 @@
  *      Chris Wilson <[email protected]>
  */
 
+#include <linux/moduleparam.h>
 #include "intel_drv.h"
 
 #define PCI_LBPC 0xf4 /* legacy/combination backlight modes */
@@ -191,6 +192,10 @@ u32 intel_panel_get_max_backlight(struct
 	return max;
 }
 
+static bool brightness_inverted;
+MODULE_PARM_DESC(brightness_inverted, "Backlight brightness value is inverted "
+	"(0 = max brightness, max value = dark)");
+module_param_named(brightness_inverted, brightness_inverted, bool, 0600);
 u32 intel_panel_get_backlight(struct drm_device *dev)
 {
 	struct drm_i915_private *dev_priv = dev->dev_private;
@@ -212,6 +217,10 @@ u32 intel_panel_get_backlight(struct drm
 	}
 
 	DRM_DEBUG_DRIVER("get backlight PWM = %d\n", val);
+	if (brightness_inverted) {
+		u32 max = intel_panel_get_max_backlight(dev);
+		val = max - val;
+	}
 	return val;
 }
 
@@ -229,6 +238,11 @@ static void intel_panel_actually_set_bac
 
 	DRM_DEBUG_DRIVER("set backlight PWM = %d\n", level);
 
+	if (brightness_inverted) {
+		u32 max = intel_panel_get_max_backlight(dev);
+		level = max - level;
+	}
+
 	if (HAS_PCH_SPLIT(dev))
 		return intel_pch_panel_set_backlight(dev, level);