I'm using Linux Mint on a ThinkPad T420s. There is this strange bug whereby the Brightness Up/Down keys sometimes behave erratically. On classic ThinkPads like mine, the key combination is Fn+Home and Fn+End. This problem is at least partly caused by the different algorithms of the power manager of the desktop environment (Cinnamon or MATE) and the laptop firmware. The power manager calculates the steps in linear steps, but the laptop doesn't do that.
This behaviour is different between Linux Mint 19.1 Cinnamon and MATE. It is also different in Ubuntu 18.04.1.
In the Live DVD (thus more or less the default install), we can observe this. In the Live DVD of Linux Mint 19.1 MATE, this problem manifests itself as erratic brightness key behaviour. For example, when we press Brightness Up and then Brightness Down, sometimes the brightness does not go back to the original value. Also, when we dial down the brightness to the minimum and then press Brightness Down again, the screen briefly turns off and then on again.
This problem is not observed in the Live DVDs of Linux Mint 19.1 Cinnamon and Ubuntu 18.04.1. I have not investigated Ubuntu 18.04.1, but in Linux Mint 19.1 Cinnamon, it is because of this default setting:
$ gsettings get org.cinnamon.settings-daemon.plugins.power backlight-helper-preference-order
org.cinnamon.settings-daemon.plugins.power backlight-helper-preference-order ['firmware', 'platform', 'raw']
csd-power
reads this setting, and then it calls
csd-backlight-helper
with those values. For example, with the setting above,
csd-power
will call
csd-backlight-helper
like this:
/usr/lib/x86_64-linux-gnu/cinnamon-settings-daemon/csd-backlight-helper --get-brightness -b firmware -b platform -b raw
Upon receiving that command,
csd-backlight-helper
looks for the brightness file with the type
firmware
, then if it's not found it looks for a brightness file with the type
platform
, and if it's also not found then it looks for a brightness file with the type
raw
. The way it does this is by asking for the list of backlight devices:
csd_backlight_helper_get_best_backlight (gchar** preference_list)
{
GList *devices;
GUdevClient *client;
client = g_udev_client_new (NULL);
devices = g_udev_client_query_by_subsystem (client, "backlight");
...
And then looking for one that matches the type specified in the command line. And the way it does that, is by querying the
type
file in the sysfs device directory:
csd_backlight_helper_get_type (GList *devices, const gchar *type)
{
const gchar *type_tmp;
GList *d;
for (d = devices; d != NULL; d = d->next) {
type_tmp = g_udev_device_get_sysfs_attr (d->data, "type");
...
What it essentially does it, it looks in the directory
/sys/class/backlight
, then for each subdirectory, it reads the file
<subdir>/type
, then checks its contents. If it matches the requested type, then proceed. On my laptop, there are two subdirectories (actually, links to other sysfs directories):
acpi_video0
and
intel_backlight
.
/sys/class/backlight/acpi_video0/type
contains
firmware
, and
/sys/class/backlight/intel_backlight
contains
raw
. Both of these subdirectories contain a
brightness
file, which can be used to set the brightness. There is also an
actual_brightness
file, which is read-only, but so far it seems that
actual_brightness
and
brightness
always contain the same value. I might be wrong, so please comment if you have more information.
Now, the problem is that both the power manager and the laptop's firmware set the brightness. They really do this twice. And they sometimes have different ideas. If we tell
csd-backlight-helper
to use the
firmware
control method, all is fine because on this laptop, the brightness values exposed by the firmware is 0 through 15 and is linear. For example, if the current brightness is 8 and we press the Brightness Up key, then csd-power reads the maximum brightness and current brightness, calculates the next step, and sets the brightness to 9. It then verifies what it's done by reading the maximum brightness and current brightness again, and shows the change with the OSD.
If we use the
platform
control method, the OSD doesn't show up because on this laptop there is no backlight device with the
platform
control method.
However, if we use the
raw
control method, there is a problem. The laptop itself sets the brightness with these values: (the numbers on the left column are the corresponding brightness values obtained via the firmware backlight device)
15 4437
14 3567
13 2871
12 2262
11 1775
10 1392
9 1096
8 853
7 626
6 470
5 348
4 261
3 191
2 139
1 104
0 70
We can see that the steps are not linear. However, using the
raw
interface, we can manually set the brightness to any value.
This is what happens to the raw brightness value when we use the
raw
method:
4437 --> the original, maximum brightness value corresponding to 15 on the firmware's reported brightness
4216 --> the original brightness value minus 221
3567 --> the brightness value set by the laptop itself, corresponding to brightness level 14
3346 --> 3567 minus 221
2871 --> brightness value corresponding to level 13
2650 --> 2871 minus 221
2262 --> brightness value corresponding to level 12
2041 --> 2262 minus 221
1775 --> brightness value corresponding to level 11
1554 --> 1775 minus 221
1392 --> brightness value corresponding to level 10
1171 --> 1392 minus 221
1096 --> brightness value corresponding to level 9
875 --> 1096 minus 221
853 --> brightness value corresponding to level 8
632 --> 853 minus 221
626 --> brightness value corresponding to level 7
405 --> 626 minus 221
470 --> brightness value corresponding to level 6
249 --> 470 minus 221
348 --> brightness value corresponding to level 5, NOTE THAT the brightness actually increased
127
261 --> brightness value corresponding to level 4
40
191 --> brightness value corresponding to level 3
0
139 --> brightness value corresponding to level 2
0
104 --> brightness value corresponding to level 1
0
70 --> brightness value corresponding to level 0
0
70 --> brightness value corresponding to level 0
... and so on. Also note that 4437 = 221 x 20 + 17, and 221 = 17 x 13.
So this is why with the
raw
method, the brightness jumps around somewhat. This also happens in MATE, because mate-power-backlight-helper always uses the
intel_backlight
interface. This code snippet is from
mate-power-manager/src/gpm-backlight-helper.c
:
/* available kernel interfaces in priority order */
static const gchar *backlight_interfaces[] = {
"gmux_backlight",
"nv_backlight",
"nvidia_backlight",
"intel_backlight",
"dell_backlight",
"asus_laptop",
"toshiba",
"eeepc",
"eeepc-wmi",
"thinkpad_screen",
"acpi_video1",
"mbp_backlight",
"acpi_video0",
"fujitsu-laptop",
"sony",
"samsung",
NULL,
};
I also found some kind of workaround. In MATE, if we set
/sys/module/video/parameters/brightness_switch_enabled
to
N
, the brightness doesn't jump around, but there is a bug that resets the brightness when we plug or when we unplug the ac adapter. The alternative is to chmod
/sys/class/backlight/intel_backlight/brightness
to 444, so mate-power-backlight-helper can't modify the brightness and it just lets the laptop to set its own brightness, without the need to set
/sys/module/video/parameters/brightness_switch_enabled
to
N
.
The laptop does the brightness adjustment by means of ACPI events. In
drivers/acpi/acpi_video.c
,
static void brightness_switch_event(struct acpi_video_device *video_device,
u32 event)
{
if (!brightness_switch_enabled)
return;
video_device->switch_brightness_event = event;
schedule_delayed_work(&video_device->switch_brightness_work, HZ / 10);
}
Looking at that code, it seems that the event being handled with a delay. This means there is a possible race condition with
csd-power
/
gsd-power
/
mate-power-manager
. Also, it seems that the acpi event modifies the brightness after the power manager does, maybe because of the delayed work. Therefore, with the
chmod
workaround, the OSD still shows up, but it's delayed. When we turn up the brightness to maximum (15 on firmware and 4437 on raw), the OSD still shows an incomplete (not full) brightness bar. This is because the power manager manages to get priority execution and reads the brightness value before the video acpi module changes the value.