Skip to content

Instantly share code, notes, and snippets.

@eugene-s
Forked from carlocaione/-
Last active July 21, 2018 19:57
Show Gist options
  • Save eugene-s/8c10ea8eef87b81cdf6c to your computer and use it in GitHub Desktop.
Save eugene-s/8c10ea8eef87b81cdf6c to your computer and use it in GitHub Desktop.
Fix brightness FN-keys in Linux on Asus laptops (N550, UX305, UX303 etc)
commit 4e9bdc8ceeffa48962ae018cf52cc8ada72dc98b
Author: Carlo Caione <[email protected]>
Date: Fri Aug 21 08:58:14 2015 +0200
drm/i915/opregion: work around buggy firmware that provides 15+ output devices
- DIDL/DDL2/DDL3/... are the DIDL fields in the IGDM OpRegion (Supported
Display Devices ID List)
- This field indicates which display devices are supported by the platform, and
therefore enumerable by the graphics driver. A maximum of 15 devices are
assumed supportable on a given platform and enumerable by the graphics driver
- The graphics driver detects or determines devices during its initialization
and prior to the first monitor enumeration call it receives from the OS
- This is currently done for the i915 driver in intel_didl_outputs() evaluating
the _ADR of each output device and setting the DIDL value according to the
Device ID returned by _ADR (see 3143751ff)
- The firmware enumerates (in this order) devices DD0->DDF (defined as
'External Digital Monitor') and then an additional device LCDD. This LCDD
device is what is associated with the function keys via the EC according to
ACPI (the method called for the brightness keys is \_SB.PCI0.GFX0.LCDD._DCS).
The device ID for this LCDD device is 0x400 ('Internal/Integrated Digital Flat
Panel')
- Being this LCDD device the 16th device, it is not used to populate the DIDL
fields (you can read 'More than 15 outputs detected via ACPI' in the journal)
for this reason
- The first 8 values in the DIDL fiels are used to initialize the CADL fields
in the OpRegion (see d627b62ff) using the intel_setup_cadls() function in the
i915 driver
- The CADL/CAL3/CAL3/... fields indicate which display devices (monitors) are
currently active. A maximum of 8 monitors are assumed active on a given
platform. The IDs should be the same as the enumerated monitor or connector
IDs. The graphics driver determines active monitors during mode set times and
during boot.
- In \_SB.PCI0.GFX0.CDDS (called by \_SB.PCI0.GFX0.LCDD._DCS) we are using
these CADL values to determine if we can control the brightness of our
current active monitor. This is done comparing the value of (SANV.DIDX &
0x0F0F) to all the CADL values saved in the OpRegion
- Please note that the content of SANV.DIDX is nothing else that the address of
the LCDD device (+ some flag) since the LCDD device is the only device for
which we can control the brightness
- The problem is that none of the 8 values in the CADL region is actually the
ID of the LCDD device (and then DIDX) since intel_setup_cadls() only copied
the first 8 DIDL values that are the IDs of the first 8 devices read in
intel_didl_outputs() and the LCDD device is not among the first 8 (it's the
15th device returned pinging the _ADR methods)
- What we do in the patch is to use the _BCM method to identify the laptop
panel so we can copy its ID in the last CADL field so that
\_SB.PCI0.GFX0.CDDS (and \_SB.PCI0.GFX0.LCDD._DCS) never fails.
- This patch is an adaptation of https://patchwork.freedesktop.org/patch/20080/
Signed-off-by: Carlo Caione <[email protected]>
diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c
index cb1c657..ae28d04 100644
--- a/drivers/gpu/drm/i915/intel_opregion.c
+++ b/drivers/gpu/drm/i915/intel_opregion.c
@@ -661,6 +661,7 @@ static void intel_didl_outputs(struct drm_device *dev)
acpi_status status;
u32 temp, max_outputs;
int i = 0;
+ bool done;
handle = ACPI_HANDLE(&dev->pdev->dev);
if (!handle || acpi_bus_get_device(handle, &acpi_dev))
@@ -692,11 +693,18 @@ static void intel_didl_outputs(struct drm_device *dev)
max_outputs = ARRAY_SIZE(opregion->acpi->didl) +
ARRAY_SIZE(opregion->acpi->did2);
+ done = false;
list_for_each_entry(acpi_cdev, &acpi_video_bus->children, node) {
if (i >= max_outputs) {
- DRM_DEBUG_KMS("More than %u outputs detected via ACPI\n",
- max_outputs);
- return;
+ DRM_DEBUG_KMS("More than %u outputs detected via ACPI, %s\n",
+ max_outputs, acpi_device_bid(acpi_cdev));
+ if (acpi_has_method(acpi_cdev->handle, "_BCM")) {
+ DRM_DEBUG_KMS("%s has _BCM, replacing 8th entry\n", acpi_device_bid(acpi_cdev));
+ i = 7;
+ done = true;
+ } else {
+ continue;
+ }
}
status = acpi_evaluate_integer(acpi_cdev->handle, "_ADR",
NULL, &device_id);
@@ -705,6 +713,9 @@ static void intel_didl_outputs(struct drm_device *dev)
goto blind_set;
set_did(opregion, i++, (u32)(device_id & 0x0f0f));
}
+
+ if (done)
+ return;
}
end:
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment