Linux backlight control
Backlight control is one of those things that you'd think would be simple, but ha ha this is computing so of course it's an utter disaster and everything is a huge mess. There's three main classes of backlight control in the x86 world, all of which have drawbacks:
- ACPI specifies a mechanism for backlight control, and the majority of modern machines implement it. It has the advantage that the brightness query interface is generally aware of anything else in the system which may have changed the brightness, so it's unlikely to get out of sync with reality if the platform tries to do something odd like change the backlight itself in response to an ambient light sensor or some other event. The main drawback is that there's typically a fairly small number of available backlight values, usually somewhere between 8 and 20.
- A platform-specific mechanism. This used to be more popular before the ACPI backlight interface took off, but some machines still require it. The idea here is that there's some sort of platform-specific way of requesting a backlight change, ranging from a vendor-specific ACPI method through to triggering system management calls by magic register writes. These methods usually (but not always) keep in sync with other firmware changes, but rarely provide any more brightness steps than the standard ACPI interface.
- Many mobile GPUs have backlight control registers built in. These usually give you a range of several thousand possible values, but using them will almost certainly leave you out of sync with reality if the firmware touches them at all. To make things worse, the firmware control of the backlight may occur after the gpu - so you could end up with two different controls that both need to be full in order to get maximum brightness. The worst case scenario is that the firmware gets confused by the values not being what it programmed and you end up with a hung machine.
Right now, if there's an ACPI backlight interface then that's usually the only thing we'll show you. We can do that because we can identify if there's an ACPI backlight interface when we parse the ACPI tables at the start of booting, and that information can be registered before we start setting up any other backlights. The problem comes when we have no ACPI backlight interface. We don't have any idea whether there's a platform mechanism until a platform driver loads, which could be at any time. As a result, we've been reluctant to expose GPU-level backlight control because doing so would often give you two separate backlight controls and no indication as to which should be used. Userspace doesn't really have a way to make that decision either, so everyone ends up unhappy.
This is especially problematic with some machines which provide no ACPI or known platform control (or, in the case of some Samsungs, only provide platform control if you have a special Linux BIOS that Samsung won't give you) but can control the backlight via the gpu. Right now you get nothing, because giving you something would potentially break other systems and the needs of the many etc. Sorry! But this is obviously problematic in the long term, especially because multi-GPU machines tend to have multiple ACPI backlight interfaces, so I've been working on a better approach.
When a backlight device is registered, it appears under /sys/class/backlight. If it's an ACPI device it has a symlink pointing to some random ACPI device. If it's a platform device it's pointing at something like "dell-laptop" which is approximately unhelpful when it comes to figuring out what it controls. If it's a GPU-level device then it probably points at the PCI device, which is helpful except in the case where you have multiple backlight controls on a single GPU. So, by and large, you have no good way to identify which backlight control is preferable unless you keep a huge list of all possible backlights along with some scoring.
The first thing I've added to improve this is a "type" attribute. This tells you whether a given backlight is firmware-level (like ACPI), platform-level (like the various laptop drivers) or performs raw register writes (like a GPU driver). That lets userspace decide which interface is preferable. It'll typically be the ACPI interface, because that's the most likely to keep synchronisation and so avoid bizarre brightness bugs. The next thing has been to start fixing up the parent links. There's nothing we can do for the platform level devices, but the ACPI drivers could at least point at PCI devices rather than into ACPI space. That means that multi-GPU systems can now identify which interface to use based on the currently active GPU. Finally, I've started pointing the GPU-level backlight controls at the specific output rather than merely at the PCI device. This probably makes little difference for laptops as such, but once we start exposing backlight control for monitors that support ddcci it'll make things much easier as we'll know which backlight control corresponds to which monitor.
I've then written a small library that accepts information about the output and picks the "best" backlight for the device. It's obviously based on a pile of heuristics and there's a couple of bits of API that I suspect need to be nailed down yet, but it means that this code only needs to be written once. It's then simple to glue this into X drivers, which means that they can expose a "Backlight" xrandr property on each relevant display. That means that backlight control is then handled at the session level with the X server acting as the privileged agent, which simplifies a bunch of things and means we can finally let hal die entirely. Long-term this means we'll have unified backlight control for all of your displays, which is a wonderful thing.
Summary: We kind of suck right now, but there's a reasonably clear path to getting better.