Forked from eugene-s/Fix brightness FN-keys on Asus laptops
Created
September 20, 2016 08:14
-
-
Save sileht/c2600abed614e20f9c0c3db05390e5fb to your computer and use it in GitHub Desktop.
Fix brightness FN-keys in Linux on Asus laptops (N550, UX305, UX303 etc)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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