Linux brightness diagnostic techniques

Disabling the ACPI backlight driver

In some cases the ACPI BIOS provides the interfaces required to control the backlight through the generic ACPI interface, but the actual methods are not implemented. So the ACPI driver registers with the hardware and prevents any laptop specific drivers from doing so. In those cases it is possible to prevent the ACPI backlight driver from starting by adding the following line to the kernel boot parameters:

acpi_backlight=vendor

Unfortunately the thinkpad-acpi driver behaves differently, as it does not act on the ACPI driver being active or not, but on the ACPI methods being defined. Thus forcing the vendor specific driver to activate requires an additional:

thinkpad-acpi.brightness_enable=1

You can check to see if your system uses the thinkpad-acpi driver by using the following command:

lsmod | grep thinkpad_acpi

Disabling ACPI brightness adjustments

If ACPI can control the brightness but the control is somehow incorrect, you can disable ACPI brightness changes with boot parameteracpi.brightness_switch_enabled=0 (also accessible in /sys/module/video/parameters/brightness_switch_enabled). Note that this does not releaseACPI control as acpi_backlight=vendor does (sometimes acpi=off is suggested but this completely turns off the ACPI subsystem). Changing this attribute merely suppresses events from the video device to be forwarded and therefore to be ignored. Writing values into the kernel interfaces should still work.

Checking the kernel interfaces

To decide whether the missing brightness control is a hotkey, a X driver or a kernel issue, testing the kernel interfaces will give additional information.

The /proc/acpi/video interface

Warning /!\ This actually is deprecated and would also only be working if the ACPI backlight driver is active. It will be described only for completeness. The ACPI backlight driver also exposes itself through the new sysfs interface.

/proc/acpi/video/
 |
 +- 
 |   |
 |   +- 
 |   |   |
 |   |   +- EDID
 |   |   +- brightness
 |   |   +- state
 |   |   +- info
 |   +- ...
 +- ...

Each discovered graphics card will have one entry and within that each defined output device will have its set of values. The brightness variable contains either "" or the possible brightness values and the currently set value. This could, for example, look like this:

#> grep -r . /proc/acpi/video/GFX0/DD04/
proc/acpi/video/GFX0/DD04/EDID:
/proc/acpi/video/GFX0/DD04/brightness:levels:  20 28 36 44 52 60 68 76 84 92 100
/proc/acpi/video/GFX0/DD04/brightness:current: 100
/proc/acpi/video/GFX0/DD04/state:state:     0x1d
/proc/acpi/video/GFX0/DD04/state:query:     0x00
/proc/acpi/video/GFX0/DD04/info:device_id:    0x0410
/proc/acpi/video/GFX0/DD04/info:type:         LCD
/proc/acpi/video/GFX0/DD04/info:known by bios: yes

So in this case there are 11 possible percentage values which, when written into the brightness file, would change the brightness of the display. Currently it is set to full brightness.

(i) Note that only those discrete percentage values are defined. Writing other values results in an error.

(i) Also note that the effect may not be immediate. When testing this, the laptop I used would take a bit and then gradually fade to the destination brightness.

The /sys/class/backlight interface

Contrary to the ACPI interface in /proc, the sysfs interface has only entries when there is an output device that supports backlight control.

/sys/class/backlight/
 |
 +- 
 |   |
 |   +- actual_brightness
 |   +- bl_power
 |   +- brightness
 |   +- ...
 |   +- max_brightness
 |   +- ...
 +- ...

The file actual_brightness contains the currently set brightness level. Reading brightness results in the same number. In max_brightness is the maximum number allowed to be written into brightness. While the ACPI /proc interface uses percentage levels, the sysfs interface uses index numbers between 0 and the maximum brightness.

See the kernel documentation of sysfs class backlight interface for the definitive description of the attributes.

#> grep . /sys/class/backlight/acpi_video0/*
/sys/class/backlight/acpi_video0/actual_brightness:10
/sys/class/backlight/acpi_video0/bl_power:0
/sys/class/backlight/acpi_video0/brightness:10
/sys/class/backlight/acpi_video0/max_brightness:10

Into the abyss: looking at the ACPI BIOS

Whenever the kernel interface does not work or is not present at all, it is time to put on the mining helmet and try to make sense of the information stored in the ACPI BIOS which can be obtained following this description. The next challenges will be to find the right section for the graphics device and the display devices defined there.

Finding the correct graphics device

The ACPI BIOS often contains not only the information for the graphics card used in the one laptop we are looking at, but has entries for every possible variation the vendor had in mind. So before further inspection, we need to find out which definition we have to look at.

If the video device is handled by the ACPI video driver and just the backlight interface is not working, the name of the used graphics device can be obtained by looking at the output of dmesg.

#> dmesg|grep 'ACPI: Video'
[    2.941100] ACPI: Video Device [GFX0] (multi-head: yes  rom: no  post: no)

So we know we need to look for GFX0 in the DSDT. But if the definition is bad enough to be not detected by the ACPI driver? In that case we might be successful through the backdoor. In other words from the info we got of the PCI bus.

#> lspci |grep VGA
00:02.0 VGA compatible controller: Intel Corporation Mobile 945GME Express Integrated Graphics Controller (rev 03)

So the graphics device is attached as PCI device 2 functional endpoint 0. Let's search for "_ADR.*00020000" in the disassembled DSDT:

            Device (GFX0)
            {
                Name (_ADR, 0x00020000)
                ...

So this nicely matches the name we are expecting. If we don't know the name and maybe there are other devices (on another bus) with the same ID. Well there are a few methods that are required for a device to be considered as a video device. The ACPI spec (Appendix B.) requires two methods to be defined at the graphics device level. Actually only one, but there is (as so often room for interpretation). Linux used to require both but this was changed to be ok with one of them.

_DOS
(Display Output Switching) This method is required by the ACPI spec for supporting brightness control. This method allows control over how the BIOS handles events to switch the output display or what to do about the brightness when changing from AC to DC. Technically this is not really required for setting the brightness and some BIOS vendors "forgot" to implement this, even when supplying methods to set the brightness.
_DOD
(Display Output Devices) This returns a list of attributes of the output devices which are connected to a certain graphics device. This allows to enumerate the attached output devices and also gather some information. Strictly this is also not required to find the methods to control the backlight, but for completeness allows to check the type of the output as brightness control is only defined to LCD screens.

Essentially one of these two methods must be defined for the graphics device. Otherwise the ACPI driver will not detect it as a valid video device. In which case only the BIOS/laptop vendor can resolve this.

Verifying the methods to control the brightiness

Every output device has to define methods which allow interaction (defined in Appendix B.6 of the ACPI spec). The following methods are related to the brightness control:

_BCL
(Backlight Control Levels) This method is supposed to return a list of percentage values. The first two are supposed to be the brightness level on AC power and on battery. The remaining should be a list of supported levels (in ascending order might be sensible, but never count on it). This seems to be a field of generous interpretation by vendors. Some leave out the first two, some sort in reverse, some use index numbers instead of percentage values or a mix of all. The Linux ACPI driver tries hard to make something of what gets returned. But there are limits.
 Method (_BCL, 0) {
   // List of supported brightness levels
   Return (Package(7){
     80, // level when machine has full power
     50, // level when machine is on batteries
     // other supported levels
     20, 40, 60, 80, 100
   }
 }
_BCM
(Backlight Control Modify) Supposed to take a percentage value defined by _BCL and set the brightness accordingly. Again, one needs to "guess" whether the actual implementation uses a relative index number instead.
_BCQ
(Backlight Control Query) Gets the currently set brightness. This would need to be a value defined in the list returned by _BCL but, as one would make an educated guess, there is variation, too.

Reading the method definitions requires some knowledge about the ACPI AML language and some persistence on hunting down more or less complex implementations. Some are more obvious than others. There have been cases where percentage values got converted into index values and then (incorrectly) back on query.

Also often the values get stored into a register of the embedded controller which does not necessarily need to have any result on the brightness.

你可能感兴趣的:(LinuxDriver)