From: Mario Limonciello <mario.limonciello@amd.com>
To: <dri-devel@lists.freedesktop.org>, <harry.wentland@amd.com>,
Simona Vetter <simona@ffwll.ch>,
Alex Deucher <alexander.deucher@amd.com>,
"Maarten Lankhorst" <maarten.lankhorst@linux.intel.com>,
Maxime Ripard <mripard@kernel.org>,
Thomas Zimmermann <tzimmermann@suse.de>,
David Airlie <airlied@gmail.com>
Cc: Xaver Hugl <xaver.hugl@gmail.com>,
<amd-gfx@lists.freedesktop.org>,
"open list:INTEL DRM DISPLAY FOR XE AND I915 DRIVERS"
<intel-gfx@lists.freedesktop.org>,
"open list:INTEL DRM DISPLAY FOR XE AND I915 DRIVERS"
<intel-xe@lists.freedesktop.org>,
Hans de Goede <hansg@kernel.org>,
Mario Limonciello <mario.limonciello@amd.com>,
"David Herrmann" <dh.herrmann@gmail.com>,
Mario Limonciello <superm1@kernel.org>
Subject: [PATCH v7 04/12] drm: add connector backlight (LUMINANCE) infrastructure
Date: Mon, 31 Aug 2026 16:05:23 -0500 [thread overview]
Message-ID: <20260831210531.2334221-5-mario.limonciello@amd.com> (raw)
In-Reply-To: <20260831210531.2334221-1-mario.limonciello@amd.com>
Backlight brightness is a property of a display, and thus of a DRM
connector, yet it has historically only been controllable through the
separate backlight sysfs interface. Add a generic, backend-agnostic
per-connector LUMINANCE range property so brightness can be driven
through the atomic modeset path like any other connector state.
A struct drm_backlight is embedded in every connector and initialized by
the core; drivers do not allocate it. A driver links a backend (today a
backlight_device, in the future DDC/CI, MIPI-DCS, ...) with
drm_backlight_link(), which creates the connector's LUMINANCE property
with the backend's range. The property value is staged into the atomic
connector state and only pushed to the hardware from the commit/enable
path, via a workqueue so that slow backends never stall a commit. DPMS
off drives the backlight to 0 and DPMS on restores the committed value.
The property range is per-connector (created from the backend's
max_brightness), so multiple panels no longer share and corrupt a single
device-wide range. drm_backlight_link() also carries the legacy-sysfs
takeover accounting used by the client capability added in a later patch.
The whole feature is guarded by CONFIG_DRM_BACKLIGHT (which depends on,
rather than selects, BACKLIGHT_CLASS_DEVICE) so DRM does not pull the
backlight subsystem into the kernel when it is not wanted.
Co-developed-by: David Herrmann <dh.herrmann@gmail.com>
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Signed-off-by: Mario Limonciello (AMD) <superm1@kernel.org>
---
drivers/gpu/drm/Kconfig | 18 +
drivers/gpu/drm/Makefile | 2 +
drivers/gpu/drm/drm_atomic_helper.c | 39 +++
drivers/gpu/drm/drm_atomic_uapi.c | 49 ++-
drivers/gpu/drm/drm_backlight.c | 515 ++++++++++++++++++++++++++++
drivers/gpu/drm/drm_connector.c | 56 +++
drivers/gpu/drm/drm_drv.c | 8 +
drivers/gpu/drm/drm_mode_config.c | 1 +
drivers/gpu/drm/drm_sysfs.c | 26 +-
include/drm/drm_atomic_helper.h | 2 +
include/drm/drm_backlight.h | 158 +++++++++
include/drm/drm_connector.h | 20 ++
12 files changed, 890 insertions(+), 4 deletions(-)
create mode 100644 drivers/gpu/drm/drm_backlight.c
create mode 100644 include/drm/drm_backlight.h
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 323422861e8f6..ddb6827d613f8 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -33,6 +33,24 @@ endmenu
if DRM
+config DRM_BACKLIGHT
+ bool "DRM connector backlight (luminance) support"
+ depends on DRM
+ depends on BACKLIGHT_CLASS_DEVICE
+ default DRM
+ help
+ Expose per-connector backlight brightness control through the DRM
+ connector LUMINANCE property, backed by the backlight subsystem (and,
+ in the future, other backends such as DDC/CI). This lets luminance
+ changes go through the same atomic modeset path as the rest of the
+ display state.
+
+ This depends on BACKLIGHT_CLASS_DEVICE. When that is disabled, DRM is
+ built without this support and does not pull the backlight subsystem
+ into the kernel.
+
+ If in doubt, say Y.
+
config DRM_MIPI_DBI
tristate
depends on DRM
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index e635fcffd3790..bcb06dbb71343 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -78,6 +78,8 @@ drm-$(CONFIG_DRM_CLIENT) += \
drm_client_event.o \
drm_client_modeset.o \
drm_client_sysrq.o
+drm-$(CONFIG_DRM_BACKLIGHT) += drm_backlight.o
+drm-$(CONFIG_DRM_LIB_RANDOM) += lib/drm_random.o
drm-$(CONFIG_COMPAT) += drm_ioc32.o
drm-$(CONFIG_DRM_PANEL) += drm_panel.o
drm-$(CONFIG_OF) += drm_of.o
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 285aac3554dfd..3603a8cce074e 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -33,6 +33,7 @@
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_atomic_uapi.h>
+#include <drm/drm_backlight.h>
#include <drm/drm_blend.h>
#include <drm/drm_bridge.h>
#include <drm/drm_colorop.h>
@@ -1230,6 +1231,10 @@ drm_atomic_helper_commit_encoder_bridge_disable(struct drm_device *dev,
* it away), so we won't call disable hooks twice.
*/
bridge = drm_bridge_chain_get_first_bridge(encoder);
+
+ /* Turn the backlight off before disabling the pipeline. */
+ drm_backlight_set_luminance(connector, 0);
+
drm_atomic_bridge_chain_disable(bridge, state);
drm_bridge_put(bridge);
@@ -1744,6 +1749,10 @@ drm_atomic_helper_commit_encoder_bridge_enable(struct drm_device *dev, struct dr
drm_atomic_bridge_chain_enable(bridge, state);
drm_bridge_put(bridge);
+
+ /* Restore the backlight once the pipeline is enabled. */
+ if (connector->state)
+ drm_atomic_helper_connector_apply_luminance(connector->state);
}
}
EXPORT_SYMBOL(drm_atomic_helper_commit_encoder_bridge_enable);
@@ -1775,6 +1784,24 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev,
}
EXPORT_SYMBOL(drm_atomic_helper_commit_modeset_enables);
+/**
+ * drm_atomic_helper_connector_apply_luminance - apply connector luminance from atomic state
+ * @conn_state: atomic connector state to apply luminance for
+ *
+ * Updates the backlight luminance from the atomic connector state. If the
+ * connector has a linked backlight and is associated with an active CRTC,
+ * push the luminance value to hardware.
+ */
+void drm_atomic_helper_connector_apply_luminance(const struct drm_connector_state *conn_state)
+{
+ struct drm_connector *connector = conn_state->connector;
+
+ if (conn_state->crtc && conn_state->crtc->state &&
+ conn_state->crtc->state->active)
+ drm_backlight_set_luminance(connector, conn_state->luminance);
+}
+EXPORT_SYMBOL(drm_atomic_helper_connector_apply_luminance);
+
/*
* For atomic updates which touch just a single CRTC, calculate the time of the
* next vblank, and inform all the fences of the deadline.
@@ -1989,6 +2016,9 @@ EXPORT_SYMBOL(drm_atomic_helper_wait_for_flip_done);
void drm_atomic_helper_commit_tail(struct drm_atomic_commit *state)
{
struct drm_device *dev = state->dev;
+ struct drm_connector *connector;
+ struct drm_connector_state *new_conn_state;
+ int i;
drm_atomic_helper_commit_modeset_disables(dev, state);
@@ -1996,6 +2026,9 @@ void drm_atomic_helper_commit_tail(struct drm_atomic_commit *state)
drm_atomic_helper_commit_modeset_enables(dev, state);
+ for_each_new_connector_in_state(state, connector, new_conn_state, i)
+ drm_atomic_helper_connector_apply_luminance(new_conn_state);
+
drm_atomic_helper_fake_vblank(state);
drm_atomic_helper_commit_hw_done(state);
@@ -2019,11 +2052,17 @@ EXPORT_SYMBOL(drm_atomic_helper_commit_tail);
void drm_atomic_helper_commit_tail_rpm(struct drm_atomic_commit *state)
{
struct drm_device *dev = state->dev;
+ struct drm_connector *connector;
+ struct drm_connector_state *new_conn_state;
+ int i;
drm_atomic_helper_commit_modeset_disables(dev, state);
drm_atomic_helper_commit_modeset_enables(dev, state);
+ for_each_new_connector_in_state(state, connector, new_conn_state, i)
+ drm_atomic_helper_connector_apply_luminance(new_conn_state);
+
drm_atomic_helper_commit_planes(dev, state,
DRM_PLANE_COMMIT_ACTIVE_ONLY);
diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c
index 1050dddadb17e..5e687600ce45e 100644
--- a/drivers/gpu/drm/drm_atomic_uapi.c
+++ b/drivers/gpu/drm/drm_atomic_uapi.c
@@ -30,6 +30,8 @@
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_atomic_uapi.h>
+#include <drm/drm_backlight.h>
+#include <drm/drm_connector.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_print.h>
#include <drm/drm_drv.h>
@@ -962,6 +964,13 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector,
state->hdmi.broadcast_rgb = val;
} else if (property == connector->color_format_property) {
state->color_format = val;
+ } else if (property == connector->luminance_property) {
+ /*
+ * Only stage the value into the atomic state; the hardware is
+ * updated from the commit path (see
+ * drm_atomic_helper_connector_apply_luminance()).
+ */
+ state->luminance = val;
} else if (connector->funcs->atomic_set_property) {
return connector->funcs->atomic_set_property(connector,
state, property, val);
@@ -1049,6 +1058,8 @@ drm_atomic_connector_get_property(struct drm_connector *connector,
*val = state->hdmi.broadcast_rgb;
} else if (property == connector->color_format_property) {
*val = state->color_format;
+ } else if (property == connector->luminance_property) {
+ *val = state->luminance;
} else if (connector->funcs->atomic_get_property) {
return connector->funcs->atomic_get_property(connector,
state, property, val);
@@ -1133,6 +1144,22 @@ static struct drm_pending_vblank_event *create_vblank_event(
return e;
}
+static void drm_atomic_crtc_set_backlight(struct drm_crtc *crtc, bool active)
+{
+ struct drm_connector_list_iter conn_iter;
+ struct drm_connector *connector;
+
+ drm_connector_list_iter_begin(crtc->dev, &conn_iter);
+ drm_for_each_connector_iter(connector, &conn_iter) {
+ if (!connector->state || connector->state->crtc != crtc)
+ continue;
+
+ drm_backlight_set_luminance(connector,
+ active ? connector->state->luminance : 0);
+ }
+ drm_connector_list_iter_end(&conn_iter);
+}
+
int drm_atomic_connector_commit_dpms(struct drm_atomic_commit *state,
struct drm_connector *connector,
int mode)
@@ -1155,9 +1182,29 @@ int drm_atomic_connector_commit_dpms(struct drm_atomic_commit *state,
if (connector->dpms == mode)
goto out;
+ crtc = connector->state ? connector->state->crtc : NULL;
+
+ /* Handle backlight brightness coordination with DPMS state changes */
+ if (old_mode != DRM_MODE_DPMS_OFF && mode == DRM_MODE_DPMS_OFF) {
+ /* DPMS ON -> OFF: dim all connectors driven by this CRTC. */
+ if (crtc)
+ drm_atomic_crtc_set_backlight(crtc, false);
+ else
+ drm_backlight_set_luminance(connector, 0);
+ }
+
connector->dpms = mode;
- crtc = connector->state->crtc;
+ /* DPMS OFF -> ON: restore brightness to property value */
+ if (old_mode == DRM_MODE_DPMS_OFF && mode == DRM_MODE_DPMS_ON &&
+ connector->state) {
+ if (crtc)
+ drm_atomic_crtc_set_backlight(crtc, true);
+ else
+ drm_backlight_set_luminance(connector,
+ connector->state->luminance);
+ }
+
if (!crtc)
goto out;
ret = drm_atomic_add_affected_connectors(state, crtc);
diff --git a/drivers/gpu/drm/drm_backlight.c b/drivers/gpu/drm/drm_backlight.c
new file mode 100644
index 0000000000000..4251cb25079c3
--- /dev/null
+++ b/drivers/gpu/drm/drm_backlight.c
@@ -0,0 +1,515 @@
+// SPDX-License-Identifier: MIT
+/*
+ * DRM Backlight Helpers
+ * Copyright (c) 2014 David Herrmann
+ * Copyright (c) 2026 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/backlight.h>
+#include <linux/list.h>
+#include <linux/notifier.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+
+#include <drm/drm_backlight.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_device.h>
+#include <drm/drm_property.h>
+
+/**
+ * DOC: Backlight Devices
+ *
+ * Backlight devices have always been managed as a separate subsystem,
+ * independent of DRM. They are usually controlled via separate hardware
+ * interfaces than the display controller, so the split works out fine.
+ * However, backlight brightness is a property of a display, and thus a
+ * property of a DRM connector. We already manage DPMS states via connector
+ * properties, so it is natural to keep brightness control at the same place.
+ *
+ * This DRM backlight interface implements a generic per-connector LUMINANCE
+ * property. The core is backend-agnostic: it does not talk to any hardware
+ * itself, it only forwards luminance requests to a backend that a driver has
+ * linked. The backend is described by a &struct drm_backlight_funcs. Today the
+ * only backend is the backlight subsystem (&struct backlight_device), linked
+ * with drm_backlight_link(), but other backends (DDC/CI, MIPI-DCS, ...) can be
+ * added by providing a different set of operations without changing the core.
+ *
+ * A &struct drm_backlight is embedded in every &struct drm_connector and
+ * initialized by the DRM core (drm_backlight_connector_init()); drivers do not
+ * allocate it. Drivers link a backend once it is available by calling
+ * drm_backlight_link(); this creates the connector's LUMINANCE property with
+ * the backend's range. Passing NULL unlinks the backend. Hardware is only ever
+ * touched from a workqueue, so slow backends never stall an atomic commit.
+ */
+
+static LIST_HEAD(drm_backlight_list);
+static DEFINE_SPINLOCK(drm_backlight_lock);
+
+/* caller must hold @drm_backlight_lock */
+static bool __drm_backlight_is_linked(struct drm_backlight *b)
+{
+ lockdep_assert_held(&drm_backlight_lock);
+ /* a backlight is live while it is on @drm_backlight_list */
+ return !list_empty(&b->list);
+}
+
+/*
+ * Return the linked &backlight_device if the current backend is the backlight
+ * subsystem, or NULL otherwise. The legacy-sysfs takeover accounting only
+ * applies to that backend.
+ */
+static const struct drm_backlight_funcs drm_backlight_bd_funcs;
+
+static struct backlight_device *drm_backlight_bd(struct drm_backlight *b)
+{
+ if (b->funcs != &drm_backlight_bd_funcs)
+ return NULL;
+ return b->backend;
+}
+
+/* caller must hold @drm_backlight_lock */
+static void __drm_backlight_schedule(struct drm_backlight *b)
+{
+ lockdep_assert_held(&drm_backlight_lock);
+ if (__drm_backlight_is_linked(b))
+ schedule_work(&b->work);
+}
+
+static void __drm_backlight_worker(struct work_struct *w)
+{
+ struct drm_backlight *b = container_of(w, struct drm_backlight, work);
+ static char *ep[] = { "BACKLIGHT=1", NULL };
+ const struct drm_backlight_funcs *funcs;
+ bool send_uevent;
+ unsigned int v;
+
+ scoped_guard(spinlock, &drm_backlight_lock) {
+ send_uevent = b->changed;
+ b->changed = false;
+ v = b->set_value;
+ funcs = b->funcs;
+ }
+
+ /*
+ * The backend stays valid here: drm_backlight_do_unlink() clears
+ * @backend and drops its reference only after cancel_work_sync(), so an
+ * in-flight worker always sees a live backend.
+ */
+ if (funcs && funcs->set_luminance)
+ WARN_ON(funcs->set_luminance(b, v));
+
+ if (send_uevent && b->connector->kdev)
+ kobject_uevent_env(&b->connector->kdev->kobj, KOBJ_CHANGE, ep);
+}
+
+/* caller must hold @drm_backlight_lock */
+static void __drm_backlight_set(struct drm_backlight *b, unsigned int v)
+{
+ unsigned int max = 0;
+ bool can_disable = false;
+
+ lockdep_assert_held(&drm_backlight_lock);
+
+ if (!b->funcs || !b->funcs->get_range)
+ return;
+
+ b->funcs->get_range(b, &max, &can_disable);
+ if (!max)
+ return;
+
+ /* clamp to the backend maximum */
+ b->set_value = min(v, max);
+ __drm_backlight_schedule(b);
+}
+
+/* caller must hold @drm_backlight_lock */
+static void __drm_backlight_readback(struct drm_backlight *b, unsigned int v)
+{
+ struct drm_connector *connector = b->connector;
+ unsigned int max = 0;
+ bool can_disable = false;
+
+ lockdep_assert_held(&drm_backlight_lock);
+
+ if (!b->funcs || !b->funcs->get_range)
+ return;
+
+ b->funcs->get_range(b, &max, &can_disable);
+ if (!max)
+ return;
+
+ /*
+ * Reflect a hardware-side brightness change (firmware hotkeys, or a
+ * legacy sysfs write while not inhibited) back into the connector's
+ * committed luminance so a read-back returns the real value.
+ */
+ if (connector->state)
+ connector->state->luminance = min(v, max);
+}
+
+/*
+ * Create and attach the per-connector LUMINANCE property. Runs in process
+ * context (driver register path) with no spinlock held, since it allocates.
+ * The property's min/max are baked in for its lifetime, so it is never mutated
+ * afterwards (which would race &drm_mode_getproperty_ioctl); it is freed by
+ * drm_mode_config_cleanup().
+ */
+static int drm_backlight_create_property(struct drm_connector *connector,
+ unsigned int max, bool can_disable)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_property *prop;
+ unsigned int min = can_disable ? 0 : 1;
+
+ prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
+ "LUMINANCE", min, max);
+ if (!prop)
+ return -ENOMEM;
+
+ prop->is_luminance = true;
+ connector->luminance_property = prop;
+ drm_object_attach_property(&connector->base, prop, min);
+ if (connector->state)
+ connector->state->luminance = min;
+
+ return 0;
+}
+
+static void drm_backlight_do_unlink(struct drm_backlight *b)
+{
+ struct backlight_device *bd;
+ unsigned int clients;
+
+ /*
+ * Stop new work first, but leave @backend in place so an in-flight
+ * worker keeps a valid backend to operate on. Capture the linked
+ * backlight_device (if any) while @funcs is still set.
+ */
+ scoped_guard(spinlock, &drm_backlight_lock) {
+ if (!b->funcs)
+ return;
+ bd = drm_backlight_bd(b);
+ clients = b->luminance_clients;
+ b->funcs = NULL;
+ list_del_init(&b->list);
+ }
+
+ cancel_work_sync(&b->work);
+
+ scoped_guard(spinlock, &drm_backlight_lock) {
+ if (clients && bd)
+ atomic_sub(clients, &bd->drm_takeover);
+ b->backend = NULL;
+ }
+
+ backlight_device_unref(bd);
+}
+
+/**
+ * drm_backlight_connector_init - initialize a connector's embedded backlight
+ * @connector: connector to initialize
+ *
+ * Called by the DRM core from drm_connector_init(). Drivers never call this.
+ */
+void drm_backlight_connector_init(struct drm_connector *connector)
+{
+ struct drm_backlight *b = &connector->backlight;
+
+ b->connector = connector;
+ INIT_LIST_HEAD(&b->list);
+ INIT_WORK(&b->work, __drm_backlight_worker);
+}
+
+/**
+ * drm_backlight_connector_cleanup - tear down a connector's embedded backlight
+ * @connector: connector being cleaned up
+ *
+ * Called by the DRM core from drm_connector_cleanup(). The LUMINANCE property
+ * itself is freed by drm_mode_config_cleanup().
+ */
+void drm_backlight_connector_cleanup(struct drm_connector *connector)
+{
+ struct drm_backlight *b = &connector->backlight;
+
+ WARN_ON(__drm_backlight_is_linked(b));
+ WARN_ON(b->funcs);
+ WARN_ON(b->luminance_clients);
+}
+
+/**
+ * drm_backlight_unregister - unlink a connector's backlight on unregister
+ * @connector: connector being unregistered
+ *
+ * Called by the DRM core from drm_connector_unregister() as a safety net in
+ * case a driver did not unlink its backend itself.
+ */
+void drm_backlight_unregister(struct drm_connector *connector)
+{
+ drm_backlight_do_unlink(&connector->backlight);
+}
+
+/**
+ * drm_backlight_link - link a backlight device to a connector
+ * @connector: connector to modify
+ * @bd: backlight device to link, or NULL to unlink
+ *
+ * Establish the link between a connector's LUMINANCE property and a registered
+ * backlight_device. On the first link the connector's LUMINANCE property is
+ * created with the backend's range. Passing NULL unlinks any linked device.
+ *
+ * User-space cannot create or modify this link.
+ *
+ * Returns: 0 on success, or a negative error code if the property could not be
+ * created (in which case no backend is linked).
+ */
+int drm_backlight_link(struct drm_connector *connector,
+ struct backlight_device *bd)
+{
+ struct drm_backlight *b = &connector->backlight;
+ unsigned int max;
+ int ret;
+
+ if (!bd) {
+ drm_backlight_do_unlink(b);
+ return 0;
+ }
+
+ /* Retarget: drop any previously linked backend first. */
+ if (b->funcs)
+ drm_backlight_do_unlink(b);
+
+ max = bd->props.max_brightness;
+ if (max && !connector->luminance_property) {
+ ret = drm_backlight_create_property(connector, max, false);
+ if (ret)
+ return ret;
+ }
+
+ scoped_guard(spinlock, &drm_backlight_lock) {
+ b->funcs = &drm_backlight_bd_funcs;
+ b->backend = bd;
+ backlight_device_ref(bd);
+ list_add(&b->list, &drm_backlight_list);
+ if (b->luminance_clients)
+ atomic_add(b->luminance_clients, &bd->drm_takeover);
+ __drm_backlight_readback(b, bd->props.brightness);
+ b->changed = true;
+ __drm_backlight_set(b, bd->props.brightness);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_backlight_link);
+
+/**
+ * drm_backlight_get_device - get the backlight_device linked to a connector
+ * @connector: connector to query
+ *
+ * Returns the &backlight_device linked to @connector, or NULL if no backlight
+ * subsystem device is linked.
+ */
+struct backlight_device *drm_backlight_get_device(struct drm_connector *connector)
+{
+ guard(spinlock)(&drm_backlight_lock);
+ return drm_backlight_bd(&connector->backlight);
+}
+EXPORT_SYMBOL(drm_backlight_get_device);
+
+/**
+ * drm_backlight_inhibit_legacy - disable legacy sysfs control of the backend
+ * @connector: connector whose backlight should be inhibited
+ *
+ * Record that one more luminance-aware DRM client has taken over this
+ * connector's backlight. While any clients are recorded, writes to the linked
+ * backlight_device's legacy ``brightness`` sysfs attribute return ``-EBUSY``.
+ * The takeover follows the linked device if the link changes.
+ *
+ * Calls must be balanced with drm_backlight_uninhibit_legacy().
+ */
+void drm_backlight_inhibit_legacy(struct drm_connector *connector)
+{
+ struct drm_backlight *b = &connector->backlight;
+ struct backlight_device *bd;
+
+ guard(spinlock)(&drm_backlight_lock);
+ b->luminance_clients++;
+ bd = drm_backlight_bd(b);
+ if (bd)
+ atomic_inc(&bd->drm_takeover);
+}
+EXPORT_SYMBOL(drm_backlight_inhibit_legacy);
+
+/**
+ * drm_backlight_uninhibit_legacy - re-enable legacy sysfs control
+ * @connector: connector to uninhibit
+ *
+ * Balances a previous drm_backlight_inhibit_legacy() call.
+ */
+void drm_backlight_uninhibit_legacy(struct drm_connector *connector)
+{
+ struct drm_backlight *b = &connector->backlight;
+ struct backlight_device *bd;
+
+ guard(spinlock)(&drm_backlight_lock);
+ if (WARN_ON(b->luminance_clients == 0))
+ return;
+ b->luminance_clients--;
+ bd = drm_backlight_bd(b);
+ if (bd)
+ atomic_dec(&bd->drm_takeover);
+}
+EXPORT_SYMBOL(drm_backlight_uninhibit_legacy);
+
+/**
+ * drm_backlight_inhibit_legacy_all - inhibit legacy sysfs on every connector
+ * @dev: DRM device whose connectors should be inhibited
+ *
+ * Used when a client declares it is luminance-aware via
+ * DRM_CLIENT_CAP_LUMINANCE.
+ */
+void drm_backlight_inhibit_legacy_all(struct drm_device *dev)
+{
+ struct drm_connector_list_iter iter;
+ struct drm_connector *connector;
+
+ drm_connector_list_iter_begin(dev, &iter);
+ drm_for_each_connector_iter(connector, &iter)
+ drm_backlight_inhibit_legacy(connector);
+ drm_connector_list_iter_end(&iter);
+}
+EXPORT_SYMBOL(drm_backlight_inhibit_legacy_all);
+
+/**
+ * drm_backlight_uninhibit_legacy_all - reverse drm_backlight_inhibit_legacy_all()
+ * @dev: DRM device whose connectors should be uninhibited
+ */
+void drm_backlight_uninhibit_legacy_all(struct drm_device *dev)
+{
+ struct drm_connector_list_iter iter;
+ struct drm_connector *connector;
+
+ drm_connector_list_iter_begin(dev, &iter);
+ drm_for_each_connector_iter(connector, &iter)
+ drm_backlight_uninhibit_legacy(connector);
+ drm_connector_list_iter_end(&iter);
+}
+EXPORT_SYMBOL(drm_backlight_uninhibit_legacy_all);
+
+/**
+ * drm_backlight_set_luminance - request a luminance change on a connector
+ * @connector: connector to update
+ * @value: luminance value to apply
+ *
+ * Clamp @value to the backend range and schedule the hardware update. Safe to
+ * call from an atomic commit tail: the actual hardware access happens later
+ * from a workqueue.
+ */
+void drm_backlight_set_luminance(struct drm_connector *connector,
+ unsigned int value)
+{
+ guard(spinlock)(&drm_backlight_lock);
+ __drm_backlight_set(&connector->backlight, value);
+}
+EXPORT_SYMBOL(drm_backlight_set_luminance);
+
+/* backlight_device backend ------------------------------------------------- */
+
+static int drm_backlight_bd_set_luminance(struct drm_backlight *b,
+ unsigned int value)
+{
+ struct backlight_device *bd = b->backend;
+ int rc;
+
+ rc = backlight_set_brightness(bd, value, BACKLIGHT_UPDATE_DRM);
+ if (rc)
+ backlight_set_brightness(bd, U16_MAX, BACKLIGHT_UPDATE_DRM);
+
+ return rc;
+}
+
+static int drm_backlight_bd_get_luminance(struct drm_backlight *b,
+ unsigned int *value)
+{
+ struct backlight_device *bd = b->backend;
+
+ if (!bd)
+ return -ENODEV;
+ *value = bd->props.brightness;
+
+ return 0;
+}
+
+static void drm_backlight_bd_get_range(struct drm_backlight *b,
+ unsigned int *max, bool *can_disable)
+{
+ struct backlight_device *bd = b->backend;
+
+ *max = bd ? bd->props.max_brightness : 0;
+ /*
+ * A generic backlight_device gives no guarantee that a value of 0 turns
+ * the panel fully off, so keep 0 reserved for the DPMS-off sentinel and
+ * expose a 1..max range.
+ */
+ *can_disable = false;
+}
+
+static const struct drm_backlight_funcs drm_backlight_bd_funcs = {
+ .set_luminance = drm_backlight_bd_set_luminance,
+ .get_luminance = drm_backlight_bd_get_luminance,
+ .get_range = drm_backlight_bd_get_range,
+};
+
+static int drm_backlight_notify(struct notifier_block *self,
+ unsigned long event, void *data)
+{
+ struct backlight_device *bd = data;
+ struct drm_backlight *b;
+
+ switch (event) {
+ case BACKLIGHT_UNREGISTERED:
+ /*
+ * Unlink every connector using @bd. drm_backlight_do_unlink()
+ * sleeps (cancel_work_sync()), so it cannot run under the list
+ * spinlock; re-scan for the next match after each unlink.
+ */
+ for (;;) {
+ struct drm_backlight *found = NULL;
+
+ scoped_guard(spinlock, &drm_backlight_lock) {
+ list_for_each_entry(b, &drm_backlight_list, list) {
+ if (drm_backlight_bd(b) == bd) {
+ found = b;
+ break;
+ }
+ }
+ }
+ if (!found)
+ break;
+ drm_backlight_do_unlink(found);
+ }
+ break;
+ case BACKLIGHT_BRIGHTNESS_CHANGED:
+ scoped_guard(spinlock, &drm_backlight_lock) {
+ list_for_each_entry(b, &drm_backlight_list, list)
+ if (drm_backlight_bd(b) == bd)
+ __drm_backlight_readback(b, bd->props.brightness);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static struct notifier_block drm_backlight_notifier = {
+ .notifier_call = drm_backlight_notify,
+};
+
+int drm_backlight_init(void)
+{
+ return backlight_register_notifier(&drm_backlight_notifier);
+}
+
+void drm_backlight_exit(void)
+{
+ backlight_unregister_notifier(&drm_backlight_notifier);
+}
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 8b4baed060f3a..92ed0c1879d53 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -21,6 +21,7 @@
*/
#include <drm/drm_auth.h>
+#include <drm/drm_backlight.h>
#include <drm/drm_connector.h>
#include <drm/drm_drv.h>
#include <drm/drm_edid.h>
@@ -316,6 +317,8 @@ static int drm_connector_init_only(struct drm_device *dev,
drm_object_attach_property(&connector->base, config->prop_crtc_id, 0);
}
+ drm_backlight_connector_init(connector);
+
connector->debugfs_entry = NULL;
out_put_type_id:
if (ret)
@@ -773,6 +776,7 @@ void drm_connector_cleanup(struct drm_connector *connector)
struct drm_device *dev = connector->dev;
struct drm_display_mode *mode, *t;
+ drm_backlight_connector_cleanup(connector);
/* The connector should have been removed from userspace long before
* it is finally destroyed.
*/
@@ -944,6 +948,8 @@ EXPORT_SYMBOL(drm_connector_dynamic_register);
void drm_connector_unregister(struct drm_connector *connector)
{
mutex_lock(&connector->mutex);
+ drm_backlight_unregister(connector);
+
if (connector->registration_state != DRM_CONNECTOR_REGISTERED) {
mutex_unlock(&connector->mutex);
return;
@@ -1532,6 +1538,56 @@ EXPORT_SYMBOL(drm_hdmi_connector_get_output_format_name);
* Summarizing: Only set "DPMS" when the connector is known to be enabled,
* assume that a successful SETCONFIG call also sets "DPMS" to on, and
* never read back the value of "DPMS" because it can be incorrect.
+ * LUMINANCE:
+ * Atomic, per-connector range property for controlling the backlight
+ * brightness level of the connector's display. It provides unified access
+ * to the display backlight through the atomic modeset path, replacing the
+ * legacy sysfs interface for brightness control.
+ *
+ * The property value is an unsigned integer. Its valid range is baked in
+ * when a backlight backend is linked to the connector and reflects the
+ * backend's capabilities:
+ *
+ * - Range 1-N: Normal operation for a backend that cannot guarantee a full
+ * off state. 1 is the minimum *visible* brightness and N is the backend
+ * maximum. Drivers are expected never to program a duty cycle of 0 for a
+ * value of 1.
+ * - Range 0-N: Used when the backend can also fully turn the panel off, so
+ * 0 is a normal in-range value.
+ *
+ * Value 0 is always accepted, even when the advertised range starts at 1:
+ * it is the sentinel used to turn the backlight off when the connector is
+ * powered down (DPMS off). Turning the backlight off this way may power the
+ * panel down entirely; unlike programming a legacy sysfs duty cycle of 0,
+ * this can stop vblank/pageflip events until the connector is enabled
+ * again, so luminance-aware clients must not rely on such events while the
+ * connector is off.
+ *
+ * Connectors without a linked backend do not expose this property at all.
+ *
+ * For atomic drivers the value is stored in &drm_connector_state.luminance
+ * and applied to the hardware from the atomic commit path once the
+ * connector is enabled. When DPMS transitions to OFF the backlight is set
+ * to 0; when it transitions back to ON the committed luminance is restored.
+ * Reading the property returns the last committed value (or the hardware's
+ * current state for backends that support reading brightness back).
+ *
+ * The property is created by the DRM core when a driver links a backlight
+ * backend with drm_backlight_link(); drivers do not create it directly.
+ *
+ * Client Capability:
+ * User-space must set the DRM_CLIENT_CAP_LUMINANCE client capability
+ * to 1 before using this property. When this capability is enabled,
+ * the legacy sysfs backlight interface is inhibited to prevent
+ * conflicts between multiple clients trying to control the same
+ * backlight. This ensures that only luminance-aware clients control
+ * the backlight through the DRM atomic interface.
+ *
+ * Legacy clients that do not set this capability should continue
+ * using the sysfs interface (if available).
+ *
+ * Note: This property can be set through the MODE_ATOMIC ioctl as part of
+ * the atomic state.
* panel_type:
* Immutable enum property to indicate the type of connected panel.
* Possible values are "unknown" (default), "OLED", and "LCD".
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index c808958a2188e..c8aa6834c1f4e 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -42,6 +42,7 @@
#include <linux/xarray.h>
#include <drm/drm_accel.h>
+#include <drm/drm_backlight.h>
#include <drm/drm_bridge.h>
#include <drm/drm_cache.h>
#include <drm/drm_client_event.h>
@@ -1250,6 +1251,7 @@ static void drm_core_exit(void)
drm_privacy_screen_lookup_exit();
drm_panic_exit();
accel_core_exit();
+ drm_backlight_exit();
unregister_chrdev(DRM_MAJOR, "drm");
drm_debugfs_remove_root();
drm_sysfs_destroy();
@@ -1273,6 +1275,12 @@ static int __init drm_core_init(void)
drm_debugfs_init_root();
drm_debugfs_bridge_params();
+ ret = drm_backlight_init();
+ if (ret < 0) {
+ DRM_ERROR("Cannot initialize backlight interface\n");
+ goto error;
+ }
+
ret = register_chrdev(DRM_MAJOR, "drm", &drm_stub_fops);
if (ret < 0)
goto error;
diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c
index 366f6d8212425..36579e8a7c0e5 100644
--- a/drivers/gpu/drm/drm_mode_config.c
+++ b/drivers/gpu/drm/drm_mode_config.c
@@ -33,6 +33,7 @@
#include <drm/drm_print.h>
#include <drm/drm_colorop.h>
#include <linux/dma-resv.h>
+#include <drm/drm_backlight.h>
#include "drm_crtc_internal.h"
#include "drm_internal.h"
diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c
index ef4e923a87284..06cdfe046ca89 100644
--- a/drivers/gpu/drm/drm_sysfs.c
+++ b/drivers/gpu/drm/drm_sysfs.c
@@ -11,6 +11,7 @@
*/
#include <linux/acpi.h>
+#include <linux/backlight.h>
#include <linux/component.h>
#include <linux/device.h>
#include <linux/err.h>
@@ -27,6 +28,7 @@
#include <drm/drm_device.h>
#include <drm/drm_file.h>
#include <drm/drm_modes.h>
+#include <drm/drm_backlight.h>
#include <drm/drm_print.h>
#include <drm/drm_property.h>
#include <drm/drm_sysfs.h>
@@ -391,15 +393,33 @@ int drm_sysfs_connector_add(struct drm_connector *connector)
int drm_sysfs_connector_add_late(struct drm_connector *connector)
{
- if (connector->ddc)
- return sysfs_create_link(&connector->kdev->kobj,
- &connector->ddc->dev.kobj, "ddc");
+ struct backlight_device *bd = drm_backlight_get_device(connector);
+
+ if (connector->ddc) {
+ int ret = sysfs_create_link(&connector->kdev->kobj,
+ &connector->ddc->dev.kobj, "ddc");
+ if (ret)
+ return ret;
+ }
+
+ if (bd) {
+ int ret = sysfs_create_link(&connector->kdev->kobj,
+ &bd->dev.kobj, "backlight");
+ if (ret) {
+ if (connector->ddc)
+ sysfs_remove_link(&connector->kdev->kobj, "ddc");
+ return ret;
+ }
+ }
return 0;
}
void drm_sysfs_connector_remove_early(struct drm_connector *connector)
{
+ if (drm_backlight_get_device(connector))
+ sysfs_remove_link(&connector->kdev->kobj, "backlight");
+
if (connector->ddc)
sysfs_remove_link(&connector->kdev->kobj, "ddc");
}
diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h
index 4cfeec70d648a..8a166ead13c55 100644
--- a/include/drm/drm_atomic_helper.h
+++ b/include/drm/drm_atomic_helper.h
@@ -116,6 +116,8 @@ void drm_atomic_helper_commit_encoder_bridge_enable(struct drm_device *dev,
void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev,
struct drm_atomic_commit *old_state);
+void drm_atomic_helper_connector_apply_luminance(const struct drm_connector_state *conn_state);
+
int drm_atomic_helper_prepare_planes(struct drm_device *dev,
struct drm_atomic_commit *state);
void drm_atomic_helper_unprepare_planes(struct drm_device *dev,
diff --git a/include/drm/drm_backlight.h b/include/drm/drm_backlight.h
new file mode 100644
index 0000000000000..5cc1912babed9
--- /dev/null
+++ b/include/drm/drm_backlight.h
@@ -0,0 +1,158 @@
+/* SPDX-License-Identifier: MIT */
+#ifndef __DRM_BACKLIGHT_H__
+#define __DRM_BACKLIGHT_H__
+
+/*
+ * Copyright (c) 2014 David Herrmann <dh.herrmann at gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <linux/list.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+
+struct backlight_device;
+struct drm_backlight;
+struct drm_connector;
+struct drm_device;
+
+/**
+ * struct drm_backlight_funcs - backend operations for a DRM backlight
+ *
+ * A DRM backlight is backend-agnostic: the core forwards luminance requests to
+ * whatever backend a driver has linked. Today the only backend is the backlight
+ * subsystem (&struct backlight_device), but the same core can drive other
+ * backends (DDC/CI, MIPI-DCS, ...) by providing a different set of these
+ * operations without any change to the core.
+ *
+ * All callbacks are invoked from process context (a workqueue), never from an
+ * atomic commit tail or while holding a spinlock, so they are allowed to sleep.
+ */
+struct drm_backlight_funcs {
+ /**
+ * @set_luminance:
+ *
+ * Push @value (already clamped to the backend range) to the hardware.
+ * Returns 0 on success or a negative error code.
+ */
+ int (*set_luminance)(struct drm_backlight *b, unsigned int value);
+
+ /**
+ * @get_luminance:
+ *
+ * Read the current hardware luminance into @value. Returns 0 on success
+ * or a negative error code. May be NULL if the backend cannot be read
+ * back.
+ */
+ int (*get_luminance)(struct drm_backlight *b, unsigned int *value);
+
+ /**
+ * @get_range:
+ *
+ * Report the backend's maximum luminance in @max and whether the
+ * backend can turn the panel off at luminance 0 in @can_disable.
+ */
+ void (*get_range)(struct drm_backlight *b, unsigned int *max,
+ bool *can_disable);
+};
+
+/**
+ * struct drm_backlight - per-connector backlight state
+ *
+ * This structure is embedded in &struct drm_connector and initialized by the
+ * DRM core; drivers never allocate it. It becomes active once a driver links a
+ * backend with drm_backlight_link().
+ */
+struct drm_backlight {
+ /** @connector: connector this backlight belongs to */
+ struct drm_connector *connector;
+ /** @funcs: backend operations, or NULL while no backend is linked */
+ const struct drm_backlight_funcs *funcs;
+ /** @backend: backend private pointer (e.g. the &backlight_device) */
+ void *backend;
+ /** @list: entry on the global list of linked DRM backlights */
+ struct list_head list;
+ /** @work: deferred hardware update and uevent */
+ struct work_struct work;
+ /** @set_value: luminance value pending application by @work */
+ unsigned int set_value;
+ /**
+ * @luminance_clients: number of luminance-aware DRM clients that have
+ * taken this backlight over. While > 0, legacy sysfs writes to the
+ * linked backend return -EBUSY.
+ */
+ unsigned int luminance_clients;
+ /** @changed: a uevent is pending for @work to emit */
+ bool changed : 1;
+};
+
+#if IS_ENABLED(CONFIG_DRM_BACKLIGHT)
+
+int drm_backlight_init(void);
+void drm_backlight_exit(void);
+
+void drm_backlight_connector_init(struct drm_connector *connector);
+void drm_backlight_connector_cleanup(struct drm_connector *connector);
+void drm_backlight_unregister(struct drm_connector *connector);
+
+int drm_backlight_link(struct drm_connector *connector,
+ struct backlight_device *bd);
+struct backlight_device *drm_backlight_get_device(struct drm_connector *connector);
+
+void drm_backlight_inhibit_legacy(struct drm_connector *connector);
+void drm_backlight_uninhibit_legacy(struct drm_connector *connector);
+void drm_backlight_inhibit_legacy_all(struct drm_device *dev);
+void drm_backlight_uninhibit_legacy_all(struct drm_device *dev);
+
+void drm_backlight_set_luminance(struct drm_connector *connector,
+ unsigned int value);
+
+#else /* CONFIG_DRM_BACKLIGHT */
+
+static inline int drm_backlight_init(void) { return 0; }
+static inline void drm_backlight_exit(void) {}
+
+static inline void drm_backlight_connector_init(struct drm_connector *connector) {}
+static inline void drm_backlight_connector_cleanup(struct drm_connector *connector) {}
+static inline void drm_backlight_unregister(struct drm_connector *connector) {}
+
+static inline int drm_backlight_link(struct drm_connector *connector,
+ struct backlight_device *bd)
+{
+ return 0;
+}
+
+static inline struct backlight_device *
+drm_backlight_get_device(struct drm_connector *connector)
+{
+ return NULL;
+}
+
+static inline void drm_backlight_inhibit_legacy(struct drm_connector *connector) {}
+static inline void drm_backlight_uninhibit_legacy(struct drm_connector *connector) {}
+static inline void drm_backlight_inhibit_legacy_all(struct drm_device *dev) {}
+static inline void drm_backlight_uninhibit_legacy_all(struct drm_device *dev) {}
+
+static inline void drm_backlight_set_luminance(struct drm_connector *connector,
+ unsigned int value) {}
+
+#endif /* CONFIG_DRM_BACKLIGHT */
+
+#endif /* __DRM_BACKLIGHT_H__ */
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index a0cf0268de483..0535f20e7dba5 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -28,6 +28,7 @@
#include <linux/ctype.h>
#include <linux/hdmi.h>
#include <linux/notifier.h>
+#include <drm/drm_backlight.h>
#include <drm/drm_mode_object.h>
#include <drm/drm_util.h>
#include <drm/drm_property.h>
@@ -1290,6 +1291,11 @@ struct drm_connector_state {
* @drm_atomic_helper_connector_hdmi_check().
*/
struct drm_connector_hdmi_state hdmi;
+
+ /**
+ * @luminance: Luminance for the connector
+ */
+ unsigned int luminance;
};
struct drm_connector_hdmi_audio_funcs {
@@ -2526,6 +2532,20 @@ struct drm_connector {
* @cec: CEC-related data.
*/
struct drm_connector_cec cec;
+
+ /**
+ * @backlight: DRM backlight state, embedded and initialized by the DRM
+ * core. Becomes active once a driver links a backend with
+ * drm_backlight_link().
+ */
+ struct drm_backlight backlight;
+
+ /**
+ * @luminance_property: Per-connector range property controlling the
+ * connector's backlight luminance. Created with the backend's range
+ * when a backlight is linked; NULL while no backlight is linked.
+ */
+ struct drm_property *luminance_property;
};
#define obj_to_connector(x) container_of(x, struct drm_connector, base)
--
2.43.0
next prev parent reply other threads:[~2026-08-31 21:06 UTC|newest]
Thread overview: 25+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-31 21:05 [PATCH v7 00/12] Add support for a DRM backlight capability Mario Limonciello
2026-08-31 21:05 ` [PATCH v7 01/12] Revert "backlight: Remove notifier" Mario Limonciello
2026-08-31 22:02 ` sashiko-bot
2026-08-31 21:05 ` [PATCH v7 02/12] backlight: add kernel-internal backlight API Mario Limonciello
2026-08-31 22:11 ` sashiko-bot
2026-08-31 21:05 ` [PATCH v7 03/12] drm/property: add a per-connector luminance flag Mario Limonciello
2026-08-31 21:05 ` Mario Limonciello [this message]
2026-08-31 22:28 ` [PATCH v7 04/12] drm: add connector backlight (LUMINANCE) infrastructure sashiko-bot
2026-08-31 21:05 ` [PATCH v7 05/12] drm: add DRM_CLIENT_CAP_LUMINANCE Mario Limonciello
2026-08-31 22:37 ` sashiko-bot
2026-08-31 21:05 ` [PATCH v7 06/12] drm/amd/display: Pass up errors reading actual brightness Mario Limonciello
2026-08-31 22:49 ` sashiko-bot
2026-08-31 21:05 ` [PATCH v7 07/12] drm/amd: Indicate driver supports luminance Mario Limonciello
2026-08-31 21:05 ` [PATCH v7 08/12] drm/amd/display: use drm backlight Mario Limonciello
2026-08-31 23:12 ` sashiko-bot
2026-08-31 21:05 ` [PATCH v7 09/12] drm/bridge: auto-link panel backlight in bridge connector Mario Limonciello
2026-08-31 23:24 ` sashiko-bot
2026-08-31 21:05 ` [PATCH v7 10/12] drm/xe: Indicate support for luminance on the connector Mario Limonciello
2026-08-31 23:30 ` sashiko-bot
2026-08-31 21:05 ` [PATCH v7 11/12] drm/i915: " Mario Limonciello
2026-08-31 21:05 ` [PATCH v7 12/12] drm/i915/display: use drm backlight Mario Limonciello
2026-08-31 23:55 ` sashiko-bot
2026-08-31 21:12 ` ✗ CI.checkpatch: warning for Add support for a DRM backlight capability (rev2) Patchwork
2026-08-31 21:13 ` ✗ CI.KUnit: failure " Patchwork
2026-09-01 0:19 ` [PATCH v7 00/12] Add support for a DRM backlight capability Mario Limonciello
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260831210531.2334221-5-mario.limonciello@amd.com \
--to=mario.limonciello@amd.com \
--cc=airlied@gmail.com \
--cc=alexander.deucher@amd.com \
--cc=amd-gfx@lists.freedesktop.org \
--cc=dh.herrmann@gmail.com \
--cc=dri-devel@lists.freedesktop.org \
--cc=hansg@kernel.org \
--cc=harry.wentland@amd.com \
--cc=intel-gfx@lists.freedesktop.org \
--cc=intel-xe@lists.freedesktop.org \
--cc=maarten.lankhorst@linux.intel.com \
--cc=mripard@kernel.org \
--cc=simona@ffwll.ch \
--cc=superm1@kernel.org \
--cc=tzimmermann@suse.de \
--cc=xaver.hugl@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox