[PATCH 09/14] ACPI: video: Make backlight class device registration a separate step
Daniel Dadap
ddadap at nvidia.com
Fri May 20 21:41:02 UTC 2022
On 5/17/22 10:23, Hans de Goede wrote:
> On x86/ACPI boards the acpi_video driver will usually initializing before
> the kms driver (except i915). This causes /sys/class/backlight/acpi_video0
> to show up and then the kms driver registers its own native backlight
> device after which the drivers/acpi/video_detect.c code unregisters
> the acpi_video0 device (when acpi_video_get_backlight_type()==native).
>
> This means that userspace briefly sees 2 devices and the disappearing of
> acpi_video0 after a brief time confuses the systemd backlight level
> save/restore code, see e.g.:
> https://bbs.archlinux.org/viewtopic.php?id=269920
>
> To fix this make backlight class device registration a separate step
> done by a new acpi_video_register_backlight() function. The intend is for
> this to be called by the drm/kms driver *after* it is done setting up its
> own native backlight device. So that acpi_video_get_backlight_type() knows
> if a native backlight will be available or not at acpi_video backlight
> registration time, avoiding the add + remove dance.
If I'm understanding this correctly, it seems we will want to call
acpi_video_register_backlight() from the NVIDIA proprietary driver in a
fallback path in case the driver's own GPU-controlled backlight handler
either should not be used, or fails to register. That sounds reasonable
enough, but I'm not sure what should be done about drivers like
nvidia-wmi-ec-backlight, which are independent of the GPU hardware, and
wouldn't be part of the acpi_video driver, either. There are a number of
other similar vendor-y/platform-y type backlight drivers in
drivers/video/backlight and drivers/platform/x86 that I think would be
in a similar situation.
From a quick skim of the ACPI video driver, it seems that perhaps
nvidia-wmi-ec-backlight is missing a call to
acpi_video_set_dmi_backlight_type(), perhaps with the
acpi_backlight_vendor value? But I'm not familiar enough with this code
to be sure that nobody will be checking acpi_video_get_backlight_type()
before nvidia-wmi-ec-backlight loads. I'll take a closer look to try to
convince myself that it makes sense.
> Note the new acpi_video_register_backlight() function is also called from
> a delayed work to ensure that the acpi_video backlight devices does get
> registered if necessary even if there is no drm/kms driver or when it is
> disabled.
It sounds like maybe everything should be fine as long as
nvidia-wmi-ec-backlight (and other vendor-y/platform-y type drivers)
gets everything set up before the delayed work which calls
acpi_video_register_backlight()? But then is it really necessary to
explicitly call acpi_video_register_backlight() from the DRM drivers if
it's going to be called later if no GPU driver registered a backlight
handler, anyway? Then we'd just need to make sure that the iGPU and dGPU
drivers won't attempt to register a backlight handler on systems where a
vendor-y/platform-y driver is supposed to handle the backlight instead,
which sounds like it has the potential to be quite messy.
Recall that on at least one system, both amdgpu and the NVIDIA
proprietary driver registered a handler even when it shouldn't:
https://patchwork.kernel.org/project/platform-driver-x86/patch/20220316203325.2242536-1-ddadap@nvidia.com/
- I didn't have direct access to this system, but the fact that the
NVIDIA driver registered a handler was almost certainly a bug in either
the driver or the system's firmware (on other systems with the same type
of backlight hardware, NVIDIA does not register a handler), and I
imagine the same is true of the amdgpu driver. In all likelihood nouveau
would have probably tried to register one too; I am not certain whether
the person who reported the issue to me had tested with nouveau. I'm not
convinced that the GPU drivers can reliably determine whether or not
they are supposed to register, but maybe cases where they aren't, such
as the system mentioned above, are supposed to be handled in a quirks
table somewhere.
> Signed-off-by: Hans de Goede <hdegoede at redhat.com>
> ---
> drivers/acpi/acpi_video.c | 45 ++++++++++++++++++++++++++++++++++++---
> include/acpi/video.h | 2 ++
> 2 files changed, 44 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c
> index 95d4868f6a8c..79e75dc86243 100644
> --- a/drivers/acpi/acpi_video.c
> +++ b/drivers/acpi/acpi_video.c
> @@ -31,6 +31,12 @@
> #define ACPI_VIDEO_BUS_NAME "Video Bus"
> #define ACPI_VIDEO_DEVICE_NAME "Video Device"
>
> +/*
> + * Display probing is known to take up to 5 seconds, so delay the fallback
> + * backlight registration by 5 seconds + 3 seconds for some extra margin.
> + */
> +#define ACPI_VIDEO_REGISTER_BACKLIGHT_DELAY (8 * HZ)
> +
> #define MAX_NAME_LEN 20
>
> MODULE_AUTHOR("Bruno Ducrot");
> @@ -80,6 +86,9 @@ static LIST_HEAD(video_bus_head);
> static int acpi_video_bus_add(struct acpi_device *device);
> static int acpi_video_bus_remove(struct acpi_device *device);
> static void acpi_video_bus_notify(struct acpi_device *device, u32 event);
> +static void acpi_video_bus_register_backlight_work(struct work_struct *ignored);
> +static DECLARE_DELAYED_WORK(video_bus_register_backlight_work,
> + acpi_video_bus_register_backlight_work);
> void acpi_video_detect_exit(void);
>
> /*
> @@ -1862,8 +1871,6 @@ static int acpi_video_bus_register_backlight(struct acpi_video_bus *video)
> if (video->backlight_registered)
> return 0;
>
> - acpi_video_run_bcl_for_osi(video);
> -
> if (acpi_video_get_backlight_type(false) != acpi_backlight_video)
> return 0;
>
> @@ -2089,7 +2096,11 @@ static int acpi_video_bus_add(struct acpi_device *device)
> list_add_tail(&video->entry, &video_bus_head);
> mutex_unlock(&video_list_lock);
>
> - acpi_video_bus_register_backlight(video);
> + /*
> + * The userspace visible backlight_device gets registered separately
> + * from acpi_video_register_backlight().
> + */
> + acpi_video_run_bcl_for_osi(video);
> acpi_video_bus_add_notify_handler(video);
>
> return 0;
> @@ -2128,6 +2139,11 @@ static int acpi_video_bus_remove(struct acpi_device *device)
> return 0;
> }
>
> +static void acpi_video_bus_register_backlight_work(struct work_struct *ignored)
> +{
> + acpi_video_register_backlight();
> +}
> +
> static int __init is_i740(struct pci_dev *dev)
> {
> if (dev->device == 0x00D1)
> @@ -2238,6 +2254,17 @@ int acpi_video_register(void)
> */
> register_count = 1;
>
> + /*
> + * acpi_video_bus_add() skips registering the userspace visible
> + * backlight_device. The intend is for this to be registered by the
> + * drm/kms driver calling acpi_video_register_backlight() *after* it is
> + * done setting up its own native backlight device. The delayed work
> + * ensures that acpi_video_register_backlight() always gets called
> + * eventually, in case there is no drm/kms driver or it is disabled.
> + */
> + schedule_delayed_work(&video_bus_register_backlight_work,
> + ACPI_VIDEO_REGISTER_BACKLIGHT_DELAY);
> +
> leave:
> mutex_unlock(®ister_count_mutex);
> return ret;
> @@ -2248,6 +2275,7 @@ void acpi_video_unregister(void)
> {
> mutex_lock(®ister_count_mutex);
> if (register_count) {
> + cancel_delayed_work_sync(&video_bus_register_backlight_work);
> acpi_bus_unregister_driver(&acpi_video_bus);
> register_count = 0;
> }
> @@ -2255,6 +2283,17 @@ void acpi_video_unregister(void)
> }
> EXPORT_SYMBOL(acpi_video_unregister);
>
> +void acpi_video_register_backlight(void)
> +{
> + struct acpi_video_bus *video;
> +
> + mutex_lock(&video_list_lock);
> + list_for_each_entry(video, &video_bus_head, entry)
> + acpi_video_bus_register_backlight(video);
> + mutex_unlock(&video_list_lock);
> +}
> +EXPORT_SYMBOL(acpi_video_register_backlight);
> +
> void acpi_video_unregister_backlight(void)
> {
> struct acpi_video_bus *video;
> diff --git a/include/acpi/video.h b/include/acpi/video.h
> index e31afb93379a..b2f7dc1f354a 100644
> --- a/include/acpi/video.h
> +++ b/include/acpi/video.h
> @@ -53,6 +53,7 @@ enum acpi_backlight_type {
> #if IS_ENABLED(CONFIG_ACPI_VIDEO)
> extern int acpi_video_register(void);
> extern void acpi_video_unregister(void);
> +extern void acpi_video_register_backlight(void);
> extern int acpi_video_get_edid(struct acpi_device *device, int type,
> int device_id, void **edid);
> extern enum acpi_backlight_type acpi_video_get_backlight_type(bool native);
> @@ -68,6 +69,7 @@ extern int acpi_video_get_levels(struct acpi_device *device,
> #else
> static inline int acpi_video_register(void) { return -ENODEV; }
> static inline void acpi_video_unregister(void) { return; }
> +static inline void acpi_video_register_backlight(void) { return; }
> static inline int acpi_video_get_edid(struct acpi_device *device, int type,
> int device_id, void **edid)
> {
More information about the amd-gfx
mailing list